elven-logs-interceptor-python 0.1.8__tar.gz → 0.1.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/PKG-INFO +2 -1
  2. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/README.md +1 -0
  3. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/pyproject.toml +1 -1
  4. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/__init__.py +3 -0
  5. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/application/config_service.py +5 -0
  6. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/config.py +2 -0
  7. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/transport/loki_json_transport.py +4 -3
  8. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/utils.py +3 -0
  9. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_config_service.py +16 -0
  10. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_loki_json_transport.py +38 -2
  11. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_utils_extra.py +2 -0
  12. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/.gitignore +0 -0
  13. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/ARCHITECTURE.md +0 -0
  14. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/examples/basic_app.py +0 -0
  15. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/examples/fastapi_integration.py +0 -0
  16. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/examples/full_config_reference.py +0 -0
  17. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/examples/high_volume.py +0 -0
  18. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/examples/tracking_usage.py +0 -0
  19. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/scripts/publish.sh +0 -0
  20. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/application/__init__.py +0 -0
  21. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/application/log_service.py +0 -0
  22. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/domain/__init__.py +0 -0
  23. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/domain/entities.py +0 -0
  24. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/domain/interfaces.py +0 -0
  25. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/domain/value_objects.py +0 -0
  26. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/__init__.py +0 -0
  27. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/buffer/__init__.py +0 -0
  28. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/buffer/memory_buffer.py +0 -0
  29. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/circuit_breaker/__init__.py +0 -0
  30. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/circuit_breaker/circuit_breaker.py +0 -0
  31. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/compression/__init__.py +0 -0
  32. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/compression/base.py +0 -0
  33. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/compression/brotli_compressor.py +0 -0
  34. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/compression/factory.py +0 -0
  35. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/compression/gzip_compressor.py +0 -0
  36. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/compression/noop_compressor.py +0 -0
  37. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/context/__init__.py +0 -0
  38. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/context/context_provider.py +0 -0
  39. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/dlq/__init__.py +0 -0
  40. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/dlq/file_dlq.py +0 -0
  41. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/dlq/memory_dlq.py +0 -0
  42. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/filter/__init__.py +0 -0
  43. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/filter/log_filter.py +0 -0
  44. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/interceptors/__init__.py +0 -0
  45. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/interceptors/runtime_interceptor.py +0 -0
  46. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/internal_capture_guard.py +0 -0
  47. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/log_noise_filter.py +0 -0
  48. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/log_record_extra.py +0 -0
  49. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/memory/__init__.py +0 -0
  50. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/memory/memory_tracker.py +0 -0
  51. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/metrics/__init__.py +0 -0
  52. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/metrics/metrics_collector.py +0 -0
  53. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/transport/__init__.py +0 -0
  54. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/transport/loki_protobuf_transport.py +0 -0
  55. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/transport/resilient_transport.py +0 -0
  56. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/transport/transport_factory.py +0 -0
  57. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/workers/__init__.py +0 -0
  58. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/infrastructure/workers/worker_pool.py +0 -0
  59. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/integrations/__init__.py +0 -0
  60. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/integrations/celery.py +0 -0
  61. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/integrations/django.py +0 -0
  62. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/integrations/fastapi.py +0 -0
  63. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/integrations/flask.py +0 -0
  64. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/integrations/logging_handler.py +0 -0
  65. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/integrations/loguru.py +0 -0
  66. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/integrations/structlog.py +0 -0
  67. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/preload.py +0 -0
  68. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/presentation/__init__.py +0 -0
  69. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/presentation/factory.py +0 -0
  70. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/src/logs_interceptor/types.py +0 -0
  71. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/test-apps/elven-live-demo/.env.example +0 -0
  72. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/test-apps/elven-live-demo/README.md +0 -0
  73. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/test-apps/elven-live-demo/app.py +0 -0
  74. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/test-apps/elven-live-demo/run.sh +0 -0
  75. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/test-apps/elven-observability-smoke/.env.example +0 -0
  76. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/test-apps/elven-observability-smoke/README.md +0 -0
  77. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/test-apps/elven-observability-smoke/app.py +0 -0
  78. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/test-apps/elven-observability-smoke/run.sh +0 -0
  79. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/integration/test_api.py +0 -0
  80. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_circuit_breaker_extra.py +0 -0
  81. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_core_components.py +0 -0
  82. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_env_config.py +0 -0
  83. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_integration_filters.py +0 -0
  84. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_log_filter_extra.py +0 -0
  85. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_log_service_unit.py +0 -0
  86. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_loguru_sink.py +0 -0
  87. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_memory_buffer_extra.py +0 -0
  88. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_protobuf_transport_safety.py +0 -0
  89. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_resilient_transport.py +0 -0
  90. {elven_logs_interceptor_python-0.1.8 → elven_logs_interceptor_python-0.1.9}/tests/unit/test_runtime_interceptor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: elven-logs-interceptor-python
3
- Version: 0.1.8
3
+ Version: 0.1.9
4
4
  Summary: Production-grade logs interceptor for Python with Loki transport, resilience, batching, and framework integrations.
5
5
  Author: Elven Observability
6
6
  License: MIT
@@ -154,6 +154,7 @@ Transport:
154
154
  - `LOGS_WORKER_TIMEOUT`
155
155
  - `LOGS_CONNECTION_POOLING`
156
156
  - `LOGS_MAX_SOCKETS`
157
+ - `LOGS_ENABLE_STRUCTURED_METADATA` (`false` by default; enable only when Loki allows `limits_config.allow_structured_metadata`)
157
158
  - `LOGS_TIMEOUT`
158
159
  - `LOGS_MAX_RETRIES`
159
160
  - `LOGS_RETRY_DELAY`
@@ -93,6 +93,7 @@ Transport:
93
93
  - `LOGS_WORKER_TIMEOUT`
94
94
  - `LOGS_CONNECTION_POOLING`
95
95
  - `LOGS_MAX_SOCKETS`
96
+ - `LOGS_ENABLE_STRUCTURED_METADATA` (`false` by default; enable only when Loki allows `limits_config.allow_structured_metadata`)
96
97
  - `LOGS_TIMEOUT`
97
98
  - `LOGS_MAX_RETRIES`
98
99
  - `LOGS_RETRY_DELAY`
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "elven-logs-interceptor-python"
7
- version = "0.1.8"
7
+ version = "0.1.9"
8
8
  description = "Production-grade logs interceptor for Python with Loki transport, resilience, batching, and framework integrations."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -104,6 +104,9 @@ def _coerce_config(user_config: LogsInterceptorConfig | dict[str, Any] | None) -
104
104
  transport_raw, "enable_connection_pooling", "enableConnectionPooling"
105
105
  ),
106
106
  max_sockets=_pick(transport_raw, "max_sockets", "maxSockets"),
107
+ enable_structured_metadata=_pick(
108
+ transport_raw, "enable_structured_metadata", "enableStructuredMetadata"
109
+ ),
107
110
  ),
108
111
  app_name=_pick(user_config, "app_name", "appName", ""),
109
112
  version=_pick(user_config, "version", "version"),
@@ -170,6 +170,11 @@ class ConfigService:
170
170
  True if transport.enable_connection_pooling is None else transport.enable_connection_pooling
171
171
  ),
172
172
  max_sockets=transport.max_sockets if transport.max_sockets is not None else 50,
173
+ enable_structured_metadata=(
174
+ False
175
+ if transport.enable_structured_metadata is None
176
+ else transport.enable_structured_metadata
177
+ ),
173
178
  )
174
179
 
175
180
  @staticmethod
@@ -26,6 +26,7 @@ class TransportConfig:
26
26
  worker_timeout: int | None = None
27
27
  enable_connection_pooling: bool | None = None
28
28
  max_sockets: int | None = None
29
+ enable_structured_metadata: bool | None = None
29
30
 
30
31
 
31
32
  @dataclass(slots=True)
@@ -131,6 +132,7 @@ class ResolvedTransportConfig:
131
132
  worker_timeout: int
132
133
  enable_connection_pooling: bool
133
134
  max_sockets: int
135
+ enable_structured_metadata: bool = False
134
136
 
135
137
 
136
138
  @dataclass(slots=True)
@@ -210,9 +210,10 @@ class LokiJsonTransport:
210
210
 
211
211
  timestamp_ns = self._timestamp_to_ns(entry.timestamp)
212
212
  value: list[Any] = [str(timestamp_ns), self._dumps(payload).decode("utf-8")]
213
- structured_metadata = self._format_structured_metadata(entry.metadata)
214
- if structured_metadata:
215
- value.append(structured_metadata)
213
+ if self._config.enable_structured_metadata:
214
+ structured_metadata = self._format_structured_metadata(entry.metadata)
215
+ if structured_metadata:
216
+ value.append(structured_metadata)
216
217
  values_map[key].append(value)
217
218
  streams[key] = labels
218
219
 
@@ -530,6 +530,9 @@ def load_config_from_env() -> LogsInterceptorConfig:
530
530
  enable_connection_pooling=parse_bool(env.get("LOGS_CONNECTION_POOLING"), True),
531
531
  max_sockets=parse_int_range(env.get("LOGS_MAX_SOCKETS"), 50, 1, 1024),
532
532
  worker_timeout=parse_int_range(env.get("LOGS_WORKER_TIMEOUT"), 30_000, 1000, 300_000),
533
+ enable_structured_metadata=parse_bool(
534
+ env.get("LOGS_ENABLE_STRUCTURED_METADATA"), False
535
+ ),
533
536
  ),
534
537
  app_name=env.get("LOGS_APP_NAME", ""),
535
538
  version=env.get("LOGS_APP_VERSION", "1.0.0"),
@@ -47,6 +47,7 @@ def test_resolve_applies_defaults() -> None:
47
47
 
48
48
  assert resolved.transport.timeout == 10_000
49
49
  assert resolved.transport.max_retries == 3
50
+ assert resolved.transport.enable_structured_metadata is False
50
51
  assert resolved.buffer.max_size == 100
51
52
  assert resolved.filter.sampling_rate == 1.0
52
53
  assert "httpx" in resolved.filter.exclude_logger_prefixes
@@ -55,6 +56,21 @@ def test_resolve_applies_defaults() -> None:
55
56
  assert resolved.performance.max_concurrent_flushes == 3
56
57
 
57
58
 
59
+ def test_resolve_enables_structured_metadata_only_when_requested() -> None:
60
+ resolved = ConfigService.resolve(
61
+ LogsInterceptorConfig(
62
+ transport=TransportConfig(
63
+ url="https://loki.example.com/loki/api/v1/push",
64
+ tenant_id="tenant",
65
+ enable_structured_metadata=True,
66
+ ),
67
+ app_name="app",
68
+ )
69
+ )
70
+
71
+ assert resolved.transport.enable_structured_metadata is True
72
+
73
+
58
74
  def test_validate_extended_ranges() -> None:
59
75
  config = LogsInterceptorConfig(
60
76
  transport=TransportConfig(
@@ -20,7 +20,10 @@ class _Response:
20
20
  text: str = ""
21
21
 
22
22
 
23
- def _config(compression: str = "gzip") -> ResolvedTransportConfig:
23
+ def _config(
24
+ compression: str = "gzip",
25
+ enable_structured_metadata: bool = False,
26
+ ) -> ResolvedTransportConfig:
24
27
  return ResolvedTransportConfig(
25
28
  url="https://loki.example.com/loki/api/v1/push",
26
29
  tenant_id="tenant",
@@ -36,6 +39,7 @@ def _config(compression: str = "gzip") -> ResolvedTransportConfig:
36
39
  worker_timeout=100,
37
40
  enable_connection_pooling=False,
38
41
  max_sockets=10,
42
+ enable_structured_metadata=enable_structured_metadata,
39
43
  )
40
44
 
41
45
 
@@ -92,7 +96,7 @@ def test_loki_json_transport_suppresses_internal_capture_during_request(
92
96
  assert observed == [True]
93
97
 
94
98
 
95
- def test_loki_json_transport_uses_structured_metadata_for_correlation_ids() -> None:
99
+ def test_loki_json_transport_keeps_correlation_metadata_in_log_line_by_default() -> None:
96
100
  transport = LokiJsonTransport(_config())
97
101
  payload = transport._format_for_loki(
98
102
  [
@@ -121,6 +125,38 @@ def test_loki_json_transport_uses_structured_metadata_for_correlation_ids() -> N
121
125
  stream = payload["streams"][0]
122
126
  assert stream["stream"] == {"service": "billing"}
123
127
  value = stream["values"][0]
128
+ assert len(value) == 2
129
+
130
+ line = json.loads(value[1])
131
+ assert line["traceId"] == "trace-123"
132
+ assert line["spanId"] == "span-123"
133
+ assert line["requestId"] == "req-123"
134
+ assert line["metadata"]["trace_id"] == "trace-123"
135
+ assert line["metadata"]["span_id"] == "span-123"
136
+ assert line["metadata"]["request_id"] == "req-123"
137
+
138
+
139
+ def test_loki_json_transport_uses_structured_metadata_when_explicitly_enabled() -> None:
140
+ transport = LokiJsonTransport(_config(enable_structured_metadata=True))
141
+ payload = transport._format_for_loki(
142
+ [
143
+ LogEntryEntity(
144
+ id="1",
145
+ timestamp="2026-01-01T00:00:00.000000+00:00",
146
+ level="info",
147
+ message="hello",
148
+ labels={"service": "billing"},
149
+ metadata={
150
+ "trace_id": "trace-123",
151
+ "span_id": "span-123",
152
+ "request_id": "req-123",
153
+ "ignored": None,
154
+ },
155
+ )
156
+ ]
157
+ )
158
+
159
+ value = payload["streams"][0]["values"][0]
124
160
  assert value[2] == {
125
161
  "trace_id": "trace-123",
126
162
  "span_id": "span-123",
@@ -207,9 +207,11 @@ def test_env_levels_and_env_loader_branches(monkeypatch: pytest.MonkeyPatch) ->
207
207
  monkeypatch.setenv("LOGS_APP_NAME", "app")
208
208
  monkeypatch.setenv("LOGS_COMPRESSION", "invalid")
209
209
  monkeypatch.setenv("LOGS_DLQ_TYPE", "invalid")
210
+ monkeypatch.setenv("LOGS_ENABLE_STRUCTURED_METADATA", "true")
210
211
 
211
212
  cfg = load_config_from_env()
212
213
  assert cfg.transport.compression == "gzip"
214
+ assert cfg.transport.enable_structured_metadata is True
213
215
  assert cfg.dead_letter_queue is not None
214
216
  assert cfg.dead_letter_queue.type == "memory"
215
217