ã¯ããã«ïŒãªã structlog ãªã®ãïŒ ð€
ãœãããŠã§ã¢éçºã«ãããŠããã®ã³ã°ã¯ãã°ã®è¿œè·¡ãã·ã¹ãã ã®ç£èŠãããã©ãŒãã³ã¹åæã«äžå¯æ¬ ãªèŠçŽ ã§ããPythonã«ã¯æšæºã§ `logging` ã¢ãžã¥ãŒã«ããããŸããã倧èŠæš¡ãªã·ã¹ãã ããã€ã¯ããµãŒãã¹ç°å¢ã§ã¯ãåŸæ¥ã®ããã¹ãããŒã¹ã®ãã°ã¯è§£æãé£ãããå¿ èŠãªæ å ±ãè¿ éã«èŠã€ãåºãã®ãå°é£ã«ãªãããšããããŸãã
ããã§ç»å Žããã®ã structlog ã§ãïŒð structlog ã¯ãPythonã®ãã®ã³ã°ãããéããç°¡åã«ããããŠåŒ·åã«ããããã®ã©ã€ãã©ãªã§ããæ倧ã®ç¹åŸŽã¯ããã°ãšã³ããªã æ§é åããŒã¿ (ããŒãšå€ã®ãã¢) ãšããŠæ±ãç¹ã«ãããŸããããã«ããããã°ã¯äººéã«ãšã£ãŠãæ©æ¢°ã«ãšã£ãŠãèªã¿ãããã解æãããã圢åŒã«ãªããŸãã
structlogã¯2013幎ãããããã¯ã·ã§ã³ç°å¢ã§å©çšãããŠããã`asyncio` ãåãã³ããšãã£ãPythonã®é²åã«ãè¿œåŸããŠããŸããã·ã³ãã«ãªAPIãé«ãããã©ãŒãã³ã¹ããããŠéçºè ã®äœéšãåäžãããå€ãã®æ©èœãæäŸããŸãã
ãã®ããã°èšäºã§ã¯ãstructlogã®åºæ¬çãªäœ¿ãæ¹ããé«åºŠãªæ©èœãèšå®æ¹æ³ããããŠæšæºã® `logging` ã¢ãžã¥ãŒã«ãšã®é£æºãéãã«è³ããŸã§ã培åºçã«è§£èª¬ããŠãããŸãããããèªãã°ãããªãã®Pythonã¢ããªã±ãŒã·ã§ã³ã®ãã®ã³ã°æŠç¥ãæ Œæ®µã«åäžããããšééããªãã§ãïŒð
åºæ¬çãªäœ¿ãæ¹ïŒæåã®ãã°ãåºåããŠã¿ããïŒ ð°
ã€ã³ã¹ããŒã«
ãŸã㯠`structlog` ãã€ã³ã¹ããŒã«ããŸããããpipã䜿ã£ãŠç°¡åã«ã€ã³ã¹ããŒã«ã§ããŸããå¿ èŠã«å¿ããŠãã³ã³ãœãŒã«åºåã«è²ãã€ãã `colorama` ãäžç·ã«ã€ã³ã¹ããŒã«ãããšäŸ¿å©ã§ãïŒWindowsç°å¢ã§ã¯ç¹ã«æšå¥šãããŸãïŒã
pip install structlog colorama
ææ°ããŒãžã§ã³ïŒå·çæç¹ã§ã¯ 25.2.0 ãªã©ïŒãã€ã³ã¹ããŒã«ããããšããå§ãããŸãã
æåã®ãã°åºå
`structlog` ã®æãã·ã³ãã«ãªäœ¿ãæ¹ã¯ã`getLogger` (ãŸã㯠`get_logger`) ãåŒã³åºããŠãã¬ãŒã€ã³ã¹ã¿ã³ã¹ãååŸãããã°ã¡ãœããïŒ`info`, `warn`, `error` ãªã©ïŒãåŒã³åºãã ãã§ãã
import structlog
# ãã¬ãŒã€ã³ã¹ã¿ã³ã¹ãååŸ
log = structlog.get_logger()
# ã·ã³ãã«ãªãã°åºå
log.info("hello_world")
# ããŒãšå€ã®ãã¢ã§æ
å ±ãè¿œå
log.info("user_login", user_id=123, status="success")
# %ã¹ã¿ã€ã«ã®ãã©ãŒããããå©çšå¯èœ (ããã©ãŒãã³ã¹ãè¯ã)
log.warning("request_failed", url="/api/users", status_code=404, error="User not found %s", "user123")
ããã©ã«ãèšå®ã§ã¯ããã°ã¯æšæºåºåã«è²ä»ãã§è¡šç€ºããïŒ`colorama` ãã€ã³ã¹ããŒã«ãããŠããå ŽåïŒãããŒã¯ãŒãåŒæ°ã§æž¡ãããæ å ±ã¯ `key=value` 圢åŒã§åºåãããŸãã
structlog ã®åæèšå®
ããã©ã«ãã®åäœã§ã䟿å©ã§ãããå€ãã®å Žåãåºå圢åŒãåŠçå 容ãã«ã¹ã¿ãã€ãºããããªããŸãã`structlog.configure()` ã䜿ã£ãŠãstructlogã®æåãã°ããŒãã«ã«èšå®ããŸããããã¯éåžžãã¢ããªã±ãŒã·ã§ã³ã®èµ·åæã«äžåºŠã ãåŒã³åºããŸãã
import sys
import logging
import structlog
structlog.configure(
processors=[
# ãã°ã¬ãã«ããã¬ãŒåããã°ã¬ã³ãŒãã«è¿œå (æšæºã©ã€ãã©ãªloggingé£æºæ)
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
# ã¿ã€ã ã¹ã¿ã³ããè¿œå (ISOãã©ãŒããã)
structlog.processors.TimeStamper(fmt="iso"),
# ããŒãã¢ã«ãã¡ãããé ã«ãœãŒã
structlog.processors.dict_sort,
# äŸå€æ
å ±ãæŽåœ¢ããŠè¿œå
structlog.processors.format_exc_info,
# Unicodeæåããã³ãŒã
structlog.processors.UnicodeDecoder(),
# éçºç°å¢åãã®èªã¿ããã圢åŒã§åºå (è²ä»ã)
structlog.dev.ConsoleRenderer(colors=True),
# æ¬çªç°å¢åãã®JSON圢åŒã§åºåããå Žå (äŸ)
# structlog.processors.JSONRenderer()
],
# æšæºã©ã€ãã©ãªloggingãšã®é£æºèšå®
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# æšæºã©ã€ãã©ãªloggingã®åºæ¬çãªèšå® (å¿
èŠã«å¿ããŠ)
logging.basicConfig(
level=logging.INFO,
format="%(message)s", # structlogåŽã§ãã©ãŒãããããã®ã§ã·ã³ãã«ã«
stream=sys.stdout,
)
# èšå®åŸã®ãã¬ãŒååŸ
log = structlog.get_logger("my_app")
log.info("configuration_complete", framework="MyAwesomeFramework")
log.error("something_went_wrong", error_code=500, exception=ValueError("Oops!"))
ãã®èšå®äŸã§ã¯ãããã€ãã®ãããã»ããµããå®çŸ©ããæçµçã« `ConsoleRenderer` ã§ã³ã³ãœãŒã«ã«èªã¿ãããåºåããŠããŸããæ¬çªç°å¢ã§ã¯ `JSONRenderer` ã«åãæ¿ããããšã§ããã°éçŽã·ã¹ãã ãšã®é£æºã容æã«ãªããŸãã
äž»èŠæ©èœè§£èª¬ïŒstructlog ã®ãã¯ãŒã解ãæŸã€ïŒ ðª
`structlog` ã®ç䟡ã¯ããã®æè»æ§ãšã«ã¹ã¿ãã€ãºæ§ã«ãããŸããäž»èŠãªæ©èœã§ãããããã»ããµãããã€ã³ãã£ã³ã°ãããã©ãŒããã¿ããã©ãããŒããç解ããããšã§ãããå¹æçãªãã®ã³ã°ãå®çŸã§ããŸãã
ããã»ããµ (Processors)ïŒãã°ãèªåšã«å å·¥ããéæ³ âš
ããã»ããµã¯ `structlog` ã®å¿èéšãšãèšããæ©èœã§ããããã»ããµã¯ããã°ã€ãã³ãïŒ`event_dict` ãšåŒã°ããèŸæžïŒãåãåãããããå å·¥ããŠæ¬¡ã®ããã»ããµã«æž¡ãããããã¯æçµçãªåºå圢åŒã«ã¬ã³ããªã³ã°ããé¢æ°ã®ãã§ãŒã³ïŒãã€ãã©ã€ã³ïŒã§ãã
ããã»ããµã¯ Python ã®é¢æ°ãŸã㯠`__call__` ã¡ãœãããæã€ã¯ã©ã¹ã€ã³ã¹ã¿ã³ã¹ã§ãã以äžã®3ã€ã®åŒæ°ãåãåããŸãã
- `logger`: ã©ããããããã¬ãŒãªããžã§ã¯ãã
- `method_name`: åŒã³åºããããã°ã¡ãœããã®ååïŒäŸ: “info”, “warning”ïŒã
- `event_dict`: çŸåšã®ã³ã³ããã¹ããšã€ãã³ãæ å ±ãå«ãèŸæžã
ããã»ããµã¯å å·¥åŸã® `event_dict` ãè¿ããŸããæåŸã®ããã»ããµïŒéåžžã¯ã¬ã³ãã©ãŒïŒã®æ»ãå€ããæçµçãªãã°åºåãšãªããŸãã
çµã¿èŸŒã¿ããã»ããµã®äŸïŒ
ããã»ããµ | 説æ |
---|---|
structlog.processors.TimeStamper(fmt="iso", utc=True) |
ã€ãã³ãèŸæžã«ã¿ã€ã ã¹ã¿ã³ããè¿œå ããŸãããã©ãŒããã (`fmt`) ãUTC (`utc`) ãæå®ã§ããŸãã |
structlog.stdlib.add_log_level |
ãã°ã¬ãã«åãã€ãã³ãèŸæžã«è¿œå ããŸã (äŸ: `level=’info’`)ãæšæºã©ã€ãã©ãªé£æºæã«äœ¿çšããŸãã |
structlog.stdlib.add_logger_name |
ãã¬ãŒåãã€ãã³ãèŸæžã«è¿œå ããŸã (äŸ: `logger=’my_app’`)ãæšæºã©ã€ãã©ãªé£æºæã«äœ¿çšããŸãã |
structlog.processors.dict_sort |
ã€ãã³ãèŸæžã®ããŒãã¢ã«ãã¡ãããé ã«ãœãŒãããŸããåºåãå®å®ããããå Žåã«äŸ¿å©ã§ãã |
structlog.processors.format_exc_info |
äŸå€æ å ±ãååŸããæŽåœ¢ããŠã€ãã³ãèŸæžã«è¿œå ããŸãã |
structlog.processors.StackInfoRenderer() |
ã¹ã¿ãã¯æ å ±ãã¬ã³ããªã³ã°ããŠè¿œå ããŸãããããã°ã«åœ¹ç«ã¡ãŸãã |
structlog.processors.UnicodeDecoder() |
ã€ãã³ãèŸæžå ã®ãã€ãæååãUnicodeæååã«ãã³ãŒãããŸãã |
structlog.processors.JSONRenderer(serializer=json.dumps, **kwargs) |
ã€ãã³ãèŸæžãJSONæååã«ã¬ã³ããªã³ã°ããŸããæåŸã®ããã»ããµãšããŠäœ¿çšããŸãã |
structlog.dev.ConsoleRenderer(colors=True, **kwargs) |
ã€ãã³ãèŸæžãéçºè ã«ãšã£ãŠèªã¿ããã圢åŒã§ã³ã³ãœãŒã«ã«åºåããŸããæåŸã®ããã»ããµãšããŠäœ¿çšããŸãã |
structlog.contextvars.merge_contextvars |
`contextvars` ã䜿ã£ãŠèšå®ãããã³ã³ããã¹ãããŒã«ã«ãªå€ãã€ãã³ãèŸæžã«ããŒãžããŸããããã»ããµãã§ãŒã³ã®æåã«çœ®ãããšãæšå¥šãããŸãã |
structlog.stdlib.filter_by_level |
æšæºã©ã€ãã©ãª `logging` ã®ãã°ã¬ãã«ã«åºã¥ããŠã€ãã³ãããã£ã«ã¿ãªã³ã°ããŸãã |
ã«ã¹ã¿ã ããã»ããµã®äœæïŒ ç¬èªã®ããã»ããµãå®çŸ©ããŠãç¹å®ã®èŠä»¶ïŒäŸ: ç¹å®ã®ããŒã®ãã¹ãã³ã°ãã«ã¹ã¿ã ãã£ãŒã«ãã®è¿œå ïŒã«å¯Ÿå¿ããããšãç°¡åã§ãã
import os
import structlog
# ããã»ã¹IDãè¿œå ããã«ã¹ã¿ã ããã»ããµ
def add_process_info(_, __, event_dict):
event_dict["process_id"] = os.getpid()
return event_dict
# ç¹å®ã®ããŒããã¹ãã³ã°ããã«ã¹ã¿ã ããã»ããµ
def mask_sensitive_data(_, __, event_dict):
if "password" in event_dict:
event_dict["password"] = "***REDACTED***"
if "api_key" in event_dict:
event_dict["api_key"] = "***REDACTED***"
return event_dict
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.stdlib.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
add_process_info, # ã«ã¹ã¿ã ããã»ããµãè¿œå
mask_sensitive_data, # ã«ã¹ã¿ã ããã»ããµãè¿œå
structlog.dev.ConsoleRenderer(colors=True),
],
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
log = structlog.get_logger()
log.info("sensitive_operation", username="alice", password="very_secret_password", api_key="abc123xyz")
# åºåäŸ (äžéš): password='***REDACTED***' api_key='***REDACTED***' process_id=12345
ããã»ããµãçµã¿åãããããšã§ããã°ã®å 容ãšåœ¢åŒãéåžžã«æè»ã«å¶åŸ¡ã§ããŸãã ð¡
ãã€ã³ãã£ã³ã° (Binding)ïŒã³ã³ããã¹ãããã°ã«å»ã ð
ãã€ã³ãã£ã³ã°ã¯ãç¹å®ã®ã³ã³ããã¹ãæ å ±ïŒäŸ: ãªã¯ãšã¹ãIDããŠãŒã¶ãŒIDãã»ãã·ã§ã³IDïŒããã¬ãŒã«çŽä»ãããã®ãã¬ãŒããåºåããããã¹ãŠã®ãã°ãšã³ããªã«èªåçã«å«ããæ©èœã§ããããã«ãããç¹å®ã®åŠçãããŒããªã¯ãšã¹ãã«é¢é£ãããã°ãç°¡åã«è¿œè·¡ã§ããããã«ãªããŸãã
`structlog` ã§ã¯ãäž»ã«ä»¥äžã®æ¹æ³ã§ã³ã³ããã¹ãããã€ã³ãããŸãã
- `bind(**kwargs)`: æ¢åã®ãã¬ãŒã«æ°ããã³ã³ããã¹ãæ å ±ãè¿œå ããæ°ãããã¬ãŒã€ã³ã¹ã¿ã³ã¹ãè¿ããŸããå ã®ãã¬ãŒã¯å€æŽãããŸããïŒã€ãã¥ãŒã¿ãã«ïŒã
- `new(**kwargs)`: æ¢åã®ã³ã³ããã¹ããç¡èŠããæå®ãããããŒã¯ãŒãåŒæ°ã®ã¿ãã³ã³ããã¹ããšããŠæã€æ°ãããã¬ãŒã€ã³ã¹ã¿ã³ã¹ãè¿ããŸãã
- `unbind(*keys)`: æå®ãããããŒãã³ã³ããã¹ãããåé€ããæ°ãããã¬ãŒã€ã³ã¹ã¿ã³ã¹ãè¿ããŸãã
- `try_unbind(*keys)`: `unbind` ãšäŒŒãŠããŸãããæå®ãããããŒãååšããªããŠããšã©ãŒã«ãªããŸããã
- `contextvars` ã¢ãžã¥ãŒã« (`bind_contextvars`, `unbind_contextvars`, `clear_contextvars`, `bound_contextvars`): Python 3.7以éã§å°å ¥ããã `contextvars` ãå©çšããŠãã¹ã¬ããããŒã«ã«ãããå®å šã§ã`asyncio` ãªã©ã®éåæã³ãŒããšãäºææ§ã®ããã³ã³ããã¹ã管çãè¡ããŸãã
`bind` ã®äœ¿çšäŸïŒ
import structlog
log = structlog.get_logger()
# ãªã¯ãšã¹ãIDããã€ã³ã
request_log = log.bind(request_id="req-abc-123")
request_log.info("request_received", path="/home")
# ããã«ãŠãŒã¶ãŒIDããã€ã³ã
user_request_log = request_log.bind(user_id="user-456")
user_request_log.info("user_authenticated")
user_request_log.warn("resource_not_found", resource_id="res-789")
# å
ã® request_log ã«ã¯ user_id ã¯å«ãŸããªã
request_log.info("request_finished")
# æåã® log ã«ã¯ request_id ã user_id ãå«ãŸããªã
log.info("server_status", status="idle")
åºåäŸ (äžéš):
[info ] request_received request_id=req-abc-123 path=/home
[info ] user_authenticated request_id=req-abc-123 user_id=user-456
[warning ] resource_not_found request_id=req-abc-123 user_id=user-456 resource_id=res-789
[info ] request_finished request_id=req-abc-123
[info ] server_status status=idle
`contextvars` ã«ããã³ã³ããã¹ã管çïŒ
Webã¢ããªã±ãŒã·ã§ã³ãéåæåŠçã§ã¯ããªã¯ãšã¹ãããšãã¿ã¹ã¯ããšã«ã³ã³ããã¹ããåé¢ããå¿ èŠããããŸãã`contextvars` ã䜿ããšããããå®å šãã€å¹ççã«å®çŸã§ããŸããPython 3.7ã§å°å ¥ããããã®æ©èœã¯ãã¹ã¬ããããŒã«ã«å€æ° (thread-local) ã®åé¡ã解決ãã`asyncio` ã®ãããªå調çãã«ãã¿ã¹ã¯ç°å¢ã§ãæ£ããã³ã³ããã¹ããä¿æã§ããŸãã
`structlog` 㧠`contextvars` ãå©çšããäžè¬çãªæé ã¯ä»¥äžã®éãã§ãã
- `structlog.configure()` ã® `processors` ãªã¹ãã®æåã« `structlog.contextvars.merge_contextvars` ãè¿œå ããŸãããããã³ã³ããã¹ãããŒã«ã«ãªå€ããã°ã€ãã³ãã«ããŒãžãã圹å²ãæãããŸãã
- ãªã¯ãšã¹ãåŠçãã¿ã¹ã¯å®è¡ã®éå§æã« `structlog.contextvars.clear_contextvars()` ãåŒã³åºããŠãåã®åŠçã®ã³ã³ããã¹ããæ®ããªãããã«ãªã»ããããŸãã
- `structlog.contextvars.bind_contextvars(**kwargs)` ã䜿ã£ãŠãçŸåšã®å®è¡ã³ã³ããã¹ãã«çŽã¥ãå€ããã€ã³ãããŸããããã¯ãåãéåæã¿ã¹ã¯å ãåãã¹ã¬ããå ã§åŸç¶ã®ãã°åŒã³åºãã«èªåçã«è¿œå ãããŸãã
- å¿ èŠã«å¿ã㊠`structlog.contextvars.unbind_contextvars(*keys)` ã§ç¹å®ã®ããŒãã¢ã³ãã€ã³ãããŸãã
- `with structlog.contextvars.bound_contextvars(**kwargs):` ã®ããã«ã³ã³ããã¹ããããŒãžã£ã䜿ããšããããã¯ãæãããšãã«èªåçã«ã¢ã³ãã€ã³ãããããã䟿å©ã§ãã
import structlog
import asyncio
from contextvars import ContextVar
# --- structlog èšå® (merge_contextvars ãæåã«è¿œå ) ---
# (loggingèšå®ãé©åã«è¡ãããŠããåæ)
logging.basicConfig(level=logging.INFO, format="%(message)s", stream=sys.stdout)
structlog.configure(
processors=[
structlog.contextvars.merge_contextvars, # <-- ãããè¿œå
structlog.stdlib.add_log_level, # logging ãšã®é£æºãèæ
®
structlog.processors.TimeStamper(fmt="iso", utc=True),
structlog.dev.ConsoleRenderer(colors=True),
],
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
log = structlog.get_logger()
# ã³ã³ããã¹ãå€æ° (äŸ: ãªã¯ãšã¹ãIDãå¿
é ã§ã¯ãªããåãããããã®ãã)
request_id_var = ContextVar("request_id", default="unknown")
async def handle_request(request_num):
req_id = f"req-{request_num}-{asyncio.current_task().get_name()[-1]}" # ã¿ã¹ã¯åãççž®
request_id_var.set(req_id) # ContextVarãèšå®
# ãªã¯ãšã¹ãéå§æã«ã³ã³ããã¹ããã¯ãªã¢ããæ°ããå€ããã€ã³ã
structlog.contextvars.clear_contextvars()
structlog.contextvars.bind_contextvars(request_id=req_id, client_ip="192.168.1.100")
log.info("request_started") # request_id, client_ip ãèªåã§ä»äžããã
# bound_contextvars ã³ã³ããã¹ããããŒãžã£ã§äžæçã«ãŠãŒã¶ãŒIDããã€ã³ã
with structlog.contextvars.bound_contextvars(user_id=f"user_{request_num}"):
log.info("processing_user_data") # request_id, client_ip, user_id ãä»äž
await asyncio.sleep(0.1)
try:
if request_num == 1:
raise ValueError("Simulated error for request 1")
log.info("user_data_processed")
except Exception as e:
log.exception("error_processing_user_data") # äŸå€æ
å ±ãèšé²
# user_id ã¯ã³ã³ããã¹ããããŒãžã£ãæããã®ã§æ¶ãã
log.info("request_processing_done") # request_id, client_ip ã®ã¿
await asyncio.sleep(0.05)
log.info("request_finished") # request_id, client_ip ã®ã¿
async def main():
tasks = [handle_request(i) for i in range(3)]
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(main())
ãã®äŸã§ã¯ãå `handle_request` ã¿ã¹ã¯ (`asyncio` ã«ãã£ãŠäžŠè¡ã«å®è¡ãããå¯èœæ§ããããŸã) ããããããç¬ç«ãã `request_id`, `client_ip`, `user_id` ãæã€ãã°ãåºåããŸãã`merge_contextvars` ããã»ããµãããããã®ã³ã³ããã¹ãããŒã«ã«ãªå€ãåãã°ã€ãã³ã (`event_dict`) ã«èªåçã«ããŒãžããŠããããããéçºè ã¯ãã°åºåæã«æ¯åãããã®æ å ±ãæå®ããå¿ èŠããããŸãããããã«ãããéåæã³ãŒãã§ãã³ã³ããã¹ããæ··ããããšãªããæ£ç¢ºãªãã°è¿œè·¡ãå¯èœã«ãªããŸããWebãã¬ãŒã ã¯ãŒã¯ (FastAPI, Django ASGIãªã©) ãšã®é£æºæã«éåžžã«åŒ·åã§ããð
ãã©ãŒããã¿ (Formatters) / ã¬ã³ãã©ãŒ (Renderers)ïŒãã°ã®èŠæ ãã決ãã ðš
`structlog` ã§ã¯ãããã»ããµãã§ãŒã³ã®æåŸã§ãã°ã€ãã³ãèŸæžãæçµçãªåºå圢åŒïŒéåžžã¯æååïŒã«å€æãã圹å²ããã¬ã³ãã©ãŒããšåŒã³ãŸããããã¯æšæº `logging` ã¢ãžã¥ãŒã«ã®ããã©ãŒããã¿ãã«çžåœããæŠå¿µã§ããã¬ã³ãã©ãŒã¯ãå å·¥ããã `event_dict` ãåãåãã人éãæ©æ¢°ãèªã¿åãã圢ã«ããŸãã
äž»ãªã¬ã³ãã©ãŒ:
- `structlog.dev.ConsoleRenderer`: éçºè åãã®ãè²ä»ãã§èªã¿ãããããŒ=å€åœ¢åŒã®åºåãçæããŸããã¿ã€ã ã¹ã¿ã³ãããã°ã¬ãã«ãç¹å¥æ±ãããã€ãã³ãã¡ãã»ãŒãžãç®ç«ãããŸãã`rich` ã `better-exceptions` ãã€ã³ã¹ããŒã«ãããŠããå ŽåãäŸå€ãã¬ãŒã¹ããã¯ããã詳现ãã€èŠããã衚瀺ããŸããããŒã«ã«éçºç°å¢ã§ã®å©çšã«æé©ã§ããð
- `structlog.processors.JSONRenderer`: ãã°ã€ãã³ãèŸæžãJSONæååã«å€æããŸããããŒã®ãœãŒããã€ã³ãã³ããã«ã¹ã¿ã ã·ãªã¢ã©ã€ã¶ã®æå®ãå¯èœã§ãããã°éçŽã·ã¹ãã ïŒELK Stack, Datadog, Splunk, Google Cloud Logging, AWS CloudWatch Logsãªã©ïŒãšã®é£æºã«ã¯äžå¯æ¬ ã§ããæ¬çªç°å¢ã§ã®æšæºçãªéžæè¢ãšãªããŸããðŸ
- `structlog.processors.KeyValueRenderer`: ã·ã³ãã«ãª `key=’value’ key2=’value2′ …` 圢åŒã®æååãçæããŸããå€ã¯ `repr()` ã§æåååãããŸãã`ConsoleRenderer` ã®åºæ¬çãªåœ¢åŒã§ãããè²ä»ããç¹å¥ãªæŽåœ¢ã¯ãããŸããã
- `structlog.stdlib.ProcessorFormatter`: ããã¯ã¬ã³ãã©ãŒãã®ãã®ã§ã¯ãªããæšæºã©ã€ãã©ãª `logging` ã® `Formatter` ãšããŠåäœããå éšã§æå®ããã `structlog` ã®ã¬ã³ãã©ãŒïŒãè¿œå ã®ããã»ããµïŒãåŒã³åºãããã®ããªããžã§ããããã«ããã`logging.getLogger()` ã§ååŸãããã¬ãŒããã®ãã°åºåãã`structlog` ã®ããã»ããµãšã¬ã³ãã©ãŒã䜿ã£ãŠçµ±äžããã圢åŒã«ã§ããŸãã
ç°å¢ã«å¿ããŠã¬ã³ãã©ãŒãåãæ¿ããã®ãäžè¬çãªãã©ã¯ãã£ã¹ã§ããäŸãã°ãç°å¢å€æ°ãèšå®ãã¡ã€ã«ã䜿ã£ãŠãéçºç°å¢ã§ã¯ `ConsoleRenderer` ããã¹ããŒãžã³ã°ãæ¬çªç°å¢ã§ã¯ `JSONRenderer` ã䜿çšããããã«æ§æããŸãã
import os
import sys
import logging
import structlog
import json # JSONRenderer ã®äŸã®ãã
# ç°å¢å€æ°ãªã©ããç°å¢ãå€å® (äŸ)
is_production = os.environ.get("APP_ENV") == "production"
# å
±éã®ããã»ããµå®çŸ©
shared_processors = [
structlog.contextvars.merge_contextvars,
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
structlog.processors.TimeStamper(fmt="iso", utc=True),
structlog.processors.dict_sort, # ããŒã®é åºãå®å®ããã
structlog.processors.format_exc_info, # äŸå€æ
å ±ãæŽåœ¢
structlog.processors.UnicodeDecoder(), # ãã€ãæååããã³ãŒã
]
# ç°å¢ã«å¿ããŠæåŸã®ã¬ã³ãã©ãŒããã»ããµãéžæ
if is_production:
# æ¬çªç°å¢: JSON圢åŒ
final_processors = shared_processors + [
# äžèŠãªæ
å ±ããã£ã«ã¿ãªã³ã°ããã«ã¹ã¿ã ããã»ããµ (äŸ)
# filter_internal_info,
# æçµçã«JSONã«ã¬ã³ããªã³ã°
structlog.processors.JSONRenderer(serializer=json.dumps), # æšæºã®jsonã䜿çš
]
log_format = "%(message)s" # JSONRendererãå®å
šãªJSONæååãçæããã®ã§ã·ã³ãã«ã«
else:
# éçºç°å¢: 人éã«èªã¿ãããã³ã³ãœãŒã«åœ¢åŒ
final_processors = shared_processors + [
structlog.dev.ConsoleRenderer(colors=True, exception_formatter=structlog.dev.plain_traceback),
]
log_format = "%(message)s" # ConsoleRendererãæŽåœ¢ãããæååãçæ
# structlog ã®èšå®
structlog.configure(
processors=final_processors,
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# æšæºã©ã€ãã©ãªloggingã®èšå®ãåãããŠè¡ã
# ProcessorFormatter ã䜿ã£ãŠ structlog ã®ã¬ã³ãã©ãŒã logging ã«é©çš
formatter = structlog.stdlib.ProcessorFormatter(
# foreign_pre_chain 㯠logging çµç±ã®ãã°ã«ã®ã¿é©çšãããããã»ããµ
# ãã㧠ConsoleRenderer ã JSONRenderer ãæå®ããªãããšã«æ³šæ
# structlog.configure ã§èšå®ããæåŸã®ããã»ããµã䜿ããã
processor=final_processors[-1], # configureã§èšå®ããã¬ã³ãã©ãŒã䜿ãããã«ããäŸ
foreign_pre_chain=shared_processors # å¿
èŠãªã logging çµç±ã®ãã°ã«è¿œå åŠç
)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
root_logger = logging.getLogger()
# æ¢åã®ãã³ãã©ãã¯ãªã¢ããŠéè€ãé²ã
if root_logger.hasHandlers():
root_logger.handlers.clear()
root_logger.addHandler(handler)
root_logger.setLevel(logging.INFO)
log = structlog.get_logger("env_aware_logger")
log.info("log_output_test", environment="production" if is_production else "development", data={"x": 1, "y": 2})
try:
1 / 0
except ZeroDivisionError:
log.error("division_by_zero", calculation="1/0")
# logging çµç±ã§ãåããã©ãŒããããé©çšããã
std_log = logging.getLogger("standard_lib_logger")
std_log.warning("This comes from standard logging", extra_info="details")
ãã®èšå®ã«ãããéçºæã«ã¯è²ä»ãã§èŠããããã°ããæ¬çªæã«ã¯JSON圢åŒã§æ§é åããããã°ãåºåããããã°ç®¡çããŒã«ã§ã®è§£æã容æã«ãªããŸããâš
ã©ãã㌠(Wrappers)ïŒæšæº logging ãšã®è¯éºãªãé£æº ð€
`structlog` ã¯ãåç¬ã§äœ¿çšããããšãã§ããŸããããã®èšèšææ³ã®äžå¿ã«ã¯ãæ¢åã®ãã®ã³ã°ã·ã¹ãã ãç¹ã«Pythonæšæºã©ã€ãã©ãªã® `logging` ãã©ããããŠæ©èœæ¡åŒµãããšããèãæ¹ããããŸããããã«ããã`logging` ãçŽæ¥äœ¿çšããŠããæ¢åã®ã³ãŒãããµãŒãããŒãã£ã©ã€ãã©ãªãåºåãããã°ãã`structlog` ã®ããã»ããµãã€ãã©ã€ã³ãéããŠåŠçããæ§é åã»çµ±äžåããããšãå¯èœã«ãªããŸãããã㯠`structlog` ã®éåžžã«åŒ·åãªç¹åŸŽã§ãã
é£æºãå®çŸããããã®äž»èŠãªã³ã³ããŒãã³ã:
- `structlog.stdlib.LoggerFactory()`: `structlog.configure()` ã® `logger_factory` åŒæ°ã«ãããæå®ãããšã`structlog.get_logger(“name”)` ãå éšã§ `logging.getLogger(“name”)` ãåŒã³åºãããã«ãªããŸããããã«ããã`structlog` 㯠`logging` ã®ãã¬ãŒéå±€ããã³ãã©èšå®ããã®ãŸãŸå©çšã§ããŸãã
- `structlog.stdlib.BoundLogger`: `structlog.configure()` ã® `wrapper_class` åŒæ°ã«ãããæå®ããŸãããã㯠`logging.Logger` ã€ã³ã¹ã¿ã³ã¹ãã©ãããã`structlog` ã¹ã¿ã€ã«ã®ã¡ãœããïŒ`bind()`, `info()`, `warn()`, `exception()` ãªã©ïŒãæäŸããŸãããããã®ã¡ãœãããåŒã°ãããšã`structlog` ã®ããã»ããµãã§ãŒã³ãå®è¡ãããæçµçã«ã©ããããã `logging.Logger` ã®å¯Ÿå¿ããã¡ãœããïŒäŸ: `_log()`, `exception()`ïŒãé©åãªåŒæ°ã§åŒã³åºãããŸããåãã³ããæäŸãããŠãããéçºãæ¯æŽããŸãã
- `structlog.stdlib.ProcessorFormatter`: ãã㯠`logging.Formatter` ã®ãµãã¯ã©ã¹ã§ãã`logging` ã®ãã³ãã©ã«ãã®ãã©ãŒããã¿ãèšå®ãããšã`logging` çµç±ã§åºåããããã°ïŒ`logging.info()` ãªã©ã§çŽæ¥åŒã³åºããããã®ïŒã«å¯ŸããŠãã`structlog` ã®ããã»ããµãã€ãã©ã€ã³ãé©çšã§ããããã«ãªããŸãã`processor` åŒæ°ã« `structlog` ã®ã¬ã³ãã©ãŒïŒäŸ: `ConsoleRenderer`, `JSONRenderer`ïŒãæå®ããå¿ èŠã«å¿ã㊠`foreign_pre_chain` åŒæ°ã§ `logging` åºæã®ãã°ã«è¿œå ãããååŠçããã»ããµãæå®ããŸãã
- `structlog.stdlib.recreate_defaults()`: `structlog` ã®ããã©ã«ãèšå®ããæšæºã©ã€ãã©ãª `logging` ãšé£æºããããã«äžæ¬ã§åæ§æãã䟿å©ãªé¢æ°ã§ããæå㧠`configure` ãåŒã³åºã代ããã«ãç°¡åãªé£æºèšå®ãè¡ãããå Žåã«äœ¿çšã§ããŸããå éšã§ `logging.basicConfig` ãåŒã³åºããªãã·ã§ã³ããããŸãã
- é£æºçšããã»ããµ (`structlog.stdlib.*`):
- `add_log_level`: `event_dict` ã« `level` ããŒãè¿œå ããŸãã
- `add_logger_name`: `event_dict` ã« `logger` ããŒãè¿œå ããŸãã
- `filter_by_level`: `logging` ã®ã¬ãã«èšå®ã«åºã¥ããŠã€ãã³ããæ©æã«ãã£ã«ã¿ãªã³ã°ããŸãã
- `PositionalArgumentsFormatter`: ãã°ã¡ãã»ãŒãžå ã® `%s` ã¹ã¿ã€ã«ã®äœçœ®åŒæ°ã `event_dict` ã« `positional_args` ãšããŠè¿œå ãããã©ãŒãããããŸãã
- `ProcessorFormatter.wrap_for_formatter`: `ProcessorFormatter` ãåŸç¶ã§æ£ããåäœããããã« `event_dict` ãæºåããããã»ããµã§ããéåžžã`ProcessorFormatter` ã䜿ãå Žåã¯ããã®ããã»ããµããã§ãŒã³ã®æåŸïŒã¬ã³ãã©ãŒã®æåïŒã«çœ®ããŸãã
`ProcessorFormatter` ã䜿ã£ãå®å šãªé£æºäŸïŒ
ãã®èšå®ã«ããã`structlog` ã§åºåãããã°ããæšæº `logging` ã§åºåãããã°ããåãããã»ããµçŸ€ãšã¬ã³ãã©ãŒã«ãã£ãŠåŠçãããå®å šã«äžè²«ããåºåãåŸãããŸãã
import sys
import logging
import structlog
import json # JSONRenderer ã®äŸã®ãã
import os
# ç°å¢èšå® (éçº or æ¬çª)
is_production = os.environ.get("APP_ENV") == "production"
# === structlog ã®èšå® ===
# å
±éããã»ããµ (ãã°ã¬ãã«ãã£ã«ã¿ããã¬ãŒåã»ã¬ãã«è¿œå ãäœçœ®åŒæ°ãã©ãŒããããªã©)
pre_chain = [
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
]
# ç°å¢ã«å¿ããæçµã¬ã³ãã©ãŒãšé¢é£ããã»ããµ
if is_production:
# æ¬çª: JSON
renderer = structlog.processors.JSONRenderer(serializer=json.dumps)
processors = pre_chain + [
structlog.processors.TimeStamper(fmt="iso", utc=True),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
# ProcessorFormatter ã®ããã«ã©ãã
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
]
else:
# éçº: Console
renderer = structlog.dev.ConsoleRenderer(colors=True)
processors = pre_chain + [
structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=False), # ããŒã«ã«ã¿ã€ã ã§èŠããã
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
# ProcessorFormatter ã®ããã«ã©ãã
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
]
structlog.configure(
processors=processors,
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# === logging ã®èšå® ===
# ProcessorFormatter ã䜿ã£ãŠ structlog ã®ã¬ã³ãã©ãŒãé©çš
formatter = structlog.stdlib.ProcessorFormatter(
processor=renderer, # configureã§å®çŸ©ããã¬ã³ãã©ãŒã䜿çš
foreign_pre_chain=pre_chain # loggingçµç±ã®ãã°ã«ãå
±éããã»ããµãé©çš
)
# ãã³ãã©ã®èšå®
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
# ã«ãŒããã¬ãŒã®èšå® (æ¢åã®ãã³ãã©ãã¯ãªã¢ããŠéè€ãé²ã)
root_logger = logging.getLogger()
if root_logger.hasHandlers():
root_logger.handlers.clear()
root_logger.addHandler(handler)
root_logger.setLevel(logging.DEBUG) # ã¢ããªã±ãŒã·ã§ã³å
šäœã®æäœã¬ãã«ãèšå®
# === ãã°åºåãã¹ã ===
# structlog çµç±ã®ãã°
slog = structlog.get_logger("my_app.module1")
slog.debug("This is a debug message from structlog", data=123)
slog.info("User logged in", user_id="alice", session_id="xyz789")
slog.warning("Disk space low", free_gb=5)
# æšæº logging çµç±ã®ãã° (ãµãŒãããŒãã£ã©ã€ãã©ãªãªã©ãæ³å®)
import requests # äŸãšã㊠requests ã©ã€ãã©ãªã®ãã°
logging.getLogger("urllib3").setLevel(logging.WARNING) # ã©ã€ãã©ãªã®ãã°ã¬ãã«èª¿æŽ
llog = logging.getLogger("external_library")
llog.info("Starting external process") # INFOã¬ãã«ãªã®ã§åºåããã
llog.error("External process failed with code %d", 500)
# äŸå€ã®ãã®ã³ã°
try:
result = requests.get("https://invalid-url-that-does-not-exist.xyz")
result.raise_for_status()
except requests.exceptions.RequestException as e:
slog.exception("Failed to fetch external data", url=e.request.url) # structlog ã§äŸå€è£è¶³
# llog.exception("Failed to fetch external data") # logging ã§ãå¯èœ
print(f"\n--- Running in {'Production' if is_production else 'Development'} mode ---")
ãã®èšå®ã«ããã`structlog.get_logger()` ã䜿ã£ãã³ãŒããã`logging.getLogger()` ã䜿ã£ãã³ãŒãããåãããã»ããµãã€ãã©ã€ã³ïŒ`pre_chain`ïŒãšæçµçãªã¬ã³ãã©ãŒïŒ`renderer`ïŒã«ãã£ãŠåŠçãããåºå圢åŒãå®å šã«çµ±äžãããŸãã`ProcessorFormatter` ããã®é£æºã®éµãšãªããŸããããã«ãããæ¢åã® `logging` ããŒã¹ã®ãšã³ã·ã¹ãã ïŒå€ãã®ãµãŒãããŒãã£ã©ã€ãã©ãªãå«ãïŒãæ倧éã«æŽ»çšãã€ã€ãæ§é åãã®ã³ã°ã®ã¡ãªããã享åã§ããŸããð
èšå®æ¹æ³ã®è©³çŽ°ïŒããªãã®ããŒãºã«åãããŠã«ã¹ã¿ãã€ãº âïž
`structlog.configure()` ã¯ã`structlog` ã®åäœãã°ããŒãã«ã«èšå®ããããã®äžå¿çãªé¢æ°ã§ããå€ãã®ãªãã·ã§ã³ããããŸãããéåžžã¯ã¢ããªã±ãŒã·ã§ã³ã®åæåã³ãŒãïŒäŸãã° `main.py` ã `app.py` ã®æåã®æ¹ïŒã§äžåºŠã ãåŒã³åºããŸããããã«ãããã¢ããªã±ãŒã·ã§ã³å šäœã§äžè²«ãããã®ã³ã°åäœãä¿èšŒããŸãã
`configure()` ã®äž»èŠãªåŒæ°
äž»èŠãªåŒæ°ãšãã®åœ¹å²ãå確èªããŸãããã
åŒæ° | 説æ | æšå¥šãããèšå® (ç¹ã«loggingé£æºæ) |
---|---|---|
processors |
ãã°ã€ãã³ã(`event_dict`)ãåŠçããããã»ããµ(é¢æ°ãcallable)ã®ãªã¹ãããªã¹ãã®é åºã§å®è¡ãããŸããæåŸã®ããã»ããµã¯éåžžãã¬ã³ãã©ãŒïŒäŸ: `JSONRenderer`ïŒãã`ProcessorFormatter.wrap_for_formatter` ã«ãªããŸãã | ã¿ã€ã ã¹ã¿ã³ãè¿œå ããã°ã¬ãã«è¿œå ãäŸå€ãã©ãŒããããã³ã³ããã¹ãããŒãž (`merge_contextvars`)ãæçµã¬ã³ãã©ãŒãªã©ãå«ããªã¹ãã |
context_class |
`logger.bind()` ãªã©ã§äœ¿ãããã³ã³ããã¹ãèŸæžã®åãé垞㯠`dict` ã§åé¡ãããŸããã`contextvars` ãå€çšããå Žåã§ãã`dict` ã§è¯ãããšãå€ãã§ãïŒ`merge_contextvars` ãåŠçããããïŒãã¬ã¬ã·ãŒãªã¹ã¬ããããŒã«ã«ã³ã³ããã¹ãã䜿ãããå Žå㯠`structlog.threadlocal.wrap_dict(dict)` ãæå®ããŸãããçŸåšã¯éæšå¥šã§ãã | `dict` |
logger_factory |
`structlog.get_logger()` ãå éšçã«äœ¿çšãããåºç€ãšãªããã¬ãŒã€ã³ã¹ã¿ã³ã¹ãçæããããã®ãã¡ã¯ããªãæšæº `logging` ãšé£æºããå Žåã¯ã`logging.getLogger` ã䜿ããã¡ã¯ããªãæå®ããŸãã | `structlog.stdlib.LoggerFactory()` |
wrapper_class |
`logger_factory` ã«ãã£ãŠçæããããã¬ãŒãã©ãããã`bind()` ãªã©ã® `structlog` ã®æ©èœãæäŸããã¯ã©ã¹ãæšæº `logging` ãšé£æºããå Žåã¯ã`logging.Logger` ãã©ããããã¯ã©ã¹ãæå®ããŸãã | `structlog.stdlib.BoundLogger` |
cache_logger_on_first_use |
`True` ã«èšå®ãããšã`get_logger(“name”)` ã§åããŠãã¬ãŒãçæãããåŸããã®ã€ã³ã¹ã¿ã³ã¹ (`BoundLogger` ã€ã³ã¹ã¿ã³ã¹) ãå éšçã«ãã£ãã·ã¥ããŸããåãååã§å床 `get_logger()` ãåŒã³åºããéã«ãã€ã³ã¹ã¿ã³ã¹çæãèšå®ã®ãªãŒããŒããããé¿ããããŸããã¢ãžã¥ãŒã«ã¬ãã«ã§ `log = structlog.get_logger()` ã®ããã«äœ¿ãå Žåã«éèŠã§ãã | `True` |
`configure()` ã¯è€æ°ååŒã³åºãããšãã§ããæå®ããåŒæ°ã ããæŽæ°ãããŸããæªæå®ã®åŒæ°ã¯ä»¥åã®èšå®å€ãä¿æãããŸãã
èšå®ã®ãã¹ããã©ã¯ãã£ã¹
- â ã¢ããªã±ãŒã·ã§ã³åæåæã«äžåºŠã ãèšå®: ã¢ããªã±ãŒã·ã§ã³ã®ãšã³ããªãŒãã€ã³ãïŒäŸ: `if __name__ == “__main__”:` ãããã¯ããWebãã¬ãŒã ã¯ãŒã¯ã®èµ·åã¹ã¯ãªããïŒã®ã§ããã ãæ©ã段é㧠`structlog.configure()` ãš `logging.basicConfig()` (ãŸã㯠`logging.dictConfig`) ãåŒã³åºããŸãã
- ð³ ç°å¢ã«å¿ããèšå®åãæ¿ã: ç°å¢å€æ° (`APP_ENV`, `FLASK_ENV` ãªã©) ãèšå®ãã¡ã€ã« (`settings.py`, `.env` ãªã©) ãèªã¿èŸŒã¿ãéçº/ã¹ããŒãžã³ã°/æ¬çªã§ããã»ããµïŒç¹ã«ã¬ã³ãã©ãŒïŒããã°ã¬ãã«ãåãæ¿ããããã«å®è£ ããã®ãäžè¬çã§ãã
- ð€ æšæºã©ã€ãã©ãª `logging` ãšã®é£æºãæè: `LoggerFactory`, `BoundLogger`, `ProcessorFormatter` ãé©åã«èšå®ãã`logging` ããŒã¹ã®ã©ã€ãã©ãªã®ãã°ãçµ±äžçã«æ±ããããã«æ§æããŸããç¹ã« `logging` ã®ãã³ãã©èšå®ãš `structlog` ã®ã¬ã³ãã©ãŒèšå®ãæŽåãããããšãéèŠã§ãã
- ð `contextvars` ã®æŽ»çš (Python 3.7+): Webã¢ããªã±ãŒã·ã§ã³ (Django, FastAPI, Flaskãªã©) ãéåæãã¬ãŒã ã¯ãŒã¯ (`asyncio`) ã䜿çšããå Žåã¯ãããã»ããµã®æåã« `structlog.contextvars.merge_contextvars` ãå«ãããªã¯ãšã¹ã/ã¿ã¹ã¯åäœã®ã³ã³ããã¹ã管çã« `structlog.contextvars.bind_contextvars` ãªã©ã掻çšããŸããããã«ãããã¹ã¬ããã»ãŒã/ã¿ã¹ã¯ã»ãŒããªã³ã³ããã¹ãæ³šå ¥ãå®çŸã§ããŸãã
- ðïž ãã¹ãæã®èšå®ãªã»ãã: ãŠããããã¹ããã€ã³ãã°ã¬ãŒã·ã§ã³ãã¹ãã§ã¯ãåãã¹ãã±ãŒã¹ã®éå§æ (`setUp` ã¡ãœããã `pytest` ã® fixture) ã« `structlog.reset_defaults()` ãåŒã³åºããŠãã°ããŒãã«ãªèšå®ããã¹ãéã§å¹²æžããªãããã«ããŸãã
- ð èšå®å 容ã®ç¢ºèª: éçºäžããããã°æã«ã`structlog.is_configured()` ã§èšå®æžã¿ãã確èªãããã`structlog.get_config()` ã§çŸåšã®èšå®èŸæžãååŸããŠå 容ã確èªããããããšäŸ¿å©ã§ãã
- ðŠ èšå®ã®ã¢ãžã¥ãŒã«å: è€éãªèšå®ã¯ãå°çšã®ã¢ãžã¥ãŒã«ïŒäŸ: `my_project/logging_config.py`ïŒã«é¢æ°ãšããŠãŸãšããã¢ããªã±ãŒã·ã§ã³ã®èµ·åæã«ãã®é¢æ°ãåŒã³åºãããã«ãããšãã³ãŒãã®èŠéããè¯ããªããŸãã
é«åºŠãªããã㯠ð
éåæåŠç (Async) ãšã®é£æº
çŸä»£çãªPythonã¢ããªã±ãŒã·ã§ã³ã§ã¯ `asyncio` ãå©çšããéåæåŠçãäžè¬çã«ãªã£ãŠããŸãã`structlog` ã¯ããã®ãããªç°å¢ã§ãã¹ã ãŒãºã«åäœããããã«èšèšãããŠããŸãã
- ðš éåæãã°ã¡ãœãã: `structlog.stdlib.BoundLogger` ãªã©ã`structlog` ã®äž»èŠãªãã¬ãŒã¯ã©ã¹ã¯ãéåžžã®åæã¡ãœãã (`info()`, `debug()` ãªã©) ã«å ããŠã`a` ãã¬ãã£ãã¯ã¹ãä»ããéåæç (`ainfo()`, `adebug()` ãªã©) ãæäŸããŸããããã㯠`await` ãšå
±ã«äœ¿çšã§ããã€ãã³ãã«ãŒãããããã¯ããããšãªããã°åŠçãéå§ã§ããŸãïŒãã ããæçµçãªI/Oãåæçãã³ãã©ã§è¡ãããå Žåã¯ããã®éšåã§ãããã¯ãçºçããå¯èœæ§ããããŸãïŒã
import asyncio import structlog import logging import sys # ç°¡åãªèšå®äŸ logging.basicConfig(level=logging.INFO, format="%(message)s", stream=sys.stdout) structlog.configure( processors=[structlog.dev.ConsoleRenderer()], logger_factory=structlog.stdlib.LoggerFactory(), wrapper_class=structlog.stdlib.BoundLogger, cache_logger_on_first_use=True ) log = structlog.get_logger() async def async_task(task_id): # éåæã¡ãœãããäœ¿çš await log.ainfo("async_task_started", task_id=task_id) await asyncio.sleep(0.1 * task_id) await log.adebug("async_work_in_progress", task_id=task_id, step=1) # DEBUGã¯åºåãããªã await asyncio.sleep(0.1) await log.ainfo("async_task_finished", task_id=task_id) async def main(): # åæã¡ãœãããéåæã¡ãœãããæ··åšå¯èœ log.info("starting_main_sync") await log.ainfo("starting_main_async") # await ãå¿ èŠ tasks = [async_task(i) for i in range(1, 4)] await asyncio.gather(*tasks) log.info("finished_main_sync") await log.ainfo("finished_main_async") # await ãå¿ èŠ if __name__ == "__main__": asyncio.run(main())
- ð `contextvars` ã«ããå®å šãªã³ã³ããã¹ã管ç: éåæã³ãŒãã«ãããæ倧ã®èª²é¡ã®äžã€ã¯ãè€æ°ã®ã¿ã¹ã¯ã䞊è¡ããŠå®è¡ãããäžã§ãåã¿ã¹ã¯ã®ã³ã³ããã¹ãïŒãªã¯ãšã¹ãIDãªã©ïŒãæ£ãããã°ã«ä»äžããããšã§ããã¹ã¬ããããŒã«ã«å€æ°ã¯ `asyncio` ç°å¢ã§ã¯æåŸ éãã«åäœããŸããããã㧠`contextvars` ãç䟡ãçºæ®ããŸããåè¿°ã®éãã`structlog.contextvars.merge_contextvars` ããã»ããµãš `structlog.contextvars.bind_contextvars` ãªã©ã䜿çšããããšã§ãå `asyncio` ã¿ã¹ã¯ã«åºæã®ã³ã³ããã¹ããå®å šã«ç®¡çãããã°ã«èªåçã«å«ããããšãã§ããŸããããã¯éåæWebãã¬ãŒã ã¯ãŒã¯ãªã©ã§ã¯å¿ é ã®ãã¯ããã¯ã§ãã
- â¡ éåæI/Oãã³ãã©: ãã°åºåèªäœãããã«ããã¯ã«ãªãå ŽåïŒç¹ã«å€§éã®ãã°ããããã¯ãŒã¯çµç±ã§éä¿¡ããå Žåãªã©ïŒã`logging` ã®ãã³ãã©ã¬ãã«ã§éåæI/Oãæ€èšããå¿ èŠããããŸãã`structlog` èªäœã¯ãã³ãã©ã®éåæåãè¡ããŸããããéåæ察å¿ã® `logging` ãã³ãã©ïŒäŸ: `aiologger`, `loguru` ã®äžéšæ©èœïŒãšçµã¿åãããããšã¯å¯èœã§ãã`structlog` ã¯ããã»ããµãã§ãŒã³ã®å®è¡åŸãæçµçã« `logging` ã®ãã³ãã©ã«åŠçãå§è²ããããããã³ãã©åŽã®å®è£ ã«äŸåããŸãã
`structlog` 㯠`asyncio` ãšã®èŠªåæ§ãé«ããç¹ã« `contextvars` ã掻çšããããšã§ãè€éãªéåæã¢ããªã±ãŒã·ã§ã³ã§ãä¿¡é Œæ§ã®é«ããã®ã³ã°ãå®çŸã§ããŸãã
ãã¹ãã§ã®å©çš
ãœãããŠã§ã¢ã®å質ãä¿èšŒããããã«ã¯ããã°åºåãæåŸ éãã«è¡ãããŠãããããã¹ãã§æ€èšŒããããšãéèŠã§ãã`structlog` ã¯ãã¹ãã容æã«ããããã®ãŠãŒãã£ãªãã£ãæäŸããŠããŸãã
- ðµïž `structlog.testing.LogCapture`: ããã¯ã€ã³ã¡ã¢ãªã§ãã°ãšã³ããªïŒ`event_dict`ïŒããã£ããã£ããããã®ç¹æ®ãªããã»ããµã§ãããã¹ã察象ã®ã³ãŒããå®è¡ããåã«ã`configure` ã§ãã®ããã»ããµãèšå®ããå®è¡åŸã«ãã£ããã£ããããã°ãšã³ããªã®ãªã¹ã (`log_capture.entries`) ãæ€èšŒããŸããããã«ããããç¹å®ã®ã€ãã³ãããã°ããããããæåŸ
ãããã³ã³ããã¹ããå«ãŸããŠãããããç¹å®ã®ãã°ã¬ãã«ã§èšé²ããããããªã©ãã¢ãµãŒãã§ããŸãã
import pytest # pytest ã䜿ãäŸ import structlog from structlog.testing import LogCapture # ãã¹ã察象ã®ã·ã³ãã«ãªé¢æ° def process_data(data_id, should_warn=False): log = structlog.get_logger() log = log.bind(data_id=data_id) log.info("processing_started") if should_warn: log.warning("potential_issue_detected") log.info("processing_finished") @pytest.fixture(autouse=True) def configure_structlog_for_test(): """åãã¹ãã®åã« structlog ããªã»ããããLogCapture ãèšå®ãã fixture""" structlog.reset_defaults() log_capture = LogCapture() structlog.configure(processors=[log_capture]) return log_capture # ãã¹ãé¢æ°ã§ãã£ããã£çµæã䜿ããããã«è¿ã def test_processing_logs_info(configure_structlog_for_test): log_capture = configure_structlog_for_test # fixture ãã LogCapture ã€ã³ã¹ã¿ã³ã¹ãååŸ process_data("data-123") assert len(log_capture.entries) == 2 assert log_capture.entries[0] == {'event': 'processing_started', 'log_level': 'info', 'data_id': 'data-123'} assert log_capture.entries[1] == {'event': 'processing_finished', 'log_level': 'info', 'data_id': 'data-123'} def test_processing_logs_warning(configure_structlog_for_test): log_capture = configure_structlog_for_test process_data("data-456", should_warn=True) assert len(log_capture.entries) == 3 # warning ãã°ã®ååšãšå 容ãç¢ºèª warning_logs = [e for e in log_capture.entries if e['log_level'] == 'warning'] assert len(warning_logs) == 1 assert warning_logs[0]['event'] == 'potential_issue_detected' assert warning_logs[0]['data_id'] == 'data-456' # æåŸã®ãã°ã finished ã§ããããšãç¢ºèª assert log_capture.entries[-1]['event'] == 'processing_finished' # 泚æ: configure_structlog_for_test fixture 㯠autouse=True ãªã®ã§ã # ãã®ãã¹ãã¢ãžã¥ãŒã«å ã®å šãŠã®ãã¹ãã§èªåçã«é©çšãããŸãã # ãã¹ãåŸã« reset_defaults 㯠fixture ã§æé»çã«è¡ãããŸãïŒæ¬¡ã®ãã¹ãã®åã«ãªã»ãããããããïŒã
- ð `structlog.reset_defaults()`: ãã¹ãã¹ã€ãŒãå šäœã§ã°ããŒãã«ãª `structlog` èšå®ãæå³ããå ±æãããŠããŸãããšãé²ãããã«ãåãã¹ãã®éå§æã«ãã®é¢æ°ãåŒã³åºãããšãéåžžã«éèŠã§ãã`pytest` ã® fixture ã `unittest` ã® `setUp` ã¡ãœããã§å®è¡ããã®ãäžè¬çã§ãã
- 𧩠`logging` ãšã®é£æºãã¹ã: `structlog` ã `logging` ãšé£æºããããã«èšå®ãããŠããå Žåã`logging` ã®æšæºçãªãã¹ããŠãŒãã£ãªãã£ïŒäŸ: `unittest.TestCase.assertLogs`ïŒã䜵çšã§ããŸããã`LogCapture` 㯠`structlog` ã® `event_dict` ã¬ãã«ã§ãã£ããã£ããããããã詳现ãªæ€èšŒãå¯èœã§ãã
`LogCapture` ã掻çšããããšã§ããã®ã³ã°ã«é¢ããæ¯ãèããæ確ã«ãã¹ãã±ãŒã¹ã«èšè¿°ãããªã°ã¬ãã·ã§ã³ãé²ãããšãã§ããŸããâ
ããã©ãŒãã³ã¹ã«é¢ããèæ ®äºé
ãã®ã³ã°ã¯ãç¹ã«é«é »åºŠã§åŒã³åºãããå Žåãè€éãªåŠçãå«ãå Žåãã¢ããªã±ãŒã·ã§ã³ã®ããã©ãŒãã³ã¹ã«ç¡èŠã§ããªã圱é¿ãäžããå¯èœæ§ããããŸãã`structlog` ã¯ããã©ãŒãã³ã¹ãæèããŠèšèšãããŠããŸããïŒäŸãã°ãæååãã©ãŒãããã« `ïŒ ` 圢åŒãæšå¥šãããªã©ïŒãæé©ãªããã©ãŒãã³ã¹ãåŸãããã«ã¯ããã€ãã®ç¹ãèæ ®ããå¿ èŠããããŸãã
- ð« ãã°ã¬ãã«ã«ããæ©æãã£ã«ã¿ãªã³ã°: æãåºæ¬çãã€å¹æçãªæé©åã¯ãäžèŠãªãã°ã¬ãã«ã®åºåãæå¶ããããšã§ããæ¬çªç°å¢ã§ã¯ `INFO` ãŸã㯠`WARNING` ã¬ãã«ä»¥äžã®ã¿ãåºåããããã«èšå®ãã`DEBUG` ã¬ãã«ã®ãã°åŒã³åºãã¯æ©æã«ïŒããã»ããµãã§ãŒã³ã«å ¥ãåã«ïŒé€å€ããŸãã`structlog.stdlib.filter_by_level` ã `logging.Logger.setLevel()` / `logging.Handler.setLevel()` ãé©åã«èšå®ããŸãã
- ðïž ããã»ããµãã§ãŒã³ã®å¹çå:
- ãã§ãŒã³ã«å«ãŸããããã»ããµã®æ°ãå¿ èŠæå°éã«ããŸãã
- åããã»ããµã®åŠçå 容ãå¹ççã§ããããšã確èªããŸããç¹ã«ãã«ãŒãå ã§å®è¡ãããããéãèšç®ãI/Oãè¡ã£ããããã«ã¹ã¿ã ããã»ããµã«ã¯æ³šæãå¿ èŠã§ãã
- ããã»ããµã®é åºã圱é¿ããããšããããŸããäŸãã°ããã£ã«ã¿ãªã³ã°ãè¡ãããã»ããµã¯ãéãåŠçãè¡ãããã»ããµãããåã«é 眮ãã¹ãã§ãã
- â³ é
延è©äŸ¡ (Lazy Evaluation): ãã°ã¡ãã»ãŒãžãä»äžããããŒã¿ãäœæããããã«é«ã³ã¹ããªèšç®ãI/Oãå¿
èŠãªå Žåããã®èšç®ã¯å®éã«ãã°ãåºåãããããšã確å®ããŠããè¡ãã¹ãã§ãã
- ãã°ã¬ãã«ãã§ãã¯: `if log.isEnabledFor(logging.DEBUG): …` ã®ããã«ããã°ãåºåãããã¬ãã«ãã©ãããäºåã«ç¢ºèªããŠãããé«ã³ã¹ããªåŠçãå®è¡ããŸãã
- % ãã©ãŒããã: `log.debug(“Complex data: %s”, expensive_function())` ã®ããã« `%` 圢åŒã䜿ããšã`expensive_function()` ã®åŒã³åºãã¯ãã°ã¬ãã«ã `DEBUG` 以äžã®å Žåã«ã®ã¿ïŒ`structlog.stdlib.PositionalArgumentsFormatter` ãªã©ã§åŠçãããéã«ïŒè¡ãããå¯èœæ§ããããŸãïŒå®è£ äŸåïŒã
- é¢æ°åŒã³åºãã®é 延: ãã°ã«å«ããå€ãšããŠãçŽæ¥èšç®çµæãæž¡ã代ããã«ãèšç®ãè¡ãé¢æ°ãªããžã§ã¯ããæž¡ããã¬ã³ãã©ãŒãå°çšã®ããã»ããµãå¿ èŠã«å¿ããŠåŒã³åºãããšããé«åºŠãªãã¯ããã¯ãèããããŸãã
- <0xF0><0x9F><0x9A><0x9A> éåæ/ãããã¡ãªã³ã°/ãã¥ãŒã€ã³ã°: 倧éã®ãã°ãçæããé«ã¹ã«ãŒããããªã¢ããªã±ãŒã·ã§ã³ã§ã¯ããã°ã®æžã蟌ã¿ïŒç¹ã«ãã¡ã€ã«ããããã¯ãŒã¯ãžã®I/OïŒãããã«ããã¯ã«ãªãããšããããŸãã
- `logging.handlers.QueueHandler` ãš `QueueListener`: æšæºã©ã€ãã©ãªã®æ©èœã䜿ã£ãŠããã°ã¬ã³ãŒãããã¥ãŒã«å ¥ããå¥ã®ã¹ã¬ããã§åŠçïŒãã©ãŒããããæžã蟌ã¿ïŒãããããšãã§ããŸããããã«ããããã°åºååŠçãã¢ããªã±ãŒã·ã§ã³ã®ã¡ã€ã³ã¹ã¬ããããããã¯ããæéãæå°éã«æããããŸãã
- éåæãã³ãã©: åè¿°ã®éããå®å šã«éåæãªãã°åŠçãè¡ãããå Žåã¯ãéåæ察å¿ã®ãã³ãã©ã©ã€ãã©ãªãæ€èšããŸãã
- ãããã¡ãªã³ã°ãã³ãã©: `logging.handlers.MemoryHandler` ãªã©ã䜿ã£ãŠããã°ãã¡ã¢ãªã«äžæçã«æºããç¹å®ã®æ¡ä»¶ïŒäŸ: ãšã©ãŒçºçæãäžå®æ°æºãŸã£ãæïŒã«ãªã£ãããŸãšããŠäžæµã®ãã³ãã©ã«éãããšãå¯èœã§ãã
- ðŸ `cache_logger_on_first_use=True`: `configure` ã§ãã®ãªãã·ã§ã³ã `True` ã«èšå®ããããšã¯ã`get_logger()` ã®åŒã³åºããªãŒããŒããããåæžããããã«äžè¬çã«æšå¥šãããŸãã
ããã©ãŒãã³ã¹ã¯åžžã«ãã¬ãŒããªãã§ããé床ãªæé©åã¯ã³ãŒãã®è€éæ§ãå¢ãå¯èœæ§ããããŸãããŸãã¯ãããã¡ã€ãªã³ã°ããŒã«ãªã©ã䜿ã£ãŠããã«ããã¯ãç¹å®ããå¹æçãªç®æã«æé©åãæœãããšãéèŠã§ãã`structlog` ã®æè»æ§ã¯ãããã©ãŒãã³ã¹èŠä»¶ã«å¿ãã調æŽãå¯èœã«ããŸããð
æšæº logging ã©ã€ãã©ãªãšã®æ¯èŒïŒäœãéãïŒ ð€
Pythonæšæºã® `logging` ã¢ãžã¥ãŒã«ã¯ãé·å¹Žã«ãããPythonãšã³ã·ã¹ãã ã®ãã®ã³ã°ã®åºç€ãšãªã£ãŠããŸãããéåžžã«æè»ã§å€ãã®æ©èœãåããŠããŸããã`structlog` ã¯ç°ãªãå²åŠãšã¢ãããŒããæã¡ãç¹ã«çŸä»£çãªã¢ããªã±ãŒã·ã§ã³éçºã«ãããŠããã€ãã®å©ç¹ãæäŸããŸããäž¡è ãæ¯èŒããŠã¿ãŸãããã
èŠ³ç¹ | æšæº logging | structlog |
---|---|---|
åºæ¬ææ³ / ããŒã¿ã¢ãã« | `LogRecord` ãªããžã§ã¯ããäžå¿ãäž»ã«æååã¡ãã»ãŒãžãçæãããã©ãŒããã¿ã§è£ 食ããã`extra` èŸæžã§è¿œå æ å ±ãä»äžã§ããã | ã€ãã³ãèŸæž (`event_dict`) ãäžå¿ãæåããããŒãšå€ã®ãã¢ã§ããŒã¿ãæ±ããããã»ããµã§èŸæžãå å·¥ã»æ¡åŒµããã¬ã³ãã©ãŒã§æçµåœ¢åŒïŒæååãJSONãªã©ïŒã«ããã |
æ§é åãã®ã³ã° | åŸä»ãã§å¯Ÿå¿å¯èœã`JSONFormatter` ãèªäœãŸãã¯å€éšã©ã€ãã©ãªã§å°å ¥ããå¿ èŠãããã`extra` èŸæžã®æ±ããäžè²«æ§ã®ç¶æã«å·¥å€«ãå¿ èŠã | èšèšã®äžå¿ã`log.info(event=”…”, key=value)` ã®ããã«èªç¶ã«æ§é åããŒã¿ãèšè¿°ã§ãã`JSONRenderer` ã§å®¹æã«åºåå¯èœãããã»ããµã§æ§é ã®æäœã容æã |
ã³ã³ããã¹ã管ç | `LoggerAdapter` ã `Filter` ã䜿ã£ãŠã³ã³ããã¹ãæ å ±ãè¿œå ã§ããããå®åçã«ãªããã¡ãã¹ã¬ããããŒã«ã« (`threading.local`) ã䜿ã£ã管çãå¯èœã ããéåæç°å¢ã§ã¯åé¡ãçããã | `bind()`, `new()`, `unbind()` ã«ããã€ãã¥ãŒã¿ãã«ãªã³ã³ããã¹ãæäœãçŽæçã`contextvars` ãšã®ãã€ãã£ããªé£æº (`merge_contextvars` ããã»ããµã`bind_contextvars` é¢æ°) ã«ãããéåæç°å¢ã§ãå®å šãã€å®¹æã«ã³ã³ããã¹ãã管çã§ããã |
èšå®æ¹æ³ | ã³ãŒã (`basicConfig`, `addHandler`ç)ã`dictConfig`, `fileConfig` (INI圢åŒ) ãªã©å€æ§ããã³ãã©ããã©ãŒããã¿ããã£ã«ã¿ã®çµã¿åãããæ¯èŒçéçãªèšå®ã | äž»ã«ã³ãŒã (`configure()`) ã§èšå®ãããã»ããµãã§ãŒã³ã«ããåçã§éåžžã«æè»ãªãã€ãã©ã€ã³æ§ç¯ãå¯èœãèšå®ã®åå©çšæ§ãé«ãã |
éçºäœéš (DX) | æšæºçã§å®å®ããŠããããèšå®ãã³ã³ããã¹ãä»äžãããåé·ã«æããããããšããããããã©ã«ãã®åºåã¯æ å ±éãå°ãªãã | `ConsoleRenderer` ã«ããéçºäžã®èŠèªæ§ãé«ãïŒè²ä»ããæŽåœ¢ïŒã`bind()` ã§ã³ã³ããã¹ãä»äžãç°¡æœãAPIãã·ã³ãã«ã§çŽæçãããã»ããµã«ããæ¡åŒµã容æã |
ãšã³ã·ã¹ãã ãšé£æº | Pythonã®ããã¡ã¯ãã¹ã¿ã³ããŒããã»ãŒå šãŠã®ã©ã€ãã©ãªã `logging` ãå©çšããã°éçŽããŒã«ã `logging` ãåæãšããæ©èœãå€ãã | `logging` ãã©ããããããã«èšèšãããŠãããå®å šãªäºææ§ãæ〠(`stdlib` ã¢ãžã¥ãŒã«å©çšæ)ãæ¢åã® `logging` ãšã³ã·ã¹ãã ïŒãã³ãã©ãã©ã€ãã©ãªãã°ïŒããã®ãŸãŸæŽ»çšã§ããã |
éåæãµããŒã | `logging` èªäœã¯ã¹ã¬ããã»ãŒãã ããéåæã³ã³ããã¹ã管çã¯èªåã§è¡ãå¿ èŠããããéåæãã³ãã©ã¯å€éšã©ã€ãã©ãªã«äŸåã | éåæãã°ã¡ãœãã (`ainfo` ç) ãæäŸã`contextvars` ãšã®é£æºã«ããéåæã³ã³ããã¹ã管çã容æãéåæãã³ãã©èªäœã¯æäŸããªãããé£æºã¯å¯èœã |
ããã©ãŒãã³ã¹ | æé©åãããŠããé«éãç¹ã« C å®è£ ã® `LogRecord` çæã¯å¹ççã | ããã©ãŒãã³ã¹ãæèããèšèšïŒäŸ: %ãã©ãŒãããæšå¥šïŒãããã»ããµãã§ãŒã³ã®è€éãã«ãã£ãŠã¯ãªãŒããŒããããçããå¯èœæ§ã¯ããããéåžžã¯ååé«éã`logging` ã©ããæ㯠`logging` ã®æ§èœãåºç€ãšãªãã |
structlog ãéžã¶äž»ãªçç±ïŒã¡ãªããïŒïŒ
- âš æ§é åã第äžçŽåžæ°: ãã°ãããŒã¿ãšããŠæ±ããããã解æãéçŽã容æã
- ð åªããã³ã³ããã¹ã管ç: ç¹ã«éåæç°å¢ã§ã®ãªã¯ãšã¹ã远跡ãªã©ãæ Œæ®µã«æ¥œã«ãªãã
- ð§ 究極ã®æè»æ§: ããã»ããµã«ãããã€ãã©ã€ã³ã¯ãã»ãŒããããèŠä»¶ã«å¯Ÿå¿å¯èœã
- ð å¿«é©ãªéçºäœéš: éçºäžã®ãã°ãèŠããããã³ãŒããç°¡æœã«ãªãåŸåãããã
- ð€ åŸæ¹äºææ§: æ¢åã® `logging` è³ç£ãç¡é§ã«ããã段éçã«å°å ¥ã§ããã
structlog ã䜿ãäžã§ã®èæ ®ç¹ïŒ
- ð åŠç¿æ²ç·: ããã»ããµããã€ã³ãã£ã³ã°ã`contextvars` ãšãã£ã `structlog` åºæã®æŠå¿µãç解ããå¿ èŠãããã
- âïž èšå®ã®èªç±åºŠïŒè€é床: éåžžã«æè»ãªåé¢ãã©ã®ãããªããã»ããµãã©ã®é åºã§çµã¿åãããã®ãæé©ããåæèšå®ã«è©Šè¡é¯èª€ãå¿ èŠã«ãªãå Žåãããã
- â äŸåé¢ä¿ã®è¿œå : æšæºã©ã€ãã©ãªä»¥å€ã®äŸåãå¢ããïŒãã ããéåžžã«åºã䜿ãããŠããã©ã€ãã©ãªïŒã
çµè«ïŒ ã·ã³ãã«ãªã¹ã¯ãªããããæ§é åã»ã³ã³ããã¹ã管çã®èŠä»¶ãäœãã¢ããªã±ãŒã·ã§ã³ã§ã¯ãæšæº `logging` ã§ååãããããŸãããããããè€éãªããžãã¹ããžãã¯ãæã€ã¢ããªã±ãŒã·ã§ã³ããã€ã¯ããµãŒãã¹ã¢ãŒããã¯ãã£ããã°ããŒã¿ã掻çšããç£èŠãåæãéèŠãªã·ã¹ãã ãéåæåŠçãå€çšããã¢ããªã±ãŒã·ã§ã³ãªã©ã§ã¯ã`structlog` ãæäŸããæ§é åãã³ã³ããã¹ã管çãã«ã¹ã¿ãã€ãºæ§ã®é«ãããéçºå¹çãšã·ã¹ãã ã®éçšæ§ãå€§å¹ ã«åäžããã匷åãªæŠåšãšãªããŸããå€ãã®å Žåã`logging` ãšé£æºãããŠäž¡æ¹ã®é·æã掻ããã®ãçŸå®çã§å¹æçãªã¢ãããŒãã§ããð¥³
ãŸãšãïŒstructlog ã§ãã®ã³ã°ã次ã®ã¬ãã«ãžïŒ ð
`structlog` ã¯ãPythonã«ããããã®ã³ã°ãçŸä»£çãªãœãããŠã§ã¢éçºã®èŠæ±ã«åãããŠé²åããããéåžžã«åŒ·åã§æè»ãªã©ã€ãã©ãªã§ããåãªãæååã®èšé²ã§ã¯ãªãããã°ãæå³ã®ããæ§é åããŒã¿ãšããŠæãããããå¹ççã«åŠçã»æŽ»çšããããã®æŽç·Žãããä»çµã¿ãæäŸããŸãã
ãã®è§£èª¬ãéããŠã`structlog` ã®æ žå¿çãªäŸ¡å€ããç解ããã ããããšæããŸãã
- ãã°ã¯æåããæ§é åããŒã¿ (`event_dict`) ãšããŠæ±ãããããŒãšå€ã®ãã¢ã§æ å ±ãè±ãã«è¡šçŸã§ããŸãã
- ããã»ããµãã§ãŒã³ã«ãããã¿ã€ã ã¹ã¿ã³ãã®ä»äžãããŒã¿ã®ãã£ã«ã¿ãªã³ã°ããã¹ãã³ã°ãäŸå€æ å ±ã®æŽåœ¢ãå€éšããŒã¿ãœãŒã¹ãšã®é£æºãªã©ããã°ã€ãã³ãã«å¯Ÿããããããå å·¥ãã¢ãžã¥ãŒã«åããåå©çšå¯èœãªåœ¢ã§å®çŸã§ããŸãã
- ãã€ã³ãã£ã³ã° (`bind`) ãš `contextvars` ãçµã¿åãããããšã§ããªã¯ãšã¹ãIDããŠãŒã¶ãŒæ å ±ããã©ã³ã¶ã¯ã·ã§ã³IDãšãã£ãã³ã³ããã¹ãæ å ±ããã³ãŒãã®å®è¡ãããŒã«åãããŠèªåçãã€å®å šã«ãã°ã«æ³šå ¥ã§ãããããã°ããã¬ãŒã·ã³ã°ãæ Œæ®µã«å®¹æã«ãªããŸãã
- ã¬ã³ãã©ãŒãåãæ¿ããããšã§ãéçºäžã¯äººéãèªã¿ããã圢åŒãæ¬çªç°å¢ã§ã¯æ©æ¢°ã解æããããJSON圢åŒãªã©ãç¶æ³ã«å¿ããæé©ãªåºå圢åŒãéžæã§ããŸãã
- æšæºã©ã€ãã©ãª `logging` ãšã®ã·ãŒã ã¬ã¹ãªé£æºã«ãããæ¢åã®Pythonãšã³ã·ã¹ãã ãšã®äºææ§ãä¿ã¡ãªããã`structlog` ã®ã¡ãªããã享åã§ããŸãã
- éåæåŠç (`asyncio`) ããã¹ã容ææ§ãååã«èæ ®ãããŠããŸãã
å¹æçãªãã®ã³ã°ã¯ãåã«ãšã©ãŒçºçæã«åå ãç¹å®ããããã ãã®ãã®ã§ã¯ãããŸãããã·ã¹ãã ã®åäœãç解ããããã©ãŒãã³ã¹ããã«ããã¯ãçºèŠãããŠãŒã¶ãŒã®è¡åãåæããã»ãã¥ãªãã£ã€ã³ã·ãã³ããæ€ç¥ããããã®è²Žéãªæ å ±æºãšãªããŸãã`structlog` ã䜿ãããšã§ããã®ãã°ããŒã¿ãæ倧éã«æŽ»çšããããã®åºç€ãæ§ç¯ã§ããŸãã
å°å ¥ã«ã¯å€å°ã®åŠç¿ã³ã¹ãã䌎ããããããŸãããããã®æè³ã«èŠåãã ãã®ã¡ãªãããç¹ã«ã³ãŒãã®å¯èªæ§åäžããããã°æéã®ççž®ãéçšå¹çã®æ¹åãšãã£ãå¹æãæåŸ ã§ããŸãã
ãã²ãããªãã®æ¬¡ã®Pythonãããžã§ã¯ãããããã¯æ¢åãããžã§ã¯ãã®æ¹åã« `structlog` ã®å°å ¥ãæ€èšããŠã¿ãŠãã ãããããæŽå¯ã«æºã¡ãã管çãããããã®ã³ã°ã®äžçãåŸ ã£ãŠããŸãïŒð€© Happy Logging! ðªµ
Let’s structure your logs!
ã³ã¡ã³ã