0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

細かい事をあんまり気にしない感じのPython Logging

Last updated at Posted at 2020-06-08

はじめに

pythonのloggingを使っていましたが大規模なプログラムを作っていなかったり、業務で使っていなかったりと適当に使っていました。
今までの使い方でも大きな支障なかったのですが、RotatingFileHandlerでログファイルを循環されている自作プログラムにおいて循環時に例外が発生するのだけは気になっていました。
自作プログラムの更新作業に伴ってそこら辺の勉強をしてみたので記事にしてみます。

対象読者

loggingの初心者で、日本語で説明されるよりもソースを見せろって方を対象にした感じです。
loggingモジュールの詳しい使い方は、記述しません。
色々検証した結果、初心者向けでないようなプログラムになっていますが、まあ細かいことは...

環境

Python 3.7.3
(更新しないと)

ファイル構成

  • main.py: メインモジュール。module1, module2を使う
  • loggerHelper.py: loggingのための補助関数群 。
  • module1.py: mainから使われるモジュール。module2を使う。
  • module2.py: main, module1から使われるモジュール。
  • loggingConfig.py: 辞書形式で環境設定を記述しているモジュール。

ソース

loggerHelper.py

loggerHelperで、handlerを設定するための関数などを定義しています。
これはPythonドキュメントをご覧になれば理解できるかと思います。

loggerHelper.py
import logging
import logging.handlers
import sys


class LevelFilter(logging.Filter):  # 特定levelだけを通過させるフィルタ
  def __init__(self, level=logging.INFO):
    super().__init__()
    self.level = level

  def filter(self, record):
    if record.levelno != self.level:
      return False  # Falseを返すとrecordの走査が止まるので見つけ次第Falseを返すと良い
    return True


def getConsoleHandler(level=logging.DEBUG,
                      format="{asctime} - {name:<30} - {levelname:<8} - {message}",
                      dateFormat="%H:%M:%S",
                      style="{",
                      stream=sys.stdout,  # sys.stderr
                      ):
  handler = logging.StreamHandler(stream)
  handler.setLevel(level)
  formatter = logging.Formatter(format, datefmt=dateFormat, style=style)
  handler.setFormatter(formatter)
  return handler


def getRotaingFileHandler(level=logging.DEBUG,
                          # format="{asctime} - {name:<30} - {levelname:<8} - {module} - {lineno} - {funcName} - {message}",
                          format="{asctime} - {name:<30} - {levelname:<8} - {message}",
                          dateFormat="%H:%M:%S",
                          style="{",
                          filename="log.txt", mode="a", maxBytes=1024 * 10, backupCount=2, encoding="utf-8", delay=False):
  handler = logging.handlers.RotatingFileHandler(
      filename, mode=mode, maxBytes=maxBytes, backupCount=backupCount, encoding=encoding, delay=delay)
  handler.setLevel(level)
  formatter = logging.Formatter(format, datefmt=dateFormat, style=style)
  handler.setFormatter(formatter)
  return handler


def addHandlers(logger, handlers=[logging.NullHandler()]):
  for handler in handlers:
    logger.addHandler(handler)


def output(logger, message):
  logger.debug(message)
  logger.info(message)
  logger.warning(message)
  logger.error(message)
  logger.critical(message)

module2.py

module2.pyは、他のモジュールから呼ばれるだけのモジュールです。
モジュールグローバルlogger(module2)、クラスlogger(module2.C)、インスタンスlogger(module2.C.XXXX)を作成します。
loggerの名前をピリオドで連結したものにすると親子と見なされます。
この場合、
親: module2 -> module2.C -> module2.C.XXXX :子
となります。
このモジュールのログを取りたい場合は、mainモジュールでhandlerlevelを設定する必要があります。

module2.py
import time
import logging
import loggerHelper

moduleLogger = logging.getLogger(__name__)
moduleLogger.addHandler(logging.NullHandler())


class C():
  classLogger = logging.getLogger(f"{__name__}.C")
  classLogger.addHandler(logging.NullHandler())

  def __init__(self):
    self.logger = logging.getLogger(f"{__name__}.C.{self.__hash__():X}")
    self.logger.setLevel(logging.NOTSET)
    self.logger.addHandler(logging.NullHandler())

  def addHandlers(self, handlers=[logging.NullHandler()]):
    for handler in handlers:
      self.logger.addHandler(handler)

  def setLevel(self, level=logging.NOTSET):
    self.logger.setLevel(level)

  def output(self, message=""):
    loggerHelper.output(self.logger, f"*at m2: method: {message}")

  @classmethod
  def clsAddHandlers(cls, handlers=[logging.NullHandler()]):
    for handler in handlers:
      cls.classLogger.addHandler(handler)

  @classmethod
  def clsSetLevel(cls, level=logging.NOTSET):
    cls.classLogger.setLevel(level)

  @classmethod
  def clsOutput(cls, message=""):
    loggerHelper.output(cls.classLogger, f"*at m2: classMethod: {message}")


def output(message):
  c2 = C()
  for i in range(1):
    loggerHelper.output(moduleLogger, f"*at m2: func: {i}: {message}")
    msg = f"#from m2: {i}"
    C.clsOutput(msg)
    c2.output(msg)
    time.sleep(0.5)

ベストプラクティスなどに書いてありますが重要な事は、loggerを用意するがhandlerlevelを設定しないことです。
こうすることで呼び出し側は、出力先と出力レベルを好きに決める事が出来ます。
また、logging.NullHandlerをデフォルトhandlerとして設定しているのは、handlerを設定し忘れた場合に何も出力させないためです。
設定し忘れるとsys.stderrWARNING以上のメッセージが出力されてしまうからです。

わざわざそんな事をする必要があるか分かりませんが、クラスのインスタンスごとに別のloggerを用意したい場合は、別の名前を付ける必要があります。
logging.getLogger()は、同じ名前の場合に同じインスタンスを返します。
そのため、例えば以下のように固定の名前にしてしまうとすべてのインスタンスで同じloggerを使うことになります。(この例では、module2のグローバルloggerを使うことになる)

  def __init__(self):
    self.logger = logging.getLogger(f"{__name__}")

もっとも、多くの場合は、モジュールグローバルloggerもクラスloggerもインスタンスloggerも同じ1つのloggerを使えば良いとは思います。
もしくは、モジュールグローバルloggerとクラスloggerの2つを用意すれば良いと思います。

module1.py

module1.pyは、module2.pyとほとんど同じです。
違うところは、関数からmodule2を使用していることです。

module1.py
import time
import logging
import loggerHelper
import module2

moduleLogger = logging.getLogger(__name__)
moduleLogger.addHandler(logging.NullHandler())  # 呼び出し側がloggerを設定しなくても警告等を出さないようにするため


class C():
  classLogger = logging.getLogger(f"{__name__}.C")
  classLogger.addHandler(logging.NullHandler())

  def __init__(self):
    self.logger = logging.getLogger(f"{__name__}.C.{self.__hash__():X}")
    self.logger.setLevel(logging.NOTSET)
    self.logger.addHandler(logging.NullHandler())

  def addHandlers(self, handlers=[logging.NullHandler()]):
    for handler in handlers:
      self.logger.addHandler(handler)

  def setLevel(self, level=logging.NOTSET):
    self.logger.setLevel(level)

  def output(self, message=""):
    loggerHelper.output(self.logger, f"*at m1: method: {message}")

  @classmethod
  def clsAddHandlers(cls, handlers=[logging.NullHandler()]):
    for handler in handlers:
      cls.classLogger.addHandler(handler)

  @classmethod
  def clsSetLevel(cls, level=logging.NOTSET):
    cls.classLogger.setLevel(level)

  @classmethod
  def clsOutput(cls, message=""):
    loggerHelper.output(cls.classLogger, f"*at m1: classMethod: {message}")


def output(message):
  c1 = C()
  c2 = module2.C()
  for i in range(1):
    loggerHelper.output(moduleLogger, f"*at m1: func: {i}: {message}")
    msg = f"#from m1: {i}"
    C.clsOutput(msg)
    c1.output(msg)
    module2.output(msg)
    module2.C.clsOutput(msg)
    c2.output(msg)
    time.sleep(0.5)

main.py

mainでは、logging.config.dictConfig()を使い環境設定を行いそれぞれのモジュールを使用しています。

main.py
import logging
import logging.handlers
import logging.config
import time

import module1
import module2
import loggerHelper
import loggerConfig

if __name__ == "__main__":

  logger = logging.getLogger(__name__)
  logging.config.dictConfig(loggerConfig.config)
  c1 = module1.C()
  c2 = module2.C()

  for i in range(2):
    message1 = f"*at main: {i}"
    message2 = f"#from main: {i}"
    loggerHelper.output(logger, message1)
    module1.C.clsOutput(message2)
    c1.output(message2)
    module1.output(message2)
    module2.C.clsOutput(message2)
    c2.output(message2)
    module2.output(message2)
    time.sleep(0.5)

loogerConfig.py

ここでは、

  • formatter
  • normal: 簡易表示用formatter
  • detail: 詳細表示用formatter
  • filter
    • level: 特定level(INFO)のメッセージしか通さないカスタムfilter
  • handler
    • null: 何もしないhandler。不使用
    • console: 標準出力へのhandler
    • fileAll: 全てのメッセージをファイル(logAll.log)へ出力するhandler
    • fileModule1: module1で発生したメッセージをファイル(logModule1.log)へ出力するhandler
    • fileModule2: module2で発生したメッセージをファイル(logModule2.log)へ出力するhandler
    • fileClass: module1とmodule2のクラスから発生したメッセージをファイル(logClass.log)へ出力するhandler
  • logger
    • __main__: main用logger
    • module1.moduleLogger.name(module1): module1用logger
    • module1.C.classLogger.name(module1.C): module1のクラス用logger
    • module2.moduleLogger.name(module2): module2logger
    • module2.C.classLogger.name(module2.C): module2のクラス用logger

の設定を辞書形式で記述しています。
環境設定は、logging.config.dictConfigによって設定します。
logging.config.fileConfigは、古いAPIで機能が劣り将来の機能拡張が見込めないような事が書いてあるので避ける事とします。

loggerConfig.py
import logging
import logging.handlers

import module1
import module2
import loggerHelper

config = {
    "version": 1,
    "formatters": {
        "normal": {
            "class": "logging.Formatter",
            "format": "{asctime} - {name:<30} - {levelname:<8} - {message}",
            "datefmt": "%H:%M:%S",
            "style": "{",
        },
        "detail": {
            "class": "logging.Formatter",
            "format": "{asctime} - {name:<30} - {levelname:<8} - {module} - {lineno} - {funcName} - {message}",
            "datefmt": "%H:%M:%S",
            "style": "{",
        },
    },
    "filters": {
        "level": {
            "()": "loggerHelper.LevelFilter", # ()は明示的にインスタンス化する。インスタンス変数で制御しているので必要
            "level": logging.INFO,  # int
        },
    },
    "handlers": {
        "null": {
            "class": "logging.NullHandler",
            "level": "DEBUG",  # logging.DEBUG も可能
        },
        "console": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "normal",
            "filters": ["level"],
        },
        "fileAll": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "DEBUG",
            "formatter": "detail",
            "filename": "logAll.log",
            "maxBytes": 1024 * 10,
            "backupCount": 2,
            "encoding": "utf-8",
        },
        "fileModule1": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "WARNING",
            "formatter": "detail",
            "filename": "logModule1.log",
            "maxBytes": 1024 * 10,
            "backupCount": 2,
            "encoding": "utf-8",
        },
        "fileModule2": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "WARNING",
            "formatter": "detail",
            "filename": "logModule2.log",
            "maxBytes": 1024 * 10,
            "backupCount": 2,
            "encoding": "utf-8",
        },
        "fileClass": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "CRITICAL",
            "formatter": "detail",
            "filename": "logClass.log",
            "maxBytes": 1024 * 10,
            "backupCount": 2,
            "encoding": "utf-8",
        },
    },
    "loggers": {
        "__main__": {
            "handlers": ["console", "fileAll"],
            "level": logging.DEBUG, # "DEBUG" も可能
        },
        module1.moduleLogger.name: { # module1
            "handlers": ["console", "fileAll", "fileModule1"],
            "level": logging.DEBUG,
        },
        module1.C.classLogger.name: { # module1.C(module1 loggerの子)
            "handlers": ["fileClass"],
            "level": logging.DEBUG,
            "propagate": True # Trueならば親のLoggerにもメッセージが渡される。つまり追加のhandlerだけを記述する。
        }, # インスタンスの出力は親のhandlerに任せる
        module2.moduleLogger.name: { # module2
            "handlers": ["console", "fileAll", "fileModule2"],
            "level": logging.DEBUG,
        },
        module2.C.classLogger.name: { # module2.C(module2 loggerの子)
            "handlers": ["console", "fileAll", "fileModule2", "fileClass"],
            "level": logging.DEBUG,
            "propagate": False # Falseならば親のLoggerにメッセージが渡されない。そのため全てのhandlerを記述する。
        },
    },
}

異なったloggerからRotatingFileHandlerを使って同じファイルに出力したい場合は、同じhandlerを使用してください。
そうしないとファイルの切り替え時に例外(PermissionError)が発生してしまいます。

filterlevelによるフィルタリングは、初めにloggerでフィルタリングされ、その後にhandlerでフィルタリングされます。
つまり、loggerlevelCRITICALにするとhandlerlevelINFOにしてもCRITICALのメッセージしか出力されません。

loggerpropagateTrue(デフォルトでTrue)の場合、子のloggerで発生した全てのメッセージが親のhandlerにも渡されます。
そのため、通常は親に設定したhandlerを子に設定する必要はありません。
子のpropagateTrueの場合に、親と子に同じhandlerを設定していると、子で発生したメッセージが2回出力される事になってしまいます。

この記事では、module1とmodule2のクラスのインスタンスloggerhandlerを設定していません。
それでもメッセージが出力されるのは子のメッセージが親のhandlerに渡されているからです。
インスタンスloggerに個別のhandlerを設定したい場合は、汎用性などを考えなければいくらでもやりようがあると思いますが、logging.config.dictConfigを使用してあらかじめ決めておくのは難しそうです。

動作結果

標準出力(抜粋)

標準出力には、以下のようなメッセージが出力されます。
特筆すべき点は、

  • 全てのloggerからメッセージが渡される。
  • カスタムfilterによりINFOメッセージしか表示されない。
  • formatterにより簡易表示にされる。

となります。

console
02:04:59 - __main__                       - INFO     - *at main: 0
02:22:47 - __main__                       - INFO     - *at main: 0
02:22:47 - module1.C                      - INFO     - *at m1: classMethod: #from main: 0
02:22:47 - module1.C.13923A959B           - INFO     - *at m1: method: #from main: 0
02:22:47 - module1                        - INFO     - *at m1: func: 0: #from main: 0
02:22:47 - module1.C                      - INFO     - *at m1: classMethod: #from m1: 0
02:22:47 - module1.C.-7FFFFFEC6DC565AF    - INFO     - *at m1: method: #from m1: 0
02:22:47 - module2                        - INFO     - *at m2: func: 0: #from m1: 0
02:22:47 - module2.C                      - INFO     - *at m2: classMethod: #from m2: 0
02:22:47 - module2.C.-7FFFFFEC6DC5659A    - INFO     - *at m2: method: #from m2: 0
02:22:48 - module2.C                      - INFO     - *at m2: classMethod: #from m1: 0
02:22:48 - module2.C.13923A9A5C           - INFO     - *at m2: method: #from m1: 0
02:22:48 - module2.C                      - INFO     - *at m2: classMethod: #from main: 0
02:22:48 - module2.C.13923A9A08           - INFO     - *at m2: method: #from main: 0
02:22:49 - module2                        - INFO     - *at m2: func: 0: #from main: 0
02:22:49 - module2.C                      - INFO     - *at m2: classMethod: #from m2: 0
02:22:49 - module2.C.13923A9A5C           - INFO     - *at m2: method: #from m2: 0
02:22:50 - __main__                       - INFO     - *at main: 1
02:22:50 - module1.C                      - INFO     - *at m1: classMethod: #from main: 1

logAll.log(抜粋)

logAll.logには、以下のようなメッセージが出力されます。
特筆すべき点は、

  • 全てのloggerからメッセージが渡される。
  • 全てのlevelが表示される。
  • formatterにより詳細表示にされる。
  • ファイルサイズがMaxBytesを超えるために、logAll.log.1が作られる

となります。

logAll.log
02:22:50 - module1.C                      - CRITICAL - loggerHelper - 54 - output - *at m1: classMethod: #from main: 1
02:22:50 - module1.C.13923A959B           - DEBUG    - loggerHelper - 50 - output - *at m1: method: #from main: 1
02:22:50 - module1.C.13923A959B           - INFO     - loggerHelper - 51 - output - *at m1: method: #from main: 1
02:22:50 - module1.C.13923A959B           - WARNING  - loggerHelper - 52 - output - *at m1: method: #from main: 1
02:22:50 - module1.C.13923A959B           - ERROR    - loggerHelper - 53 - output - *at m1: method: #from main: 1
02:22:50 - module1.C.13923A959B           - CRITICAL - loggerHelper - 54 - output - *at m1: method: #from main: 1
02:22:50 - module1                        - DEBUG    - loggerHelper - 50 - output - *at m1: func: 0: #from main: 1
02:22:50 - module1                        - INFO     - loggerHelper - 51 - output - *at m1: func: 0: #from main: 1
02:22:50 - module1                        - WARNING  - loggerHelper - 52 - output - *at m1: func: 0: #from main: 1
02:22:50 - module1                        - ERROR    - loggerHelper - 53 - output - *at m1: func: 0: #from main: 1
02:22:50 - module1                        - CRITICAL - loggerHelper - 54 - output - *at m1: func: 0: #from main: 1
02:22:50 - module1.C                      - DEBUG    - loggerHelper - 50 - output - *at m1: classMethod: #from m1: 0
02:22:50 - module1.C                      - INFO     - loggerHelper - 51 - output - *at m1: classMethod: #from m1: 0
02:22:50 - module1.C                      - WARNING  - loggerHelper - 52 - output - *at m1: classMethod: #from m1: 0
02:22:50 - module1.C                      - ERROR    - loggerHelper - 53 - output - *at m1: classMethod: #from m1: 0
02:22:50 - module1.C                      - CRITICAL - loggerHelper - 54 - output - *at m1: classMethod: #from m1: 0

logModule1.log(抜粋)

logModule1.logには、以下のようなメッセージが出力されます。
特筆すべき点は、

  • module1から発生した全てのメッセージが渡される。
  • handlerlevelによりWARNING以上のメッセージのみが表示される。
  • formatterにより詳細表示にされる。

となります。

logModule1.log
02:04:59 - module1.C                      - WARNING  - loggerHelper - 52 - output - *at m1: classMethod: *from main: 0
02:04:59 - module1.C                      - ERROR    - loggerHelper - 53 - output - *at m1: classMethod: *from main: 0
02:04:59 - module1.C                      - CRITICAL - loggerHelper - 54 - output - *at m1: classMethod: *from main: 0
02:04:59 - module1.C.-7FFFFFE5DFC5ECA8    - WARNING  - loggerHelper - 52 - output - *at m1: method: *from main: 0
02:04:59 - module1.C.-7FFFFFE5DFC5ECA8    - ERROR    - loggerHelper - 53 - output - *at m1: method: *from main: 0
02:04:59 - module1.C.-7FFFFFE5DFC5ECA8    - CRITICAL - loggerHelper - 54 - output - *at m1: method: *from main: 0
02:04:59 - module1                        - WARNING  - loggerHelper - 52 - output - *at m1: func: 0: *from main: 0
02:04:59 - module1                        - ERROR    - loggerHelper - 53 - output - *at m1: func: 0: *from main: 0
02:04:59 - module1                        - CRITICAL - loggerHelper - 54 - output - *at m1: func: 0: *from main: 0
02:04:59 - module1.C                      - WARNING  - loggerHelper - 52 - output - *at m1: classMethod: *#from m1: 0
02:04:59 - module1.C                      - ERROR    - loggerHelper - 53 - output - *at m1: classMethod: *#from m1: 0
02:04:59 - module1.C                      - CRITICAL - loggerHelper - 54 - output - *at m1: classMethod: *#from m1: 0
02:04:59 - module1.C.-7FFFFFE5DFC59CB6    - WARNING  - loggerHelper - 52 - output - *at m1: method: *#from m1: 0
02:04:59 - module1.C.-7FFFFFE5DFC59CB6    - ERROR    - loggerHelper - 53 - output - *at m1: method: *#from m1: 0
02:04:59 - module1.C.-7FFFFFE5DFC59CB6    - CRITICAL - loggerHelper - 54 - output - *at m1: method: *#from m1: 0
02:05:01 - module1.C                      - WARNING  - loggerHelper - 52 - output - *at m1: classMethod: *from main: 1
02:05:01 - module1.C                      - ERROR    - loggerHelper - 53 - output - *at m1: classMethod: *from main: 1
02:05:01 - module1.C                      - CRITICAL - loggerHelper - 54 - output - *at m1: classMethod: *from main: 1

logModule2.log(省略)

logModule2.logは、省略します。

logClass.log

logClass.logには、以下のようなメッセージが出力されます。
特筆すべき点は、

  • module1のクラスとmodule2のクラスから発生した全てのメッセージが渡される。
  • handlerlevelによりCRITICALのメッセージのみが表示される。
  • formatterにより詳細表示にされる。

となります。

logClass.log
02:22:47 - module1.C                      - CRITICAL - loggerHelper - 54 - output - *at m1: classMethod: #from main: 0
02:22:47 - module1.C.13923A959B           - CRITICAL - loggerHelper - 54 - output - *at m1: method: #from main: 0
02:22:47 - module1.C                      - CRITICAL - loggerHelper - 54 - output - *at m1: classMethod: #from m1: 0
02:22:47 - module1.C.-7FFFFFEC6DC565AF    - CRITICAL - loggerHelper - 54 - output - *at m1: method: #from m1: 0
02:22:47 - module2.C                      - CRITICAL - loggerHelper - 54 - output - *at m2: classMethod: #from m2: 0
02:22:47 - module2.C.-7FFFFFEC6DC5659A    - CRITICAL - loggerHelper - 54 - output - *at m2: method: #from m2: 0
02:22:48 - module2.C                      - CRITICAL - loggerHelper - 54 - output - *at m2: classMethod: #from m1: 0
02:22:48 - module2.C.13923A9A5C           - CRITICAL - loggerHelper - 54 - output - *at m2: method: #from m1: 0
02:22:48 - module2.C                      - CRITICAL - loggerHelper - 54 - output - *at m2: classMethod: #from main: 0
02:22:49 - module2.C.13923A9A08           - CRITICAL - loggerHelper - 54 - output - *at m2: method: #from main: 0
02:22:49 - module2.C                      - CRITICAL - loggerHelper - 54 - output - *at m2: classMethod: #from m2: 0
02:22:49 - module2.C.13923A9A5C           - CRITICAL - loggerHelper - 54 - output - *at m2: method: #from m2: 0
02:22:50 - module1.C                      - CRITICAL - loggerHelper - 54 - output - *at m1: classMethod: #from main: 1
02:22:50 - module1.C.13923A959B           - CRITICAL - loggerHelper - 54 - output - *at m1: method: #from main: 1
02:22:50 - module1.C                      - CRITICAL - loggerHelper - 54 - output - *at m1: classMethod: #from m1: 0
02:22:50 - module1.C.13923A9A5C           - CRITICAL - loggerHelper - 54 - output - *at m1: method: #from m1: 0
02:22:50 - module2.C                      - CRITICAL - loggerHelper - 54 - output - *at m2: classMethod: #from m2: 0
02:22:50 - module2.C.13923A9A7F           - CRITICAL - loggerHelper - 54 - output - *at m2: method: #from m2: 0
02:22:50 - module2.C                      - CRITICAL - loggerHelper - 54 - output - *at m2: classMethod: #from m1: 0
02:22:50 - module2.C.13923A9A71           - CRITICAL - loggerHelper - 54 - output - *at m2: method: #from m1: 0
02:22:51 - module2.C                      - CRITICAL - loggerHelper - 54 - output - *at m2: classMethod: #from main: 1
02:22:51 - module2.C.13923A9A08           - CRITICAL - loggerHelper - 54 - output - *at m2: method: #from main: 1
02:22:51 - module2.C                      - CRITICAL - loggerHelper - 54 - output - *at m2: classMethod: #from m2: 0
02:22:51 - module2.C.13923A9A71           - CRITICAL - loggerHelper - 54 - output - *at m2: method: #from m2: 0

最後に

自分が気になっていた点を凝縮して記述しました。
そのため、良く分からないような構成になってしまったかもしれません。
しかし、個人的には、loggingへの理解が深まり大分楽になりました。
皆様の役にも立てばよいのですが(´・ω・`)


付録

main.py logging.config.dictConfig不使用版

logging.config.dictConfigを使用しないで同等な動作を実現したい場合は、以下のようになります。

main.py
import logging
import logging.handlers
import logging.config
import time

import module1
import module2
import loggerHelper
import loggerConfig

if __name__ == "__main__":

  console = loggerHelper.getConsoleHandler(level=logging.INFO)
  console.addFilter(loggerHelper.LevelFilter(logging.INFO))
  fileAll = loggerHelper.getRotaingFileHandler(filename="logAll_.log", level=logging.DEBUG)
  fileModule1 = loggerHelper.getRotaingFileHandler(filename="logModule1_.log", level=logging.WARNING)
  fileModule2 = loggerHelper.getRotaingFileHandler(filename="logModule2_.log", level=logging.WARNING)
  fileClass = loggerHelper.getRotaingFileHandler(filename="logClass_.log", level=logging.CRITICAL)

  logger = logging.getLogger(__name__)
  logger.setLevel(logging.DEBUG)
  loggerHelper.addHandlers(logger, [console, fileAll])

  module1.moduleLogger.setLevel(logging.DEBUG)
  loggerHelper.addHandlers(module1.moduleLogger, [console, fileAll, fileModule1])
  module1.C.clsSetLevel(logging.DEBUG)
  module1.C.clsAddHandlers([fileClass])

  module2.moduleLogger.setLevel(logging.DEBUG)
  loggerHelper.addHandlers(module2.moduleLogger, [console, fileAll, fileModule2])
  module2.C.clsSetLevel(logging.DEBUG)
  module2.C.clsAddHandlers([fileClass])

  c1 = module1.C()
  c2 = module2.C()

  for i in range(2):
    message1 = f"*at main: {i}"
    message2 = f"#from main: {i}"
    loggerHelper.output(logger, message1)
    module1.C.clsOutput(message2)
    c1.output(message2)
    module1.output(message2)
    module2.C.clsOutput(message2)
    c2.output(message2)
    module2.output(message2)
    time.sleep(0.5)

フィルタリングの流れ

logging.png

図のPlantUML

plantUML
@startuml
title  "logging Filter"
start
:message;
:Logger;
if (logger level?) then (true)
  :Logger Filter;
  if (Logger Filter) then (true)
    :Handler;
    if (Handler level?) then (ture)
      :Handler Filter;
      if (Handler filter?) then (true)
        :output;
      else (false)
        -[#blue]->
        stop
      endif
    else (false)
      -[#blue]->
      stop
    endif
  else (false)
    -[#blue]->
    stop
  endif
else (false)
  -[#blue]->
  stop
endif
end
@enduml
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?