otel-utils 0.1.18__tar.gz → 0.1.19__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of otel-utils might be problematic. Click here for more details.
- {otel_utils-0.1.18 → otel_utils-0.1.19}/PKG-INFO +1 -1
- {otel_utils-0.1.18 → otel_utils-0.1.19}/pyproject.toml +1 -1
- {otel_utils-0.1.18 → otel_utils-0.1.19}/src/otel_utils/configurator.py +23 -6
- {otel_utils-0.1.18 → otel_utils-0.1.19}/src/otel_utils/logging.py +41 -47
- {otel_utils-0.1.18 → otel_utils-0.1.19}/README.md +0 -0
- {otel_utils-0.1.18 → otel_utils-0.1.19}/src/otel_utils/__init__.py +0 -0
- {otel_utils-0.1.18 → otel_utils-0.1.19}/src/otel_utils/metrics.py +0 -0
- {otel_utils-0.1.18 → otel_utils-0.1.19}/src/otel_utils/tracing.py +0 -0
|
@@ -20,6 +20,8 @@ from opentelemetry.sdk.resources import Resource
|
|
|
20
20
|
from opentelemetry.sdk.trace import TracerProvider
|
|
21
21
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
22
22
|
|
|
23
|
+
from otel_utils.logging import JsonFormatter, StructuredLogger
|
|
24
|
+
|
|
23
25
|
|
|
24
26
|
@dataclass
|
|
25
27
|
class OtelConfig:
|
|
@@ -33,6 +35,7 @@ class OtelConfig:
|
|
|
33
35
|
metric_export_interval_ms: int = 30000
|
|
34
36
|
log_level: int = logging.INFO
|
|
35
37
|
enable_console_logging: bool = True
|
|
38
|
+
json_logging: bool = True
|
|
36
39
|
|
|
37
40
|
@property
|
|
38
41
|
def trace_endpoint(self) -> Optional[str]:
|
|
@@ -143,20 +146,34 @@ class OtelConfigurator:
|
|
|
143
146
|
|
|
144
147
|
if self.config.enable_console_logging:
|
|
145
148
|
console_handler = logging.StreamHandler()
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
|
|
150
|
+
if self.config.json_logging:
|
|
151
|
+
formatter = JsonFormatter()
|
|
152
|
+
else:
|
|
153
|
+
formatter = logging.Formatter(
|
|
154
|
+
'[%(asctime)s] %(levelname)s [%(name)s] [trace_id=%(otelTraceID)s span_id=%(otelSpanID)s] - %(message)s',
|
|
155
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
156
|
+
)
|
|
157
|
+
|
|
150
158
|
console_handler.setFormatter(formatter)
|
|
151
159
|
root_logger.addHandler(console_handler)
|
|
152
160
|
root_logger.setLevel(self.config.log_level)
|
|
153
161
|
|
|
154
162
|
LoggingInstrumentor().instrument(
|
|
155
163
|
logger_provider=logger_provider,
|
|
156
|
-
set_logging_format=
|
|
164
|
+
set_logging_format=False,
|
|
157
165
|
log_level=self.config.log_level,
|
|
158
166
|
tracer_provider=trace.get_tracer_provider(),
|
|
159
167
|
meter_provider=metrics.get_meter_provider(),
|
|
160
168
|
)
|
|
161
169
|
|
|
162
|
-
self.
|
|
170
|
+
self.structured_logger = StructuredLogger(self.config.service_name)
|
|
171
|
+
self.logger = self.structured_logger.logger
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def get_logger(service_name: str = None, config: OtelConfig = None) -> StructuredLogger:
|
|
175
|
+
if config:
|
|
176
|
+
configurator = OtelConfigurator(config)
|
|
177
|
+
return configurator.structured_logger
|
|
178
|
+
|
|
179
|
+
return StructuredLogger(service_name or "default-service")
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
-
import platform
|
|
4
3
|
from contextlib import contextmanager
|
|
5
4
|
from datetime import datetime
|
|
6
5
|
from typing import Any, Dict, Optional
|
|
@@ -9,22 +8,27 @@ from opentelemetry import trace
|
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
class JsonFormatter(logging.Formatter):
|
|
11
|
+
|
|
12
12
|
def format(self, record):
|
|
13
|
-
|
|
13
|
+
log_record = {"timestamp": datetime.utcnow().isoformat(), "level": record.levelname, "logger": record.name,
|
|
14
|
+
"message": record.getMessage()}
|
|
15
|
+
|
|
16
|
+
if hasattr(record, "otelTraceID"):
|
|
17
|
+
log_record["trace_id"] = getattr(record, "otelTraceID")
|
|
18
|
+
if hasattr(record, "otelSpanID"):
|
|
19
|
+
log_record["span_id"] = getattr(record, "otelSpanID")
|
|
14
20
|
|
|
15
|
-
if hasattr(record,
|
|
16
|
-
|
|
17
|
-
if key not in (
|
|
18
|
-
'args', 'exc_info', 'exc_text', 'msg', 'message', 'levelname',
|
|
19
|
-
'levelno', 'pathname', 'filename', 'module', 'stack_info',
|
|
20
|
-
'lineno', 'funcName', 'created', 'msecs', 'relativeCreated',
|
|
21
|
-
'name', 'thread', 'threadName', 'processName', 'process'
|
|
22
|
-
):
|
|
23
|
-
log_data[key] = value
|
|
21
|
+
if hasattr(record, "context") and record.context:
|
|
22
|
+
log_record["context"] = record.context
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
if hasattr(record, "operation"):
|
|
25
|
+
log_record["operation"] = record.operation
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
for attr in ["service_name", "status", "error_type", "error_message"]:
|
|
28
|
+
if hasattr(record, attr):
|
|
29
|
+
log_record[attr] = getattr(record, attr)
|
|
30
|
+
|
|
31
|
+
return json.dumps(log_record)
|
|
28
32
|
|
|
29
33
|
|
|
30
34
|
class StructuredLogger:
|
|
@@ -38,13 +42,10 @@ class StructuredLogger:
|
|
|
38
42
|
default_attributes: Optional[Dict[str, Any]] = None
|
|
39
43
|
):
|
|
40
44
|
self.logger = logging.getLogger(service_name)
|
|
41
|
-
self.
|
|
42
|
-
|
|
43
|
-
"host.name": platform.node(),
|
|
44
|
-
**(default_attributes or {})
|
|
45
|
-
}
|
|
45
|
+
self.service_name = service_name
|
|
46
|
+
self.default_attributes = default_attributes or {}
|
|
46
47
|
|
|
47
|
-
if not self.logger.handlers
|
|
48
|
+
if not self.logger.handlers:
|
|
48
49
|
handler = logging.StreamHandler()
|
|
49
50
|
handler.setFormatter(JsonFormatter())
|
|
50
51
|
self.logger.addHandler(handler)
|
|
@@ -69,40 +70,30 @@ class StructuredLogger:
|
|
|
69
70
|
*args,
|
|
70
71
|
**kwargs
|
|
71
72
|
):
|
|
72
|
-
|
|
73
|
+
operation = kwargs.pop("operation", None)
|
|
74
|
+
status = kwargs.pop("status", None)
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
**self.default_attributes,
|
|
76
|
-
"timestamp": datetime.utcnow().isoformat(),
|
|
77
|
-
"severity": logging.getLevelName(level),
|
|
78
|
-
"logger.name": self.logger.name
|
|
79
|
-
}
|
|
76
|
+
context = kwargs.copy() if kwargs else None
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
extra_data = {
|
|
79
|
+
"service_name": self.service_name
|
|
80
|
+
}
|
|
84
81
|
|
|
85
|
-
operation = kwargs.pop("operation", None)
|
|
86
82
|
if operation:
|
|
87
|
-
|
|
83
|
+
extra_data["operation"] = operation
|
|
88
84
|
|
|
89
|
-
status = kwargs.pop("status", None)
|
|
90
85
|
if status:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
self.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"otel.kind": "event",
|
|
103
|
-
**log_attributes
|
|
104
|
-
}
|
|
105
|
-
)
|
|
86
|
+
extra_data["status"] = status
|
|
87
|
+
|
|
88
|
+
if context:
|
|
89
|
+
extra_data["context"] = context
|
|
90
|
+
|
|
91
|
+
trace_ctx = self._get_trace_context()
|
|
92
|
+
if trace_ctx:
|
|
93
|
+
extra_data["otelTraceID"] = trace_ctx.get("trace_id")
|
|
94
|
+
extra_data["otelSpanID"] = trace_ctx.get("span_id")
|
|
95
|
+
|
|
96
|
+
self.logger.log(level, message, extra=extra_data)
|
|
106
97
|
|
|
107
98
|
def debug(self, message: str, *args, **kwargs):
|
|
108
99
|
self._log(logging.DEBUG, message, *args, **kwargs)
|
|
@@ -110,6 +101,9 @@ class StructuredLogger:
|
|
|
110
101
|
def info(self, message: str, *args, **kwargs):
|
|
111
102
|
self._log(logging.INFO, message, *args, **kwargs)
|
|
112
103
|
|
|
104
|
+
def warning(self, message: str, *args, **kwargs):
|
|
105
|
+
self._log(logging.WARN, message, *args, **kwargs)
|
|
106
|
+
|
|
113
107
|
def error(self, message: str, *args, **kwargs):
|
|
114
108
|
self._log(logging.ERROR, message, *args, **kwargs)
|
|
115
109
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|