Mój obecny ciąg formatu to:
formatter = logging.Formatter('%(asctime)s : %(message)s')
i chcę dodać nowe pole o nazwie, app_name
które będzie miało inną wartość w każdym skrypcie zawierającym ten formater.
import logging
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.addHandler(syslog)
Ale nie jestem pewien, jak przekazać tę app_name
wartość do programu rejestrującego, aby interpolować do ciągu formatu. Oczywiście mogę sprawić, że pojawi się w komunikacie dziennika, przekazując go za każdym razem, ale jest to bałagan.
Próbowałem:
logging.info('Log message', app_name='myapp')
logging.info('Log message', {'app_name', 'myapp'})
logging.info('Log message', 'myapp')
ale żadna nie działa.
log
połączenia? Jeśli tak, spójrz na dokumenty, w których jest napisane „Ta funkcja może być użyta do wstrzyknięcia własnych wartości do LogRecord…” Ale wydaje się, że jest to główny przypadek użycialogger = logging.getLogger('myapp')
i upieczenia go wlogger.info
wywołaniu.logger
obiektu w każdej aplikacji, można dokonać każdy z nich użyć innej nazwy instancji przez swojelogger
s tak:logger = logging.getLogger(myAppName)
. zwróć uwagę, że__name__
jest to nazwa modułu Pythona, więc jeśli każda aplikacja jest własnym modułem Pythona, to również zadziała.Odpowiedzi:
Możesz użyć LoggerAdapter, więc nie musisz przekazywać dodatkowych informacji przy każdym wywołaniu logowania:
import logging extra = {'app_name':'Super App'} logger = logging.getLogger(__name__) syslog = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s') syslog.setFormatter(formatter) logger.setLevel(logging.INFO) logger.addHandler(syslog) logger = logging.LoggerAdapter(logger, extra) logger.info('The sky is so blue')
dzienniki (coś w rodzaju)
2013-07-09 17:39:33,596 Super App : The sky is so blue
Filtry mogą być również używane do dodawania informacji kontekstowych.
import logging class AppFilter(logging.Filter): def filter(self, record): record.app_name = 'Super App' return True logger = logging.getLogger(__name__) logger.addFilter(AppFilter()) syslog = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s') syslog.setFormatter(formatter) logger.setLevel(logging.INFO) logger.addHandler(syslog) logger.info('The sky is so blue')
tworzy podobny zapis dziennika.
źródło
config.ini
pliku? Chcę dodać aktualną nazwę hostasocket.gethostname()
.import uuid uniqueId = str(uuid.uuid4()) extra = {"u_id" : uniqueId} RotatingHandler = RotatingFileHandler(LOG_FILENAME,encoding='utf-8',maxBytes=maxSize, backupCount=batchSize) logger.basicConfig(handlers=[RotatingHandler],level=logLevel.upper(),format='%(levelname)s %(u_id)s %(funcName)s %(asctime)s %(message)s ',datefmt='%m/%d/%Y %I:%M:%S %p') logger = logger.LoggerAdapter(logger=logger, extra=extra)
Musisz przekazać dict jako parametr extra, aby zrobić to w ten sposób.
logging.info('Log message', extra={'app_name': 'myapp'})
Dowód:
>>> import logging >>> logging.basicConfig(format="%(foo)s - %(message)s") >>> logging.warning('test', extra={'foo': 'bar'}) bar - test
Ponadto, jako uwaga, jeśli spróbujesz zarejestrować wiadomość bez przekazania dyktanda, to się nie powiedzie.
>>> logging.warning('test') Traceback (most recent call last): File "/usr/lib/python2.7/logging/__init__.py", line 846, in emit msg = self.format(record) File "/usr/lib/python2.7/logging/__init__.py", line 723, in format return fmt.format(record) File "/usr/lib/python2.7/logging/__init__.py", line 467, in format s = self._fmt % record.__dict__ KeyError: 'foo' Logged from file <stdin>, line 1
źródło
logging.info()
? Nie udało się, kiedy próbowałem ostatni. : /logging.Formatter
klasę: class CustomFormatter (logging.Formatter): def format (self, record): if not hasattr (record, 'foo'): record.foo = 'default_foo' return super (CustomFormatter, self.format (record) h = loggin.StreamHandler () h.setFormatter (CustomFormatter ('% (foo) s% (message) s') logger = logging.getLogger ('bar') logger.addHandler ( h) logger.error ('hey!', extra = {'foo': 'FOO'}) logger.error ('hey!')Python3
Od wersji Python3.2 możesz teraz używać LogRecordFactory
>>> import logging >>> logging.basicConfig(format="%(custom_attribute)s - %(message)s") >>> old_factory = logging.getLogRecordFactory() >>> def record_factory(*args, **kwargs): record = old_factory(*args, **kwargs) record.custom_attribute = "my-attr" return record >>> logging.setLogRecordFactory(record_factory) >>> logging.info("hello") my-attr - hello
Oczywiście
record_factory
można dostosować, aby można było je wywołać, a wartośćcustom_attribute
można zaktualizować, jeśli zachowasz odniesienie do wywoływanej fabryki.Dlaczego jest to lepsze niż używanie adapterów / filtrów?
logger = logging.getLogger(..)
), teraz będą miały ten sam format dziennika. (nie dotyczy to filtrów / adapterów, w których musisz używać tego samego obiektu rejestratora)źródło
Innym sposobem jest utworzenie niestandardowego LoggerAdapter. Jest to szczególnie przydatne, gdy nie możesz zmienić formatu LUB jeśli format jest współdzielony z kodem, który nie wysyła unikalnego klucza (w twoim przypadku nazwa_aplikacji ):
class LoggerAdapter(logging.LoggerAdapter): def __init__(self, logger, prefix): super(LoggerAdapter, self).__init__(logger, {}) self.prefix = prefix def process(self, msg, kwargs): return '[%s] %s' % (self.prefix, msg), kwargs
W swoim kodzie utworzyłbyś i zainicjował swój rejestrator jak zwykle:
logger = logging.getLogger(__name__) # Add any custom handlers, formatters for this logger myHandler = logging.StreamHandler() myFormatter = logging.Formatter('%(asctime)s %(message)s') myHandler.setFormatter(myFormatter) logger.addHandler(myHandler) logger.setLevel(logging.INFO)
Na koniec należy utworzyć adapter opakowania, aby w razie potrzeby dodać przedrostek:
logger = LoggerAdapter(logger, 'myapp') logger.info('The world bores you when you are cool.')
Wynik będzie wyglądał mniej więcej tak:
2013-07-09 17:39:33,596 [myapp] The world bores you when you are cool.
źródło
Znalazłem to pytanie SO po zaimplementowaniu go samodzielnie. Mam nadzieję, że to komuś pomoże. W poniższym kodzie indukuję dodatkowy klucz o nazwie
claim_id
w formacie rejestratora. Będzie rejestrować claim_id za każdym razem, gdyclaim_id
w środowisku znajduje się klucz. W moim przypadku potrzebowałem zarejestrować te informacje dla funkcji AWS Lambda.import logging import os LOG_FORMAT = '%(asctime)s %(name)s %(levelname)s %(funcName)s %(lineno)s ClaimID: %(claim_id)s: %(message)s' class AppLogger(logging.Logger): # Override all levels similarly - only info overriden here def info(self, msg, *args, **kwargs): return super(AppLogger, self).info(msg, extra={"claim_id": os.getenv("claim_id", "")}) def get_logger(name): """ This function sets log level and log format and then returns the instance of logger""" logging.setLoggerClass(AppLogger) logging.basicConfig(level=logging.INFO, format=LOG_FORMAT) logger = logging.getLogger(name) logger.setLevel(logging.INFO) return logger LOGGER = get_logger(__name__) LOGGER.info("Hey") os.environ["claim_id"] = "12334" LOGGER.info("Hey")
Streszczenie: https://gist.github.com/ramanujam/306f2e4e1506f302504fb67abef50652
źródło
Korzystając z odpowiedzi mr2ert, wymyśliłem to wygodne rozwiązanie (choć myślę, że nie jest to zalecane) - Zastąp wbudowane metody logowania, aby zaakceptować niestandardowy argument i utwórz
extra
słownik wewnątrz metod:import logging class CustomLogger(logging.Logger): def debug(self, msg, foo, *args, **kwargs): extra = {'foo': foo} if self.isEnabledFor(logging.DEBUG): self._log(logging.DEBUG, msg, args, extra=extra, **kwargs) *repeat for info, warning, etc* logger = CustomLogger('CustomLogger', logging.DEBUG) formatter = logging.Formatter('%(asctime)s [%(foo)s] %(message)s') handler = logging.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) logger.debug('test', 'bar')
Wynik:
2019-03-02 20:06:51,998 [bar] test
Oto wbudowana funkcja w celach informacyjnych:
def debug(self, msg, *args, **kwargs): """ Log 'msg % args' with severity 'DEBUG'. To pass exception information, use the keyword argument exc_info with a true value, e.g. logger.debug("Houston, we have a %s", "thorny problem", exc_info=1) """ if self.isEnabledFor(DEBUG): self._log(DEBUG, msg, args, **kwargs)
źródło
importowanie logowania;
klasa LogFilter (logging.Filter):
def __init__(self, code): self.code = code def filter(self, record): record.app_code = self.code return True
logging.basicConfig (format = '[% (asctime) s:% (levelname) s] :: [% (module) s ->% (name) s] - APP_CODE:% (app_code) s - MSG:% (message ) s ');
klasa Logger:
def __init__(self, className): self.logger = logging.getLogger(className) self.logger.setLevel(logging.ERROR) @staticmethod def getLogger(className): return Logger(className) def logMessage(self, level, code, msg): self.logger.addFilter(LogFilter(code)) if level == 'WARN': self.logger.warning(msg) elif level == 'ERROR': self.logger.error(msg) else: self.logger.info(msg)
class Test: logger = Logger.getLogger ('Test')
if __name__=='__main__': logger.logMessage('ERROR','123','This is an error')
źródło