Skip to content

Commit 666a10c

Browse files
committed
feat(aws-lambda): support stable http semantic convension
1 parent 123f556 commit 666a10c

File tree

4 files changed

+615
-41
lines changed

4 files changed

+615
-41
lines changed

instrumentation/opentelemetry-instrumentation-aws-lambda/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ dependencies = [
2828
"opentelemetry-instrumentation == 0.60b0.dev",
2929
"opentelemetry-semantic-conventions == 0.60b0.dev",
3030
"opentelemetry-propagator-aws-xray ~= 1.0",
31+
"opentelemetry-util-http == 0.60b0.dev",
3132
]
3233

3334
[project.optional-dependencies]

instrumentation/opentelemetry-instrumentation-aws-lambda/src/opentelemetry/instrumentation/aws_lambda/__init__.py

Lines changed: 108 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ def custom_event_context_extractor(lambda_event):
8080

8181
from opentelemetry import context as context_api
8282
from opentelemetry.context.context import Context
83+
from opentelemetry.instrumentation._semconv import (
84+
_get_schema_url,
85+
_OpenTelemetrySemanticConventionStability,
86+
_OpenTelemetryStabilitySignalType,
87+
_set_http_method,
88+
_set_http_net_host,
89+
_set_http_scheme,
90+
_set_http_status_code,
91+
_set_http_target,
92+
_set_http_user_agent,
93+
_StabilityMode,
94+
)
8395
from opentelemetry.instrumentation.aws_lambda.package import _instruments
8496
from opentelemetry.instrumentation.aws_lambda.version import __version__
8597
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
@@ -95,15 +107,7 @@ def custom_event_context_extractor(lambda_event):
95107
FAAS_TRIGGER,
96108
)
97109
from opentelemetry.semconv._incubating.attributes.http_attributes import (
98-
HTTP_METHOD,
99110
HTTP_ROUTE,
100-
HTTP_SCHEME,
101-
HTTP_STATUS_CODE,
102-
HTTP_TARGET,
103-
HTTP_USER_AGENT,
104-
)
105-
from opentelemetry.semconv._incubating.attributes.net_attributes import (
106-
NET_HOST_NAME,
107111
)
108112
from opentelemetry.trace import (
109113
Span,
@@ -113,6 +117,10 @@ def custom_event_context_extractor(lambda_event):
113117
get_tracer_provider,
114118
)
115119
from opentelemetry.trace.status import Status, StatusCode
120+
from opentelemetry.util.http import (
121+
_parse_url_query,
122+
sanitize_method,
123+
)
116124

117125
logger = logging.getLogger(__name__)
118126

@@ -181,86 +189,135 @@ def _determine_parent_context(
181189

182190

183191
def _set_api_gateway_v1_proxy_attributes(
184-
lambda_event: Any, span: Span
192+
lambda_event: Any,
193+
span: Span,
194+
sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT,
185195
) -> Span:
186196
"""Sets HTTP attributes for REST APIs and v1 HTTP APIs
187197
188198
More info:
189199
https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
190200
"""
191-
span.set_attribute(HTTP_METHOD, lambda_event.get("httpMethod"))
201+
http_method = lambda_event.get("httpMethod")
202+
if not http_method:
203+
http_method = (
204+
lambda_event.get("requestContext", {})
205+
.get("http", {})
206+
.get("method")
207+
)
208+
result = {}
209+
_set_http_method(
210+
result, http_method, sanitize_method(http_method), sem_conv_opt_in_mode
211+
)
192212

193213
if lambda_event.get("headers"):
194214
if "User-Agent" in lambda_event["headers"]:
195-
span.set_attribute(
196-
HTTP_USER_AGENT,
215+
_set_http_user_agent(
216+
result,
197217
lambda_event["headers"]["User-Agent"],
218+
sem_conv_opt_in_mode,
198219
)
199220
if "X-Forwarded-Proto" in lambda_event["headers"]:
200-
span.set_attribute(
201-
HTTP_SCHEME,
221+
_set_http_scheme(
222+
result,
202223
lambda_event["headers"]["X-Forwarded-Proto"],
224+
sem_conv_opt_in_mode,
203225
)
204226
if "Host" in lambda_event["headers"]:
205-
span.set_attribute(
206-
NET_HOST_NAME,
207-
lambda_event["headers"]["Host"],
208-
)
227+
host = lambda_event["headers"]["Host"]
228+
_set_http_net_host(result, host, sem_conv_opt_in_mode)
209229
if "resource" in lambda_event:
210230
span.set_attribute(HTTP_ROUTE, lambda_event["resource"])
211231

212232
if lambda_event.get("queryStringParameters"):
213-
span.set_attribute(
214-
HTTP_TARGET,
215-
f"{lambda_event['resource']}?{urlencode(lambda_event['queryStringParameters'])}",
233+
http_target = f"{lambda_event['resource']}?{urlencode(lambda_event['queryStringParameters'])}"
234+
path, query = _parse_url_query(http_target)
235+
_set_http_target(
236+
result,
237+
http_target,
238+
path,
239+
query,
240+
sem_conv_opt_in_mode,
216241
)
217242
else:
218-
span.set_attribute(HTTP_TARGET, lambda_event["resource"])
243+
res = lambda_event["resource"]
244+
path, query = _parse_url_query(res)
245+
_set_http_target(
246+
result,
247+
res,
248+
path,
249+
query,
250+
sem_conv_opt_in_mode,
251+
)
252+
253+
for key, value in result.items():
254+
span.set_attribute(key, value)
219255

220256
return span
221257

222258

223259
def _set_api_gateway_v2_proxy_attributes(
224-
lambda_event: Any, span: Span
260+
lambda_event: Any,
261+
span: Span,
262+
sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT,
225263
) -> Span:
226264
"""Sets HTTP attributes for v2 HTTP APIs
227265
228266
More info:
229267
https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
230268
"""
269+
result = {}
231270
if "domainName" in lambda_event["requestContext"]:
232-
span.set_attribute(
233-
NET_HOST_NAME,
271+
_set_http_net_host(
272+
result,
234273
lambda_event["requestContext"]["domainName"],
274+
sem_conv_opt_in_mode,
235275
)
236276

237277
if lambda_event["requestContext"].get("http"):
238278
if "method" in lambda_event["requestContext"]["http"]:
239-
span.set_attribute(
240-
HTTP_METHOD,
241-
lambda_event["requestContext"]["http"]["method"],
279+
http_method = lambda_event["requestContext"]["http"]["method"]
280+
_set_http_method(
281+
result,
282+
http_method,
283+
sanitize_method(http_method),
284+
sem_conv_opt_in_mode,
242285
)
243286
if "userAgent" in lambda_event["requestContext"]["http"]:
244-
span.set_attribute(
245-
HTTP_USER_AGENT,
287+
_set_http_user_agent(
288+
result,
246289
lambda_event["requestContext"]["http"]["userAgent"],
290+
sem_conv_opt_in_mode,
247291
)
248292
if "path" in lambda_event["requestContext"]["http"]:
249293
span.set_attribute(
250294
HTTP_ROUTE,
251295
lambda_event["requestContext"]["http"]["path"],
252296
)
253297
if lambda_event.get("rawQueryString"):
254-
span.set_attribute(
255-
HTTP_TARGET,
256-
f"{lambda_event['requestContext']['http']['path']}?{lambda_event['rawQueryString']}",
298+
http_target = f"{lambda_event['requestContext']['http']['path']}?{lambda_event['rawQueryString']}"
299+
path, query = _parse_url_query(http_target)
300+
_set_http_target(
301+
result,
302+
http_target,
303+
path,
304+
query,
305+
sem_conv_opt_in_mode,
257306
)
258307
else:
259-
span.set_attribute(
260-
HTTP_TARGET,
261-
lambda_event["requestContext"]["http"]["path"],
308+
http_target = lambda_event["requestContext"]["http"]["path"]
309+
path, query = _parse_url_query(http_target)
310+
_set_http_target(
311+
result,
312+
http_target,
313+
path,
314+
query,
315+
sem_conv_opt_in_mode,
262316
)
263317

318+
for key, value in result.items():
319+
span.set_attribute(key, value)
320+
264321
return span
265322

266323

@@ -272,6 +329,7 @@ def _instrument(
272329
event_context_extractor: Callable[[Any], Context],
273330
tracer_provider: TracerProvider = None,
274331
meter_provider: MeterProvider = None,
332+
sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT,
275333
):
276334
# pylint: disable=too-many-locals
277335
# pylint: disable=too-many-statements
@@ -314,7 +372,7 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches
314372
__name__,
315373
__version__,
316374
tracer_provider,
317-
schema_url="https://opentelemetry.io/schemas/1.11.0",
375+
schema_url=_get_schema_url(sem_conv_opt_in_mode),
318376
)
319377

320378
token = context_api.attach(parent_context)
@@ -371,18 +429,22 @@ def _instrumented_lambda_handler_call( # noqa pylint: disable=too-many-branches
371429

372430
if lambda_event.get("version") == "2.0":
373431
_set_api_gateway_v2_proxy_attributes(
374-
lambda_event, span
432+
lambda_event, span, sem_conv_opt_in_mode
375433
)
376434
else:
377435
_set_api_gateway_v1_proxy_attributes(
378-
lambda_event, span
436+
lambda_event, span, sem_conv_opt_in_mode
379437
)
380438

381439
if isinstance(result, dict) and result.get("statusCode"):
382-
span.set_attribute(
383-
HTTP_STATUS_CODE,
440+
attribute = {}
441+
_set_http_status_code(
442+
attribute,
384443
result.get("statusCode"),
444+
sem_conv_opt_in_mode,
385445
)
446+
for key, value in attribute.items():
447+
span.set_attribute(key, value)
386448
finally:
387449
if token:
388450
context_api.detach(token)
@@ -480,6 +542,10 @@ def _instrument(self, **kwargs):
480542
flush_timeout_env,
481543
)
482544

545+
sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
546+
_OpenTelemetryStabilitySignalType.HTTP,
547+
)
548+
483549
_instrument(
484550
self._wrapped_module_name,
485551
self._wrapped_function_name,
@@ -489,6 +555,7 @@ def _instrument(self, **kwargs):
489555
),
490556
tracer_provider=kwargs.get("tracer_provider"),
491557
meter_provider=kwargs.get("meter_provider"),
558+
sem_conv_opt_in_mode=sem_conv_opt_in_mode,
492559
)
493560

494561
def _uninstrument(self, **kwargs):

0 commit comments

Comments
 (0)