Skip to content

Commit a75e070

Browse files
committed
Update logging exporter for opentelemetry-sdk 1.39.0
Fixes #458
1 parent f40699d commit a75e070

File tree

5 files changed

+89
-58
lines changed

5 files changed

+89
-58
lines changed

dev-constraints.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ setuptools==69.5.1
1717

1818
# pinned for snapshot tests. this should be bumped regularly and snapshots updated by running
1919
# tox -f py311-test -- --snapshot-update
20-
opentelemetry-api==1.38.0
21-
opentelemetry-sdk==1.38.0
20+
opentelemetry-api==1.39.0
21+
opentelemetry-sdk==1.39.0

opentelemetry-exporter-gcp-logging/setup.cfg

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,8 @@ packages=find_namespace:
2727
install_requires =
2828
google-cloud-logging ~= 3.0
2929

30-
# Set upper bound for breaking changes in 1.39.0, see
31-
# https://github.com/open-telemetry/opentelemetry-python/pull/4771. Will increase the
32-
# minimum version after that release.
33-
opentelemetry-sdk >= 1.35.0, < 1.39.0
34-
opentelemetry-api >= 1.35.0
30+
opentelemetry-sdk >= 1.39.0
31+
opentelemetry-api >= 1.39.0
3532

3633
opentelemetry-resourcedetector-gcp >= 1.5.0dev0, == 1.*
3734

opentelemetry-exporter-gcp-logging/src/opentelemetry/exporter/cloud_logging/__init__.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,11 @@
5757
get_monitored_resource,
5858
)
5959
from opentelemetry.sdk import version as opentelemetry_sdk_version
60-
from opentelemetry.sdk._logs import LogData
61-
from opentelemetry.sdk._logs.export import LogExporter
60+
from opentelemetry.sdk._logs import ReadableLogRecord
61+
from opentelemetry.sdk._logs.export import (
62+
LogRecordExporter,
63+
LogRecordExportResult,
64+
)
6265
from opentelemetry.sdk.resources import Resource
6366
from opentelemetry.trace import format_span_id, format_trace_id
6467
from opentelemetry.util.types import AnyValue
@@ -251,7 +254,7 @@ def _get_monitored_resource(
251254
)
252255

253256

254-
class CloudLoggingExporter(LogExporter):
257+
class CloudLoggingExporter(LogRecordExporter):
255258
def __init__(
256259
self,
257260
project_id: Optional[str] = None,
@@ -320,12 +323,14 @@ def pick_log_id(self, log_name_attr: Any, event_name: str | None) -> str:
320323
return event_name.replace("/", "%2F")
321324
return self.default_log_name
322325

323-
def export(self, batch: Sequence[LogData]):
326+
def export(
327+
self, batch: Sequence[ReadableLogRecord]
328+
) -> LogRecordExportResult:
324329
now = datetime.datetime.now()
325330
log_entries = []
326-
for log_data in batch:
331+
for readable_log_record in batch:
327332
log_entry = LogEntry()
328-
log_record = log_data.log_record
333+
log_record = readable_log_record.log_record
329334
attributes = log_record.attributes or {}
330335
project_id = str(
331336
attributes.get(PROJECT_ID_ATTRIBUTE_KEY, self.project_id)
@@ -342,7 +347,7 @@ def export(self, batch: Sequence[LogData]):
342347
ts.FromDatetime(now)
343348
log_entry.timestamp = ts
344349
if monitored_resource := _get_monitored_resource(
345-
log_record.resource
350+
readable_log_record.resource
346351
):
347352
log_entry.resource = monitored_resource
348353
log_entry.trace_sampled = (
@@ -371,6 +376,8 @@ def export(self, batch: Sequence[LogData]):
371376

372377
self._write_log_entries(log_entries)
373378

379+
return LogRecordExportResult.SUCCESS
380+
374381
@staticmethod
375382
def _write_log_entries_to_file(file: TextIO, log_entries: list[LogEntry]):
376383
"""Formats logs into the Cloud Logging structured log format, and writes them to the

opentelemetry-exporter-gcp-logging/tests/fixtures/cloud_logging_fake.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
unary_unary_rpc_method_handler,
3838
)
3939
from opentelemetry.exporter.cloud_logging import CloudLoggingExporter
40-
from opentelemetry.sdk._logs import LogData
40+
from opentelemetry.sdk._logs import ReadableLogRecord
4141
from syrupy.assertion import SnapshotAssertion
4242
from syrupy.extensions.json import JSONSnapshotExtension
4343

@@ -133,7 +133,7 @@ def fixture_cloudloggingfake() -> Iterable[CloudLoggingFake]:
133133
server.stop(None)
134134

135135

136-
ExportAndAssertSnapshot = Callable[[Sequence[LogData]], None]
136+
ExportAndAssertSnapshot = Callable[[Sequence[ReadableLogRecord]], None]
137137

138138

139139
@pytest.fixture(
@@ -148,7 +148,9 @@ def fixture_export_and_assert_snapshot(
148148
"cloudloggingfake"
149149
)
150150

151-
def export_and_assert_snapshot(log_data: Sequence[LogData]) -> None:
151+
def export_and_assert_snapshot(
152+
log_data: Sequence[ReadableLogRecord],
153+
) -> None:
152154
cloudloggingfake.exporter.export(log_data)
153155

154156
assert cloudloggingfake.get_calls() == snapshot(
@@ -158,7 +160,9 @@ def export_and_assert_snapshot(log_data: Sequence[LogData]) -> None:
158160
return export_and_assert_snapshot
159161

160162
# pylint: disable=function-redefined
161-
def export_and_assert_snapshot(log_data: Sequence[LogData]) -> None:
163+
def export_and_assert_snapshot(
164+
log_data: Sequence[ReadableLogRecord],
165+
) -> None:
162166
buf = StringIO()
163167
exporter = CloudLoggingExporter(
164168
project_id=PROJECT_ID, structured_json_file=buf

opentelemetry-exporter-gcp-logging/tests/test_cloud_logging.py

Lines changed: 63 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
2626
Be sure to review the changes.
2727
"""
28+
2829
import re
2930
from io import StringIO
3031
from textwrap import dedent
@@ -44,10 +45,15 @@
4445
CloudLoggingExporter,
4546
is_log_id_valid,
4647
)
47-
from opentelemetry.sdk._logs import LogData
48+
from opentelemetry.sdk._logs import ReadableLogRecord
4849
from opentelemetry.sdk._logs._internal import LogRecord
4950
from opentelemetry.sdk.resources import Resource
5051
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
52+
from opentelemetry.trace import (
53+
NonRecordingSpan,
54+
SpanContext,
55+
set_span_in_context,
56+
)
5157

5258
PROJECT_ID = "fakeproject"
5359

@@ -127,13 +133,13 @@ def test_too_large_log_raises_warning(caplog) -> None:
127133
)
128134
no_default_logname.export(
129135
[
130-
LogData(
136+
ReadableLogRecord(
131137
log_record=LogRecord(
132138
body="abc",
133-
resource=Resource({}),
134139
attributes={str(i): "i" * 10000 for i in range(1000)},
135140
),
136141
instrumentation_scope=InstrumentationScope("test"),
142+
resource=Resource.get_empty(),
137143
)
138144
]
139145
)
@@ -146,12 +152,12 @@ def test_too_large_log_raises_warning(caplog) -> None:
146152
def test_user_agent(cloudloggingfake: CloudLoggingFake) -> None:
147153
cloudloggingfake.exporter.export(
148154
[
149-
LogData(
155+
ReadableLogRecord(
150156
log_record=LogRecord(
151157
body="abc",
152-
resource=Resource({}),
153158
),
154159
instrumentation_scope=InstrumentationScope("test"),
160+
resource=Resource.get_empty(),
155161
)
156162
]
157163
)
@@ -169,53 +175,53 @@ def test_agent_engine_monitored_resources(
169175
export_and_assert_snapshot: ExportAndAssertSnapshot,
170176
) -> None:
171177
log_data = [
172-
LogData(
178+
ReadableLogRecord(
173179
log_record=LogRecord(
174180
body="valid agent engine",
175181
timestamp=1736976310997977393,
176-
resource=Resource(
177-
{
178-
"cloud.resource_id": "//aiplatform.googleapis.com/projects/some-project123-321/locations/europe-west3/reasoningEngines/8477639270431981568"
179-
}
180-
),
181182
),
182183
instrumentation_scope=InstrumentationScope("test"),
184+
resource=Resource(
185+
{
186+
"cloud.resource_id": "//aiplatform.googleapis.com/projects/some-project123-321/locations/europe-west3/reasoningEngines/8477639270431981568"
187+
}
188+
),
183189
),
184-
LogData(
190+
ReadableLogRecord(
185191
log_record=LogRecord(
186192
body="invalid 1",
187193
timestamp=1736976310997977393,
188-
resource=Resource(
189-
{
190-
"cloud.resource_id": "//aiplatform.googleapis.com/locations/europe-west3/reasoningEngines/8477639270431981568"
191-
}
192-
),
193194
),
194195
instrumentation_scope=InstrumentationScope("test"),
196+
resource=Resource(
197+
{
198+
"cloud.resource_id": "//aiplatform.googleapis.com/locations/europe-west3/reasoningEngines/8477639270431981568"
199+
}
200+
),
195201
),
196-
LogData(
202+
ReadableLogRecord(
197203
log_record=LogRecord(
198204
body="invalid 2",
199205
timestamp=1736976310997977393,
200-
resource=Resource(
201-
{
202-
"cloud.resource_id": "//aiplatform.googleapis.com/projects/some-project123-321/locations/europe-west3/reasoningEngines//8477639270431981568"
203-
}
204-
),
205206
),
206207
instrumentation_scope=InstrumentationScope("test"),
208+
resource=Resource(
209+
{
210+
"cloud.resource_id": "//aiplatform.googleapis.com/projects/some-project123-321/locations/europe-west3/reasoningEngines//8477639270431981568"
211+
}
212+
),
207213
),
208-
LogData(
214+
ReadableLogRecord(
209215
log_record=LogRecord(
210216
body="invalid 3",
211217
timestamp=1736976310997977393,
212-
resource=Resource(
213-
{
214-
"cloud.resource_id": "aiplatform.googleapis.com/projects/some-project123-321/locations/europe-west3/reasoningEngines//8477639270431981568"
215-
}
216-
),
217218
),
218219
instrumentation_scope=InstrumentationScope("test"),
220+
resource=Resource(
221+
{
222+
"cloud.resource_id": "aiplatform.googleapis.com/projects/some-project123-321/locations/europe-west3/reasoningEngines//8477639270431981568"
223+
}
224+
),
219225
),
220226
]
221227
export_and_assert_snapshot(log_data)
@@ -225,13 +231,18 @@ def test_convert_otlp_dict_body(
225231
export_and_assert_snapshot: ExportAndAssertSnapshot,
226232
) -> None:
227233
log_data = [
228-
LogData(
234+
ReadableLogRecord(
229235
log_record=LogRecord(
230236
event_name="random.genai.event",
231237
timestamp=1736976310997977393,
232238
severity_number=SeverityNumber(20),
233-
trace_id=25,
234-
span_id=22,
239+
context=set_span_in_context(
240+
NonRecordingSpan(
241+
context=SpanContext(
242+
trace_id=25, span_id=22, is_remote=False
243+
)
244+
)
245+
),
235246
attributes={
236247
"gen_ai.system": True,
237248
"test": 23,
@@ -252,6 +263,7 @@ def test_convert_otlp_dict_body(
252263
},
253264
),
254265
instrumentation_scope=InstrumentationScope("test"),
266+
resource=Resource.get_empty(),
255267
)
256268
]
257269
export_and_assert_snapshot(log_data)
@@ -261,7 +273,7 @@ def test_convert_otlp_various_different_types_in_attrs_and_bytes_body(
261273
export_and_assert_snapshot: ExportAndAssertSnapshot,
262274
) -> None:
263275
log_data = [
264-
LogData(
276+
ReadableLogRecord(
265277
log_record=LogRecord(
266278
timestamp=1736976310997977393,
267279
attributes={
@@ -273,6 +285,7 @@ def test_convert_otlp_various_different_types_in_attrs_and_bytes_body(
273285
body=b'{"Date": "2016-05-21T21:35:40Z", "CreationDate": "2012-05-05", "LogoType": "png", "Ref": 164611595, "Classe": ["Email addresses", "Passwords"],"Link":"http://some_link.com"}',
274286
),
275287
instrumentation_scope=InstrumentationScope("test"),
288+
resource=Resource.get_empty(),
276289
)
277290
]
278291
export_and_assert_snapshot(log_data)
@@ -282,12 +295,13 @@ def test_convert_non_json_dict_bytes(
282295
export_and_assert_snapshot: ExportAndAssertSnapshot,
283296
) -> None:
284297
log_data = [
285-
LogData(
298+
ReadableLogRecord(
286299
log_record=LogRecord(
287300
timestamp=1736976310997977393,
288301
body=b"123",
289302
),
290303
instrumentation_scope=InstrumentationScope("test"),
304+
resource=Resource.get_empty(),
291305
)
292306
]
293307
export_and_assert_snapshot(log_data)
@@ -297,13 +311,14 @@ def test_convert_gen_ai_body(
297311
export_and_assert_snapshot: ExportAndAssertSnapshot,
298312
) -> None:
299313
log_data = [
300-
LogData(
314+
ReadableLogRecord(
301315
log_record=LogRecord(
302316
event_name="gen_ai.client.inference.operation.details",
303317
timestamp=1736976310997977393,
304318
body=GEN_AI_DICT,
305319
),
306320
instrumentation_scope=InstrumentationScope("test"),
321+
resource=Resource.get_empty(),
307322
)
308323
]
309324
export_and_assert_snapshot(log_data)
@@ -361,12 +376,13 @@ def test_convert_various_types_of_bodies(
361376
body: Union[str, bool, None, Mapping],
362377
) -> None:
363378
log_data = [
364-
LogData(
379+
ReadableLogRecord(
365380
log_record=LogRecord(
366381
timestamp=1736976310997977393,
367382
body=body,
368383
),
369384
instrumentation_scope=InstrumentationScope("test"),
385+
resource=Resource.get_empty(),
370386
)
371387
]
372388
export_and_assert_snapshot(log_data)
@@ -376,7 +392,7 @@ def test_convert_various_types_of_attributes(
376392
export_and_assert_snapshot: ExportAndAssertSnapshot,
377393
) -> None:
378394
log_data = [
379-
LogData(
395+
ReadableLogRecord(
380396
log_record=LogRecord(
381397
attributes={
382398
"a": [{"key": b"bytes"}],
@@ -387,6 +403,7 @@ def test_convert_various_types_of_attributes(
387403
timestamp=1736976310997977393,
388404
),
389405
instrumentation_scope=InstrumentationScope("test"),
406+
resource=Resource.get_empty(),
390407
)
391408
]
392409
export_and_assert_snapshot(log_data)
@@ -399,17 +416,23 @@ def test_structured_json_lines():
399416
)
400417
exporter.export(
401418
[
402-
LogData(
419+
ReadableLogRecord(
403420
log_record=LogRecord(
404421
event_name="foo",
405422
timestamp=1736976310997977393,
406423
severity_number=SeverityNumber(20),
407-
trace_id=25,
408-
span_id=22,
424+
context=set_span_in_context(
425+
NonRecordingSpan(
426+
context=SpanContext(
427+
trace_id=25, span_id=22, is_remote=False
428+
)
429+
)
430+
),
409431
attributes={"key": f"{i}"},
410432
body="hello",
411433
),
412434
instrumentation_scope=InstrumentationScope("test"),
435+
resource=Resource.get_empty(),
413436
)
414437
for i in range(5)
415438
]

0 commit comments

Comments
 (0)