FreshPixels commited on
Commit
b242b5b
·
verified ·
1 Parent(s): b298989

Create core/logging/logger.py

Browse files
Files changed (1) hide show
  1. core/logging/logger.py +130 -0
core/logging/logger.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import logging.config
5
+ import sys
6
+ import threading
7
+ from typing import Literal
8
+
9
+ import structlog
10
+
11
+ _logging_configured: bool = False
12
+ _logging_lock: threading.Lock = threading.Lock()
13
+
14
+
15
+ def setup_logging(
16
+ log_level: str = "INFO",
17
+ log_format: Literal["json", "console"] = "json",
18
+ ) -> None:
19
+ """Configure structured logging with JSON or console output.
20
+
21
+ Integrates structlog with the standard library logging module
22
+ so that all loggers (including third-party) produce consistent
23
+ structured output. Thread-safe and idempotent — safe to call
24
+ multiple times.
25
+
26
+ Args:
27
+ log_level: Logging level string (DEBUG, INFO, WARNING, ERROR, CRITICAL).
28
+ log_format: Output format — "json" for production, "console" for development.
29
+ """
30
+ global _logging_configured
31
+
32
+ with _logging_lock:
33
+ if _logging_configured:
34
+ return
35
+
36
+ level = getattr(logging, log_level.upper(), logging.INFO)
37
+
38
+ renderer: structlog.types.Processor
39
+ if log_format == "console":
40
+ renderer = structlog.dev.ConsoleRenderer()
41
+ else:
42
+ renderer = structlog.processors.JSONRenderer()
43
+
44
+ shared_processors: list[structlog.types.Processor] = [
45
+ structlog.contextvars.merge_contextvars,
46
+ structlog.stdlib.add_log_level,
47
+ structlog.stdlib.add_logger_name,
48
+ structlog.processors.TimeStamper(fmt="iso"),
49
+ structlog.processors.UnicodeDecoder(),
50
+ structlog.processors.StackInfoRenderer(),
51
+ structlog.processors.format_exc_info,
52
+ ]
53
+
54
+ structlog.configure(
55
+ processors=[
56
+ *shared_processors,
57
+ structlog.stdlib.filter_by_level,
58
+ structlog.stdlib.PositionalArgumentsFormatter(),
59
+ structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
60
+ ],
61
+ context_class=dict,
62
+ logger_factory=structlog.stdlib.LoggerFactory(),
63
+ wrapper_class=structlog.stdlib.BoundLogger,
64
+ cache_logger_on_first_use=True,
65
+ )
66
+
67
+ formatter = structlog.stdlib.ProcessorFormatter(
68
+ processors=[
69
+ structlog.stdlib.ProcessorFormatter.remove_processors_meta,
70
+ renderer,
71
+ ],
72
+ foreign_pre_chain=[
73
+ structlog.contextvars.merge_contextvars,
74
+ structlog.stdlib.add_log_level,
75
+ structlog.stdlib.add_logger_name,
76
+ structlog.processors.TimeStamper(fmt="iso"),
77
+ structlog.processors.UnicodeDecoder(),
78
+ structlog.processors.StackInfoRenderer(),
79
+ structlog.processors.format_exc_info,
80
+ ],
81
+ )
82
+
83
+ logging.config.dictConfig(
84
+ {
85
+ "version": 1,
86
+ "disable_existing_loggers": False,
87
+ "formatters": {
88
+ "structlog": {
89
+ "()": structlog.stdlib.ProcessorFormatter,
90
+ "processors": [
91
+ structlog.stdlib.ProcessorFormatter.remove_processors_meta,
92
+ renderer,
93
+ ],
94
+ "foreign_pre_chain": [
95
+ structlog.contextvars.merge_contextvars,
96
+ structlog.stdlib.add_log_level,
97
+ structlog.stdlib.add_logger_name,
98
+ structlog.processors.TimeStamper(fmt="iso"),
99
+ structlog.processors.UnicodeDecoder(),
100
+ structlog.processors.StackInfoRenderer(),
101
+ structlog.processors.format_exc_info,
102
+ ],
103
+ },
104
+ },
105
+ "handlers": {
106
+ "default": {
107
+ "class": "logging.StreamHandler",
108
+ "stream": sys.stdout,
109
+ "formatter": "structlog",
110
+ },
111
+ },
112
+ "root": {
113
+ "level": level,
114
+ "handlers": ["default"],
115
+ },
116
+ }
117
+ )
118
+
119
+ _logging_configured = True
120
+
121
+
122
+ def get_logger(name: str = "app") -> structlog.stdlib.BoundLogger:
123
+ """Return a structured logger bound with the given name.
124
+
125
+ Args:
126
+ name: Logger name, typically __name__ of the calling module.
127
+ """
128
+ logger: structlog.stdlib.BoundLogger = structlog.get_logger(name)
129
+ return logger
130
+