mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-30 05:32:30 +08:00
Fixing w3c baggage support in opentelemetry-instrumentation-aws-lambda (#2589)
* Fixing w3c baggage support in opentelemetry-instrumentation-aws-lambda * Changelog update * Passing context not needed * Fixing unit test after rebase
This commit is contained in:
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- `opentelemetry-instrumentation-aws-lambda` Avoid exception when a handler is not present.
|
- `opentelemetry-instrumentation-aws-lambda` Avoid exception when a handler is not present.
|
||||||
@ -17,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
([#2483](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2484))
|
([#2483](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2484))
|
||||||
- `opentelemetry-instrumentation-fastapi` Fix fastapi-slim support
|
- `opentelemetry-instrumentation-fastapi` Fix fastapi-slim support
|
||||||
([#2756](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2756))
|
([#2756](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2756))
|
||||||
|
- `opentelemetry-instrumentation-aws-lambda` Fixing w3c baggage support
|
||||||
|
([#2589](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2589))
|
||||||
|
|
||||||
## Version 1.26.0/0.47b0 (2024-07-23)
|
## Version 1.26.0/0.47b0 (2024-07-23)
|
||||||
|
|
||||||
@ -113,7 +119,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610))
|
([#2610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2610))
|
||||||
- `opentelemetry-instrumentation-asgi` Bugfix: Middleware did not set status code attribute on duration metrics for non-recording spans.
|
- `opentelemetry-instrumentation-asgi` Bugfix: Middleware did not set status code attribute on duration metrics for non-recording spans.
|
||||||
([#2627](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2627))
|
([#2627](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2627))
|
||||||
|
<<<<<<< HEAD
|
||||||
- `opentelemetry-instrumentation-mysql` Add support for `mysql-connector-python` v9 ([#2751](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2751))
|
- `opentelemetry-instrumentation-mysql` Add support for `mysql-connector-python` v9 ([#2751](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2751))
|
||||||
|
=======
|
||||||
|
>>>>>>> 5a623233 (Changelog update)
|
||||||
|
|
||||||
## Version 1.25.0/0.46b0 (2024-05-31)
|
## Version 1.25.0/0.46b0 (2024-05-31)
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ from urllib.parse import urlencode
|
|||||||
|
|
||||||
from wrapt import wrap_function_wrapper
|
from wrapt import wrap_function_wrapper
|
||||||
|
|
||||||
|
from opentelemetry import context as context_api
|
||||||
from opentelemetry.context.context import Context
|
from opentelemetry.context.context import Context
|
||||||
from opentelemetry.instrumentation.aws_lambda.package import _instruments
|
from opentelemetry.instrumentation.aws_lambda.package import _instruments
|
||||||
from opentelemetry.instrumentation.aws_lambda.version import __version__
|
from opentelemetry.instrumentation.aws_lambda.version import __version__
|
||||||
@ -303,65 +304,74 @@ def _instrument(
|
|||||||
schema_url="https://opentelemetry.io/schemas/1.11.0",
|
schema_url="https://opentelemetry.io/schemas/1.11.0",
|
||||||
)
|
)
|
||||||
|
|
||||||
with tracer.start_as_current_span(
|
token = context_api.attach(parent_context)
|
||||||
name=orig_handler_name,
|
try:
|
||||||
context=parent_context,
|
with tracer.start_as_current_span(
|
||||||
kind=span_kind,
|
name=orig_handler_name,
|
||||||
) as span:
|
kind=span_kind,
|
||||||
if span.is_recording():
|
) as span:
|
||||||
lambda_context = args[1]
|
if span.is_recording():
|
||||||
# NOTE: The specs mention an exception here, allowing the
|
lambda_context = args[1]
|
||||||
# `SpanAttributes.CLOUD_RESOURCE_ID` attribute to be set as a span
|
# NOTE: The specs mention an exception here, allowing the
|
||||||
# attribute instead of a resource attribute.
|
# `SpanAttributes.CLOUD_RESOURCE_ID` attribute to be set as a span
|
||||||
#
|
# attribute instead of a resource attribute.
|
||||||
# See more:
|
#
|
||||||
# https://github.com/open-telemetry/semantic-conventions/blob/main/docs/faas/aws-lambda.md#resource-detector
|
# See more:
|
||||||
span.set_attribute(
|
# https://github.com/open-telemetry/semantic-conventions/blob/main/docs/faas/aws-lambda.md#resource-detector
|
||||||
SpanAttributes.CLOUD_RESOURCE_ID,
|
|
||||||
lambda_context.invoked_function_arn,
|
|
||||||
)
|
|
||||||
span.set_attribute(
|
|
||||||
SpanAttributes.FAAS_INVOCATION_ID,
|
|
||||||
lambda_context.aws_request_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
# NOTE: `cloud.account.id` can be parsed from the ARN as the fifth item when splitting on `:`
|
|
||||||
#
|
|
||||||
# See more:
|
|
||||||
# https://github.com/open-telemetry/semantic-conventions/blob/main/docs/faas/aws-lambda.md#all-triggers
|
|
||||||
account_id = lambda_context.invoked_function_arn.split(":")[4]
|
|
||||||
span.set_attribute(
|
|
||||||
ResourceAttributes.CLOUD_ACCOUNT_ID,
|
|
||||||
account_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
exception = None
|
|
||||||
result = None
|
|
||||||
try:
|
|
||||||
result = call_wrapped(*args, **kwargs)
|
|
||||||
except Exception as exc: # pylint: disable=W0703
|
|
||||||
exception = exc
|
|
||||||
span.set_status(Status(StatusCode.ERROR))
|
|
||||||
span.record_exception(exception)
|
|
||||||
|
|
||||||
# If the request came from an API Gateway, extract http attributes from the event
|
|
||||||
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/instrumentation/aws-lambda.md#api-gateway
|
|
||||||
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-server-semantic-conventions
|
|
||||||
if isinstance(lambda_event, dict) and lambda_event.get(
|
|
||||||
"requestContext"
|
|
||||||
):
|
|
||||||
span.set_attribute(SpanAttributes.FAAS_TRIGGER, "http")
|
|
||||||
|
|
||||||
if lambda_event.get("version") == "2.0":
|
|
||||||
_set_api_gateway_v2_proxy_attributes(lambda_event, span)
|
|
||||||
else:
|
|
||||||
_set_api_gateway_v1_proxy_attributes(lambda_event, span)
|
|
||||||
|
|
||||||
if isinstance(result, dict) and result.get("statusCode"):
|
|
||||||
span.set_attribute(
|
span.set_attribute(
|
||||||
SpanAttributes.HTTP_STATUS_CODE,
|
SpanAttributes.CLOUD_RESOURCE_ID,
|
||||||
result.get("statusCode"),
|
lambda_context.invoked_function_arn,
|
||||||
)
|
)
|
||||||
|
span.set_attribute(
|
||||||
|
SpanAttributes.FAAS_INVOCATION_ID,
|
||||||
|
lambda_context.aws_request_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# NOTE: `cloud.account.id` can be parsed from the ARN as the fifth item when splitting on `:`
|
||||||
|
#
|
||||||
|
# See more:
|
||||||
|
# https://github.com/open-telemetry/semantic-conventions/blob/main/docs/faas/aws-lambda.md#all-triggers
|
||||||
|
account_id = lambda_context.invoked_function_arn.split(
|
||||||
|
":"
|
||||||
|
)[4]
|
||||||
|
span.set_attribute(
|
||||||
|
ResourceAttributes.CLOUD_ACCOUNT_ID,
|
||||||
|
account_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
exception = None
|
||||||
|
result = None
|
||||||
|
try:
|
||||||
|
result = call_wrapped(*args, **kwargs)
|
||||||
|
except Exception as exc: # pylint: disable=W0703
|
||||||
|
exception = exc
|
||||||
|
span.set_status(Status(StatusCode.ERROR))
|
||||||
|
span.record_exception(exception)
|
||||||
|
|
||||||
|
# If the request came from an API Gateway, extract http attributes from the event
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/instrumentation/aws-lambda.md#api-gateway
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-server-semantic-conventions
|
||||||
|
if isinstance(lambda_event, dict) and lambda_event.get(
|
||||||
|
"requestContext"
|
||||||
|
):
|
||||||
|
span.set_attribute(SpanAttributes.FAAS_TRIGGER, "http")
|
||||||
|
|
||||||
|
if lambda_event.get("version") == "2.0":
|
||||||
|
_set_api_gateway_v2_proxy_attributes(
|
||||||
|
lambda_event, span
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_set_api_gateway_v1_proxy_attributes(
|
||||||
|
lambda_event, span
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(result, dict) and result.get("statusCode"):
|
||||||
|
span.set_attribute(
|
||||||
|
SpanAttributes.HTTP_STATUS_CODE,
|
||||||
|
result.get("statusCode"),
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
context_api.detach(token)
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
_tracer_provider = tracer_provider or get_tracer_provider()
|
_tracer_provider = tracer_provider or get_tracer_provider()
|
||||||
|
@ -12,9 +12,14 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from opentelemetry import baggage as baggage_api
|
||||||
|
|
||||||
|
|
||||||
def handler(event, context):
|
def handler(event, context):
|
||||||
return "200 ok"
|
baggage_content = dict(baggage_api.get_all().items())
|
||||||
|
return json.dumps({"baggage_content": baggage_content})
|
||||||
|
|
||||||
|
|
||||||
def rest_api_handler(event, context):
|
def rest_api_handler(event, context):
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from importlib import import_module, reload
|
from importlib import import_module, reload
|
||||||
@ -19,6 +19,7 @@ from typing import Any, Callable, Dict
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from opentelemetry import propagate
|
from opentelemetry import propagate
|
||||||
|
from opentelemetry.baggage.propagation import W3CBaggagePropagator
|
||||||
from opentelemetry.environment_variables import OTEL_PROPAGATORS
|
from opentelemetry.environment_variables import OTEL_PROPAGATORS
|
||||||
from opentelemetry.instrumentation.aws_lambda import (
|
from opentelemetry.instrumentation.aws_lambda import (
|
||||||
_HANDLER,
|
_HANDLER,
|
||||||
@ -79,6 +80,9 @@ MOCK_W3C_TRACE_CONTEXT_SAMPLED = (
|
|||||||
MOCK_W3C_TRACE_STATE_KEY = "vendor_specific_key"
|
MOCK_W3C_TRACE_STATE_KEY = "vendor_specific_key"
|
||||||
MOCK_W3C_TRACE_STATE_VALUE = "test_value"
|
MOCK_W3C_TRACE_STATE_VALUE = "test_value"
|
||||||
|
|
||||||
|
MOCK_W3C_BAGGAGE_KEY = "baggage_key"
|
||||||
|
MOCK_W3C_BAGGAGE_VALUE = "baggage_value"
|
||||||
|
|
||||||
|
|
||||||
def mock_execute_lambda(event=None):
|
def mock_execute_lambda(event=None):
|
||||||
"""Mocks the AWS Lambda execution.
|
"""Mocks the AWS Lambda execution.
|
||||||
@ -97,7 +101,7 @@ def mock_execute_lambda(event=None):
|
|||||||
|
|
||||||
module_name, handler_name = os.environ[_HANDLER].rsplit(".", 1)
|
module_name, handler_name = os.environ[_HANDLER].rsplit(".", 1)
|
||||||
handler_module = import_module(module_name.replace("/", "."))
|
handler_module = import_module(module_name.replace("/", "."))
|
||||||
getattr(handler_module, handler_name)(event, MOCK_LAMBDA_CONTEXT)
|
return getattr(handler_module, handler_name)(event, MOCK_LAMBDA_CONTEXT)
|
||||||
|
|
||||||
|
|
||||||
class TestAwsLambdaInstrumentor(TestBase):
|
class TestAwsLambdaInstrumentor(TestBase):
|
||||||
@ -181,6 +185,9 @@ class TestAwsLambdaInstrumentor(TestBase):
|
|||||||
expected_state_value: str = None
|
expected_state_value: str = None
|
||||||
expected_trace_state_len: int = 0
|
expected_trace_state_len: int = 0
|
||||||
propagators: str = "tracecontext"
|
propagators: str = "tracecontext"
|
||||||
|
expected_baggage: str = None
|
||||||
|
disable_aws_context_propagation: bool = False
|
||||||
|
disable_aws_context_propagation_envvar: str = ""
|
||||||
|
|
||||||
def custom_event_context_extractor(lambda_event):
|
def custom_event_context_extractor(lambda_event):
|
||||||
return get_global_textmap().extract(lambda_event["foo"]["headers"])
|
return get_global_textmap().extract(lambda_event["foo"]["headers"])
|
||||||
@ -266,6 +273,24 @@ class TestAwsLambdaInstrumentor(TestBase):
|
|||||||
expected_state_value=MOCK_W3C_TRACE_STATE_VALUE,
|
expected_state_value=MOCK_W3C_TRACE_STATE_VALUE,
|
||||||
xray_traceid=MOCK_XRAY_TRACE_CONTEXT_SAMPLED,
|
xray_traceid=MOCK_XRAY_TRACE_CONTEXT_SAMPLED,
|
||||||
),
|
),
|
||||||
|
TestCase(
|
||||||
|
name="baggage_propagation",
|
||||||
|
custom_extractor=None,
|
||||||
|
context={
|
||||||
|
"headers": {
|
||||||
|
TraceContextTextMapPropagator._TRACEPARENT_HEADER_NAME: MOCK_W3C_TRACE_CONTEXT_SAMPLED,
|
||||||
|
TraceContextTextMapPropagator._TRACESTATE_HEADER_NAME: f"{MOCK_W3C_TRACE_STATE_KEY}={MOCK_W3C_TRACE_STATE_VALUE},foo=1,bar=2",
|
||||||
|
W3CBaggagePropagator._BAGGAGE_HEADER_NAME: f"{MOCK_W3C_BAGGAGE_KEY}={MOCK_W3C_BAGGAGE_VALUE}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expected_traceid=MOCK_W3C_TRACE_ID,
|
||||||
|
expected_parentid=MOCK_W3C_PARENT_SPAN_ID,
|
||||||
|
expected_trace_state_len=3,
|
||||||
|
expected_state_value=MOCK_W3C_TRACE_STATE_VALUE,
|
||||||
|
xray_traceid=MOCK_XRAY_TRACE_CONTEXT_NOT_SAMPLED,
|
||||||
|
expected_baggage=MOCK_W3C_BAGGAGE_VALUE,
|
||||||
|
propagators="tracecontext,baggage",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
for test in tests:
|
for test in tests:
|
||||||
|
|
||||||
@ -284,7 +309,9 @@ class TestAwsLambdaInstrumentor(TestBase):
|
|||||||
AwsLambdaInstrumentor().instrument(
|
AwsLambdaInstrumentor().instrument(
|
||||||
event_context_extractor=test.custom_extractor,
|
event_context_extractor=test.custom_extractor,
|
||||||
)
|
)
|
||||||
mock_execute_lambda(test.context)
|
result = mock_execute_lambda(test.context)
|
||||||
|
result = json.loads(result)
|
||||||
|
|
||||||
spans = self.memory_exporter.get_finished_spans()
|
spans = self.memory_exporter.get_finished_spans()
|
||||||
assert spans
|
assert spans
|
||||||
self.assertEqual(len(spans), 1)
|
self.assertEqual(len(spans), 1)
|
||||||
@ -305,6 +332,10 @@ class TestAwsLambdaInstrumentor(TestBase):
|
|||||||
parent_context.trace_state.get(MOCK_W3C_TRACE_STATE_KEY),
|
parent_context.trace_state.get(MOCK_W3C_TRACE_STATE_KEY),
|
||||||
test.expected_state_value,
|
test.expected_state_value,
|
||||||
)
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
result["baggage_content"].get(MOCK_W3C_BAGGAGE_KEY),
|
||||||
|
test.expected_baggage,
|
||||||
|
)
|
||||||
self.assertTrue(parent_context.is_remote)
|
self.assertTrue(parent_context.is_remote)
|
||||||
self.memory_exporter.clear()
|
self.memory_exporter.clear()
|
||||||
AwsLambdaInstrumentor().uninstrument()
|
AwsLambdaInstrumentor().uninstrument()
|
||||||
|
Reference in New Issue
Block a user