From 98772d4920980e160a3193bbf5b963479fc3a298 Mon Sep 17 00:00:00 2001 From: Lukas Hering Date: Thu, 11 Jun 2026 16:51:04 -0400 Subject: [PATCH 1/4] opentelemetry-sdk: add 'force_flush' method to LogRecordExporter ABC --- .../sdk/_logs/_internal/export/__init__.py | 10 ++++++++++ .../_logs/_internal/export/in_memory_log_exporter.py | 3 +++ opentelemetry-sdk/tests/logs/test_export.py | 3 +++ opentelemetry-sdk/tests/test_configurator.py | 3 +++ 4 files changed, 19 insertions(+) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py index 31d4b9667c8..5a8b4224c15 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py @@ -119,6 +119,13 @@ def shutdown(self): Called when the SDK is shut down. """ + @abc.abstractmethod + def force_flush(self, timeout_millis: float = 10_000) -> bool: + """Hint to ensure that the export of any ``ReadableLogRecord`` objects + the exporter has received prior to the call to ``force_flush`` SHOULD be + completed as soon as possible, preferably before returning from this method. + """ + @deprecated( "Use LogRecordExporter. Since logs are not stable yet this WILL be removed in future releases." @@ -154,6 +161,9 @@ def export(self, batch: Sequence[ReadableLogRecord]): def shutdown(self): pass + def force_flush(self, timeout_millis: float = 10_000) -> bool: + return True + @deprecated( "Use ConsoleLogRecordExporter. Since logs are not stable yet this WILL be removed in future releases." diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py index 1f793745458..3b55a2531dd 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/in_memory_log_exporter.py @@ -46,6 +46,9 @@ def export( def shutdown(self) -> None: self._stopped = True + def force_flush(self, timeout_millis: float = 10_000) -> bool: + return True + @deprecated( "Use InMemoryLogRecordExporter. Since logs are not stable yet this WILL be removed in future releases." diff --git a/opentelemetry-sdk/tests/logs/test_export.py b/opentelemetry-sdk/tests/logs/test_export.py index 06af6ef03a8..b3c7ecc68aa 100644 --- a/opentelemetry-sdk/tests/logs/test_export.py +++ b/opentelemetry-sdk/tests/logs/test_export.py @@ -70,6 +70,9 @@ class Exporter(LogRecordExporter): def shutdown(self): pass + def force_flush(self, timeout_millis: float = 10_000) -> bool: + return True + def export(self, batch: Sequence[ReadableLogRecord]): logger = logging.getLogger("any logger..") logger.warning("Something happened.") diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index c66deeb2d81..a1f742151f3 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -252,6 +252,9 @@ def export(self, batch): def shutdown(self): pass + def force_flush(self, timeout_millis: float = 10_000) -> bool: + return True + class CustomSampler(Sampler): def __init__(self) -> None: From cf9b2bd07a0b5b4c6b2c2f8615ee3d9098806fae Mon Sep 17 00:00:00 2001 From: Lukas Hering Date: Thu, 11 Jun 2026 16:56:59 -0400 Subject: [PATCH 2/4] update unit tests --- opentelemetry-sdk/tests/logs/test_export.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/opentelemetry-sdk/tests/logs/test_export.py b/opentelemetry-sdk/tests/logs/test_export.py index b3c7ecc68aa..cc242470a53 100644 --- a/opentelemetry-sdk/tests/logs/test_export.py +++ b/opentelemetry-sdk/tests/logs/test_export.py @@ -932,3 +932,13 @@ def formatter(record): # pylint: disable=unused-argument exporter.export([EMPTY_LOG]) mock_stdout.write.assert_called_once_with(mock_record_str) + + def test_force_flush(self): + exporter = ConsoleLogRecordExporter() + self.assertTrue(exporter.force_flush()) + + +class TestInMemoryLogRecordExporter(unittest.TestCase): + def test_force_flush(self): + exporter = InMemoryLogRecordExporter() + self.assertTrue(exporter.force_flush()) From de7364f00fd26b9f9d8b307f17eda2b5be5fbd3b Mon Sep 17 00:00:00 2001 From: Lukas Hering Date: Thu, 11 Jun 2026 17:02:28 -0400 Subject: [PATCH 3/4] update 'force_flush' doc-string --- .../opentelemetry/sdk/_logs/_internal/export/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py index 5a8b4224c15..b852a51b69f 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/export/__init__.py @@ -124,6 +124,14 @@ def force_flush(self, timeout_millis: float = 10_000) -> bool: """Hint to ensure that the export of any ``ReadableLogRecord`` objects the exporter has received prior to the call to ``force_flush`` SHOULD be completed as soon as possible, preferably before returning from this method. + + Args: + timeout_millis: The maximum amount of time to wait for the flush to + complete, in milliseconds. + + Returns: + ``True`` if the flush completed successfully within the timeout, + ``False`` otherwise. """ From accda1cc90e49423767357ee03e4940fde8aad1e Mon Sep 17 00:00:00 2001 From: Lukas Hering Date: Thu, 11 Jun 2026 17:03:15 -0400 Subject: [PATCH 4/4] add changelog fragment --- .changelog/5294.added | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changelog/5294.added diff --git a/.changelog/5294.added b/.changelog/5294.added new file mode 100644 index 00000000000..cb0141c6e42 --- /dev/null +++ b/.changelog/5294.added @@ -0,0 +1 @@ +`opentelemetry-sdk`: add `force_flush` method to `LogRecordExporter` ABC