Korzystanie z Pythona logowanie do wielu modułów

Mam mały projekt Pythona, który ma następującą strukturę -

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

Planuję użyć domyślnego modułu logowania do drukowania wiadomości na stdout i pliku dziennika. Aby użyć modułu logging, wymagana jest pewna inicjalizacja -

import logging.config

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

logger.info('testing')

Obecnie wykonuję tę inicjalizację w każdym module, zanim zacznę rejestrować wiadomości. Czy możliwe jest przeprowadzenie tej inicjalizacji tylko raz w jednym miejscu tak, że te same ustawienia są ponownie używane przez logowanie całego projektu?

Author: Renato Damas, 0000-00-00

7 answers

Najlepszą praktyką w każdym module jest zdefiniowanie loggera w następujący sposób:

import logging
logger = logging.getLogger(__name__)

W górnej części modułu, a następnie w innym kodzie w module do np.

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

Jeśli chcesz podzielić aktywność logowania wewnątrz modułu, użyj np.

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

Oraz odpowiednio log do loggerA i loggerB.

W Twoim głównym programie lub programach wykonaj np.:

def main():
    "your program code"

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

Lub

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

if __name__ == '__main__':
    main()

Zobacz tutaj dla logowania z wielu modułów, i tutaj do logowania konfiguracji kodu, który będzie używany jako moduł biblioteki przez inny kod.

Update: podczas wywoływania fileConfig(), możesz określić disable_existing_loggers=False Jeśli używasz Pythona 2.6 lub nowszego (zobacz dokumenty aby uzyskać więcej informacji). Domyślną wartością jest True dla zgodności wstecznej, co powoduje, że wszystkie istniejące Rejestratory są wyłączone przez fileConfig(), chyba że są one lub ich przodek są jawnie nazwane w konfiguracji. Z wartością ustawioną na False pozostaną istniejące rejestratory sam. Jeśli używasz Pythona 2.7/Pythona 3.2 lub nowszego, możesz rozważyć API dictConfig(), które jest lepsze niż fileConfig(), ponieważ daje większą kontrolę nad konfiguracją.

 182
Author: Vinay Sajip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-13 07:47:38

Właściwie każdy rejestrator jest potomkiem rodzica (np. package.subpackage.module dziedziczy konfigurację z package.subpackage), więc wystarczy tylko skonfigurować rejestrator główny. Można to osiągnąć poprzez logging.config.fileConfig (własny config dla loggerów) lub logging.basicConfig (ustawia root logger). Konfiguracja logowania w module wejściowym (__main__.py lub cokolwiek chcesz uruchomić, na przykład main_script.py. __init__.py działa również)

Używanie basicConfig:

# package/__main__.py
import logging
import sys

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

Za pomocą fileConfig:

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

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

A następnie utwórz każdy logger używając:

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

log.info("Hello logging!")

Aby uzyskać więcej informacji, zobacz Advanced Logging Tutorial.

 72
Author: Stan Prokop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-20 08:34:01

Zawsze robię to jak poniżej.

Użyj pojedynczego pliku Pythona, aby skonfigurować mój dziennik jako wzorzec Singletona o nazwie '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')

W innym module, po prostu zaimportuj konfigurację.

from log_conf import Logger

Logger.logger.info("Hello World")

Jest to wzór Singletona do logowania, prosto i efektywnie.

 19
Author: Yarkee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-07 12:08:15

@ Yarkee rozwiązanie wydawało się lepsze. Chciałbym dodać coś do niego -

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)

Więc LoggerManager może być podłączany do całej aplikacji. Mam nadzieję, że to ma sens i wartość.

 5
Author: deeshank,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-06 09:28:23

Dorzucam inne rozwiązanie.

W głównym module init mam coś takiego:

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

Następnie w każdej klasie potrzebuję loggera, robię:

from [modname] import get_module_logger
logger = get_module_logger(__name__)

Gdy logi zostaną pominięte, możesz odróżnić ich źródło po module, z którego pochodzą.

 5
Author: Tommy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-12 01:11:58

Kilka z tych odpowiedzi sugeruje, że na górze modułu robisz

import logging
logger = logging.getLogger(__name__)

Rozumiem, że jest to uważane za bardzo złą praktykę. Powodem jest to, że plik config domyślnie wyłączy wszystkie istniejące rejestratory. Np.

#my_module
import logging

logger = logging.getLogger(__name__)

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

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

I w głównym module:

#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()

Teraz log określony w logowaniu.ini będzie puste, ponieważ istniejący rejestrator został wyłączony przez wywołanie fileconfig.

Podczas gdy jest z pewnością możliwe, aby obejść to (disable_existing_Loggers=False), realistycznie wielu klientów twojej Biblioteki nie będzie wiedzieć o tym zachowaniu i nie otrzyma Twoich dzienników. Ułatw to swoim klientom, zawsze dzwoniąc do logowania.getLogger lokalnie. Hat Tip: dowiedziałem się o tym zachowaniu z strony internetowej Victora Lin.

Więc dobrą praktyką jest zawsze wywoływanie logowania.getLogger lokalnie. Np.

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

Również, jeśli używasz fileconfig w głównym, Ustaw disable_existing_loggers=False, na wszelki wypadek projektanci bibliotek używają instancji rejestratora poziomu modułów.

 4
Author: phil_20686,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-18 11:34:02

Możesz też wymyślić coś takiego!

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

Teraz możesz użyć wielu loggerów w tym samym module i w całym projekcie, jeśli powyższy jest zdefiniowany w oddzielnym module i zaimportowany w innych modułach wymagane jest logowanie.

a=get_logger("__app___")
b=get_logger("__basic_log__")
a.info("Starting logging!")
b.debug("Debug Mode")
 3
Author: deeshank,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-11 18:29:24