python:在程序执行过程中处理模块的日志输出

python: Handling log output of module during program execution

提问人:squarespiral 提问时间:2/18/2023 最后编辑:vvvvvsquarespiral 更新时间:4/3/2023 访问量:90

问:

我正在我的脚本中设置一个记录器,如底部所示。这适用于我的目的,并记录我的日志消息以及我用于 stdout 和日志文件的任何模块的日志消息。__main__

在程序执行期间,我使用的模块调用在某些情况下会引发错误,并生成以下日志输出:xarray.open_dataset(file, engine="cfgrib")

2023-02-18 10:02:06,731 cfgrib.dataset ERROR skipping variable: paramId==228029 shortName='i10fg'
Traceback (most recent call last):
...

如何在程序执行期间访问此输出?

cfgrib 模块中引发的错误在那里得到优雅处理,程序可以继续执行,但我的程序逻辑要求我访问错误消息,特别是为了详尽地处理错误而说的部分。shortName='i10fg'

以下是我的记录器的设置方式:

def init_log():
    """initialize logging

    returns logger using log settings from the config file (settings.toml)
    """
    # all settings from a settings file with reasonable defaults
    lg.basicConfig(
        level=settings.logging.log_level,
        format=settings.logging.format,
        filemode=settings.logging.logfile_mode,
        filename=settings.logging.filename,
    )

    mylogger = lg.getLogger(__name__)
    stream = lg.StreamHandler()
    mylogger.addHandler(stream)

    clg.install(
        level=settings.logging.log_level,
        logger=mylogger,
        fmt="%(asctime)s %(levelname)s:\t%(message)s",
    )
    return mylogger

# main
log = init_log()
log.info('...reading files...')

我浏览了 python 日志记录文档和食谱。虽然这包含大量关于如何出于各种目的修改日志记录的示例,但我找不到在程序执行期间访问和响应日志消息的示例。

我的日志中的异常如下所示:

2023-02-20 12:22:37,209 cfgrib.dataset ERROR skipping variable: paramId==228029 shortName='i10fg'
Traceback (most recent call last):
  File "/home/foo/projects/windgrabber/.venv/lib/python3.10/site-packages/cfgrib/dataset.py", line 660, in build_dataset_components
    dict_merge(variables, coord_vars)
  File "/home/foo/projects/windgrabber/.venv/lib/python3.10/site-packages/cfgrib/dataset.py", line 591, in dict_merge
    raise DatasetBuildError(
cfgrib.dataset.DatasetBuildError: key present and new value is different: key='time' value=Variable(dimensions=('time',), data=array([1640995200, 1640998800, 1641002400, ..., 1672520400, 1672524000,
       1672527600])) new_value=Variable(dimensions=('time',), data=array([1640973600, 1641016800, 1641060000, 1641103200, 1641146400,
       1641189600, 1641232800, 1641276000, 1641319200, 1641362400,

由于某种原因,我无法直接捕获异常:

...
import sys
from cfgrib.dataset import DatasetBuildError
...

    try:
        df = xr.open_dataset(file, engine="cfgrib").to_dataframe()
        # triggering error manually like with the two lines below works as expected
        # raise Exception()
        # raise DatasetBuildError()
    except Exception as e:
        print('got an Exception')
        print(e)
        print(e.args)
    except BaseException as e:
        print('got a BaseException')
        print(e.args)
    except DatasetBuildError as e:
        print(e)
    except:
        print('got any and all exception')
        type, value, traceback = sys.exc_info()
        print(type)
        print(value)
        print(traceback)

除非我取消注释手动引发异常的两行,否则永远不会触发子句,尽管我可以在日志中看到事件。exceptDatabaseBuildError

不确定这是否有任何影响,但是虽然我可以在我的文件日志中看到上面引用的异常,但它没有打印到 stdout。

编辑:

正如@dskrypa在评论中建议的那样,传递给函数调用确实允许我处理异常。不幸的是,这让我陷入了困境:电话非常昂贵(需要很长时间)。它返回一堆变量的时间序列。当异常发生时,调用会返回它所能返回的任何内容,我必须在单独的调用中获取其余变量。errors='raise'xr.open_dataset()

现在,当我提出错误并且调用失败时,它不会返回任何时间序列,我必须再进行 2 次调用才能恢复(一次用于获取第一组变量,同时忽略异常,第二次用于获取其余部分)。

相反,当我没有提出错误时(记录了异常,但调用已完成部分 = 原始问题),我必须在另一个语句中重复每个调用,并假设它可能没有获得所有变量/时间序列。不幸的是,这种例外发生在大约一半的情况下。try

我需要的是一种在运行时确定是否记录了异常的方法,同时仍然允许调用继续并传递 var 的前半部分(如果没有异常,则提供所有 var)。只有当有异常日志时,我才会再次调用以获取其余变量。这基本上又是原来的问题陈述。

看起来我可能不得不重构整个事情,将 grib 转换为 netcdf(据报道更快),然后从那里读取。希望即使我遇到同样的异常,它在时间上的影响也会较小。

Python 异常 错误日志记录 python-logging

评论

0赞 dskrypa 2/19/2023
为什么需要在生成日志的程序中对日志做出反应?您应该将调用包装在 / 中,并在发生错误的地方对错误做出反应。如果出于某种原因,您需要堆栈跟踪作为字符串,则可以使用 traceback.format_exc(),它提供与日志中相同的回溯输出。xarray.open_dataset(file, engine="cfgrib")try:except ... as e:
0赞 squarespiral 2/20/2023
谢谢@dskrypa!我在问题中添加了一个关于尝试捕获异常的部分。我可以手动引发异常以触发 except 子句 (),但在正常程序执行期间,即使我在日志文件中看到异常,except 子句也永远不会被触发。raise DatasetBuildError
0赞 dskrypa 2/21/2023
查看源代码后,现在更清楚了 - 库不会传播异常,而是记录异常本身。事后看来,日志中确实表明......我建议您在打电话时重试,尽管这可能不会给您或...namecfgrib.dataseterrors='raise'open_datasetparam_idshortName
0赞 squarespiral 2/25/2023
errors='raise'有效,感谢您找到它!但不幸的是,让我处于第 1 个方块:提出错误意味着,我必须对函数进行 2 次额外的调用 - 我将在问题中添加详细信息。

答: 暂无答案