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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: otel-utils
3
- Version: 0.1.18
3
+ Version: 0.1.19
4
4
  Summary: Utilidades simplificadas para instrumentación con OpenTelemetry
5
5
  License: Proprietary
6
6
  Author: Harold Portocarrero
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "otel-utils"
3
- version = "0.1.18"
3
+ version = "0.1.19"
4
4
  description = "Utilidades simplificadas para instrumentación con OpenTelemetry"
5
5
  authors = ["Harold Portocarrero <harold@getcometa.com>"]
6
6
  readme = "README.md"
@@ -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
- formatter = logging.Formatter(
147
- '[%(asctime)s] %(levelname)s [%(name)s] [trace_id=%(otelTraceID)s span_id=%(otelSpanID)s] - %(message)s',
148
- datefmt='%Y-%m-%d %H:%M:%S'
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=True,
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.logger = logging.getLogger(self.config.service_name)
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
- log_data = {}
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, 'structured') and record.structured:
16
- for key, value in record.__dict__.items():
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
- log_data['message'] = record.getMessage()
24
+ if hasattr(record, "operation"):
25
+ log_record["operation"] = record.operation
26
26
 
27
- return json.dumps(log_data)
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.default_attributes = {
42
- "service.name": service_name,
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 or not any(isinstance(h.formatter, JsonFormatter) for h in 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
- trace_context = self._get_trace_context()
73
+ operation = kwargs.pop("operation", None)
74
+ status = kwargs.pop("status", None)
73
75
 
74
- log_attributes = {
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
- if trace_context:
82
- log_attributes["trace_id"] = trace_context.get("trace_id")
83
- log_attributes["span_id"] = trace_context.get("span_id")
78
+ extra_data = {
79
+ "service_name": self.service_name
80
+ }
84
81
 
85
- operation = kwargs.pop("operation", None)
86
82
  if operation:
87
- log_attributes["operation"] = operation
83
+ extra_data["operation"] = operation
88
84
 
89
- status = kwargs.pop("status", None)
90
85
  if status:
91
- log_attributes["status"] = status
92
-
93
- if kwargs:
94
- log_attributes["context"] = kwargs
95
-
96
- self.logger.log(
97
- level,
98
- message,
99
- extra={
100
- "structured": True,
101
- "otel.name": "log",
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