Skip to content

Commit 99594e1

Browse files
Merge branch 'main' into add-docs-example-sqlcommenter
2 parents 85471d9 + 22d1fd1 commit 99594e1

File tree

22 files changed

+607
-16
lines changed

22 files changed

+607
-16
lines changed

CHANGELOG.md

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

1515
- Add experimental composite samplers
1616
([#4714](https://github.com/open-telemetry/opentelemetry-python/pull/4714))
17+
- Add new environment variables to the SDK `OTEL_PYTHON_EXPORTER_OTLP_{HTTP/GRPC}_{METRICS/TRACES/LOGS}_CREDENTIAL_PROVIDER` that can be used to
18+
inject a `requests.Session` or `grpc.ChannelCredentials` object into OTLP exporters created during auto instrumentation [#4689](https://github.com/open-telemetry/opentelemetry-python/pull/4689).
1719
- Filter duplicate logs out of some internal `logger`'s logs on the export logs path that might otherwise endlessly log or cause a recursion depth exceeded issue in cases where logging itself results in an exception.
1820
([#4695](https://github.com/open-telemetry/opentelemetry-python/pull/4695)).
1921
- docs: linked the examples with their github source code location and added Prometheus example
@@ -22,6 +24,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2224
([#4634](https://github.com/open-telemetry/opentelemetry-python/pull/4634))
2325
- semantic-conventions: Bump to 1.37.0
2426
([#4731](https://github.com/open-telemetry/opentelemetry-python/pull/4731))
27+
- Performance: Cache `importlib_metadata.entry_points`
28+
([#4735](https://github.com/open-telemetry/opentelemetry-python/pull/4735))
29+
- opentelemetry-sdk: fix calling Logger.emit with an API LogRecord instance
30+
([#4741](https://github.com/open-telemetry/opentelemetry-python/pull/4741))
2531
- docs: Added sqlcommenter example
2632
([#4734](https://github.com/open-telemetry/opentelemetry-python/pull/4734))
2733

exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ def _encode_log(log_data: LogData) -> PB2LogRecord:
6161
log_data.log_record.attributes, allow_null=True
6262
),
6363
dropped_attributes_count=log_data.log_record.dropped_attributes,
64-
severity_number=log_data.log_record.severity_number.value,
64+
severity_number=getattr(
65+
log_data.log_record.severity_number, "value", None
66+
),
6567
event_name=log_data.log_record.event_name,
6668
)
6769

exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def test_dropped_attributes_count(self):
8989

9090
@staticmethod
9191
def _get_sdk_log_data() -> List[LogData]:
92+
# pylint:disable=too-many-locals
9293
ctx_log1 = set_span_in_context(
9394
NonRecordingSpan(
9495
SpanContext(
@@ -304,7 +305,29 @@ def _get_sdk_log_data() -> List[LogData]:
304305
"extended_name", "extended_version"
305306
),
306307
)
307-
return [log1, log2, log3, log4, log5, log6, log7, log8]
308+
309+
ctx_log9 = set_span_in_context(
310+
NonRecordingSpan(
311+
SpanContext(
312+
212592107417388365804938480559624925566,
313+
6077757853989569466,
314+
False,
315+
TraceFlags(0x01),
316+
)
317+
)
318+
)
319+
log9 = LogData(
320+
log_record=SDKLogRecord(
321+
# these are otherwise set by default
322+
observed_timestamp=1644650584292683045,
323+
context=ctx_log9,
324+
resource=SDKResource({}),
325+
),
326+
instrumentation_scope=InstrumentationScope(
327+
"empty_log_record_name", "empty_log_record_version"
328+
),
329+
)
330+
return [log1, log2, log3, log4, log5, log6, log7, log8, log9]
308331

309332
def get_test_logs(
310333
self,
@@ -593,6 +616,29 @@ def get_test_logs(
593616
),
594617
],
595618
),
619+
PB2ScopeLogs(
620+
scope=PB2InstrumentationScope(
621+
name="empty_log_record_name",
622+
version="empty_log_record_version",
623+
),
624+
log_records=[
625+
PB2LogRecord(
626+
time_unix_nano=None,
627+
observed_time_unix_nano=1644650584292683045,
628+
trace_id=_encode_trace_id(
629+
212592107417388365804938480559624925566
630+
),
631+
span_id=_encode_span_id(
632+
6077757853989569466,
633+
),
634+
flags=int(TraceFlags(0x01)),
635+
severity_text=None,
636+
severity_number=None,
637+
body=None,
638+
attributes=None,
639+
),
640+
],
641+
),
596642
],
597643
),
598644
]

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from opentelemetry.sdk._logs import LogRecord as SDKLogRecord
3333
from opentelemetry.sdk._logs.export import LogExporter, LogExportResult
3434
from opentelemetry.sdk.environment_variables import (
35+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_LOGS_CREDENTIAL_PROVIDER,
3536
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
3637
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE,
3738
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY,
@@ -73,6 +74,7 @@ def __init__(
7374
):
7475
credentials = _get_credentials(
7576
credentials,
77+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_LOGS_CREDENTIAL_PROVIDER,
7678
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
7779
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY,
7880
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE,

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
from opentelemetry.proto.resource.v1.resource_pb2 import Resource # noqa: F401
6262
from opentelemetry.sdk._shared_internal import DuplicateFilter
6363
from opentelemetry.sdk.environment_variables import (
64+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER,
6465
OTEL_EXPORTER_OTLP_CERTIFICATE,
6566
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
6667
OTEL_EXPORTER_OTLP_CLIENT_KEY,
@@ -73,6 +74,7 @@
7374
from opentelemetry.sdk.metrics.export import MetricsData
7475
from opentelemetry.sdk.resources import Resource as SDKResource
7576
from opentelemetry.sdk.trace import ReadableSpan
77+
from opentelemetry.util._importlib_metadata import entry_points
7678
from opentelemetry.util.re import parse_env_headers
7779

7880
_RETRYABLE_ERROR_CODES = frozenset(
@@ -169,12 +171,36 @@ def _load_credentials(
169171

170172
def _get_credentials(
171173
creds: Optional[ChannelCredentials],
174+
credential_entry_point_env_key: str,
172175
certificate_file_env_key: str,
173176
client_key_file_env_key: str,
174177
client_certificate_file_env_key: str,
175178
) -> ChannelCredentials:
176179
if creds is not None:
177180
return creds
181+
_credential_env = environ.get(credential_entry_point_env_key)
182+
if _credential_env:
183+
try:
184+
maybe_channel_creds = next(
185+
iter(
186+
entry_points(
187+
group="opentelemetry_otlp_credential_provider",
188+
name=_credential_env,
189+
)
190+
)
191+
).load()()
192+
except StopIteration:
193+
raise RuntimeError(
194+
f"Requested component '{_credential_env}' not found in "
195+
f"entry point 'opentelemetry_otlp_credential_provider'"
196+
)
197+
if isinstance(maybe_channel_creds, ChannelCredentials):
198+
return maybe_channel_creds
199+
else:
200+
raise RuntimeError(
201+
f"Requested component '{_credential_env}' is of type {type(maybe_channel_creds)}"
202+
f" must be of type `grpc.ChannelCredentials`."
203+
)
178204

179205
certificate_file = environ.get(certificate_file_env_key)
180206
if certificate_file:
@@ -278,15 +304,16 @@ def __init__(
278304
options=self._channel_options,
279305
)
280306
else:
281-
credentials = _get_credentials(
307+
self._credentials = _get_credentials(
282308
credentials,
309+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER,
283310
OTEL_EXPORTER_OTLP_CERTIFICATE,
284311
OTEL_EXPORTER_OTLP_CLIENT_KEY,
285312
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
286313
)
287314
self._channel = secure_channel(
288315
self._endpoint,
289-
credentials,
316+
self._credentials,
290317
compression=compression,
291318
options=self._channel_options,
292319
)

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
)
4444
from opentelemetry.proto.metrics.v1 import metrics_pb2 as pb2 # noqa: F401
4545
from opentelemetry.sdk.environment_variables import (
46+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_METRICS_CREDENTIAL_PROVIDER,
4647
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE,
4748
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE,
4849
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY,
@@ -118,6 +119,7 @@ def __init__(
118119
):
119120
credentials = _get_credentials(
120121
credentials,
122+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_METRICS_CREDENTIAL_PROVIDER,
121123
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE,
122124
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY,
123125
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE,

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
Span as CollectorSpan,
4747
)
4848
from opentelemetry.sdk.environment_variables import (
49+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_TRACES_CREDENTIAL_PROVIDER,
4950
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE,
5051
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE,
5152
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY,
@@ -106,6 +107,7 @@ def __init__(
106107
):
107108
credentials = _get_credentials(
108109
credentials,
110+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_TRACES_CREDENTIAL_PROVIDER,
109111
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE,
110112
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY,
111113
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE,

exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from google.rpc.error_details_pb2 import ( # pylint: disable=no-name-in-module
2929
RetryInfo,
3030
)
31-
from grpc import Compression, StatusCode, server
31+
from grpc import ChannelCredentials, Compression, StatusCode, server
3232

3333
from opentelemetry.exporter.otlp.proto.common.trace_encoder import (
3434
encode_spans,
@@ -49,6 +49,7 @@
4949
add_TraceServiceServicer_to_server,
5050
)
5151
from opentelemetry.sdk.environment_variables import (
52+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER,
5253
OTEL_EXPORTER_OTLP_COMPRESSION,
5354
)
5455
from opentelemetry.sdk.trace import ReadableSpan, _Span
@@ -60,6 +61,15 @@
6061
logger = getLogger(__name__)
6162

6263

64+
class IterEntryPoint:
65+
def __init__(self, name, class_type):
66+
self.name = name
67+
self.class_type = class_type
68+
69+
def load(self):
70+
return self.class_type
71+
72+
6373
# The below tests use this test SpanExporter and Spans, but are testing the
6474
# underlying behavior in the mixin. A MetricExporter or LogExporter could
6575
# just as easily be used.
@@ -276,6 +286,55 @@ def test_otlp_exporter_otlp_compression_unspecified(
276286
),
277287
)
278288

289+
@patch.dict(
290+
"os.environ",
291+
{
292+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER: "credential_provider"
293+
},
294+
)
295+
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.entry_points")
296+
def test_that_credential_gets_passed_to_exporter(self, mock_entry_points):
297+
credential = ChannelCredentials(None)
298+
299+
def f():
300+
return credential
301+
302+
mock_entry_points.configure_mock(
303+
return_value=[IterEntryPoint("custom_credential", f)]
304+
)
305+
exporter = OTLPSpanExporterForTesting(insecure=False)
306+
# pylint: disable=protected-access
307+
assert exporter._credentials is credential
308+
309+
@patch.dict(
310+
"os.environ",
311+
{
312+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER: "credential_provider"
313+
},
314+
)
315+
def test_that_missing_entry_point_raises_exception(self):
316+
with self.assertRaises(RuntimeError):
317+
OTLPSpanExporterForTesting(insecure=False)
318+
319+
@patch.dict(
320+
"os.environ",
321+
{
322+
_OTEL_PYTHON_EXPORTER_OTLP_GRPC_CREDENTIAL_PROVIDER: "credential_provider"
323+
},
324+
)
325+
@patch("opentelemetry.exporter.otlp.proto.grpc.exporter.entry_points")
326+
def test_that_entry_point_returning_bad_type_raises_exception(
327+
self, mock_entry_points
328+
):
329+
def f():
330+
return 1
331+
332+
mock_entry_points.configure_mock(
333+
return_value=[IterEntryPoint("custom_credential", f)]
334+
)
335+
with self.assertRaises(RuntimeError):
336+
OTLPSpanExporterForTesting(insecure=False)
337+
279338
# pylint: disable=no-self-use, disable=unused-argument
280339
@patch(
281340
"opentelemetry.exporter.otlp.proto.grpc.exporter.ssl_channel_credentials"

exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_common/__init__.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,58 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from os import environ
16+
from typing import Literal, Optional
17+
1518
import requests
1619

20+
from opentelemetry.sdk.environment_variables import (
21+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER,
22+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER,
23+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER,
24+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER,
25+
)
26+
from opentelemetry.util._importlib_metadata import entry_points
27+
1728

1829
def _is_retryable(resp: requests.Response) -> bool:
1930
if resp.status_code == 408:
2031
return True
2132
if resp.status_code >= 500 and resp.status_code <= 599:
2233
return True
2334
return False
35+
36+
37+
def _load_session_from_envvar(
38+
cred_envvar: Literal[
39+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER,
40+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_TRACES_CREDENTIAL_PROVIDER,
41+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_METRICS_CREDENTIAL_PROVIDER,
42+
],
43+
) -> Optional[requests.Session]:
44+
_credential_env = environ.get(
45+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_CREDENTIAL_PROVIDER
46+
) or environ.get(cred_envvar)
47+
if _credential_env:
48+
try:
49+
maybe_session = next(
50+
iter(
51+
entry_points(
52+
group="opentelemetry_otlp_credential_provider",
53+
name=_credential_env,
54+
)
55+
)
56+
).load()()
57+
except StopIteration:
58+
raise RuntimeError(
59+
f"Requested component '{_credential_env}' not found in "
60+
f"entry point 'opentelemetry_otlp_credential_provider'"
61+
)
62+
if isinstance(maybe_session, requests.Session):
63+
return maybe_session
64+
else:
65+
raise RuntimeError(
66+
f"Requested component '{_credential_env}' is of type {type(maybe_session)}"
67+
f" must be of type `requests.Session`."
68+
)
69+
return None

exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
)
3333
from opentelemetry.exporter.otlp.proto.http._common import (
3434
_is_retryable,
35+
_load_session_from_envvar,
3536
)
3637
from opentelemetry.sdk._logs import LogData
3738
from opentelemetry.sdk._logs.export import (
@@ -40,6 +41,7 @@
4041
)
4142
from opentelemetry.sdk._shared_internal import DuplicateFilter
4243
from opentelemetry.sdk.environment_variables import (
44+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER,
4345
OTEL_EXPORTER_OTLP_CERTIFICATE,
4446
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
4547
OTEL_EXPORTER_OTLP_CLIENT_KEY,
@@ -120,7 +122,14 @@ def __init__(
120122
)
121123
)
122124
self._compression = compression or _compression_from_env()
123-
self._session = session or requests.Session()
125+
self._session = (
126+
session
127+
or _load_session_from_envvar(
128+
_OTEL_PYTHON_EXPORTER_OTLP_HTTP_LOGS_CREDENTIAL_PROVIDER
129+
)
130+
or requests.Session()
131+
)
132+
self._session.headers.update(self._headers)
124133
self._session.headers.update(_OTLP_HTTP_HEADERS)
125134
# let users override our defaults
126135
self._session.headers.update(self._headers)

0 commit comments

Comments
 (0)