Skip to content

Commit 9014116

Browse files
committed
fix(logging): fix logging duplicating
1 parent b49f5a9 commit 9014116

File tree

5 files changed

+51
-18
lines changed

5 files changed

+51
-18
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [1.15.1] - 2025-10-06
10+
### Fixed
11+
- Logging duplication on CloudWatch side
12+
13+
914
## [1.15.0] - 2025-09-04
1015
### Security
1116
- Upgraded internal dependency `urllib3` to version `2.5.0`

src/corva/configuration.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
import pydantic
44

55

6+
def parse_truthy(v):
7+
if isinstance(v, bool):
8+
return v
9+
return str(v or "").strip().lower() in {"1", "true", "t", "yes", "y", "on"}
10+
11+
612
class Settings(pydantic.BaseSettings):
713
# api
814
API_ROOT_URL: pydantic.AnyHttpUrl
@@ -32,5 +38,11 @@ class Settings(pydantic.BaseSettings):
3238
MAX_RETRY_COUNT: int = 3 # If `0` then retries will be disabled
3339
BACKOFF_FACTOR: float = 1.0
3440

41+
OTEL_LOG_SENDING_DISABLED: bool = False
42+
43+
_validate_otel_log_sending_disabled = pydantic.validator(
44+
"OTEL_LOG_SENDING_DISABLED", pre=True, allow_reuse=True
45+
)(parse_truthy)
46+
3547

3648
SETTINGS = Settings()

src/corva/logger.py

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import contextlib
22
import logging
3-
import os
43
import sys
54
import time
5+
from contextlib import suppress
66
from typing import Optional
77

88
from corva.configuration import SETTINGS
@@ -22,14 +22,6 @@
2222
CORVA_LOGGER.propagate = False
2323

2424

25-
def _is_truthy(value: Optional[str]) -> bool:
26-
return str(value).strip().lower() in {"1", "true", "t", "yes", "y", "on"}
27-
28-
29-
# Cache at import time: env rarely changes at runtime
30-
_OTEL_X_LOG_SENDING_DISABLED = _is_truthy(os.getenv("OTEL_X_LOG_SENDING_DISABLED"))
31-
32-
3325
def _is_otel_handler(handler: logging.Handler) -> bool:
3426
"""Best-effort detection of OTel log sending handlers.
3527
@@ -263,14 +255,12 @@ def __enter__(self):
263255
# If OTel log sending is enabled and an OTel handler exists on root,
264256
# attach it to CORVA_LOGGER as well so propagation can remain disabled
265257
# (avoids AWS root duplication) while still exporting logs via OTel.
266-
if not _OTEL_X_LOG_SENDING_DISABLED:
267-
try:
268-
for h in _gather_otel_handlers_from_root():
269-
if h not in handlers:
270-
handlers.append(h)
271-
except Exception:
272-
# Fail-safe: never break logging if detection fails
273-
pass
258+
259+
if not SETTINGS.OTEL_LOG_SENDING_DISABLED:
260+
with suppress(Exception): # Fail-safe: never break logging if detection fails
261+
for handler in _gather_otel_handlers_from_root():
262+
if handler not in handlers:
263+
handlers.append(handler)
274264

275265
self.logger.handlers = handlers
276266

src/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = "1.15.0"
1+
VERSION = "1.15.1"

tests/unit/test_logging.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import datetime
22
import logging
3+
from unittest.mock import MagicMock
34

45
import freezegun
56
import pytest
@@ -8,6 +9,7 @@
89
from corva import Logger
910
from corva.configuration import SETTINGS
1011
from corva.handlers import scheduled, stream, task
12+
from corva.logger import LoggingContext
1113
from corva.models.context import CorvaContext
1214
from corva.models.scheduled.raw import RawScheduledDataTimeEvent, RawScheduledEvent
1315
from corva.models.scheduled.scheduler_type import SchedulerType
@@ -326,3 +328,27 @@ def app(event, api):
326328

327329
assert 'The app failed to execute.' in captured.out
328330
assert 'The app failed to execute.' in captured.err
331+
332+
333+
@pytest.mark.parametrize("handler_cls_name", ("opentelemetry", "otel"))
334+
def test__otel_handler_passed_to_logging_context__success(monkeypatch, handler_cls_name):
335+
336+
otel_handler = MagicMock()
337+
otel_handler.__class__.__name__ = handler_cls_name
338+
339+
# Attach to the root logger
340+
logging.getLogger().addHandler(otel_handler)
341+
342+
monkeypatch.setenv("OTEL_SDK_DISABLED", "false")
343+
344+
with LoggingContext(
345+
aws_request_id=MagicMock(),
346+
asset_id=MagicMock(),
347+
app_connection_id=MagicMock(),
348+
handler=MagicMock(),
349+
user_handler=MagicMock(),
350+
logger=MagicMock(),
351+
) as context:
352+
assert otel_handler in context.logger.handlers
353+
354+
logging.getLogger().removeHandler(otel_handler)

0 commit comments

Comments
 (0)