Skip to content

Commit

Permalink
Add support for custom attributes in OTLPHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
adriangb committed Jul 14, 2021
1 parent 196a19c commit 4c6d5e9
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 3 deletions.
21 changes: 18 additions & 3 deletions opentelemetry-sdk/src/opentelemetry/sdk/logs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,20 +244,35 @@ def force_flush(self, timeout_millis: int = 30000) -> bool:
return True


# skip natural LogRecord attributes
# http://docs.python.org/library/logging.html#logrecord-attributes
_RESERVED_ATTRS = frozenset((
'args', 'asctime', 'created', 'exc_info', 'exc_text', 'filename',
'funcName', 'levelname', 'levelno', 'lineno', 'module',
'msecs', 'message', 'msg', 'name', 'pathname', 'process',
'processName', 'relativeCreated', 'stack_info', 'thread', 'threadName')
)


class OTLPHandler(logging.Handler):
"""A handler class which writes logging records, in OTLP format, to
a network destination or file.
"""

def __init__(self, level=logging.NOTSET, log_emitter=None) -> None:
def __init__(self, level=logging.NOTSET, log_emitter=None, *, attributes_key: Optional[str] = None) -> None:
super().__init__(level=level)
self._log_emitter = log_emitter or get_log_emitter(__name__)
self.attributes_key = attributes_key

def _get_attributes(self, record: logging.LogRecord) -> Attributes:
if self.attributes_key is not None:
return vars(record)[self.attributes_key]
return {k: v for k, v in vars(record).items() if k not in _RESERVED_ATTRS}

def _translate(self, record: logging.LogRecord) -> LogRecord:
timestamp = int(record.created * 1e9)
span_context = get_current_span().get_span_context()
# TODO: attributes (or resource attributes?) from record metadata
attributes: Attributes = {}
attributes = self._get_attributes(record)
severity_number = std_to_otlp(record.levelno)
return LogRecord(
timestamp=timestamp,
Expand Down
26 changes: 26 additions & 0 deletions opentelemetry-sdk/tests/logs/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@ def test_log_record_no_span_context(self):
log_record.trace_flags, INVALID_SPAN_CONTEXT.trace_flags
)

def test_log_record_user_attributes(self):
"""Attributes can be injected into logs by adding them as attributes to the LogRecord"""
emitter_mock = Mock(spec=LogEmitter)
logger = get_logger(log_emitter=emitter_mock)
# Assert emit gets called for warning message
logger.warning("Wanrning message", extra={"http.status_code": 200})
args, _ = emitter_mock.emit.call_args_list[0]
log_record = args[0]

self.assertIsNotNone(log_record)
self.assertEqual(log_record.attributes, {"http.status_code": 200})

def test_log_record_user_attributes_key(self):
"""Users can specify a key to extract attributes from"""
emitter_mock = Mock(spec=LogEmitter)
logger = logging.getLogger(__name__)
handler = OTLPHandler(level=logging.NOTSET, log_emitter=emitter_mock, attributes_key="opentelemetry")
logger.addHandler(handler)
# Assert emit gets called for warning message
logger.warning("Wanrning message", extra={"opentelemetry": {"http.status_code": 200}, "ignored": True})
args, _ = emitter_mock.emit.call_args_list[0]
log_record = args[0]

self.assertIsNotNone(log_record)
self.assertEqual(log_record.attributes, {"http.status_code": 200})

def test_log_record_trace_correlation(self):
emitter_mock = Mock(spec=LogEmitter)
logger = get_logger(log_emitter=emitter_mock)
Expand Down

0 comments on commit 4c6d5e9

Please sign in to comment.