Skip to content

Commit 3db260f

Browse files
Bump opentelemetry to latest version (#66)
1 parent cd79fb1 commit 3db260f

File tree

16 files changed

+358
-351
lines changed

16 files changed

+358
-351
lines changed

NOTICE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The following notices are required by licensors of software used in the Snowflak
66
--------------------------------------------------------------------------------
77

88
This library includes software which is copied from or derived from the OpenTelemetry Python API and SDK.
9-
OpenTelemetry Python v1.26.0
9+
OpenTelemetry Python v1.35.0
1010
https://github.com/open-telemetry/opentelemetry-python
1111

1212
Apache License

anaconda/meta.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ requirements:
1414
- setuptools >=40.0.0
1515
run:
1616
- python
17-
- opentelemetry-api ==1.26.0
18-
- opentelemetry-sdk ==1.26.0
17+
- opentelemetry-api ==1.35.0
18+
- opentelemetry-sdk ==1.35.0
1919

2020
about:
2121
home: https://www.snowflake.com/

scripts/vendor_otlp_proto_common.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# fixes needed in the OTLP exporter.
1010

1111
# Pinned commit/branch/tag for the current version used in opentelemetry-proto python package.
12-
REPO_BRANCH_OR_COMMIT="v1.26.0"
12+
REPO_BRANCH_OR_COMMIT="v1.35.0"
1313

1414
set -e
1515

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
long_description=LONG_DESCRIPTION,
3131
python_requires=REQUIRED_PYTHON_VERSION,
3232
install_requires=[
33-
"opentelemetry-api == 1.26.0",
34-
"opentelemetry-sdk == 1.26.0",
33+
"opentelemetry-api == 1.35.0",
34+
"opentelemetry-sdk == 1.35.0",
3535
],
3636
packages=find_namespace_packages(
3737
where='src'

src/snowflake/telemetry/_internal/exporter/otlp/proto/logs/__init__.py

Lines changed: 21 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@
1515

1616
import abc
1717
import logging
18-
import logging.config
19-
import threading
2018
import typing
21-
import opentelemetry.sdk.util.instrumentation as otel_instrumentation
22-
import opentelemetry.sdk._logs._internal as _logs_internal
2319

2420
from snowflake.telemetry._internal.opentelemetry.exporter.otlp.proto.common._log_encoder import (
2521
encode_logs,
@@ -84,29 +80,40 @@ class SnowflakeLoggingHandler(_logs.LoggingHandler):
8480
discarded by the original implementation.
8581
"""
8682

87-
LOGGER_NAME_TEMP_ATTRIBUTE = "__snow.logging.temp.logger_name"
83+
CODE_FILEPATH: typing.Final = "code.filepath"
84+
CODE_FILE_PATH: typing.Final = "code.file.path"
85+
CODE_FUNCTION: typing.Final = "code.function"
86+
CODE_FUNCTION_NAME: typing.Final = "code.function.name"
87+
CODE_LINENO: typing.Final = "code.lineno"
88+
CODE_LINE_NUMBER: typing.Final = "code.line.number"
8889

8990
def __init__(
9091
self,
9192
log_writer: LogWriter,
9293
):
9394
exporter = _ProtoLogExporter(log_writer)
94-
provider = _SnowflakeTelemetryLoggerProvider()
95-
provider.add_log_record_processor(
96-
export.SimpleLogRecordProcessor(exporter)
95+
processor = export.SimpleLogRecordProcessor(exporter)
96+
provider = _logs.LoggerProvider(
97+
resource=Resource.get_empty(),
98+
multi_log_record_processor=processor
9799
)
98100
super().__init__(logger_provider=provider)
99101

100102
@staticmethod
101103
def _get_attributes(record: logging.LogRecord) -> types.Attributes:
102104
attributes = _logs.LoggingHandler._get_attributes(record) # pylint: disable=protected-access
103105

104-
# Temporarily storing logger's name in record's attributes.
105-
# This attribute will be removed by the logger.
106-
#
107-
# TODO (SNOW-1235374): opentelemetry-python issue #2485: Record logger
108-
# name as the instrumentation scope name
109-
attributes[SnowflakeLoggingHandler.LOGGER_NAME_TEMP_ATTRIBUTE] = record.name
106+
# Preserving old naming conventions for code attributes that were changed as part of
107+
# https://github.com/open-telemetry/opentelemetry-python/commit/1b1e8d80c764ad3aa76abfb56a7002ddea11fdb5 in
108+
# order to avoid a behavior change for Snowflake customers.
109+
if SnowflakeLoggingHandler.CODE_FILE_PATH in attributes:
110+
attributes[SnowflakeLoggingHandler.CODE_FILEPATH] = attributes.pop(SnowflakeLoggingHandler.CODE_FILE_PATH)
111+
if SnowflakeLoggingHandler.CODE_FUNCTION_NAME in attributes:
112+
attributes[SnowflakeLoggingHandler.CODE_FUNCTION] = attributes.pop(
113+
SnowflakeLoggingHandler.CODE_FUNCTION_NAME)
114+
if SnowflakeLoggingHandler.CODE_LINE_NUMBER in attributes:
115+
attributes[SnowflakeLoggingHandler.CODE_LINENO] = attributes.pop(SnowflakeLoggingHandler.CODE_LINE_NUMBER)
116+
110117
return attributes
111118

112119
def _translate(self, record: logging.LogRecord) -> _logs.LogRecord:
@@ -115,75 +122,6 @@ def _translate(self, record: logging.LogRecord) -> _logs.LogRecord:
115122
return otel_record
116123

117124

118-
class _SnowflakeTelemetryLogger(_logs.Logger):
119-
"""
120-
An Open Telemetry Logger which creates an InstrumentationScope for each
121-
logger name it encounters.
122-
"""
123-
124-
def __init__(
125-
self,
126-
resource: Resource,
127-
multi_log_record_processor: typing.Union[
128-
_logs_internal.SynchronousMultiLogRecordProcessor,
129-
_logs_internal.ConcurrentMultiLogRecordProcessor,
130-
],
131-
instrumentation_scope: otel_instrumentation.InstrumentationScope,
132-
):
133-
super().__init__(resource, multi_log_record_processor, instrumentation_scope)
134-
self._lock = threading.Lock()
135-
self.cached_scopes = {}
136-
137-
def emit(self, record: _logs.LogRecord):
138-
if SnowflakeLoggingHandler.LOGGER_NAME_TEMP_ATTRIBUTE not in record.attributes:
139-
# The record doesn't contain our custom attribute with a logger name,
140-
# so we can call the superclass's `emit` method. It will emit a log
141-
# record with the default instrumentation scope.
142-
super().emit(record)
143-
return
144-
145-
# Creating an InstrumentationScope for each logger name,
146-
# and caching those scopes.
147-
logger_name = record.attributes[SnowflakeLoggingHandler.LOGGER_NAME_TEMP_ATTRIBUTE]
148-
del record.attributes[SnowflakeLoggingHandler.LOGGER_NAME_TEMP_ATTRIBUTE]
149-
with self._lock:
150-
if logger_name in self.cached_scopes:
151-
current_scope = self.cached_scopes[logger_name]
152-
else:
153-
current_scope = otel_instrumentation.InstrumentationScope(logger_name)
154-
self.cached_scopes[logger_name] = current_scope
155-
156-
# Emitting a record with a scope that corresponds to the logger
157-
# that logged it. NOT calling the superclass here for two reasons:
158-
# 1. Logger.emit takes a LogRecord, not LogData.
159-
# 2. It would emit a log record with the default instrumentation scope,
160-
# not with the scope we want.
161-
log_data = _logs.LogData(record, current_scope)
162-
self._multi_log_record_processor.emit(log_data)
163-
164-
165-
class _SnowflakeTelemetryLoggerProvider(_logs.LoggerProvider):
166-
"""
167-
A LoggerProvider that creates SnowflakeTelemetryLoggers
168-
"""
169-
170-
def get_logger(
171-
self, name: str,
172-
version: types.Optional[str] = None,
173-
schema_url: types.Optional[str] = None,
174-
attributes: types.Optional[types.Attributes] = None,
175-
) -> _logs.Logger:
176-
return _SnowflakeTelemetryLogger(
177-
Resource.get_empty(),
178-
self._multi_log_record_processor,
179-
otel_instrumentation.InstrumentationScope(
180-
name,
181-
version,
182-
schema_url,
183-
),
184-
)
185-
186-
187125
__all__ = [
188126
"LogWriter",
189127
"SnowflakeLoggingHandler",

src/snowflake/telemetry/_internal/opentelemetry/exporter/otlp/proto/common/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#
1515
# This file has been modified from the original source code at
1616
#
17-
# https://github.com/open-telemetry/opentelemetry-python/tree/v1.26.0
17+
# https://github.com/open-telemetry/opentelemetry-python/tree/v1.35.0
1818
#
1919
# by Snowflake Inc.
2020

src/snowflake/telemetry/_internal/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py

Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,42 +14,42 @@
1414
#
1515
# This file has been modified from the original source code at
1616
#
17-
# https://github.com/open-telemetry/opentelemetry-python/tree/v1.26.0
17+
# https://github.com/open-telemetry/opentelemetry-python/tree/v1.35.0
1818
#
1919
# by Snowflake Inc.
2020

2121

22+
from __future__ import annotations
23+
2224
import logging
2325
from collections.abc import Sequence
24-
from itertools import count
2526
from typing import (
2627
Any,
28+
Callable,
29+
Dict,
30+
List,
2731
Mapping,
2832
Optional,
29-
List,
30-
Callable,
3133
TypeVar,
32-
Dict,
33-
Iterator,
3434
)
3535

36-
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
36+
from snowflake.telemetry._internal.opentelemetry.proto.common.v1.common_marshaler import AnyValue as PB2AnyValue
3737
from snowflake.telemetry._internal.opentelemetry.proto.common.v1.common_marshaler import (
38-
InstrumentationScope as PB2InstrumentationScope,
38+
ArrayValue as PB2ArrayValue,
3939
)
40-
from snowflake.telemetry._internal.opentelemetry.proto.resource.v1.resource_marshaler import (
41-
Resource as PB2Resource,
40+
from snowflake.telemetry._internal.opentelemetry.proto.common.v1.common_marshaler import (
41+
InstrumentationScope as PB2InstrumentationScope,
4242
)
43-
from snowflake.telemetry._internal.opentelemetry.proto.common.v1.common_marshaler import AnyValue as PB2AnyValue
4443
from snowflake.telemetry._internal.opentelemetry.proto.common.v1.common_marshaler import KeyValue as PB2KeyValue
4544
from snowflake.telemetry._internal.opentelemetry.proto.common.v1.common_marshaler import (
4645
KeyValueList as PB2KeyValueList,
4746
)
48-
from snowflake.telemetry._internal.opentelemetry.proto.common.v1.common_marshaler import (
49-
ArrayValue as PB2ArrayValue,
47+
from snowflake.telemetry._internal.opentelemetry.proto.resource.v1.resource_marshaler import (
48+
Resource as PB2Resource,
5049
)
5150
from opentelemetry.sdk.trace import Resource
52-
from opentelemetry.util.types import Attributes
51+
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
52+
from opentelemetry.util.types import _ExtendedAttributes
5353

5454
_logger = logging.getLogger(__name__)
5555

@@ -65,14 +65,19 @@ def _encode_instrumentation_scope(
6565
return PB2InstrumentationScope(
6666
name=instrumentation_scope.name,
6767
version=instrumentation_scope.version,
68+
attributes=_encode_attributes(instrumentation_scope.attributes),
6869
)
6970

7071

7172
def _encode_resource(resource: Resource) -> PB2Resource:
7273
return PB2Resource(attributes=_encode_attributes(resource.attributes))
7374

7475

75-
def _encode_value(value: Any) -> PB2AnyValue:
76+
def _encode_value(
77+
value: Any, allow_null: bool = False
78+
) -> Optional[PB2AnyValue]:
79+
if allow_null is True and value is None:
80+
return None
7681
if isinstance(value, bool):
7782
return PB2AnyValue(bool_value=value)
7883
if isinstance(value, str):
@@ -81,21 +86,49 @@ def _encode_value(value: Any) -> PB2AnyValue:
8186
return PB2AnyValue(int_value=value)
8287
if isinstance(value, float):
8388
return PB2AnyValue(double_value=value)
89+
if isinstance(value, bytes):
90+
return PB2AnyValue(bytes_value=value)
8491
if isinstance(value, Sequence):
8592
return PB2AnyValue(
86-
array_value=PB2ArrayValue(values=[_encode_value(v) for v in value])
93+
array_value=PB2ArrayValue(
94+
values=_encode_array(value, allow_null=allow_null)
95+
)
8796
)
8897
elif isinstance(value, Mapping):
8998
return PB2AnyValue(
9099
kvlist_value=PB2KeyValueList(
91-
values=[_encode_key_value(str(k), v) for k, v in value.items()]
100+
values=[
101+
_encode_key_value(str(k), v, allow_null=allow_null)
102+
for k, v in value.items()
103+
]
92104
)
93105
)
94106
raise Exception(f"Invalid type {type(value)} of value {value}")
95107

96108

97-
def _encode_key_value(key: str, value: Any) -> PB2KeyValue:
98-
return PB2KeyValue(key=key, value=_encode_value(value))
109+
def _encode_key_value(
110+
key: str, value: Any, allow_null: bool = False
111+
) -> PB2KeyValue:
112+
return PB2KeyValue(
113+
key=key, value=_encode_value(value, allow_null=allow_null)
114+
)
115+
116+
117+
def _encode_array(
118+
array: Sequence[Any], allow_null: bool = False
119+
) -> Sequence[PB2AnyValue]:
120+
if not allow_null:
121+
# Let the exception get raised by _encode_value()
122+
return [_encode_value(v, allow_null=allow_null) for v in array]
123+
124+
return [
125+
_encode_value(v, allow_null=allow_null)
126+
if v is not None
127+
# Use an empty AnyValue to represent None in an array. Behavior may change pending
128+
# https://github.com/open-telemetry/opentelemetry-specification/issues/4392
129+
else PB2AnyValue()
130+
for v in array
131+
]
99132

100133

101134
def _encode_span_id(span_id: int) -> bytes:
@@ -107,14 +140,17 @@ def _encode_trace_id(trace_id: int) -> bytes:
107140

108141

109142
def _encode_attributes(
110-
attributes: Attributes,
143+
attributes: _ExtendedAttributes,
144+
allow_null: bool = False,
111145
) -> Optional[List[PB2KeyValue]]:
112146
if attributes:
113147
pb2_attributes = []
114148
for key, value in attributes.items():
115149
# pylint: disable=broad-exception-caught
116150
try:
117-
pb2_attributes.append(_encode_key_value(key, value))
151+
pb2_attributes.append(
152+
_encode_key_value(key, value, allow_null=allow_null)
153+
)
118154
except Exception as error:
119155
_logger.exception("Failed to encode key %s: %s", key, error)
120156
else:
@@ -145,38 +181,3 @@ def _get_resource_data(
145181
)
146182
)
147183
return resource_data
148-
149-
150-
def _create_exp_backoff_generator(max_value: int = 0) -> Iterator[int]:
151-
"""
152-
Generates an infinite sequence of exponential backoff values. The sequence starts
153-
from 1 (2^0) and doubles each time (2^1, 2^2, 2^3, ...). If a max_value is specified
154-
and non-zero, the generated values will not exceed this maximum, capping at max_value
155-
instead of growing indefinitely.
156-
157-
Parameters:
158-
- max_value (int, optional): The maximum value to yield. If 0 or not provided, the
159-
sequence grows without bound.
160-
161-
Returns:
162-
Iterator[int]: An iterator that yields the exponential backoff values, either uncapped or
163-
capped at max_value.
164-
165-
Example:
166-
```
167-
gen = _create_exp_backoff_generator(max_value=10)
168-
for _ in range(5):
169-
print(next(gen))
170-
```
171-
This will print:
172-
1
173-
2
174-
4
175-
8
176-
10
177-
178-
Note: this functionality used to be handled by the 'backoff' package.
179-
"""
180-
for i in count(0):
181-
out = 2**i
182-
yield min(out, max_value) if max_value else out

0 commit comments

Comments
 (0)