Lager库使用介绍

本文详细介绍了如何使用Lager库来管理和扩展Erlang应用的日志,包括基础使用、配置、日志扩展、日志格式化、过载保护等。Lager提供了一套完善的日志解决方案,支持日志等级设定、文件轮转、自定义backend等功能,且能与OTP 21.0后的Logger模块兼容。

使用Lager库来管理erlang应用的日志模块

erlang本身已经提供了基础的日志模块error_logger还有OTP 21.0之后的Logger模块,这些模块在项目比较轻量,日志可控,功能简单的情况下完全可以满足需要,但是Lager提供了一个更加成熟的完整的解决方案,从日志文件的配置,等级设定,到跟踪,日志文件轮转提供了完整的生态接口,通过Lager几乎可以满足所有的日志需求,而且不需要考虑erlang版本变更带来的问题。

基础使用

Lager的ReadMe已经描述了最基础的使用范例,简单来说需要以下几步:

  1. 需要在编译选项中加入{parse_transform, lager_transform},因为Lager是通过parse_transform来完成了lager:info这种函数的具体实现,不像传统的日志模块,直接调用,所有的module都必须在该编译选项下才能调用lager

对于使用rebar的项目,只需要在rebar.config中修改或者增加erl_opts项,加入{parse_transform, lager_transform}即可,MakeFile同理
如果只给单个文件提供日志功能,可以在erl中加入编译选项声明-compile([{parse_transform, lager_transform}]).

不要在自己项目的基础模块中将lager调用包装成函数,然后其他模块再调用函数,这样会影响后续lager的功能,只能通过宏来完成,宏到时候会进行代码替换,确保lager功能都是直接原生调用

  1. 编写测试代码,加入lager调用,创建lage_test.erl 把下列代码写入到文件中,使用带编译项的第一步配置进行编译
-module(lager_test).
%% API
-export([test/0]).
test() ->
    application:ensure_all_started(lager),
    lager:error("error msg"),
    lager:warning("warning msg"),
    lager:debug("debug msg"),
    lager:info("info msg"),
    lager:critical("critical msg"),
    erlang:spawn_link(fun() -> erlang:throw("this is test") end).
  1. 启动命令行,执行lager_test:test(). 命令行启动的时候通过-pa把lager加入到代码加载路径

执行后在当前目录的log目录下会生成error.logconsole.log,因为lager的默认日志handle配置就是

-define(DEFAULT_HANDLER_CONF,
        [{lager_console_backend, [{level, info}]},
         {lager_file_backend,
          [{file, "log/error.log"}, {level, error},
           {size, 10485760}, {date, "$D0"}, {count, 5}]
         },
         {lager_file_backend,
          [{file, "log/console.log"}, {level, info},
           {size, 10485760}, {date, "$D0"}, {count, 5}]
         }
        ]).

lager提供的日志等级定义在

-define(LEVELS,
    [debug, info, notice, warning, error, critical, alert, emergency, none]).

列表中的值都可以使用,lager:LEVEL进行调用

配置

Lager配置主要通过配置Lager的application环境变量来实现,也就是他的一部分基础配置来自于lager.app,也就是lager.app.src中

{env, [
            %% Note: application:start(lager) overwrites previously defined environment variables
            %%       thus declaration of default handlers is done at lager_app.erl

            %% What colors to use with what log levels
            {colored, false},
            {colors, [
              {debug,     "\e[0;38m" },
              {info,      "\e[1;37m" },
              {notice,    "\e[1;36m" },
              {warning,   "\e[1;33m" },
              {error,     "\e[1;31m" },
              {critical,  "\e[1;35m" },
              {alert,     "\e[1;44m" },
              {emergency, "\e[1;41m" }

            ]},

            %% Whether to write a crash log, and where. False means no crash logger.
            {crash_log, "log/crash.log"},
            %% Maximum size in bytes of events in the crash log - defaults to 65536
            {crash_log_msg_size, 65536},
            %% Maximum size of the crash log in bytes, before its rotated, set
            %% to 0 to disable rotation - default is 0
            {crash_log_size, 10485760},
            %% What time to rotate the crash log - default is no time
            %% rotation. See the README for a description of this format.
            {crash_log_date, "$D0"},
            %% Number of rotated crash logs to keep, 0 means keep only the
            %% current one - default is 0
            {crash_log_count, 5},
            %% Crash Log Rotator Module - default is lager_rotator_default
            {crash_log_rotator, lager_rotator_default},
            %% Whether to redirect error_logger messages into the default lager_event sink - defaults to true
            {error_logger_redirect, true},
            %% How many messages per second to allow from error_logger before we start dropping them
            {error_logger_hwm, 50},
            %% How big the gen_event mailbox can get before it is
            %% switched into sync mode.  This value only applies to
            %% the default sink; extra sinks can supply their own.
            {async_threshold, 20},
            %% Switch back to async mode, when gen_event mailbox size
            %% decrease from `async_threshold' to async_threshold -
            %% async_threshold_window. This value only applies to the
            %% default sink; extra sinks can supply their own.
            {async_threshold_window, 5}
        ]},

这部分的内容都是可以通过beam启动的时候通过设置配置文件来覆盖,类似于 erl -config app.config,而app.config中内容也就是对lager的env进行扩展,一份简单的内容如下:

{lager, [
  {log_root, "/var/log/hello"},
  {handlers, [
    {lager_console_backend, [{level, info}]},
    {lager_file_backend, [{file, "error.log"}, {level, error}]},
    {lager_file_backend, [{file, "console.log"}, {level, info}]}
  ]}
]}.

通过log_root指定了日志文件所在的目录,如果是相对路径就是相对的Lager启动的时候pwd()函数返回的路径
handles就是日志处理模块指定,也是配置的核心内容,上述配置中启用了1个lager_console_backend接收器用来将日志输出到命令行中,两个将日志输出到文件中的lager_file_backend分别将error级别以上的日志和info级别以上的日志分别写入到"error.log"和"console.log"中

日志扩展

上一步中讲到的日志都是基于内建的lager_event事件来进行的配置,因为类似lager:info这种调用最终调用的就是lager:dispatch_log/6,也就是给lager_event发送一个event事件,除了这个默认的日志逻辑,可以额外扩展自己的单独的日志处理
要创建自己的日志处理需要以下两步:

  1. 在编译选项中
<think>首先,用户的问题是关于解决一个错误:"ValueError: Bounding box of ROI is larger than image space"。我需要根据系统级指令和用户可见层的要求来构建回答。 系统级指令: 1. 所有行内数学表达式必须使用 $...$ 格式。 2. 独立公式使用 $$...$$ 格式并单独成段。 3. LaTeX语法正确。 4. 使用中文回答。 5. 生成相关问题。 6. 回答中引用的段落末尾自然地添加引用标识,例如 [^1]。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 - 参考站内引用,但用户提供的引用是示例,我需要基于这些引用构建回答。 用户提供的引用: - [^1]: 描述了一个错误:ROI 的边界框大于图像空间。 - [^2]: 建议在 MONAI 中使用填充来处理裁剪操作。 - [^3]: 另一个错误,关于文件名无效。 - [^4]: 关于 wandb 的配置。 - [^5]: 关于 ArcGIS Server 的边界框和图像大小关系。 用户的问题聚焦于解决 "ValueError: Bounding box of ROI is larger than image space" 错误。所以,我应该从引用中提取相关信息。 关键引用: - [^1] 直接描述了这个错误。 - [^2] 提供解决方案:在 MONAI 中使用填充和调整裁剪操作。 - [^5] 讨论了边界框和图像大小的关系,但可能不直接相关。 基于 [^1] 和 [^2],解决方案包括: 1. 确保 ROI 边界框在图像范围内。 2. 使用填充或调整裁剪大小。 3. 在代码中添加错误处理。 我的回答应该: - 解释错误原因。 - 提供逐步解决方案。 - 使用中文。 - 结构清晰:介绍、原因、解决方案、代码示例(如果适用)、总结。 - 生成相关问题。 结构: 1. **介绍错误**:简要描述错误及其常见场景。 2. **错误原因**:解释为什么发生这个错误。 3. **解决方案**:提供具体步骤。 - 参考 [^2],建议使用填充和调整裁剪。 - 可能包括代码示例。 4. **预防措施**:如何避免类似错误。 5. **生成相关问题**:在结尾添加相关问题。 代码示例:如果适用,使用 Python 或其他语言展示解决方案。 确保回答真实可靠:基于提供的引用,但不要编造信息。引用是示例,所以我应该基于它们构建逻辑回答。 最终回答: - 开头:解释错误。 - 主体:逐步解决方案。 - 结尾:相关问题。 生成相关问题:基于错误主题,如: 1. 如何正确处理 ROI 和图像大小不匹配? 2. 在深度学习中,有哪些可以自动处理边界框问题? 3. 如何优化图像处理流程以避免此类错误?</think>### 解决 "ValueError: Bounding box of ROI is larger than image space" 错误的方法 这个错误通常出现在图像处理或医学影像分析(如使用 PyRadiomics、MONAI 等)时,表示您定义的感兴趣区域(ROI)的边界框(Bounding Box)超出了图像的物理尺寸范围。例如,引用[^1] 中描述:"ROI bounds (image coordinate space) ((-86.5, 85.5, 80.5), (-114.5, 46.5, 121.5)) Image Size (240, 240, 155)",这会导致算法无法正确执行裁剪或分析操作。错误的核心原因是 ROI 的坐标范围(如最小值和最大值)超过了图像的实际宽度、高度或深度。下面我将逐步解释错误原因,并提供详细的解决方案。解决方案基于常见实践和参考引用(如 [^2]),确保可靠性和可操作性。 #### 错误原因分析 1. **ROI 定义错误**:ROI 的边界框坐标(例如 `(x_min, y_min, z_min)` 到 `(x_max, y_max, z_max)`)计算有误,导致部分坐标值小于 0 或大于图像尺寸(如宽度 $W$、高度 $H$、深度 $D$)。数学上,这违反了约束条件:$0 \leq x_{\text{min}} \leq x_{\text{max}} \leq W$ 等。 2. **图像与掩码不匹配**:引用[^1] 指出:"Image/Mask geometry mismatch",即图像和掩码(Mask)文件的空间坐标系不一致,例如掩码文件可能来自不同来源或未对齐。 3. **裁剪操作不当**:在深度学习框架(如 MONAI)中,随机裁剪等预处理步骤可能生成过大的 ROI。引用[^2] 提到:"The size of the proposed random crop ROI is larger than the image size",这常见于使用 `RandCropByPosNegLabeld` 等函数时未处理小尺寸图像。 4. **单位转换问题**:引用[^5] 暗示了坐标单位(如度与米)的转换错误,但本错误更常见于像素坐标系下的不一致。 #### 逐步解决方案 遵循以下步骤可解决此问题。解决方案基于引用[^1] 和 [^2] 的实践经验,并适配常见如 PyRadiomics、MONAI 或 OpenCV。 ##### 步骤 1: 验证并修正 ROI 边界框 - **检查坐标范围**:使用代码检查 ROI 边界框是否在图像范围内。假设图像尺寸为 $(W, H, D)$,ROI 边界框为 $(x_{\text{min}}, y_{\text{min}}, z_{\text{min}})$ 到 $(x_{\text{max}}, y_{\text{max}}, z_{\text{max}})$,确保: $$ 0 \leq x_{\text{min}} \leq x_{\text{max}} \leq W, \quad 0 \leq y_{\text{min}} \leq y_{\text{max}} \leq H, \quad 0 \leq z_{\text{min}} \leq z_{\text{max}} \leq D $$ 如果超出范围,手动调整坐标(例如,将负值设为 0,或将最大值设为图像尺寸)。 - **代码示例**(Python 中使用 NumPy): ```python import numpy as np def adjust_roi(roi_bounds, image_size): """ 调整 ROI 边界框使其在图像范围内。 :param roi_bounds: 元组 ((x_min, y_min, z_min), (x_max, y_max, z_max)) :param image_size: 元组 (W, H, D) :return: 修正后的 ROI 边界框 """ (x_min, y_min, z_min), (x_max, y_max, z_max) = roi_bounds W, H, D = image_size # 约束坐标到 [0, image_size] 范围内 x_min = max(0, min(x_min, W)) y_min = max(0, min(y_min, H)) z_min = max(0, min(z_min, D)) x_max = max(0, min(x_max, W)) y_max = max(0, min(y_max, H)) z_max = max(0, min(z_max, D)) return ((x_min, y_min, z_min), (x_max, y_max, z_max)) # 示例使用 image_size = (240, 240, 155) # 引用[^1] 中的图像尺寸 roi_bounds = ((-86.5, 85.5, 80.5), (-114.5, 46.5, 121.5)) # 错误示例 adjusted_roi = adjust_roi(roi_bounds, image_size) print(f"修正后的 ROI: {adjusted_roi}") # 输出应确保所有坐标在范围内 ``` ##### 步骤 2: 处理图像与掩码的不匹配 - **重新对齐图像和掩码**:确保图像和掩码文件使用相同的坐标系和分辨率。引用[^1] 提到:"Case-1_Image: ... Case-1_Mask: ...",建议使用工具如 ITK-SNAP 或 SimpleITK 检查文件头信息(如 spacing 和 origin)。 - **强制重采样**:在代码中,使用函数重采样掩码以匹配图像几何。例如,在 PyRadiomics 中: ```python import SimpleITK as sitk def resample_mask_to_image(image_path, mask_path): image = sitk.ReadImage(image_path) mask = sitk.ReadImage(mask_path) resampled_mask = sitk.Resample(mask, image, sitk.Transform(), sitk.sitkNearestNeighbor, 0.0, mask.GetPixelID()) return resampled_mask # 示例使用 resampled_mask = resample_mask_to_image("t1ce_corrected.nii.gz", "mask.nii.gz") # 引用[^1] 的文件名 sitk.WriteImage(resampled_mask, "resampled_mask.nii.gz") ``` ##### 步骤 3: 修改预处理管道(针对深度学习框架) - **添加填充(Padding)或裁剪(Crop)**:引用[^2] 提供了关键方案:在 MONAI 等的 transform 中加入填充操作,以处理小尺寸图像。使用 `ResizeWithPadOrCropd` 确保 ROI 尺寸不超过图像。 - 核心思路:当裁剪尺寸大于图像时,自动填充边缘值(如常数 0)。 - 代码示例(MONAI 框架): ```python from monai.transforms import RandCropByPosNegLabeld, ResizeWithPadOrCropd transforms = [ RandCropByPosNegLabeld( keys=["image", "label"], label_key="label", spatial_size=(96, 96, 96), # 目标裁剪尺寸 pos=1, neg=1, num_samples=4, allow_smaller=True # 允许图像小于裁剪尺寸 ), ResizeWithPadOrCropd( # 添加填充或裁剪以匹配尺寸 keys=["image", "label"], spatial_size=(96, 96, 96), mode='constant' # 填充常数(通常为 0) ) ] # 应用此 transform 序列可避免错误 ``` - **参数说明**: - `allow_smaller=True`:允许图像尺寸小于 `spatial_size`。 - `mode='constant'`:填充边缘值为常数(可自定义)。 - 这解决了引用[^2] 中的问题:"将切片尺寸小于裁剪尺寸时,自动 padding"。 ##### 步骤 4: 预防性措施 - **输入验证**:在代码开头添加检查,确保 ROI 和图像尺寸兼容。 - **单位一致性**:如果涉及物理单位(如引用[^5]),使用转换公式:$ \text{像素坐标} = \frac{\text{物理坐标}}{\text{spacing}} $,其中 spacing 是图像元数据。 - **错误处理**:在关键步骤添加 try-except 块,捕获异常并记录日志。 ```python try: # 执行裁剪或分析操作 except ValueError as e: print(f"错误捕获: {e}. 正在调整 ROI...") # 调用步骤 1 的调整函数 ``` #### 总结 通过以上步骤,您可以有效解决 "Bounding box of ROI is larger than image space" 错误。核心是确保 ROI 坐标在图像范围内,并使用填充或重采样处理不匹配。如果问题持续,检查图像文件元数据(如使用 `sitk.GetSize()`)或参考文档(如 PyRadiomics 或 MONAI)。引用[^1] 和 [^2] 提供了实际案例和解决方案,但需根据您的具体上下文调整参数[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值