|
27 | 27 | LoggingHandler,
|
28 | 28 | LogRecordProcessor,
|
29 | 29 | )
|
| 30 | +from opentelemetry.sdk.environment_variables import OTEL_ATTRIBUTE_COUNT_LIMIT |
30 | 31 | from opentelemetry.semconv._incubating.attributes import code_attributes
|
31 | 32 | from opentelemetry.semconv.attributes import exception_attributes
|
32 | 33 | from opentelemetry.trace import (
|
33 | 34 | INVALID_SPAN_CONTEXT,
|
34 | 35 | set_span_in_context,
|
35 | 36 | )
|
36 | 37 |
|
37 |
| - |
| 38 | +# pylint: disable=too-many-public-methods |
38 | 39 | class TestLoggingHandler(unittest.TestCase):
|
39 | 40 | def test_handler_default_log_level(self):
|
40 | 41 | processor, logger = set_up_test_logging(logging.NOTSET)
|
@@ -367,6 +368,111 @@ def test_handler_root_logger_with_disabled_sdk_does_not_go_into_recursion_error(
|
367 | 368 |
|
368 | 369 | self.assertEqual(processor.emit_count(), 0)
|
369 | 370 |
|
| 371 | + @patch.dict(os.environ, {OTEL_ATTRIBUTE_COUNT_LIMIT: "3"}) |
| 372 | + def test_otel_attribute_count_limit_respected_in_logging_handler(self): |
| 373 | + """Test that OTEL_ATTRIBUTE_COUNT_LIMIT is properly respected by LoggingHandler.""" |
| 374 | + # Create a new LoggerProvider within the patched environment |
| 375 | + # This will create LogLimits() that reads from the environment variable |
| 376 | + logger_provider = LoggerProvider() |
| 377 | + processor = FakeProcessor() |
| 378 | + logger_provider.add_log_record_processor(processor) |
| 379 | + logger = logging.getLogger("env_test") |
| 380 | + handler = LoggingHandler( |
| 381 | + level=logging.WARNING, logger_provider=logger_provider |
| 382 | + ) |
| 383 | + logger.addHandler(handler) |
| 384 | + |
| 385 | + # Create a log record with many extra attributes |
| 386 | + extra_attrs = {f"custom_attr_{i}": f"value_{i}" for i in range(10)} |
| 387 | + |
| 388 | + with self.assertLogs(level=logging.WARNING): |
| 389 | + logger.warning( |
| 390 | + "Test message with many attributes", extra=extra_attrs |
| 391 | + ) |
| 392 | + |
| 393 | + log_record = processor.get_log_record(0) |
| 394 | + |
| 395 | + # With OTEL_ATTRIBUTE_COUNT_LIMIT=3, should have exactly 3 attributes |
| 396 | + total_attrs = len(log_record.attributes) |
| 397 | + self.assertEqual( |
| 398 | + total_attrs, |
| 399 | + 3, |
| 400 | + f"Should have exactly 3 attributes due to limit, got {total_attrs}", |
| 401 | + ) |
| 402 | + |
| 403 | + # Should have 10 dropped attributes (10 custom + 3 code - 3 kept = 10 dropped) |
| 404 | + self.assertEqual( |
| 405 | + log_record.dropped_attributes, |
| 406 | + 10, |
| 407 | + f"Should have 10 dropped attributes, got {log_record.dropped_attributes}", |
| 408 | + ) |
| 409 | + |
| 410 | + @patch.dict(os.environ, {OTEL_ATTRIBUTE_COUNT_LIMIT: "5"}) |
| 411 | + def test_otel_attribute_count_limit_includes_code_attributes(self): |
| 412 | + """Test that OTEL_ATTRIBUTE_COUNT_LIMIT applies to all attributes including code attributes.""" |
| 413 | + # Create a new LoggerProvider within the patched environment |
| 414 | + # This will create LogLimits() that reads from the environment variable |
| 415 | + logger_provider = LoggerProvider() |
| 416 | + processor = FakeProcessor() |
| 417 | + logger_provider.add_log_record_processor(processor) |
| 418 | + logger = logging.getLogger("env_test_2") |
| 419 | + handler = LoggingHandler( |
| 420 | + level=logging.WARNING, logger_provider=logger_provider |
| 421 | + ) |
| 422 | + logger.addHandler(handler) |
| 423 | + |
| 424 | + # Create a log record with some extra attributes |
| 425 | + extra_attrs = {f"user_attr_{i}": f"value_{i}" for i in range(8)} |
| 426 | + |
| 427 | + with self.assertLogs(level=logging.WARNING): |
| 428 | + logger.warning("Test message", extra=extra_attrs) |
| 429 | + |
| 430 | + log_record = processor.get_log_record(0) |
| 431 | + |
| 432 | + # With OTEL_ATTRIBUTE_COUNT_LIMIT=5, should have exactly 5 attributes |
| 433 | + total_attrs = len(log_record.attributes) |
| 434 | + self.assertEqual( |
| 435 | + total_attrs, |
| 436 | + 5, |
| 437 | + f"Should have exactly 5 attributes due to limit, got {total_attrs}", |
| 438 | + ) |
| 439 | + |
| 440 | + # Should have 6 dropped attributes (8 user + 3 code - 5 kept = 6 dropped) |
| 441 | + self.assertEqual( |
| 442 | + log_record.dropped_attributes, |
| 443 | + 6, |
| 444 | + f"Should have 6 dropped attributes, got {log_record.dropped_attributes}", |
| 445 | + ) |
| 446 | + |
| 447 | + def test_logging_handler_without_env_var_uses_default_limit(self): |
| 448 | + """Test that without OTEL_ATTRIBUTE_COUNT_LIMIT, default limit (128) should apply.""" |
| 449 | + processor, logger = set_up_test_logging(logging.WARNING) |
| 450 | + |
| 451 | + # Create a log record with many attributes (more than default limit of 128) |
| 452 | + extra_attrs = {f"attr_{i}": f"value_{i}" for i in range(150)} |
| 453 | + |
| 454 | + with self.assertLogs(level=logging.WARNING): |
| 455 | + logger.warning( |
| 456 | + "Test message with many attributes", extra=extra_attrs |
| 457 | + ) |
| 458 | + |
| 459 | + log_record = processor.get_log_record(0) |
| 460 | + |
| 461 | + # Should be limited to default limit (128) total attributes |
| 462 | + total_attrs = len(log_record.attributes) |
| 463 | + self.assertEqual( |
| 464 | + total_attrs, |
| 465 | + 128, |
| 466 | + f"Should have exactly 128 attributes (default limit), got {total_attrs}", |
| 467 | + ) |
| 468 | + |
| 469 | + # Should have 25 dropped attributes (150 user + 3 code - 128 kept = 25 dropped) |
| 470 | + self.assertEqual( |
| 471 | + log_record.dropped_attributes, |
| 472 | + 25, |
| 473 | + f"Should have 25 dropped attributes, got {log_record.dropped_attributes}", |
| 474 | + ) |
| 475 | + |
370 | 476 |
|
371 | 477 | def set_up_test_logging(level, formatter=None, root_logger=False):
|
372 | 478 | logger_provider = LoggerProvider()
|
|
0 commit comments