簡體   English   中英

使用dictConfig的Python日志記錄,使用兩個流處理程序以不同的消息級別發布到stdout和stderr

[英]Python logging with dictConfig using two streamhandlers posting to stdout and stderr at different message levels

我正在嘗試實現這些帖子中的操作, 在stdout和stderr之間進行Python日志記錄拆分

Python日志記錄在stdout和stderr之間拆分

但是使用dictConfig,到目前為止沒有成功。 這是我的代碼和配置:

這是我用於生成標准輸出日志的配置

# logging.json
{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "console": {
            "format": "%(asctime)s: %(levelname)s: %(pathname)s: \n%(message)s\n"
        },
        "file": {
            "format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: \n%(message)s\n"
        }
    },
    "handlers": {
            "console": {
                "level": "INFO",
                "formatter": "console",
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stdout"
            },
            "file": {
                "level": "DEBUG",
                "formatter": "file",
                "class": "logging.FileHandler",
                "encoding": "utf-8",
                "filename": "app.log"
            }
    },
    "loggers": {
        "": {
            "handlers": ["console", "file"],
            "level": "INFO",
            "propagate": false
        },
        "default": {
            "handlers": ["console", "file"],
            "level": "DEBUG",
            "propagate": false
        }

    }
}

這是用於stderr日志的

# logging_stderr.json

{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "console": {
            "format": "%(asctime)s: %(levelname)s: %(pathname)s: \n%(message)s\n"
        },
        "file": {
            "format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: \n%(message)s\n"
        }
    },
    "handlers": {
            "console": {
                "level": "WARN",
                "formatter": "console",
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stderr"
            },
            "file": {
                "level": "DEBUG",
                "formatter": "file",
                "class": "logging.FileHandler",
                "encoding": "utf-8",
                "filename": "wusync.log"
            }
    },
    "loggers": {
        "": {
            "handlers": ["console", "file"],
            "level": "INFO",
            "propagate": false
        },
        "default": {
            "handlers": ["console", "file"],
            "level": "DEBUG",
            "propagate": false
        }

    }
}

然后在我的代碼中,我有一個輔助函數。

import logging
import logging.config
import os
from os.path import abspath, basename, dirname, exists, isfile, isdir, join, split, splitext
import sys

_script_dir = abspath(dirname(__file__))


def build_default_logger(logdir, name=None, cfgfile=None):
    """
    Create per-file logger and output to shared log file.
    - If found config file under script folder, use it;
    - Otherwise use default config: save to /project_root/project_name.log.
    - 'filename' in config is a filename; must prepend folder path to it.
    :logdir: directory the log file is saved into.
    :name: basename of the log file,
    :cfgfile: config file in the format of dictConfig.
    :return: logger object.
    """
    try:
        os.makedirs(logdir)
    except:
        pass

    cfg_file = cfgfile or join(_script_dir, 'logging.json')
    logging_config = None
    try:
        if sys.version_info.major > 2:
            with open(cfg_file, 'r', encoding=TXT_CODEC, errors='backslashreplace', newline=None) as f:
                text = f.read()
        else:
            with open(cfg_file, 'rU') as f:
                text = f.read()
        # Add object_pairs_hook=collections.OrderedDict hook for py3.5 and lower.
        logging_config = json.loads(text, object_pairs_hook=collections.OrderedDict)
        logging_config['handlers']['file']['filename'] = join(logdir, logging_config['handlers']['file']['filename'])
    except Exception:
        filename = name or basename(basename(logdir.strip('\\/')))
        log_path = join(logdir, '{}.log'.format(filename))
        logging_config = {
            "version": 1,
            "disable_existing_loggers": False,
            "formatters": {
                "console": {
                    "format": "%(asctime)s: %(levelname)s: %(pathname)s: \n%(message)s\n"
                },
                "file": {
                    "format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: \n%(message)s\n"
                }
            },
            "handlers": {
                    "console": {
                        "level": "INFO",
                        "formatter": "console",
                        "class": "logging.StreamHandler",
                        "stream": "ext://sys.stdout"
                    },
                    "file": {
                        "level": "DEBUG",
                        "formatter": "file",
                        "class": "logging.FileHandler",
                        "encoding": "utf-8",
                        "filename": log_path
                    }
            },
            "loggers": {
                "": {
                    "handlers": ["console", "file"],
                    "level": "INFO",
                    "propagate": True
                },
                "default": {
                    "handlers": ["console", "file"],
                    "level": "WARN",
                    "propagate": True
                }
            }
        }
    if name:
        logging_config['loggers'][name] = logging_config['loggers']['default']
    logging.config.dictConfig(logging_config)
    return logging.getLogger(name or 'default')

終於在我的日常工作中

# main.py

_script_dir = abspath(dirname(__file__))
_logger = util.build_default_logger(logdir='temp', cfgfile=abspath(join(_script_dir, 'logging_stderr.json')))
_stderr_logger = util.build_default_logger(logdir='temp', name='myerrorlog', cfgfile=abspath(join(_script_dir, 'logging_stderr.json')))


...

_logger.info('my info')
_stderr_logger.warning('my warning')

我希望該信息將通過stdout顯示,並通過stderr警告。 但是結果是,只有警告,信息完全消失了。

如果僅使用_logger ,那么所有內容都會通過stdout進行處理。

我哪里錯了? dictconfig僅支持一個流處理程序嗎?

我通過使用過濾器解決了自己的問題。

這篇文章對我有幫助:

使用dictConfig在python的日志記錄級別安裝過濾器

我基本上想將INFO發送到stdout,將WARNING-to-CRITICAL發送到stderr。 這意味着要有一個范圍,兩端都為處理程序定義。 處理程序的level屬性僅定義低端。

現在過濾救援。 我最終使用了此配置:

{
    "version": 1,
    "disable_existing_loggers": false,
    "filters": {
        "infofilter": {
          "()": "util.LowPassFilter",
          "level": 20
        },
        "warnfilter": {
          "()": "util.HighPassFilter",
          "level": 30
        }
    },
    "formatters": {
        "console": {
            "format": "%(asctime)s: %(levelname)s: %(pathname)s: \n%(message)s\n"
        },
        "file": {
            "format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: \n%(message)s\n"
        }
    },
    "handlers": {
            "console": {
                "level": "INFO",
                "formatter": "console",
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stdout",
                "filters": ["infofilter"]
            },
            "console_err": {
                "level": "WARN",
                "formatter": "console",
                "class": "logging.StreamHandler",
                "stream": "ext://sys.stderr",
                "filters": ["warnfilter"]
            },
            "file": {
                "level": "DEBUG",
                "formatter": "file",
                "class": "logging.FileHandler",
                "encoding": "utf-8",
                "filename": "app.log"
            }
    },
    "loggers": {
        "": {
            "handlers": ["console", "console_err", "file"],
            "level": "INFO",
            "propagate": false
        },
        "default": {
            "handlers": ["console", "console_err", "file"],
            "level": "DEBUG",
            "propagate": true
        }
    }
}

和過濾器

class LowPassFilter(object):
    """
    Logging filter: Show log messages below input level.
    - CRITICAL = 50
    - FATAL = CRITICAL
    - ERROR = 40
    - WARNING = 30
    - WARN = WARNING
    - INFO = 20
    - DEBUG = 10
    - NOTSET = 0
    """
    def __init__(self, level):
        self.__level = level

    def filter(self, log):
        return log.levelno <= self.__level


class HighPassFilter(object):
    """Logging filter: Show log messages above input level."""
    def __init__(self, level):
        self.__level = level

    def filter(self, log):
        return log.levelno >= self.__level

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM