여러 모듈에서 로깅 사용


257

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

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')

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


3
내 답변에 대한 귀하의 의견에 대한 답변 : fileConfig모든 모듈에 if __name__ == '__main__'논리가 없는 한 로깅을 수행하는 모든 모듈 을 호출 할 필요는 없습니다 . 패키지가 라이브러리라면 prost의 대답은 좋은 습관이 아닙니다.하지만 당신을 위해 작동 할 수도 있습니다-라이브러리 패키지에 로깅을 구성하지 않아야합니다 NullHandler.
Vinay Sajip

1
prost는 모든 모듈에서 import 및 logger stmts를 호출하고 기본 모듈에서 fileconfig stmt 만 호출해야 함을 암시했습니다. 당신이 말하는 것과 비슷하지 않습니까?
Quest Monger

6
prost는 로깅 구성 코드를에 넣어야한다고 말합니다 package/__init__.py. 그것은 일반적으로 if __name__ == '__main__'코드 를 넣는 곳이 아닙니다 . 또한 prost의 예제는 가져 오기 할 때 무조건 구성 코드를 호출하는 것처럼 보이지만 나에게는 맞지 않습니다. 일반적으로 로깅 구성 코드는 한 곳에서 수행해야하며 __main__을 가져올 때를 제외하고는 가져 오기의 부작용으로 발생하지 않아야합니다.
Vinay Sajip

당신 말이 맞아요, 그의 코드 샘플에서 '# package / __ init__.py'줄을 완전히 놓쳤습니다. 지적하고 양해 해 주셔서 감사합니다.
Quest Monger

1
여러 개가 있으면 어떻게됩니까 if __name__ == '__main__'? (이 경우에는 명시 적으로 문제의 언급이 없습니다)
kon psych

답변:


293

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

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()있습니다.


21
내 예를 보면 위의 제안을 이미하고 있습니다. 내 질문은이 로깅 초기화를 어떻게 중앙 집중화하여 해당 3 개의 문을 반복하지 않아도되는지에 대한 질문이었습니다. 또한 귀하의 예에서 'logging.config.fileConfig ('logging.conf ')'stmt를 놓쳤습니다. 이 표준은 실제로 내 우려의 근본 원인입니다. 모든 모듈에서 로거를 시작한 경우 모든 모듈에이 stmt를 입력해야합니다. 그것은 모든 모듈에서 conf 파일의 경로를 추적하는 것을 의미합니다. 이것은 나에게 모범 사례처럼 보이지 않습니다 (모듈 / 패키지 위치를 변경할 때 혼란을 상상하십시오).
Quest Monger

4
로거를 작성한 후 fileConfig를 호출하면 동일하거나 다른 모듈에서 (예 : 파일 맨 위에 로거를 작성할 때) 작동하지 않습니다. 로깅 구성은 이후에 작성된 로거에만 적용됩니다. 따라서이 방법은 작동하지 않거나 여러 모듈에서 실행 가능한 옵션이 아닙니다. @Quest Monger : 구성 파일의 위치를 ​​보유하는 다른 파일을 언제든지 만들 수 있습니다 ..;)
Vincent Ketelaars

2
@Oxidator : 반드시 그런 것은 아닙니다. 기본적으로 disable_existing_loggers플래그 True로 설정되어 있지만로 설정할 수 있습니다 False.
Vinay Sajip

1
@Vinay Sajip, 감사합니다. 모듈에서 작동하지만 클래스 외부에서 작동하는 로거에 대한 권장 사항이 있습니까? 주요 기능이 호출되기 전에 가져 오기가 수행되므로 해당 로그가 이미 기록되었습니다. 메인 모듈의 모든 가져 오기가 유일한 방법이기 전에 로거를 설정하는 것 같아요? 원하는 경우이 로거를 기본으로 덮어 쓸 수 있습니다.
Vincent Ketelaars

1
모든 모듈 특정 로거의 로깅 수준이 기본 경고와 다르도록하려면 각 모듈에서 해당 설정을 수행해야합니까? 모든 모듈을 INFO에 기록하고 싶습니다.
Raj

127

실제로 모든 로거는 부모의 패키지 로거의 자식입니다 (즉 ,의 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!")

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


15
이것은 지금까지 문제에 대한 가장 간단한 해결책이며, 모듈 사이의 부모-자식 관계를 드러내고 활용합니다. 감사합니다.
Quest Monger

당신이 맞아요 vinay가 그의 게시물에서 지적했듯이 init .py 모듈에 없는 한 귀하의 솔루션은 옳습니다 . 메인 모듈 (입장 점)에 솔루션을 적용했을 때 솔루션이 작동했습니다.
Quest Monger

2
질문이 별도의 모듈과 관련되어 있기 때문에 실제로 훨씬 더 관련있는 답변입니다.
Jan Sila

1
멍청한 질문 : 로거가없는 __main__.py경우 (예 : 로거가없는 스크립트에서 모듈을 사용하려는 경우) logging.getLogger(__name__)여전히 모듈에 어떤 종류의 로깅을 수행합니까? 아니면 예외가 발생합니까?
Bill

1
드디어. 로거가 작동했지만 Windows에서 병렬 실행을 위해 joblib을 사용하여 실패했습니다. 필자는 이것이 시스템을 수동으로 조정 한 것으로 생각합니다. Parallel에 다른 문제가 있습니다. 그러나 확실히 작동합니다! 감사합니다
B 퍼 타도

17

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

단일 파이썬 파일을 사용하여 내 로그를 ' 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")

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


1
싱글 톤 패턴을 자세히 설명해 주셔서 감사합니다. 나는 이것을 구현할 계획 이었지만 @prost 솔루션은 훨씬 간단하고 내 요구에 완벽하게 부합합니다. 그러나 귀하의 솔루션이 유용한 것은 주요 진입 점이 아닌 여러 진입 점이있는 더 큰 프로젝트입니다. 감사합니다.
Quest Monger

46
이것은 쓸모가 없습니다. 루트 로거는 이미 싱글 톤입니다. Logger.logr.info 대신 logging.info를 사용하십시오.
포드

9

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

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')    

또한 기본에서 fileconfig를 사용하는 경우 라이브러리 디자이너가 모듈 레벨 로거 인스턴스를 사용하는 경우를 대비하여 disable_existing_loggers = False를 설정하십시오.


당신은 logging.config.fileConfig('logging.ini')전에 실행할 수 있습니까 import my_module? 이 답변에서 제안한대로 .
lucid_dreamer

확실하지는 않지만 수입과 실행 코드를 혼합하는 것은 나쁜 습관으로 간주됩니다. 또한 특히 사소한 대안이있을 때 클라이언트가 가져 오기 전에 로깅을 구성해야하는지 여부를 확인하지 않아도됩니다! 요청과 같이 널리 사용되는 라이브러리가 그렇게했다고 상상해보십시오.
phil_20686

"확실하지는 않지만 수입과 실행 코드를 혼합하는 것은 나쁜 습관으로 간주 될 것입니다." - 왜?
lucid_dreamer

그것이 왜 나쁜지 너무 명확하지 않습니다. 그리고 나는 당신의 모범을 완전히 이해하지 못합니다. 이 예제에 대한 설정을 게시하고 사용법을 보여줄 수 있습니까?
lucid_dreamer

1
당신은 모순되는 것처럼 보인다 공식 문서를 '로거 이름을 지정하면 다음과 같이 이름, 로깅을 사용하는 각 모듈, 모듈 수준 로거를 사용할 때 좋은 규칙 사용 : logger = logging.getLogger(__name__)'
iron9

8

여러 모듈에서 하나의 로깅 라이브러리 인스턴스를 사용하는 간단한 방법은 다음과 같은 해결책이었습니다.

base_logger.py

import logging

logger = logging
logger.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)

다른 파일들

from base_logger import logger

if __name__ == '__main__':
    logger.info("This is an info message")

7

다른 해결책으로 던지기.

내 모듈의 init .py에는 다음과 같은 것이 있습니다.

# 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

그런 다음 각 모듈에서 로거가 필요합니다.

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

로그가 누락되면 해당 모듈에서 소스를 구별 할 수 있습니다.


"내 모듈의 기본 초기화"는 무엇을 의미합니까? 그리고 "각 수업마다 로거가 필요합니다." _module.py라는 샘플과 caller_module.py 모듈에서 가져 오기로 사용하는 예제를 제공 할 수 있습니까? 내가 요구하는 형식에 대한 영감을 얻으려면 이 답변참조하십시오 . 후원하려고하지 않습니다. 나는 당신의 대답을 이해하려고 노력하고 있으며 당신이 그런 식으로 그것을 쓰면 내가 할 것이라고 알고 있습니다.
lucid_dreamer

1
@ lucid_dreamer 나는 명확히했다.
Tommy

4

이와 같은 것을 생각 해낼 수도 있습니다!

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

위의 내용이 별도의 모듈에 정의되어 있고 다른 모듈로 가져온 경우 로깅이 필요한 경우 동일한 모듈과 전체 프로젝트에서 여러 로거를 사용할 수 있습니다.

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

4

@Yarkee의 솔루션이 더 좋아 보였다. 더 추가하고 싶습니다-

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)

따라서 LoggerManager는 전체 애플리케이션에 플러그 가능합니다. 그것이 의미와 가치가 있기를 바랍니다.


11
로깅 모듈은 이미 싱글 톤을 처리합니다. logging.getLogger ( "Hello")는 모든 모듈에서 동일한 로거를 얻습니다.
포드

2

몇 가지 답변이 있습니다. 나는 나에게 이해가되는 유사하지만 다른 솔루션으로 끝났으며 어쩌면 그것은 당신에게도 의미가 있습니다. 내 주요 목표는 로그를 레벨별로 핸들러에 전달할 수 있도록하는 것이 었습니다 (디버그 레벨 로그는 콘솔에, 경고 이상은 파일에).

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)

logger.py라는 멋진 유틸리티 파일을 만들었습니다.

import logging

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

flask.app는 플라스크의 하드 코딩 된 값입니다. 응용 프로그램 로거는 항상 flask.app를 모듈 이름으로 시작합니다.

이제 각 모듈에서 다음 모드로 사용할 수 있습니다.

from logger import get_logger
logger = get_logger(__name__)

logger.info("new log")

최소한의 노력으로 "app.flask.MODULE_NAME"에 대한 새 로그가 생성됩니다.


2

가장 좋은 방법은 호출 메소드에 로거 핸들러를 제공하는 태스크가 하나 인 메소드를 별도로 작성하는 것입니다. 이 파일을 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

이제 로거 핸들러가 필요할 때마다 getlogger () 메소드를 호출하십시오.

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

1
추가 매개 변수가없는 경우에 좋습니다. 그러나 --debug앱에 옵션 이
대부

@ TheGodfather 예,이 방법론으로는 달성하기 어렵습니다. 이 상황에서 우리가 할 수있는 일은 객체를 만들 때 포맷터를 매개 변수로 사용하고 로거 핸들러를 반환하는 비슷한 함수를 갖는 클래스를 만드는 것입니다.
Mousam Singh

예, 비슷한 get_logger(level=logging.INFO)종류의 싱글 톤을 반환하도록 만들었 으므로 메인 응용 프로그램에서 처음 호출 할 때 로거와 핸들러를 적절한 수준으로 초기화 한 다음 동일한 logger객체를 다른 모든 메서드에 반환합니다 .
대부

0

파이썬에 익숙하지 않아 이것이 권장되는지 알 수 없지만 상용구를 다시 쓰지 않는 경우 효과적입니다.

프로젝트에 init .py 가 있어야 모듈로로드 할 수 있습니다

# Put this in your module's __init__.py
import logging.config
import sys

# I used this dictionary test, you would put:
# logging.config.fileConfig('logging.conf')
# The "" entry in loggers is the root logger, tutorials always 
# use "root" but I can't get that to work
logging.config.dictConfig({
    "version": 1,
    "formatters": {
        "default": {
            "format": "%(asctime)s %(levelname)s %(name)s %(message)s"
        },
    },
    "handlers": {
        "console": {
            "level": 'DEBUG',
            "class": "logging.StreamHandler",
            "stream": "ext://sys.stdout"
        }
    },
    "loggers": {
        "": {
            "level": "DEBUG",
            "handlers": ["console"]
        }
    }
})

def logger():
    # Get the name from the caller of this function
    return logging.getLogger(sys._getframe(1).f_globals['__name__'])

sys._getframe(1)제안은 여기 에서 온다

그런 다음 다른 파일에서 로거를 사용하려면 다음을 수행하십시오.

from [your module name here] import logger

logger().debug("FOOOOOOOOO!!!")

주의 사항 :

  1. 파일을 모듈로 실행해야합니다. 그렇지 import [your module]않으면 작동하지 않습니다.
    • python -m [your module name].[your filename without .py]
  2. 프로그램의 진입 점에 대한 로거 이름은 __main__이지만 사용하는 모든 솔루션에 __name__해당 문제가 있습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.