structlogã·ãªã¼ãºãä»åï¼ãã¶ãæçµåï¼ã¯ get_logger() ã«ã¤ãã¦ãã½ã¼ã¹ã³ã¼ãã解説ãã¦ããåã«ãã¾ãéè¦ãªæåããã
>>> import structlog >>> from structlog import PrintLogger >>> def logger_factory(): ... print("creating logger") ... return PrintLogger() ... >>> structlog.configure(logger_factory=logger_factory) >>> logger = structlog.get_logger() >>> logger <BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())> >>> logger.info("hello") creating logger 2024-06-24 15:31:41 [info ] hello >>> logger.info("hello") creating logger 2024-06-24 15:31:43 [info ] hello
- logger_factory 㯠Bound Logger ã«æ¸¡ã wrapped logger ãçæãã颿°
- get_logger() 㯠Bound Logger ã§ã¯ãªããBoundLoggerLazyProxy ã¨ããã¯ã©ã¹ã®ã¤ã³ã¹ã¿ã³ã¹ãè¿ã
- BoundLoggerLazyProxyã®ã¡ã½ãããå¼ã³åºããã³ã« Bound Logger ãä½ããããã®åº¦ã« logger_factory ãå®è¡ããã
structlog ã¯åãªãã¢ããªã®ãã®ã³ã°ã ãã§ãªãæ¨æºã©ã¤ãã©ãªã® logging ã®ä»£ããã«ä½¿ããããã¨ãèãã¦ä½ããã¦ããã®ã§ãæ§æè¨å®åã« get_logger ãå¼ã°ãããã¨ãèãã¦ããã©ã«ãã§ã¯æ¯åè¨å®ããBoundLoggerãçæããããã«ãªã£ã¦ãã¾ãã
ãããæµç³ã«ããã¯å¹çãæ§è½é¢ã§è¯ããªãã®ã§ã cache_logger_on_first_use
ã¨ãããªãã·ã§ã³ã使ããã¨ããå§ããã¾ãããã®ãªãã·ã§ã³ãæå¹ã«ãã㨠logger (BoundLoggerLazyProxy) ã¯ä¸åº¦ä½ã£ãBoundLoggerãåå©ç¨ããããã«ãªãã¾ãã
>>> structlog.configure(cache_logger_on_first_use=True) >>> logger.info("hello") creating logger 2024-06-24 15:56:03 [info ] hello >>> logger.info("hello") 2024-06-24 15:56:06 [info ] hello
- BoundLoggerLazyProxy ã¯ããã©ã«ãã§ã°ãã¼ãã«ã® cache_logger_on_first_use è¨å®ãåç
§ãã
- get_logger() ã§ logger ãä½ãããå¾ã« cache_logger_on_first_use=True ãè¨å®ãã¦ãè¯ãããã«ãªã£ã¦ãã
- ãã ããç°ãªã BoundLoggerLazyProxy éã§ã¯ãã®ãã£ãã·ã¥ã¯å
±æããã¦ããªã
- åä¸ã® logger ã¤ã³ã¹ã¿ã³ã¹ãè¤æ°ã® logger ãã使ãããå ´åã¯ã logger factory ãä½åº¦å¼ã°ãã¦ãåä¸ã® logger ã¤ã³ã¹ã¿ã³ã¹ãè¿ãããã«ãã
ã½ã¼ã¹ã³ã¼ã解説
get_logger()
ã BoundLoggerLazyProxy ãã structlog._config
ã¨ããã¢ã¸ã¥ã¼ã«ã§å®è£
ããã¦ãã¾ãã
def get_logger(*args: Any, **initial_values: Any) -> Any: return wrap_logger(None, logger_factory_args=args, **initial_values) def wrap_logger( logger: WrappedLogger | None, processors: Iterable[Processor] | None = None, wrapper_class: type[BindableLogger] | None = None, context_class: type[Context] | None = None, cache_logger_on_first_use: bool | None = None, logger_factory_args: Iterable[Any] | None = None, **initial_values: Any, ) -> Any: return BoundLoggerLazyProxy( logger, wrapper_class=wrapper_class, processors=processors, context_class=context_class, cache_logger_on_first_use=cache_logger_on_first_use, initial_values=initial_values, logger_factory_args=logger_factory_args, )
- get_logger() 㯠logger_factory_args ã¨ãã弿°ï¼å¾è¿°ï¼ã渡ãããããªã£ã¦ãã以å¤ã¯ã wrap_logger() ãããã©ã«ã弿°ã®ã¾ã¾å¼ãã§ããã ã
- wrap_logger() 㯠BoundLoggerLazyProxy() ãçæãããã°ãã¼ãã«ã®è¨å®ã弿°ã§ä¸æ¸ãã§ããããã«ãªã£ã¦ããã
class BoundLoggerLazyProxy: def __getattr__(self, name: str) -> Any: bl = self.bind() return getattr(bl, name) def bind(self, **new_values: Any) -> BindableLogger: ... cls = self._wrapper_class or _CONFIG.default_wrapper_class logger = cls(_logger, processors=procs, context=ctx) def finalized_bind(**new_values: Any) -> BindableLogger: """ Use cached assembled logger to bind potentially new values. """ if new_values: return logger.bind(**new_values) return logger if self._cache_logger_on_first_use is True or ( self._cache_logger_on_first_use is None and _CONFIG.cache_logger_on_first_use is True ): self.bind = finalized_bind # type: ignore[method-assign] return finalized_bind(**new_values)
- BoundLoggerLazyProxy ã¯ãã°ã¡ã½ãããå¼ã°ãããã³ã« bind() ãå®è¡ãã¦ãã
- å
ã«æç¤ºçã«
log = logger.bind()
ãã¦ããã°ã BoundLoggerLazyProxy ãåé¿ãã¦ç´æ¥ Bound Logger ã®ã¡ã½ãããå¼ã³åºãã
structlog.stdlibã«ã¤ãã¦
structlogã®ããã©ã«ãã®è¨å®ã¯PrintLoggerã使ãããã«ãªã£ã¦ããã®ã§ããã structlog.stdlib
ã¢ã¸ã¥ã¼ã«ã® recreate_defaults()
ã使ãã¨ã»ã¼åãåºåãã logging çµç±ã§åºåã§ããããã«ãªã£ã¦ãã¾ãã
def recreate_defaults(*, log_level: int | None = logging.NOTSET) -> None: if log_level is not None: kw = {"force": True} logging.basicConfig( format="%(message)s", stream=sys.stdout, level=log_level, **kw, # type: ignore[arg-type] ) _config.reset_defaults() _config.configure( processors=[ ... # ããã©ã«ãã¨ä¼¼ãããã¹ãã«ãã©ã¼ãããããè¨å® ], wrapper_class=BoundLogger, logger_factory=LoggerFactory(), ) def get_logger(*args: Any, **initial_values: Any) -> BoundLogger: return _config.get_logger(*args, **initial_values) class LoggerFactory: def __call__(self, *args: Any) -> logging.Logger: if args: return logging.getLogger(args[0]) _, name = _find_first_app_frame_and_name(self._ignore) return logging.getLogger(name)
stdlib.recreates_defaults()
ã使ãã¨ã stdlib.BoundLogger 㨠stdlib.LoggerFactory ã使ãããã«ãªã- BoundLoggerã¯ä»¥åã«èª¬æããéãã logging.Logger ã¨ã·ã°ããã£ãä¼¼ã㦠static type hint ãã¤ããä¸ã§ããã°ã¡ãã»ã¼ã¸ã®ä½ç½®å¼æ°ã«å¯¾å¿ãããã®
- ãã ãprocessorsã«ã¯ PositionalArgumentsFormatter ãå
¥ã£ã¦ããªãã®ã§ãä½ç½®å¼æ°ã¯ãã©ã¼ããããããã
event_dict["positional_args"]
ã«å ¥ã£ãã¾ã¾ãã©ã¼ãããããã - log_level ãæ¸¡ã㨠logging.basicConfig() ããã¦ãããããã°ãã©ã¼ãããã¯
"%(message)s"
ã§ããã§ã« structlog ããã©ã¼ããããããã°ããã®ã¾ã¾è¡¨ç¤ºããã ãï¼ã¿ã¤ã ã¹ã¿ã³ãçã¯structlogãã¤ããï¼ stdlib.get_logger()
ã¯æ»ãå¤ã®åãã³ããstdlib.BoundLogger
ã«ãªãã ãã§ããã¨ã¯structlog.get_logger()
ã®ã¾ã¾ãrecreates_defaults()
ã使ã£ã¦ãªãã¦ãåãã³ãã®ããã ãã«ä½¿ããã¨ã¯ã§ãããããã®å ´åã¯åãã³ãã§ã¯ä½ç½®å¼æ°ãåããã¨ã«ãªãã®ã§æ³¨æãå¿ è¦ã
stdlib.LoggerFactory
ã¯logging.getLogger()
ãå¼ã¶ãstructlog.get_logger()
ã®å¼æ°ã LoggerFactory ã«æ¸¡ãããä»çµã¿ãããã§çãã¦ãããstructlog.get_logger("foo.bar")
ã¯logging.getLogger("foo.bar")
ãå¼ã¶ã- 弿°ã渡ããªãå ´åã¯å¼ã³åºãå ã®ã¢ã¸ã¥ã¼ã«åãå©ç¨ããã
ã¢ããªã±ã¼ã·ã§ã³ã§structlogã使ãå ´å
stdlib.LoggerFactoryãstdlib.BoundLoggerã使ããã©ããã¯ã©ã¡ãã§ãè¯ããloggingã¨structlogãçµã¿åãããå¥ã®æ¹æ³ã¯éå»ã«æ¸ããè¨äºãåèã«ã
structlogとloggingの併用方法について - methaneのブログ
ã¢ããªã±ã¼ã·ã§ã³å
ã§ãã°ãæ¸ãå ´åãã©ã¤ãã©ãªãæ¸ãå ´åã¨éã£ã¦ã¢ããªã±ã¼ã·ã§ã³ã®èµ·åç´å¾ã«ã¾ããã®ã³ã°ã®è¨å®ãå§ãããããããããã¨ã structlog.get_logger()
ã使ãçç±ã¯ãã¾ããªããªãã myapp.log
ã¢ã¸ã¥ã¼ã«ã§ logger = structlog.BoundLogger(...)
ã®ããã«ç´æ¥ BoundLogger ã¤ã³ã¹ã¿ã³ã¹ãä½ã£ã¦ããã以å¤ã®ã¢ã¸ã¥ã¼ã«ã¯ from myapp.log import logger
ããã°è¯ãã
loggingã使ã£ã¦ããæã«æ
£ç¿ã¨ã㦠logging.getLogger(__name__)
ã使ã£ã¦ããå ´åã¯ãstructlogã«ç§»è¡ããæã«ãã®æ
£ç¿ãæ®ããã©ããèããªãã¨è¯ãã ããã
stdlib.LoggerFactory
ã¨ã¢ã¸ã¥ã¼ã«ãã¨ã®logger = structlog.get_logger(__name__)
ã使ãlogger = myapp.log.logger.bind(module=__name__)
ã®ããã«ãã¦ãæ§é åãã°ã®ä¸èº«ã« module ãå ¥ãã- å®å
¨ã«ãã®æ
£ç¿ãæ¢ãã代ããã«å¼ã³åºãå
ã®å ´æã
CallsiteParameterAdder
ã§è¡æ°ã¾ã§æ®ãã
ã¾ããè¤æ°ç¨®é¡ã®ãã°ãæ®ãããå ´åãã structlog.get_logger()
ã使ã代ããã«ç´æ¥ BoundLogger ã¤ã³ã¹ã¿ã³ã¹ãä½ãã¨è¯ãã ãããããããã°ãã°ã®ç¨®é¡ãã¨ã«åºåå
ããã©ã¼ããããªã©ã®è¨å®ãå¤ãããããä¾ãã°ãããã°ç¨ã®ããã¹ããã°ãæ¨æºåºåã«åºãã¦ãå¥ã«ãªã¯ã¨ã¹ããã¨ã«æ§é åã¤ãã³ããJSONãã©ã¼ãããã§fluentbitçµç±ã§BigQueryã«éããªã©ã®çµã¿åãããå¯è½ã ã