Python의 로깅 기능에 사용자 지정 로그 수준을 추가하는 방법
애플리케이션에 대한 로그 레벨 TRACE(5)를 갖고 싶습니다.debug()충니다합또. 한분ally또▁addition한다충.log(5, msg)내가 원하는 게 아닙니다.Python 로거에 사용자 지정 로그 수준을 추가하려면 어떻게 해야 합니까?
나는 있습니다.mylogger.py다음 내용을 포함합니다.
import logging
@property
def log(obj):
myLogger = logging.getLogger(obj.__class__.__name__)
return myLogger
내 코드에서는 다음과 같은 방식으로 사용합니다.
class ExampleClass(object):
from mylogger import log
def __init__(self):
'''The constructor with the logger'''
self.log.debug("Init runs")
이제 전화하겠습니다.self.log.trace("foo bar")
편집(2016년 12월 8일):저는 승인된 답변을 에릭 S의 매우 좋은 제안을 바탕으로 한 훌륭한 솔루션인 IMHO로 변경했습니다.
2022년과 그 이후에 읽는 사람들에게: 당신은 아마도 여기에서 현재 두 번째로 높은 평가를 받은 답을 확인해야 할 것입니다: https://stackoverflow.com/a/35804945/1691778
저의 원래 답변은 아래와 같습니다.
--
@에릭 S.
Eric S.의 답변은 훌륭하지만, 저는 실험을 통해 로그 수준이 설정된 것과 상관없이 항상 새로운 디버그 수준에서 기록된 메시지가 인쇄된다는 것을 알게 되었습니다.그래서 만약 당신이 새로운 레벨 번호를 만든다면.9에 setLevel(50)하위 수준 메시지가 잘못 인쇄됩니다.
이를 방지하려면 "debugv" 함수 내부에 해당 로깅 수준이 실제로 활성화되었는지 확인하는 다른 줄이 필요합니다.
로깅 수준이 활성화되었는지 확인하는 수정된 예:
import logging
DEBUG_LEVELV_NUM = 9
logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV")
def debugv(self, message, *args, **kws):
if self.isEnabledFor(DEBUG_LEVELV_NUM):
# Yes, logger takes its '*args' as 'args'.
self._log(DEBUG_LEVELV_NUM, message, args, **kws)
logging.Logger.debugv = debugv
코드를 보면 다음과 같습니다.class Loggerlogging.__init__.pyPython 2.7의 경우 이는 모든 표준 로그 함수(.critical, .debug 등)가 수행하는 작업입니다.
평판이 안 좋아서 다른 사람들의 답변에 댓글을 달 수가 없는 것 같아요...에릭이 이것을 본다면 그의 게시물을 업데이트하기를 바랍니다.=)
기존의 모든 답변과 다양한 사용 경험을 결합하여 새로운 수준의 완벽한 사용을 보장하기 위해 수행해야 할 모든 작업 목록을 만들었다고 생각합니다.의 단계들은 하고 있다고 합니다.TRACE이 가있인logging.DEBUG - 5 == 5:
logging.addLevelName(logging.DEBUG - 5, 'TRACE')이름으로 참조할 수 있도록 새 수준을 내부적으로 등록하려면 호출해야 합니다.- 은 새수을속추합니의 .
logging일성을위한자체관::logging.TRACE = logging.DEBUG - 5. - ▁a라는 방법.
trace는 에해야합에 .logging모듈.다음과 같이 동작해야 합니다.debug,info타기. - ▁a라는 방법.
trace현재 구성된 로거 클래스에 추가해야 합니다.에 것보이 100% 장는것아은때니다니문입기되이▁since다니때입문▁is.logging.Logger,사용하다logging.getLoggerClass()대신.
모든 단계가 아래 방법에 나와 있습니다.
def addLoggingLevel(levelName, levelNum, methodName=None):
"""
Comprehensively adds a new logging level to the `logging` module and the
currently configured logging class.
`levelName` becomes an attribute of the `logging` module with the value
`levelNum`. `methodName` becomes a convenience method for both `logging`
itself and the class returned by `logging.getLoggerClass()` (usually just
`logging.Logger`). If `methodName` is not specified, `levelName.lower()` is
used.
To avoid accidental clobberings of existing attributes, this method will
raise an `AttributeError` if the level name is already an attribute of the
`logging` module or if the method name is already present
Example
-------
>>> addLoggingLevel('TRACE', logging.DEBUG - 5)
>>> logging.getLogger(__name__).setLevel("TRACE")
>>> logging.getLogger(__name__).trace('that worked')
>>> logging.trace('so did this')
>>> logging.TRACE
5
"""
if not methodName:
methodName = levelName.lower()
if hasattr(logging, levelName):
raise AttributeError('{} already defined in logging module'.format(levelName))
if hasattr(logging, methodName):
raise AttributeError('{} already defined in logging module'.format(methodName))
if hasattr(logging.getLoggerClass(), methodName):
raise AttributeError('{} already defined in logger class'.format(methodName))
# This method was inspired by the answers to Stack Overflow post
# http://stackoverflow.com/q/2183233/2988730, especially
# http://stackoverflow.com/a/13638084/2988730
def logForLevel(self, message, *args, **kwargs):
if self.isEnabledFor(levelNum):
self._log(levelNum, message, args, **kwargs)
def logToRoot(message, *args, **kwargs):
logging.log(levelNum, message, *args, **kwargs)
logging.addLevelName(levelNum, levelName)
setattr(logging, levelName, levelNum)
setattr(logging.getLoggerClass(), methodName, logForLevel)
setattr(logging, methodName, logToRoot)
내가 관리하는 유틸리티 라이브러리, haggis에서 훨씬 더 자세한 구현을 찾을 수 있습니다.이 기능은 이 답변의 보다 생산 준비가 된 구현입니다.
저는 "람다" 대답을 보지 않으려고 했고, 어디에 있는지 수정해야 했습니다.log_at_my_log_level추가 중이었습니다.저도 폴이 한 문제를 보았습니다. 저는 이것이 효과가 없다고 생각합니다. 당신은 첫 번째 아르긴으로 로거가 필요하지 않나요?이것은 나에게 효과가 있었습니다.
import logging
DEBUG_LEVELV_NUM = 9
logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV")
def debugv(self, message, *args, **kws):
# Yes, logger takes its '*args' as 'args'.
self._log(DEBUG_LEVELV_NUM, message, args, **kws)
logging.Logger.debugv = debugv
이 질문은 다소 오래된 것이지만, 저는 같은 주제를 다루었고 이미 언급된 것들과 유사한 방법을 찾았는데, 이는 제가 보기에 조금 더 깨끗해 보입니다.이것은 3.4에서 테스트되었기 때문에 사용된 방법이 이전 버전에 있는지 확실하지 않습니다.
from logging import getLoggerClass, addLevelName, setLoggerClass, NOTSET
VERBOSE = 5
class MyLogger(getLoggerClass()):
def __init__(self, name, level=NOTSET):
super().__init__(name, level)
addLevelName(VERBOSE, "VERBOSE")
def verbose(self, msg, *args, **kwargs):
if self.isEnabledFor(VERBOSE):
self._log(VERBOSE, msg, args, **kwargs)
setLoggerClass(MyLogger)
우리는 이미 많은 정답을 가지고 있지만, 제 생각에는 다음과 같은 것들이 더 비단결적입니다.
import logging
from functools import partial, partialmethod
logging.TRACE = 5
logging.addLevelName(logging.TRACE, 'TRACE')
logging.Logger.trace = partialmethod(logging.Logger.log, logging.TRACE)
logging.trace = partial(logging.log, logging.TRACE)
당신이 경우용을 사용하고 .mypy에는, 코서의드에, 것권다니장됩이는하추를 추가하는 .# type: ignore경고가 속성을 추가하지 못하도록 합니다.
방법을 했습니까?self._log왜의 답은 그것을 그리고 왜 각각의 답은 그것에 근거합니까?!은 파썬적인해다같습음다니과은결책이다같▁would를 사용하는 입니다.self.log대신 내부 문제를 처리할 필요가 없습니다.
import logging
SUBDEBUG = 5
logging.addLevelName(SUBDEBUG, 'SUBDEBUG')
def subdebug(self, message, *args, **kws):
self.log(SUBDEBUG, message, *args, **kws)
logging.Logger.subdebug = subdebug
logging.basicConfig()
l = logging.getLogger()
l.setLevel(SUBDEBUG)
l.subdebug('test')
l.setLevel(logging.DEBUG)
l.subdebug('test')
내 생각에 당신은 하위 분류를 해야 할 것 같습니다.Logger클래스를 지정하고 메서드를 추가합니다.trace그것은 기본적으로 요구됩니다.Logger.log보다 낮은 수준으로DEBUG저는 이것을 시도해 본 적이 없지만 이것은 문서에 나와 있는 것입니다.
log() 함수를 통과하는 로거 개체에 대한 새 속성을 만드는 것이 더 쉽다는 것을 알게 되었습니다.바로 이러한 이유로 로거 모듈이 addLevelName()과 log()를 제공한다고 생각합니다.따라서 하위 클래스나 새로운 방법이 필요하지 않습니다.
import logging
@property
def log(obj):
logging.addLevelName(5, 'TRACE')
myLogger = logging.getLogger(obj.__class__.__name__)
setattr(myLogger, 'trace', lambda *args: myLogger.log(5, *args))
return myLogger
지금이다
mylogger.trace('This is a trace message')
예상대로 작동해야 합니다.
사용자 지정 로거를 만드는 데 필요한 팁:
- 을 사용하지 .
_log,사용하다log됩니다.)isEnabledFor) - 로깅 모듈은 사용자 지정 로거를 생성하는 인스턴스여야 합니다. 로깅 모듈은 에서 마법을 수행하기 때문입니다.
getLogger은 클래설합니야다정해를스따로 수업을 해야 할 것입니다.setLoggerClass - 당신은 필정의할가없다니습요를 .
__init__, 합니다.
# Lower than debug which is 10
TRACE = 5
class MyLogger(logging.Logger):
def trace(self, msg, *args, **kwargs):
self.log(TRACE, msg, *args, **kwargs)
는 이로를호때사용을 합니다.setLoggerClass(MyLogger)이 로거를 기본 로거로 설정하려면 다음과 같이 하십시오.getLogger
logging.setLoggerClass(MyLogger)
log = logging.getLogger(__name__)
# ...
log.trace("something specific")
은 ▁you 필요할 입니다.setFormatter,setHandler,그리고.setLevel(TRACE)에서.handler 그고위에에.log가 이 의 트레이스를 볼 수 것입니다.
이것은 저에게 효과가 있었습니다.
import logging
logging.basicConfig(
format=' %(levelname)-8.8s %(funcName)s: %(message)s',
)
logging.NOTE = 32 # positive yet important
logging.addLevelName(logging.NOTE, 'NOTE') # new level
logging.addLevelName(logging.CRITICAL, 'FATAL') # rename existing
log = logging.getLogger(__name__)
log.note = lambda msg, *args: log._log(logging.NOTE, msg, args)
log.note('school\'s out for summer! %s', 'dude')
log.fatal('file not found.')
lambda/funcName 문제는 로거로 해결되었습니다.@marked가 지적한 대로 _log.람다를 사용하는 것이 조금 더 깔끔해 보이지만, 키워드 인수를 사용할 수 없다는 것이 단점입니다.저는 그것을 직접 사용해 본 적이 없기 때문에, 큰 것은 아닙니다.
메모 설정: 학교는 여름 방학입니다! 친구.치명적인 설정: 파일을 찾을 수 없습니다.
Logger ▁the▁to▁logger▁i▁as▁using다니▁an권을 사용하는 것을 추천합니다.Logger.log(level, msg)방법.
import logging
TRACE = 5
logging.addLevelName(TRACE, 'TRACE')
FORMAT = '%(levelname)s:%(name)s:%(lineno)d:%(message)s'
logging.basicConfig(format=FORMAT)
l = logging.getLogger()
l.setLevel(TRACE)
l.log(TRACE, 'trace message')
l.setLevel(logging.DEBUG)
l.log(TRACE, 'disabled trace message')
제 경험상, 이게 작전의 문제를 해결할 수 있는 완전한 해결책은...메시지를 내보내는 기능으로 "메시지"가 표시되지 않도록 하려면 더 자세히 설명합니다.
MY_LEVEL_NUM = 25
logging.addLevelName(MY_LEVEL_NUM, "MY_LEVEL_NAME")
def log_at_my_log_level(self, message, *args, **kws):
# Yes, logger takes its '*args' as 'args'.
self._log(MY_LEVEL_NUM, message, args, **kws)
logger.log_at_my_log_level = log_at_my_log_level
독립 실행형 로거 클래스로 작업해 본 적은 없지만 기본적인 아이디어는 같다고 생각합니다(_log 사용).
파일 이름과 줄 번호를 올바르게 가져오기 위한 Mad Physics 예제에 추가:
def logToRoot(message, *args, **kwargs):
if logging.root.isEnabledFor(levelNum):
logging.root._log(levelNum, message, args, **kwargs)
고정된 답변을 기반으로, 나는 자동으로 새로운 로깅 수준을 만드는 작은 방법을 썼습니다.
def set_custom_logging_levels(config={}):
"""
Assign custom levels for logging
config: is a dict, like
{
'EVENT_NAME': EVENT_LEVEL_NUM,
}
EVENT_LEVEL_NUM can't be like already has logging module
logging.DEBUG = 10
logging.INFO = 20
logging.WARNING = 30
logging.ERROR = 40
logging.CRITICAL = 50
"""
assert isinstance(config, dict), "Configuration must be a dict"
def get_level_func(level_name, level_num):
def _blank(self, message, *args, **kws):
if self.isEnabledFor(level_num):
# Yes, logger takes its '*args' as 'args'.
self._log(level_num, message, args, **kws)
_blank.__name__ = level_name.lower()
return _blank
for level_name, level_num in config.items():
logging.addLevelName(level_num, level_name.upper())
setattr(logging.Logger, level_name.lower(), get_level_func(level_name, level_num))
구성은 다음과 같이 수행할 수 있습니다.
new_log_levels = {
# level_num is in logging.INFO section, that's why it 21, 22, etc..
"FOO": 21,
"BAR": 22,
}
누군가 루트 수준 사용자 지정 로깅을 수행하고 logging.get_logger(')를 사용하지 않도록 할 수 있습니다.
import logging
from datetime import datetime
c_now=datetime.now()
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] :: %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("../logs/log_file_{}-{}-{}-{}.log".format(c_now.year,c_now.month,c_now.day,c_now.hour))
]
)
DEBUG_LEVELV_NUM = 99
logging.addLevelName(DEBUG_LEVELV_NUM, "CUSTOM")
def custom_level(message, *args, **kws):
logging.Logger._log(logging.root,DEBUG_LEVELV_NUM, message, args, **kws)
logging.custom_level = custom_level
# --- --- --- ---
logging.custom_level("Waka")
혼란스럽습니다. 파이썬 3.5에서는 적어도 작동합니다.
import logging
TRACE = 5
"""more detail than debug"""
logging.basicConfig()
logging.addLevelName(TRACE,"TRACE")
logger = logging.getLogger('')
logger.debug("n")
logger.setLevel(logging.DEBUG)
logger.debug("y1")
logger.log(TRACE,"n")
logger.setLevel(TRACE)
logger.log(TRACE,"y2")
출력:
디버그:루트:y1
트레이스:루트:y2
Eric S.와 Mad Physics의 최고의 답변에 대한 후속 조치:
로깅 수준이 활성화되었는지 확인하는 수정된 예:
import logging DEBUG_LEVELV_NUM = 9 logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV") def debugv(self, message, *args, **kws): if self.isEnabledFor(DEBUG_LEVELV_NUM): # Yes, logger takes its '*args' as 'args'. self._log(DEBUG_LEVELV_NUM, message, args, **kws) logging.Logger.debugv = debugv
이 코드 스니펫
- 새 로그 수준 "DEBUGV"를 추가하고 숫자 9를 할당합니다.
- 는 을 합니다.
debugv- 9보다 값수준 "DEBUGmethod - "DEBUG" - "DEBUGV")으로 되지 않은 메시지를 "DEBUGV합니다. -
logging.Logger-ㄹ게요, 당이전수있도록화할스클래.logger.debugv
제안된 구현은 나에게 잘 먹혔지만,
- 코드완인못합니다지식하가를 .
logger.debugv - CI를 추적할 수 없기 합니다. 왜냐하면 그것은 추적할 수 없기 때문입니다.
debugvof -member of the -memberLogger-class (멤버 추가에 대한 동적 정보는 https://github.com/microsoft/pylance-release/issues/2335 참조)
저는 결국 누팔 이브라힘의 대답에서 제안된 것처럼 상속을 사용하게 되었습니다.
Logger 클래스를 하위 분류하고 기본적으로 DEBUG보다 낮은 수준의 Logger.log를 호출하는 trace라는 메서드를 추가해야 할 것 같습니다.저는 이것을 시도해 본 적이 없지만 이것은 문서에 나와 있는 것입니다.
Noufal Ibrahim의 제안을 실행하는 것은 효과가 있었고 Pyright는 행복합니다.
import logging
# add log-level DEBUGV
DEBUGV = 9 # slightly lower than DEBUG (10)
logging.addLevelName(DEBUGV, "DEBUGV")
class MyLogger(logging.Logger):
"""Inherit from standard Logger and add level DEBUGV."""
def debugv(self, msg, *args, **kwargs):
"""Log 'msg % args' with severity 'DEBUGV'."""
if self.isEnabledFor(DEBUGV):
self._log(DEBUGV, msg, args, **kwargs)
logging.setLoggerClass(MyLogger)
그런 다음 로거 관리자를 사용하여 확장 로거의 인스턴스를 초기화할 수 있습니다.
logger = logging.getLogger("whatever_logger_name")
편집: 파이라이트가 다음을 인식하도록 하기 위해debugv-, - 예들어, 로캐해야할있다수니습도트스를거에 의해 된 해야 할 수도 .logging.getLogger이것을 원하는 사람:
import logging
from typing import cast
logger = cast(MyLogger, logging.getLogger("whatever_logger_name"))
저는 스레드에서 모든 최고의 답변(특히 https://stackoverflow.com/a/35804945 과 https://stackoverflow.com/a/55276759) 을 재조합하여 아래와 같이 IMO 범용적이고 가장 파이썬적인 방법을 얻었습니다.
import logging
from functools import partial, partialmethod
def add_logging_level(level_name, level_num, method_name=None):
"""
Comprehensively adds a new logging level to the `logging` module and the
currently configured logging class.
`level_name` becomes an attribute of the `logging` module with the value
`level_num`.
`methodName` becomes a convenience method for both `logging` itself
and the class returned by `logging.getLoggerClass()` (usually just
`logging.Logger`).
If `methodName` is not specified, `levelName.lower()` is used.
To avoid accidental clobberings of existing attributes, this method will
raise an `AttributeError` if the level name is already an attribute of the
`logging` module or if the method name is already present
Example
-------
>>> add_logging_level('TRACE', logging.DEBUG - 5)
>>> logging.getLogger(__name__).setLevel('TRACE')
>>> logging.getLogger(__name__).trace('that worked')
>>> logging.trace('so did this')
>>> logging.TRACE
5
"""
if not method_name:
method_name = level_name.lower()
if hasattr(logging, level_name):
raise AttributeError(f'{level_name} already defined in logging module')
if hasattr(logging, method_name):
raise AttributeError(
f'{method_name} already defined in logging module'
)
if hasattr(logging.getLoggerClass(), method_name):
raise AttributeError(f'{method_name} already defined in logger class')
# This method was inspired by the answers to Stack Overflow post
# http://stackoverflow.com/q/2183233/2988730, especially
# https://stackoverflow.com/a/35804945
# https://stackoverflow.com/a/55276759
logging.addLevelName(level_num, level_name)
setattr(logging, level_name, level_num)
setattr(
logging.getLoggerClass(), method_name,
partialmethod(logging.getLoggerClass().log, level_num)
)
setattr(logging, method_name, partial(logging.log, level_num))
새로운 로깅 수준을 로깅 모듈에 동적으로 추가하는 자동화된 방법을 원하는 사람이 있을 경우, 저는 @pfa의 답변을 확장하여 이 함수를 만들었습니다.
def add_level(log_name,custom_log_module=None,log_num=None,
log_call=None,
lower_than=None, higher_than=None, same_as=None,
verbose=True):
'''
Function to dynamically add a new log level to a given custom logging module.
<custom_log_module>: the logging module. If not provided, then a copy of
<logging> module is used
<log_name>: the logging level name
<log_num>: the logging level num. If not provided, then function checks
<lower_than>,<higher_than> and <same_as>, at the order mentioned.
One of those three parameters must hold a string of an already existent
logging level name.
In case a level is overwritten and <verbose> is True, then a message in WARNING
level of the custom logging module is established.
'''
if custom_log_module is None:
import imp
custom_log_module = imp.load_module('custom_log_module',
*imp.find_module('logging'))
log_name = log_name.upper()
def cust_log(par, message, *args, **kws):
# Yes, logger takes its '*args' as 'args'.
if par.isEnabledFor(log_num):
par._log(log_num, message, args, **kws)
available_level_nums = [key for key in custom_log_module._levelNames
if isinstance(key,int)]
available_levels = {key:custom_log_module._levelNames[key]
for key in custom_log_module._levelNames
if isinstance(key,str)}
if log_num is None:
try:
if lower_than is not None:
log_num = available_levels[lower_than]-1
elif higher_than is not None:
log_num = available_levels[higher_than]+1
elif same_as is not None:
log_num = available_levels[higher_than]
else:
raise Exception('Infomation about the '+
'log_num should be provided')
except KeyError:
raise Exception('Non existent logging level name')
if log_num in available_level_nums and verbose:
custom_log_module.warn('Changing ' +
custom_log_module._levelNames[log_num] +
' to '+log_name)
custom_log_module.addLevelName(log_num, log_name)
if log_call is None:
log_call = log_name.lower()
setattr(custom_log_module.Logger, log_call, cust_log)
return custom_log_module
언급URL : https://stackoverflow.com/questions/2183233/how-to-add-a-custom-loglevel-to-pythons-logging-facility
'sourcecode' 카테고리의 다른 글
| C와 C++의 표준 헤더 파일 목록 (0) | 2023.06.17 |
|---|---|
| Angular 앱에서 rxjs 관련 메모리 누수를 감지하는 방법 (0) | 2023.06.17 |
| Panda 'Freq' 태그에서 사용할 수 있는 값은 무엇입니까? (0) | 2023.06.17 |
| 배치 스크립트: 관리자 권한 확인 방법 (0) | 2023.06.17 |
| 이 SQL 쿼리를 최적화할 수 있습니까? (0) | 2023.06.17 |