-
Notifications
You must be signed in to change notification settings - Fork 624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Zipkin exporter: Add timeout support and implement shutdown #1799
Changes from all commits
d98f966
6006a7d
34b4eab
9d3ba19
c6bc102
ecc87ee
362811d
f33d4c1
b2e9a6a
295a496
8572111
7fd8ab0
a17373c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,6 +48,7 @@ | |
# local_node_ipv6="2001:db8::c001", | ||
# local_node_port=31313, | ||
# max_tag_value_length=256 | ||
# timeout=5 (in seconds) | ||
) | ||
|
||
# Create a BatchSpanProcessor and add the exporter to it | ||
|
@@ -62,6 +63,7 @@ | |
The exporter supports the following environment variable for configuration: | ||
|
||
- :envvar:`OTEL_EXPORTER_ZIPKIN_ENDPOINT` | ||
- :envvar:`OTEL_EXPORTER_ZIPKIN_TIMEOUT` | ||
|
||
API | ||
--- | ||
|
@@ -83,6 +85,7 @@ | |
from opentelemetry.exporter.zipkin.node_endpoint import IpInput, NodeEndpoint | ||
from opentelemetry.sdk.environment_variables import ( | ||
OTEL_EXPORTER_ZIPKIN_ENDPOINT, | ||
OTEL_EXPORTER_ZIPKIN_TIMEOUT, | ||
) | ||
from opentelemetry.sdk.resources import SERVICE_NAME | ||
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult | ||
|
@@ -103,7 +106,24 @@ def __init__( | |
local_node_ipv6: IpInput = None, | ||
local_node_port: Optional[int] = None, | ||
max_tag_value_length: Optional[int] = None, | ||
timeout: Optional[int] = None, | ||
): | ||
"""Zipkin exporter. | ||
|
||
Args: | ||
version: The protocol version to be used. | ||
endpoint: The endpoint of the Zipkin collector. | ||
local_node_ipv4: Primary IPv4 address associated with this connection. | ||
local_node_ipv6: Primary IPv6 address associated with this connection. | ||
local_node_port: Depending on context, this could be a listen port or the | ||
client-side of a socket. | ||
max_tag_value_length: Max length string attribute values can have. | ||
timeout: Maximum time the Zipkin exporter will wait for each batch export. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth calling out the 10s default?
codeboten marked this conversation as resolved.
Show resolved
Hide resolved
|
||
The default value is 10s. | ||
|
||
The tuple (local_node_ipv4, local_node_ipv6, local_node_port) is used to represent | ||
the network context of a node in the service graph. | ||
""" | ||
self.local_node = NodeEndpoint( | ||
local_node_ipv4, local_node_ipv6, local_node_port | ||
) | ||
|
@@ -119,7 +139,22 @@ def __init__( | |
elif version == Protocol.V2: | ||
self.encoder = JsonV2Encoder(max_tag_value_length) | ||
|
||
self.session = requests.Session() | ||
self.session.headers.update( | ||
{"Content-Type": self.encoder.content_type()} | ||
) | ||
self._closed = False | ||
self.timeout = timeout or int( | ||
environ.get(OTEL_EXPORTER_ZIPKIN_TIMEOUT, 10) | ||
) | ||
|
||
def export(self, spans: Sequence[Span]) -> SpanExportResult: | ||
# After the call to Shutdown subsequent calls to Export are | ||
# not allowed and should return a Failure result | ||
if self._closed: | ||
logger.warning("Exporter already shutdown, ignoring batch") | ||
return SpanExportResult.FAILURE | ||
|
||
# Populate service_name from first span | ||
# We restrict any SpanProcessor to be only associated with a single | ||
# TracerProvider, so it is safe to assume that all Spans in a single | ||
|
@@ -129,10 +164,10 @@ def export(self, spans: Sequence[Span]) -> SpanExportResult: | |
service_name = spans[0].resource.attributes.get(SERVICE_NAME) | ||
if service_name: | ||
self.local_node.service_name = service_name | ||
result = requests.post( | ||
result = self.session.post( | ||
url=self.endpoint, | ||
data=self.encoder.serialize(spans, self.local_node), | ||
headers={"Content-Type": self.encoder.content_type()}, | ||
timeout=self.timeout, | ||
) | ||
|
||
if result.status_code not in REQUESTS_SUCCESS_STATUS_CODES: | ||
|
@@ -145,4 +180,8 @@ def export(self, spans: Sequence[Span]) -> SpanExportResult: | |
return SpanExportResult.SUCCESS | ||
|
||
def shutdown(self) -> None: | ||
pass | ||
if self._closed: | ||
logger.warning("Exporter already shutdown, ignoring call") | ||
return | ||
self.session.close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what does this do to in-flight requests? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't affect the in-flight requests. Internally it uses this pool manager https://urllib3.readthedocs.io/en/1.24.3/reference/index.html#urllib3.poolmanager.PoolManager.clear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So it won't drop them but it won't wait for them to finish either? I think we should probably wait for them to finish if it can be done easily. |
||
self._closed = True |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,7 @@ | |
# local_node_ipv6="2001:db8::c001", | ||
# local_node_port=31313, | ||
# max_tag_value_length=256 | ||
# timeout=5 (in seconds) | ||
) | ||
|
||
# Create a BatchSpanProcessor and add the exporter to it | ||
|
@@ -61,6 +62,7 @@ | |
The exporter supports the following environment variable for configuration: | ||
|
||
- :envvar:`OTEL_EXPORTER_ZIPKIN_ENDPOINT` | ||
- :envvar:`OTEL_EXPORTER_ZIPKIN_TIMEOUT` | ||
|
||
API | ||
--- | ||
|
@@ -81,6 +83,7 @@ | |
from opentelemetry.exporter.zipkin.node_endpoint import IpInput, NodeEndpoint | ||
from opentelemetry.sdk.environment_variables import ( | ||
OTEL_EXPORTER_ZIPKIN_ENDPOINT, | ||
OTEL_EXPORTER_ZIPKIN_TIMEOUT, | ||
) | ||
from opentelemetry.sdk.resources import SERVICE_NAME | ||
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult | ||
|
@@ -100,7 +103,24 @@ def __init__( | |
local_node_ipv6: IpInput = None, | ||
local_node_port: Optional[int] = None, | ||
max_tag_value_length: Optional[int] = None, | ||
timeout: Optional[int] = None, | ||
): | ||
"""Zipkin exporter. | ||
|
||
Args: | ||
version: The protocol version to be used. | ||
endpoint: The endpoint of the Zipkin collector. | ||
local_node_ipv4: Primary IPv4 address associated with this connection. | ||
local_node_ipv6: Primary IPv6 address associated with this connection. | ||
local_node_port: Depending on context, this could be a listen port or the | ||
client-side of a socket. | ||
max_tag_value_length: Max length string attribute values can have. | ||
timeout: Maximum time the Zipkin exporter will wait for each batch export. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same note about mentioning the default timeout
codeboten marked this conversation as resolved.
Show resolved
Hide resolved
|
||
The default value is 10s. | ||
|
||
The tuple (local_node_ipv4, local_node_ipv6, local_node_port) is used to represent | ||
the network context of a node in the service graph. | ||
""" | ||
self.local_node = NodeEndpoint( | ||
local_node_ipv4, local_node_ipv6, local_node_port | ||
) | ||
|
@@ -113,7 +133,21 @@ def __init__( | |
|
||
self.encoder = ProtobufEncoder(max_tag_value_length) | ||
|
||
self.session = requests.Session() | ||
self.session.headers.update( | ||
{"Content-Type": self.encoder.content_type()} | ||
) | ||
self._closed = False | ||
self.timeout = timeout or int( | ||
environ.get(OTEL_EXPORTER_ZIPKIN_TIMEOUT, 10) | ||
) | ||
|
||
def export(self, spans: Sequence[Span]) -> SpanExportResult: | ||
# After the call to Shutdown subsequent calls to Export are | ||
# not allowed and should return a Failure result | ||
if self._closed: | ||
logger.warning("Exporter already shutdown, ignoring batch") | ||
return SpanExportResult.FAILURE | ||
# Populate service_name from first span | ||
# We restrict any SpanProcessor to be only associated with a single | ||
# TracerProvider, so it is safe to assume that all Spans in a single | ||
|
@@ -123,10 +157,10 @@ def export(self, spans: Sequence[Span]) -> SpanExportResult: | |
service_name = spans[0].resource.attributes.get(SERVICE_NAME) | ||
if service_name: | ||
self.local_node.service_name = service_name | ||
result = requests.post( | ||
result = self.session.post( | ||
url=self.endpoint, | ||
data=self.encoder.serialize(spans, self.local_node), | ||
headers={"Content-Type": self.encoder.content_type()}, | ||
timeout=self.timeout, | ||
) | ||
|
||
if result.status_code not in REQUESTS_SUCCESS_STATUS_CODES: | ||
|
@@ -139,4 +173,8 @@ def export(self, spans: Sequence[Span]) -> SpanExportResult: | |
return SpanExportResult.SUCCESS | ||
|
||
def shutdown(self) -> None: | ||
pass | ||
if self._closed: | ||
logger.warning("Exporter already shutdown, ignoring call") | ||
return | ||
self.session.close() | ||
self._closed = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this env var be proposed to the specs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR in spec repo open-telemetry/opentelemetry-specification#1636. It has already two approvals and I don't see any objection coming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice looks like it merged!