IT story

여러 모듈에서 Python 로깅 사용

hot-time 2020. 5. 1. 08:09
반응형

여러 모듈에서 Python 로깅 사용


다음과 같은 구조의 작은 파이썬 프로젝트가 있습니다-

Project 
 -- pkg01
   -- test01.py
 -- pkg02
   -- test02.py
 -- logging.conf

기본 로깅 모듈을 사용하여 메시지를 stdout 및 로그 파일에 인쇄하려고합니다. 로깅 모듈을 사용하려면 약간의 초기화가 필요합니다.

import logging.config

logging.config.fileConfig('logging.conf')
logger = logging.getLogger('pyApp')

logger.info('testing')

현재 메시지 로깅을 시작하기 전에 모든 모듈에서이 초기화를 수행합니다. 이 초기화를 한곳에서 한 번만 수행하여 프로젝트 전체에 로깅하여 동일한 설정을 재사용 할 수 있습니까?


모범 사례는 각 모듈에서 다음과 같이 로거를 정의하는 것입니다.

import logging
logger = logging.getLogger(__name__)

모듈 상단 근처에서 모듈의 다른 코드에서

logger.debug('My message with %s', 'variable data')

모듈 내에서 로깅 활동을 세분화해야하는 경우 다음을 사용하십시오.

loggerA = logging.getLogger(__name__ + '.A')
loggerB = logging.getLogger(__name__ + '.B')

과에 기록 loggerA하고 loggerB적절하게.

기본 프로그램에서 다음을 수행하십시오.

def main():
    "your program code"

if __name__ == '__main__':
    import logging.config
    logging.config.fileConfig('/path/to/logging.conf')
    main()

또는

def main():
    import logging.config
    logging.config.fileConfig('/path/to/logging.conf')
    # your program code

if __name__ == '__main__':
    main()

참조 여기에 여러 개의 모듈에서 로그온하고, 여기에 다른 코드에 의해 라이브러리 모듈로 사용되는 코드에 대한 로깅 구성.

업데이트 : 호출 할 때 Python 2.6 이상을 사용 중인지 fileConfig()지정할 disable_existing_loggers=False수 있습니다 (자세한 내용 은 문서 참조 ). 기본값은 True이전 버전과의 호환성을위한 것으로, 모든 로거가 fileConfig()해당 조상 또는 구성에서 명시 적으로 이름을 지정하지 않으면 모든 로거가 비활성화됩니다 . 값을로 설정하면 False기존 로거가 그대로 유지됩니다. Python 2.7 / Python 3.2 이상을 사용하는 경우 구성을보다 잘 제어 할 수있는 dictConfig()것보다 더 나은 API 를 고려할 수 fileConfig()있습니다.


실제로 모든 로거는 부모의 패키지 로거의 자식입니다 (즉 ,의 package.subpackage.module구성을 상속 package.subpackage)하므로 루트 로거를 구성하기 만하면됩니다. 로거에 logging.config.fileConfig대한 고유 한 구성) 또는 logging.basicConfig(루트 로거를 설정하여) . 사용자의 입력 모듈에서 설정 로깅 ( __main__.py또는 예를 들어, 실행 원하는대로 main_script.py.이 __init__.py뿐만 아니라 작동)

basicConfig 사용 :

# package/__main__.py
import logging
import sys

logging.basicConfig(stream=sys.stdout, level=logging.INFO)

fileConfig를 사용하여 :

# package/__main__.py
import logging
import logging.config

logging.config.fileConfig('logging.conf')

다음을 사용하여 모든 로거를 만듭니다.

# package/submodule.py
# or
# package/subpackage/submodule.py
import logging
log = logging.getLogger(__name__)

log.info("Hello logging!")

자세한 정보는 고급 로깅 ​​학습서를 참조하십시오 .


나는 항상 아래와 같이합니다.

단일 파이썬 파일을 사용하여 내 로그를 ' log_conf.py' 라는 단일 패턴으로 구성하십시오.

#-*-coding:utf-8-*-

import logging.config

def singleton(cls):
    instances = {}
    def get_instance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return get_instance()

@singleton
class Logger():
    def __init__(self):
        logging.config.fileConfig('logging.conf')
        self.logr = logging.getLogger('root')

다른 모듈에서는 구성을 가져 오기만하면됩니다.

from log_conf import Logger

Logger.logr.info("Hello World")

이것은 간단하고 효율적으로 기록하는 단일 패턴입니다.


이 답변 중 일부는 모듈 상단에서 수행하는 작업을 제안합니다.

import logging
logger = logging.getLogger(__name__)

이것이 매우 나쁜 관행으로 간주된다는 것이 나의 이해입니다 . 그 이유는 파일 구성이 기본적으로 모든 기존 로거를 비활성화하기 때문입니다. 예 :

#my_module
import logging

logger = logging.getLogger(__name__)

def foo():
    logger.info('Hi, foo')

class Bar(object):
    def bar(self):
        logger.info('Hi, bar')

그리고 메인 모듈에서 :

#main
import logging

# load my module - this now configures the logger
import my_module

# This will now disable the logger in my module by default, [see the docs][1] 
logging.config.fileConfig('logging.ini')

my_module.foo()
bar = my_module.Bar()
bar.bar()

기존의 로거가 fileconfig 호출에 의해 비활성화되었으므로 logging.ini에 지정된 로그가 비어 있습니다.

이 문제를 해결할 수는 있지만 (disable_existing_Loggers = False) 실제로 라이브러리의 많은 클라이언트는이 동작에 대해 알지 못하며 로그를받지 않습니다. 항상 logging.getLogger를 로컬로 호출하여 클라이언트가 쉽게 사용할 수 있도록하십시오. 모자 팁 :이 행동에 대해 Victor Lin의 웹 사이트 에서 배웠습니다 .

따라서 항상 logging.getLogger를 로컬로 호출하는 것이 좋습니다. 예 :

#my_module
import logging

logger = logging.getLogger(__name__)

def foo():
    logging.getLogger(__name__).info('Hi, foo')

class Bar(object):
    def bar(self):
        logging.getLogger(__name__).info('Hi, bar')    

Also, if you use fileconfig in your main, set disable_existing_loggers=False, just in case your library designers use module level logger instances.


Throwing in another solution.

In my module's init.py I have something like:

# mymodule/__init__.py
import logging

def get_module_logger(mod_name):
  logger = logging.getLogger(mod_name)
  handler = logging.StreamHandler()
  formatter = logging.Formatter(
        '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
  handler.setFormatter(formatter)
  logger.addHandler(handler)
  logger.setLevel(logging.DEBUG)
  return logger

Then in each module I need a logger, I do:

# mymodule/foo.py
from [modname] import get_module_logger
logger = get_module_logger(__name__)

When the logs are missed, you can differentiate their source by the module they came from.


@Yarkee's solution seemed better. I would like to add somemore to it -

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances.keys():
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]


class LoggerManager(object):
    __metaclass__ = Singleton

    _loggers = {}

    def __init__(self, *args, **kwargs):
        pass

    @staticmethod
    def getLogger(name=None):
        if not name:
            logging.basicConfig()
            return logging.getLogger()
        elif name not in LoggerManager._loggers.keys():
            logging.basicConfig()
            LoggerManager._loggers[name] = logging.getLogger(str(name))
        return LoggerManager._loggers[name]    


log=LoggerManager().getLogger("Hello")
log.setLevel(level=logging.DEBUG)

So LoggerManager can be a pluggable to the entire application. Hope it makes sense and value.


You could also come up with something like this!

def get_logger(name=None):
    default = "__app__"
    formatter = logging.Formatter('%(levelname)s: %(asctime)s %(funcName)s(%(lineno)d) -- %(message)s',
                              datefmt='%Y-%m-%d %H:%M:%S')
    log_map = {"__app__": "app.log", "__basic_log__": "file1.log", "__advance_log__": "file2.log"}
    if name:
        logger = logging.getLogger(name)
    else:
        logger = logging.getLogger(default)
    fh = logging.FileHandler(log_map[name])
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    logger.setLevel(logging.DEBUG)
    return logger

Now you could use multiple loggers in same module and across whole project if the above is defined in a separate module and imported in other modules were logging is required.

a=get_logger("__app___")
b=get_logger("__basic_log__")
a.info("Starting logging!")
b.debug("Debug Mode")

There are several answers. i ended up with a similar yet different solution that makes sense to me, maybe it will make sense to you as well. My main objective was to be able to pass logs to handlers by their level (debug level logs to the console, warnings and above to files):

from flask import Flask
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

# make default logger output everything to the console
logging.basicConfig(level=logging.DEBUG)

rotating_file_handler = RotatingFileHandler(filename="logs.log")
rotating_file_handler.setLevel(logging.INFO)

app.logger.addHandler(rotating_file_handler)

created a nice util file named logger.py:

import logging

def get_logger(name):
    return logging.getLogger("flask.app." + name)

the flask.app is a hardcoded value in flask. the application logger is always starting with flask.app as its the module's name.

now, in each module, i'm able to use it in the following mode:

from logger import get_logger
logger = get_logger(__name__)

logger.info("new log")

This will create a new log for "app.flask.MODULE_NAME" with minimum effort.


The best practice would be to create a module separately which has only one method whose task we be to give a logger handler to the the calling method. Save this file as m_logger.py

import logger, logging

def getlogger():
    # logger
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    # create console handler and set level to debug
    #ch = logging.StreamHandler()
    ch = logging.FileHandler(r'log.txt')
    ch.setLevel(logging.DEBUG)
    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    # add formatter to ch
    ch.setFormatter(formatter)
    # add ch to logger
    logger.addHandler(ch)
    return logger

Now call the getlogger() method whenever logger handler is needed.

from m_logger import getlogger
logger = getlogger()
logger.info('My mssg')

참고URL : https://stackoverflow.com/questions/15727420/using-python-logging-in-multiple-modules

반응형