Python의 로깅 기능에 사용자 지정 로그 수준을 추가하는 방법
내 응용 프로그램에 대해 loglevel TRACE (5)를 갖고 싶습니다 debug()
. 충분 하지 않다고 생각합니다 . 또한 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 일) : Eric S의 아주 좋은 제안을 기반으로 한 탁월한 솔루션 인 IMHO 인 pfa에 대한 수락 된 답변을 변경했습니다 .
@ 에릭 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
Python 2.7 용 class Logger
in 코드를 살펴보면 logging.__init__.py
모든 표준 로그 함수 (.critical, .debug 등)가 수행하는 작업입니다.
평판이 부족해서 다른 사람의 답변에 대한 답글을 게시 할 수없는 것 같습니다. Eric이이 사실을 발견하면 게시물을 업데이트하기를 바랍니다. =)
나는 "람다를 보지 않기"대답을 취했고 log_at_my_log_level이 추가되는 위치를 수정해야했다. 나도 Paul이 "이것이 작동하지 않는다고 생각합니다. log_at_my_log_level의 첫 번째 인자로 logger가 필요하지 않습니까?"라는 문제를 보았습니다. 이것은 나를 위해 일했습니다.
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
기존의 모든 답변과 많은 사용 경험을 결합하여 새로운 레벨을 완벽하게 원활하게 사용하기 위해 수행해야 할 모든 일의 목록을 생각해 냈다고 생각합니다. 아래 단계에서는 TRACE
값이 있는 새 수준 을 추가한다고 가정합니다 logging.DEBUG - 5 == 5
.
logging.addLevelName(logging.DEBUG - 5, 'TRACE')
이름으로 참조 할 수 있도록 내부적으로 등록 된 새 레벨을 가져 오려면 호출해야합니다.logging
일관성을 위해 새 레벨을 속성으로 추가해야합니다logging.TRACE = logging.DEBUG - 5
.- 호출 된 메서드를 모듈에
trace
추가해야합니다logging
. 그것은 단지처럼 행동한다debug
,info
등 trace
현재 구성된 로거 클래스에 호출 된 메서드 를 추가해야합니다. 이 보장 100 %가 아니기 때문에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)
이 질문은 다소 오래되었지만 동일한 주제를 다루었 고 이미 언급 한 것과 유사한 방법을 찾았는데, 이는 나에게 조금 더 깔끔해 보입니다. 이것은 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)
내부 방법 ( self._log
) 을 사용하는 나쁜 관행을 시작한 사람은 누구 이며 각 답변이 그에 기반한 이유는 무엇입니까?! Pythonic 솔루션은 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')
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')
예상대로 작동합니다.
Logger
클래스 를 하위 클래스로 만들고 trace
기본적으로 Logger.log
보다 낮은 수준 으로 호출 하는 메서드를 추가 해야한다고 생각합니다 DEBUG
. 나는 이것을 시도하지 않았지만 이것은 문서가 나타내는 것 입니다.
사용자 정의 로거 생성을위한 팁 :
- 사용하지 말고
_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")
당신은해야합니다 setFormatter
, setHandler
그리고 setLevel(TRACE)
온 handler
과에 log
실제로 낮은 수준의 추적을 SE는 자체
이것은 나를 위해 일했습니다.
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 문제는 @marqueed가 지적한대로 logger._log로 해결됩니다. 람다를 사용하는 것이 조금 더 깔끔해 보이지만 단점은 키워드 인수를 사용할 수 없다는 것입니다. 나는 그것을 직접 사용하지 않았으므로 큰 문제가 아닙니다.
참고 설정 : 여름에 학교가 나갑니다! 친구 치명적 설정 : 파일을 찾을 수 없습니다.
제 경험상, 이것이 op의 문제에 대한 완전한 해결책입니다. 메시지가 방출되는 함수로 "람다"를 보지 않으려면 더 깊이 들어가십시오.
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 Physicists 예제에 추가하여 올바른 파일 이름과 줄 번호를 얻으십시오.
def logToRoot(message, *args, **kwargs):
if logging.root.isEnabledFor(levelNum):
logging.root._log(levelNum, message, args, **kwargs)
우리는 이미 많은 정답을 가지고 있지만, 제 생각에는 다음이 더 비단뱀 적이라고 생각합니다.
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
속성 추가에서 경고를 표시하지 않도록 추가 하는 것이 좋습니다 .
Logger 클래스에 추가 메서드를 추가하는 대신 메서드를 사용하는 것이 좋습니다 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')
혼란 스럽습니다. 파이썬 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")
산출:
DEBUG:root:y1
TRACE:root:y2
based on pinned answer, i wrote a little method which automaticaly create new logging levels
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))
config may smth like that:
new_log_levels = {
# level_num is in logging.INFO section, that's why it 21, 22, etc..
"FOO": 21,
"BAR": 22,
}
In case anyone wants an automated way to add a new logging level to the logging module (or a copy of it) dynamically, I have created this function, expanding @pfa's answer:
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
'program tip' 카테고리의 다른 글
Eclipse에서 선택한 단어의 모든 발생을 강조 표시 할 수 없습니다. (0) | 2020.08.17 |
---|---|
PHP에서 stdClass 객체를 배열로 변환 (0) | 2020.08.17 |
보기 위해 프로세스 출력에 첨부 (0) | 2020.08.17 |
XmlDocument에서 줄 바꿈을 사용하여 들여 쓰기 된 XML을 얻는 가장 간단한 방법은 무엇입니까? (0) | 2020.08.17 |
C ++ / Linux에서 디렉토리 트리를 어떻게 만들 수 있습니까? (0) | 2020.08.17 |