|
15 | 15 | # pylint: disable=protected-access |
16 | 16 | import logging |
17 | 17 | import os |
| 18 | +import sys |
18 | 19 | import time |
19 | 20 | import unittest |
20 | 21 | from concurrent.futures import ( # pylint: disable=no-name-in-module |
21 | 22 | ThreadPoolExecutor, |
22 | 23 | ) |
23 | | -from sys import version_info |
| 24 | +from typing import Sequence |
24 | 25 | from unittest.mock import Mock, patch |
25 | 26 |
|
26 | 27 | from pytest import mark |
|
38 | 39 | BatchLogRecordProcessor, |
39 | 40 | ConsoleLogRecordExporter, |
40 | 41 | InMemoryLogRecordExporter, |
| 42 | + LogRecordExporter, |
41 | 43 | SimpleLogRecordProcessor, |
42 | 44 | ) |
43 | 45 | from opentelemetry.sdk.environment_variables import ( |
|
63 | 65 |
|
64 | 66 |
|
65 | 67 | class TestSimpleLogRecordProcessor(unittest.TestCase): |
| 68 | + @mark.skipif( |
| 69 | + (3, 13, 0) <= sys.version_info <= (3, 13, 5), |
| 70 | + reason="This will fail on 3.13.5 due to https://github.com/python/cpython/pull/131812 which prevents recursive log messages but was rolled back in 3.13.6.", |
| 71 | + ) |
| 72 | + def test_simple_log_record_processor_doesnt_enter_recursive_loop(self): |
| 73 | + class Exporter(LogRecordExporter): |
| 74 | + def shutdown(self): |
| 75 | + pass |
| 76 | + |
| 77 | + def export(self, batch: Sequence[ReadableLogRecord]): |
| 78 | + logger = logging.getLogger("any logger..") |
| 79 | + logger.warning("Something happened.") |
| 80 | + |
| 81 | + exporter = Exporter() |
| 82 | + logger_provider = LoggerProvider() |
| 83 | + logger_provider.add_log_record_processor( |
| 84 | + SimpleLogRecordProcessor(exporter) |
| 85 | + ) |
| 86 | + root_logger = logging.getLogger() |
| 87 | + # Add the OTLP handler to the root logger like is done in auto instrumentation. |
| 88 | + # This causes logs generated from within SimpleLogRecordProcessor.on_emit (such as the above log in export) |
| 89 | + # to be sent back to SimpleLogRecordProcessor.on_emit |
| 90 | + handler = LoggingHandler( |
| 91 | + level=logging.DEBUG, logger_provider=logger_provider |
| 92 | + ) |
| 93 | + root_logger.addHandler(handler) |
| 94 | + propagate_false_logger = logging.getLogger( |
| 95 | + "opentelemetry.sdk._logs._internal.export.propagate.false" |
| 96 | + ) |
| 97 | + # This would cause a max recursion depth exceeded error.. |
| 98 | + try: |
| 99 | + with self.assertLogs(propagate_false_logger) as cm: |
| 100 | + root_logger.warning("hello!") |
| 101 | + assert ( |
| 102 | + "SimpleLogRecordProcessor.on_emit has entered a recursive loop" |
| 103 | + in cm.output[0] |
| 104 | + ) |
| 105 | + finally: |
| 106 | + root_logger.removeHandler(handler) |
| 107 | + |
66 | 108 | def test_simple_log_record_processor_default_level(self): |
67 | 109 | exporter = InMemoryLogRecordExporter() |
68 | 110 | logger_provider = LoggerProvider() |
@@ -406,39 +448,6 @@ def bulk_emit(num_emit): |
406 | 448 | time.sleep(2) |
407 | 449 | assert len(exporter.get_finished_logs()) == total_expected_logs |
408 | 450 |
|
409 | | - @mark.skipif( |
410 | | - version_info < (3, 10), |
411 | | - reason="assertNoLogs only exists in python 3.10+.", |
412 | | - ) |
413 | | - def test_logging_lib_not_invoked_in_batch_log_record_emit(self): # pylint: disable=no-self-use |
414 | | - # See https://github.com/open-telemetry/opentelemetry-python/issues/4261 |
415 | | - exporter = Mock() |
416 | | - processor = BatchLogRecordProcessor(exporter) |
417 | | - logger_provider = LoggerProvider( |
418 | | - resource=SDKResource.create( |
419 | | - { |
420 | | - "service.name": "shoppingcart", |
421 | | - "service.instance.id": "instance-12", |
422 | | - } |
423 | | - ), |
424 | | - ) |
425 | | - logger_provider.add_log_record_processor(processor) |
426 | | - handler = LoggingHandler( |
427 | | - level=logging.INFO, logger_provider=logger_provider |
428 | | - ) |
429 | | - sdk_logger = logging.getLogger("opentelemetry.sdk") |
430 | | - # Attach OTLP handler to SDK logger |
431 | | - sdk_logger.addHandler(handler) |
432 | | - # If `emit` calls logging.log then this test will throw a maximum recursion depth exceeded exception and fail. |
433 | | - try: |
434 | | - with self.assertNoLogs(sdk_logger, logging.NOTSET): |
435 | | - processor.on_emit(EMPTY_LOG) |
436 | | - processor.shutdown() |
437 | | - with self.assertNoLogs(sdk_logger, logging.NOTSET): |
438 | | - processor.on_emit(EMPTY_LOG) |
439 | | - finally: |
440 | | - sdk_logger.removeHandler(handler) |
441 | | - |
442 | 451 | def test_args(self): |
443 | 452 | exporter = InMemoryLogRecordExporter() |
444 | 453 | log_record_processor = BatchLogRecordProcessor( |
|
0 commit comments