otel-utils 0.1.17__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.17
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.17"
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,27 +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', 'levelno', 'pathname',
19
- 'filename',
20
- 'module', 'stack_info', 'lineno', 'funcName', 'created', 'msecs', 'relativeCreated', 'name',
21
- 'thread',
22
- 'threadName', 'processName', 'process'):
23
- log_data[key] = value
21
+ if hasattr(record, "context") and record.context:
22
+ log_record["context"] = record.context
24
23
 
25
- if 'attributes' in log_data:
26
- for key, value in log_data['attributes'].items():
27
- log_data[key] = value
28
- del log_data['attributes']
24
+ if hasattr(record, "operation"):
25
+ log_record["operation"] = record.operation
29
26
 
30
- log_data['message'] = record.getMessage()
27
+ for attr in ["service_name", "status", "error_type", "error_message"]:
28
+ if hasattr(record, attr):
29
+ log_record[attr] = getattr(record, attr)
31
30
 
32
- return json.dumps(log_data)
31
+ return json.dumps(log_record)
33
32
 
34
33
 
35
34
  class StructuredLogger:
@@ -43,13 +42,10 @@ class StructuredLogger:
43
42
  default_attributes: Optional[Dict[str, Any]] = None
44
43
  ):
45
44
  self.logger = logging.getLogger(service_name)
46
- self.default_attributes = {
47
- "service.name": service_name,
48
- "host.name": platform.node(),
49
- **(default_attributes or {})
50
- }
45
+ self.service_name = service_name
46
+ self.default_attributes = default_attributes or {}
51
47
 
52
- if not self.logger.handlers or not any(isinstance(h.formatter, JsonFormatter) for h in self.logger.handlers):
48
+ if not self.logger.handlers:
53
49
  handler = logging.StreamHandler()
54
50
  handler.setFormatter(JsonFormatter())
55
51
  self.logger.addHandler(handler)
@@ -74,29 +70,30 @@ class StructuredLogger:
74
70
  *args,
75
71
  **kwargs
76
72
  ):
77
- trace_context = self._get_trace_context()
78
-
79
- log_attributes = {
80
- **self.default_attributes,
81
- **trace_context,
82
- "timestamp": datetime.utcnow().isoformat(),
83
- "severity": logging.getLevelName(level),
84
- "logger.name": self.logger.name
73
+ operation = kwargs.pop("operation", None)
74
+ status = kwargs.pop("status", None)
75
+
76
+ context = kwargs.copy() if kwargs else None
77
+
78
+ extra_data = {
79
+ "service_name": self.service_name
85
80
  }
86
81
 
87
- for key, value in kwargs.items():
88
- log_attributes[key] = value
89
-
90
- self.logger.log(
91
- level,
92
- message,
93
- extra={
94
- "structured": True,
95
- "otel.name": "log",
96
- "otel.kind": "event",
97
- **log_attributes
98
- }
99
- )
82
+ if operation:
83
+ extra_data["operation"] = operation
84
+
85
+ if status:
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)
100
97
 
101
98
  def debug(self, message: str, *args, **kwargs):
102
99
  self._log(logging.DEBUG, message, *args, **kwargs)
@@ -104,6 +101,9 @@ class StructuredLogger:
104
101
  def info(self, message: str, *args, **kwargs):
105
102
  self._log(logging.INFO, message, *args, **kwargs)
106
103
 
104
+ def warning(self, message: str, *args, **kwargs):
105
+ self._log(logging.WARN, message, *args, **kwargs)
106
+
107
107
  def error(self, message: str, *args, **kwargs):
108
108
  self._log(logging.ERROR, message, *args, **kwargs)
109
109
 
File without changes