diff --git a/CHANGELOG.md b/CHANGELOG.md index 256312c8c4..cc7a26bb78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- opentelemetry-sdk: use stable code attributes: `code.function` -> `code.function.name`, `code.lineno` -> `code.line.number`, `code.filepath` -> `code.file.path` + ([#4508](https://github.com/open-telemetry/opentelemetry-python/pull/4508)) - Fix serialization of extended attributes for logs signal ([#4342](https://github.com/open-telemetry/opentelemetry-python/pull/4342)) - docs: updated and added to the metrics and log examples diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py index 58872f6802..9060e49aac 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py @@ -45,7 +45,8 @@ from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.util import ns_to_iso_str from opentelemetry.sdk.util.instrumentation import InstrumentationScope -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes import code_attributes +from opentelemetry.semconv.attributes import exception_attributes from opentelemetry.trace import ( format_span_id, format_trace_id, @@ -487,22 +488,24 @@ def _get_attributes(record: logging.LogRecord) -> _ExtendedAttributes: } # Add standard code attributes for logs. - attributes[SpanAttributes.CODE_FILEPATH] = record.pathname - attributes[SpanAttributes.CODE_FUNCTION] = record.funcName - attributes[SpanAttributes.CODE_LINENO] = record.lineno + attributes[code_attributes.CODE_FILE_PATH] = record.pathname + attributes[code_attributes.CODE_FUNCTION_NAME] = record.funcName + attributes[code_attributes.CODE_LINE_NUMBER] = record.lineno if record.exc_info: exctype, value, tb = record.exc_info if exctype is not None: - attributes[SpanAttributes.EXCEPTION_TYPE] = exctype.__name__ + attributes[exception_attributes.EXCEPTION_TYPE] = ( + exctype.__name__ + ) if value is not None and value.args: - attributes[SpanAttributes.EXCEPTION_MESSAGE] = str( + attributes[exception_attributes.EXCEPTION_MESSAGE] = str( value.args[0] ) if tb is not None: - # https://github.com/open-telemetry/opentelemetry-specification/blob/9fa7c656b26647b27e485a6af7e38dc716eba98a/specification/trace/semantic_conventions/exceptions.md#stacktrace-representation - attributes[SpanAttributes.EXCEPTION_STACKTRACE] = "".join( - traceback.format_exception(*record.exc_info) + # https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/#stacktrace-representation + attributes[exception_attributes.EXCEPTION_STACKTRACE] = ( + "".join(traceback.format_exception(*record.exc_info)) ) return attributes diff --git a/opentelemetry-sdk/tests/logs/test_handler.py b/opentelemetry-sdk/tests/logs/test_handler.py index 1b62cc6c78..3817c44025 100644 --- a/opentelemetry-sdk/tests/logs/test_handler.py +++ b/opentelemetry-sdk/tests/logs/test_handler.py @@ -27,7 +27,8 @@ LoggingHandler, LogRecordProcessor, ) -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv._incubating.attributes import code_attributes +from opentelemetry.semconv.attributes import exception_attributes from opentelemetry.trace import INVALID_SPAN_CONTEXT @@ -127,17 +128,19 @@ def test_log_record_user_attributes(self): self.assertEqual(len(log_record.attributes), 4) self.assertEqual(log_record.attributes["http.status_code"], 200) self.assertTrue( - log_record.attributes[SpanAttributes.CODE_FILEPATH].endswith( + log_record.attributes[code_attributes.CODE_FILE_PATH].endswith( "test_handler.py" ) ) self.assertEqual( - log_record.attributes[SpanAttributes.CODE_FUNCTION], + log_record.attributes[code_attributes.CODE_FUNCTION_NAME], "test_log_record_user_attributes", ) # The line of the log statement is not a constant (changing tests may change that), # so only check that the attribute is present. - self.assertTrue(SpanAttributes.CODE_LINENO in log_record.attributes) + self.assertTrue( + code_attributes.CODE_LINE_NUMBER in log_record.attributes + ) self.assertTrue(isinstance(log_record.attributes, BoundedAttributes)) def test_log_record_exception(self): @@ -156,15 +159,15 @@ def test_log_record_exception(self): self.assertTrue(isinstance(log_record.body, str)) self.assertEqual(log_record.body, "Zero Division Error") self.assertEqual( - log_record.attributes[SpanAttributes.EXCEPTION_TYPE], + log_record.attributes[exception_attributes.EXCEPTION_TYPE], ZeroDivisionError.__name__, ) self.assertEqual( - log_record.attributes[SpanAttributes.EXCEPTION_MESSAGE], + log_record.attributes[exception_attributes.EXCEPTION_MESSAGE], "division by zero", ) stack_trace = log_record.attributes[ - SpanAttributes.EXCEPTION_STACKTRACE + exception_attributes.EXCEPTION_STACKTRACE ] self.assertIsInstance(stack_trace, str) self.assertTrue("Traceback" in stack_trace) @@ -189,15 +192,15 @@ def test_log_record_recursive_exception(self): self.assertIsNotNone(log_record) self.assertEqual(log_record.body, "Zero Division Error") self.assertEqual( - log_record.attributes[SpanAttributes.EXCEPTION_TYPE], + log_record.attributes[exception_attributes.EXCEPTION_TYPE], ZeroDivisionError.__name__, ) self.assertEqual( - log_record.attributes[SpanAttributes.EXCEPTION_MESSAGE], + log_record.attributes[exception_attributes.EXCEPTION_MESSAGE], "division by zero", ) stack_trace = log_record.attributes[ - SpanAttributes.EXCEPTION_STACKTRACE + exception_attributes.EXCEPTION_STACKTRACE ] self.assertIsInstance(stack_trace, str) self.assertTrue("Traceback" in stack_trace) @@ -219,12 +222,14 @@ def test_log_exc_info_false(self): self.assertIsNotNone(log_record) self.assertEqual(log_record.body, "Zero Division Error") - self.assertNotIn(SpanAttributes.EXCEPTION_TYPE, log_record.attributes) self.assertNotIn( - SpanAttributes.EXCEPTION_MESSAGE, log_record.attributes + exception_attributes.EXCEPTION_TYPE, log_record.attributes + ) + self.assertNotIn( + exception_attributes.EXCEPTION_MESSAGE, log_record.attributes ) self.assertNotIn( - SpanAttributes.EXCEPTION_STACKTRACE, log_record.attributes + exception_attributes.EXCEPTION_STACKTRACE, log_record.attributes ) def test_log_record_exception_with_object_payload(self): @@ -246,15 +251,15 @@ def __str__(self): self.assertTrue(isinstance(log_record.body, str)) self.assertEqual(log_record.body, "CustomException stringified") self.assertEqual( - log_record.attributes[SpanAttributes.EXCEPTION_TYPE], + log_record.attributes[exception_attributes.EXCEPTION_TYPE], CustomException.__name__, ) self.assertEqual( - log_record.attributes[SpanAttributes.EXCEPTION_MESSAGE], + log_record.attributes[exception_attributes.EXCEPTION_MESSAGE], "CustomException message", ) stack_trace = log_record.attributes[ - SpanAttributes.EXCEPTION_STACKTRACE + exception_attributes.EXCEPTION_STACKTRACE ] self.assertIsInstance(stack_trace, str) self.assertTrue("Traceback" in stack_trace)