Skip to content

Commit 94e4f8e

Browse files
authored
Merge branch 'main' into issue_1968
2 parents 2afd1ba + 3cee4ef commit 94e4f8e

File tree

21 files changed

+176
-72
lines changed

21 files changed

+176
-72
lines changed

.github/workflows/close-stale-issues.yml

-18
This file was deleted.

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
([#2096](https://github.com/open-telemetry/opentelemetry-python/pull/2096))
1414
- Fix propagation bug caused by counting skipped entries
1515
([#2071](https://github.com/open-telemetry/opentelemetry-python/pull/2071))
16+
- Add entry point for exporters with default protocol
17+
([#2093](https://github.com/open-telemetry/opentelemetry-python/pull/2093))
1618
- Do not skip sequence attribute on decode error
1719
([#2097](https://github.com/open-telemetry/opentelemetry-python/pull/2097))
1820
- `opentelemetry-test`: Add `HttpTestBase` to allow tests with actual TCP sockets
1921
([#2101](https://github.com/open-telemetry/opentelemetry-python/pull/2101))
22+
- Fix incorrect headers parsing via environment variables
23+
([#2103](https://github.com/open-telemetry/opentelemetry-python/pull/2103))
24+
- `opentelemetry-exporter-otlp`: Add `opentelemetry-otlp-proto-http` as dependency
25+
- ([#2147](https://github.com/open-telemetry/opentelemetry-python/pull/2147))
2026

2127
## [1.5.0-0.24b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.5.0-0.24b0) - 2021-08-26
2228

docs/examples/auto-instrumentation/README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,5 +233,5 @@ Additional resources
233233
~~~~~~~~~~~~~~~~~~~~
234234

235235
In order to send telemetry to an OpenTelemetry Collector without doing any
236-
additional configuration, read about the `OpenTelemetry Distro <../distro/README.html>`_
236+
additional configuration, read about the `OpenTelemetry Distro <../distro>`_
237237
package.

exporter/opentelemetry-exporter-jaeger-proto-grpc/setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,5 @@ where = src
5252
test =
5353

5454
[options.entry_points]
55-
opentelemetry_exporter =
55+
opentelemetry_traces_exporter =
5656
jaeger_proto = opentelemetry.exporter.jaeger.proto.grpc:JaegerExporter

exporter/opentelemetry-exporter-jaeger-thrift/setup.cfg

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,5 @@ where = src
5151
test =
5252

5353
[options.entry_points]
54-
opentelemetry_exporter =
55-
jaeger_thrift = opentelemetry.exporter.jaeger.thrift:JaegerExporter
54+
opentelemetry_traces_exporter =
55+
jaeger_thrift = opentelemetry.exporter.jaeger.thrift:JaegerExporter

exporter/opentelemetry-exporter-jaeger/setup.cfg

+4
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,7 @@ install_requires =
4444

4545
[options.extras_require]
4646
test =
47+
48+
[options.entry_points]
49+
opentelemetry_traces_exporter =
50+
jaeger = opentelemetry.exporter.jaeger.proto.grpc:JaegerExporter

exporter/opentelemetry-exporter-opencensus/setup.cfg

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,5 @@ where = src
5353
test =
5454

5555
[options.entry_points]
56-
opentelemetry_exporter =
57-
opencensus = opentelemetry.exporter.opencensus.trace_exporter:OpenCensusSpanExporter
56+
opentelemetry_traces_exporter =
57+
opencensus = opentelemetry.exporter.opencensus.trace_exporter:OpenCensusSpanExporter

exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@ test =
5454
where = src
5555

5656
[options.entry_points]
57-
opentelemetry_exporter =
58-
otlp_proto_grpc_span = opentelemetry.exporter.otlp.proto.grpc.trace_exporter:OTLPSpanExporter
57+
opentelemetry_traces_exporter =
58+
otlp_proto_grpc = opentelemetry.exporter.otlp.proto.grpc.trace_exporter:OTLPSpanExporter

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

+3-13
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
OTEL_EXPORTER_OTLP_TIMEOUT,
5151
)
5252
from opentelemetry.sdk.resources import Resource as SDKResource
53+
from opentelemetry.util.re import parse_headers
5354

5455
logger = logging.getLogger(__name__)
5556
SDKDataT = TypeVar("SDKDataT")
@@ -228,19 +229,8 @@ def __init__(
228229

229230
self._headers = headers or environ.get(OTEL_EXPORTER_OTLP_HEADERS)
230231
if isinstance(self._headers, str):
231-
temp_headers = []
232-
for header_pair in self._headers.split(","):
233-
key, value = header_pair.split("=", maxsplit=1)
234-
key = key.strip().lower()
235-
value = value.strip()
236-
temp_headers.append(
237-
(
238-
key,
239-
value,
240-
)
241-
)
242-
243-
self._headers = tuple(temp_headers)
232+
temp_headers = parse_headers(self._headers)
233+
self._headers = tuple(temp_headers.items())
244234

245235
self._timeout = timeout or int(
246236
environ.get(OTEL_EXPORTER_OTLP_TIMEOUT, 10)

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
OTEL_EXPORTER_OTLP_TRACES_HEADERS,
4747
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
4848
)
49-
from opentelemetry.sdk.trace import Span as ReadableSpan
49+
from opentelemetry.sdk.trace import ReadableSpan
5050
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
5151
from opentelemetry.trace import StatusCode
5252

exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,5 @@ test =
5353
where = src
5454

5555
[options.entry_points]
56-
opentelemetry_exporter =
57-
otlp_proto_http_span = opentelemetry.exporter.otlp.proto.http.trace_exporter:OTLPSpanExporter
56+
opentelemetry_traces_exporter =
57+
otlp_proto_http = opentelemetry.exporter.otlp.proto.http.trace_exporter:OTLPSpanExporter

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

+6-19
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from opentelemetry.exporter.otlp.proto.http.trace_exporter.encoder import (
4141
_ProtobufEncoder,
4242
)
43+
from opentelemetry.util.re import parse_headers
4344

4445

4546
_logger = logging.getLogger(__name__)
@@ -70,7 +71,11 @@ def __init__(
7071
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE,
7172
environ.get(OTEL_EXPORTER_OTLP_CERTIFICATE, True),
7273
)
73-
self._headers = headers or _headers_from_env()
74+
headers_string = environ.get(
75+
OTEL_EXPORTER_OTLP_TRACES_HEADERS,
76+
environ.get(OTEL_EXPORTER_OTLP_HEADERS, ""),
77+
)
78+
self._headers = headers or parse_headers(headers_string)
7479
self._timeout = timeout or int(
7580
environ.get(
7681
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
@@ -155,24 +160,6 @@ def shutdown(self):
155160
self._shutdown = True
156161

157162

158-
def _headers_from_env() -> Optional[Dict[str, str]]:
159-
headers_str = environ.get(
160-
OTEL_EXPORTER_OTLP_TRACES_HEADERS,
161-
environ.get(OTEL_EXPORTER_OTLP_HEADERS),
162-
)
163-
headers = {}
164-
if headers_str:
165-
for header in headers_str.split(","):
166-
try:
167-
header_name, header_value = header.split("=")
168-
headers[header_name.strip()] = header_value.strip()
169-
except ValueError:
170-
_logger.warning(
171-
"Skipped invalid OTLP exporter header: %r", header
172-
)
173-
return headers
174-
175-
176163
def _compression_from_env() -> Compression:
177164
compression = (
178165
environ.get(

exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def test_constructor_default(self):
6464
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE: "traces/certificate.env",
6565
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION: Compression.Deflate.value,
6666
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: "https://traces.endpoint.env",
67-
OTEL_EXPORTER_OTLP_TRACES_HEADERS: "tracesEnv1=val1,tracesEnv2=val2",
67+
OTEL_EXPORTER_OTLP_TRACES_HEADERS: "tracesEnv1=val1,tracesEnv2=val2,traceEnv3===val3==",
6868
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT: "40",
6969
},
7070
)
@@ -77,7 +77,11 @@ def test_exporter_traces_env_take_priority(self):
7777
self.assertIs(exporter._compression, Compression.Deflate)
7878
self.assertEqual(
7979
exporter._headers,
80-
{"tracesEnv1": "val1", "tracesEnv2": "val2"},
80+
{
81+
"tracesenv1": "val1",
82+
"tracesenv2": "val2",
83+
"traceenv3": "==val3==",
84+
},
8185
)
8286

8387
@patch.dict(
@@ -127,7 +131,7 @@ def test_exporter_env(self):
127131
self.assertEqual(exporter._timeout, int(OS_ENV_TIMEOUT))
128132
self.assertIs(exporter._compression, Compression.Gzip)
129133
self.assertEqual(
130-
exporter._headers, {"envHeader1": "val1", "envHeader2": "val2"}
134+
exporter._headers, {"envheader1": "val1", "envheader2": "val2"}
131135
)
132136

133137
@patch.dict(
@@ -143,5 +147,5 @@ def test_headers_parse_from_env(self):
143147

144148
self.assertEqual(
145149
cm.records[0].message,
146-
"Skipped invalid OTLP exporter header: 'missingValue'",
150+
"Header doesn't match the format: missingValue.",
147151
)

exporter/opentelemetry-exporter-otlp/setup.cfg

+5
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,8 @@ python_requires = >=3.6
3939
packages=find_namespace:
4040
install_requires =
4141
opentelemetry-exporter-otlp-proto-grpc == 1.5.0
42+
opentelemetry-exporter-otlp-proto-http == 1.5.0
43+
44+
[options.entry_points]
45+
opentelemetry_traces_exporter =
46+
otlp = opentelemetry.exporter.otlp.proto.grpc.trace_exporter:OTLPSpanExporter

exporter/opentelemetry-exporter-zipkin-json/setup.cfg

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,5 @@ where = src
5252
test =
5353

5454
[options.entry_points]
55-
opentelemetry_exporter =
56-
zipkin_json = opentelemetry.exporter.zipkin.json:ZipkinExporter
55+
opentelemetry_traces_exporter =
56+
zipkin_json = opentelemetry.exporter.zipkin.json:ZipkinExporter

exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@ where = src
5454
test =
5555

5656
[options.entry_points]
57-
opentelemetry_exporter =
58-
zipkin_proto = opentelemetry.exporter.zipkin.proto.http:ZipkinExporter
57+
opentelemetry_traces_exporter =
58+
zipkin_proto = opentelemetry.exporter.zipkin.proto.http:ZipkinExporter

exporter/opentelemetry-exporter-zipkin/setup.cfg

+4
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ install_requires =
4343

4444
[options.extras_require]
4545
test =
46+
47+
[options.entry_points]
48+
opentelemetry_traces_exporter =
49+
zipkin = opentelemetry.exporter.zipkin.proto.http:ZipkinExporter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import logging
16+
from re import compile, split
17+
from typing import Mapping
18+
19+
_logger = logging.getLogger(__name__)
20+
21+
22+
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables
23+
_OWS = r"[ \t]*"
24+
# A key contains one or more US-ASCII character except CTLs or separators.
25+
_KEY_FORMAT = (
26+
r"[\x21\x23-\x27\x2a\x2b\x2d\x2e\x30-\x39\x41-\x5a\x5e-\x7a\x7c\x7e]+"
27+
)
28+
# A value contains a URL encoded UTF-8 string.
29+
_VALUE_FORMAT = r"[\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]*"
30+
_HEADER_FORMAT = _KEY_FORMAT + _OWS + r"=" + _OWS + _VALUE_FORMAT
31+
_HEADER_PATTERN = compile(_HEADER_FORMAT)
32+
_DELIMITER_PATTERN = compile(r"[ \t]*,[ \t]*")
33+
34+
35+
# pylint: disable=invalid-name
36+
def parse_headers(s: str) -> Mapping[str, str]:
37+
"""
38+
Parse ``s`` (a ``str`` instance containing HTTP headers). Uses W3C Baggage
39+
HTTP header format https://www.w3.org/TR/baggage/#baggage-http-header-format, except that
40+
additional semi-colon delimited metadata is not supported.
41+
"""
42+
headers = {}
43+
for header in split(_DELIMITER_PATTERN, s):
44+
if not header: # empty string
45+
continue
46+
match = _HEADER_PATTERN.fullmatch(header.strip())
47+
if not match:
48+
_logger.warning("Header doesn't match the format: %s.", header)
49+
continue
50+
# value may contain any number of `=`
51+
name, value = match.string.split("=", 1)
52+
name = name.strip().lower()
53+
value = value.strip()
54+
headers[name] = value
55+
56+
return headers
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# type: ignore
16+
17+
import unittest
18+
19+
from opentelemetry.util.re import parse_headers
20+
21+
22+
class TestParseHeaders(unittest.TestCase):
23+
def test_parse_headers(self):
24+
inp = [
25+
# invalid header name
26+
("=value", [], True),
27+
("}key=value", [], True),
28+
("@key()=value", [], True),
29+
("/key=value", [], True),
30+
# invalid header value
31+
("name=\\", [], True),
32+
('name=value"', [], True),
33+
("name=;value", [], True),
34+
# different header values
35+
("name=", [("name", "")], False),
36+
("name===value=", [("name", "==value=")], False),
37+
# mix of valid and invalid headers
38+
(
39+
"name1=value1,invalidName, name2 = value2 , name3=value3==",
40+
[
41+
(
42+
"name1",
43+
"value1",
44+
),
45+
("name2", "value2"),
46+
("name3", "value3=="),
47+
],
48+
True,
49+
),
50+
(
51+
"=name=valu3; key1; key2, content = application, red=\tvelvet; cake",
52+
[("content", "application")],
53+
True,
54+
),
55+
]
56+
for case in inp:
57+
s, expected, warn = case
58+
if warn:
59+
with self.assertLogs(level="WARNING") as cm:
60+
self.assertEqual(parse_headers(s), dict(expected))
61+
self.assertTrue(
62+
"Header doesn't match the format:"
63+
in cm.records[0].message,
64+
)
65+
else:
66+
self.assertEqual(parse_headers(s), dict(expected))

opentelemetry-sdk/setup.cfg

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ where = src
5252
[options.entry_points]
5353
opentelemetry_tracer_provider =
5454
sdk_tracer_provider = opentelemetry.sdk.trace:TracerProvider
55-
opentelemetry_exporter =
56-
console_span = opentelemetry.sdk.trace.export:ConsoleSpanExporter
55+
opentelemetry_traces_exporter =
56+
console = opentelemetry.sdk.trace.export:ConsoleSpanExporter
5757
opentelemetry_id_generator =
5858
random = opentelemetry.sdk.trace.id_generator:RandomIdGenerator
5959
opentelemetry_environment_variables =

opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def _import_exporters(
109109
exporter_name,
110110
exporter_impl,
111111
) in _import_tracer_provider_config_components(
112-
exporter_names, "opentelemetry_exporter"
112+
exporter_names, "opentelemetry_traces_exporter"
113113
):
114114
if issubclass(exporter_impl, SpanExporter):
115115
trace_exporters[exporter_name] = exporter_impl

0 commit comments

Comments
 (0)