Skip to content

Commit

Permalink
Merge branch '3.x-staging' into christophe-papazian/remove_appsec_dep…
Browse files Browse the repository at this point in the history
…endencies_if_serverless
  • Loading branch information
christophe-papazian authored Jan 31, 2025
2 parents d9cf473 + 9eeae42 commit 9271c33
Show file tree
Hide file tree
Showing 16 changed files with 290 additions and 243 deletions.
56 changes: 54 additions & 2 deletions ddtrace/_trace/trace_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,14 @@ def _get_parameters_for_new_span_directly_from_context(ctx: core.ExecutionContex
def _start_span(ctx: core.ExecutionContext, call_trace: bool = True, **kwargs) -> "Span":
span_kwargs = _get_parameters_for_new_span_directly_from_context(ctx)
call_trace = ctx.get_item("call_trace", call_trace)
tracer = (ctx.get_item("middleware") or ctx["pin"]).tracer
tracer = ctx.get_item("tracer") or (ctx.get_item("middleware") or ctx["pin"]).tracer
distributed_headers_config = ctx.get_item("distributed_headers_config")
if distributed_headers_config:
trace_utils.activate_distributed_headers(
tracer, int_config=distributed_headers_config, request_headers=ctx["distributed_headers"]
tracer,
int_config=distributed_headers_config,
request_headers=ctx["distributed_headers"],
override=ctx.get_item("distributed_headers_config_override"),
)
distributed_context = ctx.get_item("distributed_context")
if distributed_context and not call_trace:
Expand All @@ -126,6 +129,42 @@ def _start_span(ctx: core.ExecutionContext, call_trace: bool = True, **kwargs) -
return span


def _set_web_frameworks_tags(ctx, span, int_config):
span.set_tag_str(COMPONENT, int_config.integration_name)
span.set_tag_str(SPAN_KIND, SpanKind.SERVER)
span.set_tag(_SPAN_MEASURED_KEY)

analytics_enabled = ctx.get_item("analytics_enabled")
analytics_sample_rate = ctx.get_item("analytics_sample_rate", True)

# Configure trace search sample rate
if (config._analytics_enabled and analytics_enabled is not False) or analytics_enabled is True:
span.set_tag(_ANALYTICS_SAMPLE_RATE_KEY, analytics_sample_rate)


def _on_web_framework_start_request(ctx, int_config):
request_span = ctx.get_item("req_span")
_set_web_frameworks_tags(ctx, request_span, int_config)


def _on_web_framework_finish_request(
span, int_config, method, url, status_code, query, req_headers, res_headers, route, finish
):
trace_utils.set_http_meta(
span=span,
integration_config=int_config,
method=method,
url=url,
status_code=status_code,
query=query,
request_headers=req_headers,
response_headers=res_headers,
route=route,
)
if finish:
span.finish()


def _on_traced_request_context_started_flask(ctx):
current_span = ctx["pin"].tracer.current_span()
if not ctx["pin"].enabled or not current_span:
Expand Down Expand Up @@ -761,6 +800,10 @@ def listen():
core.on("azure.functions.request_call_modifier", _on_azure_functions_request_span_modifier)
core.on("azure.functions.start_response", _on_azure_functions_start_response)

# web frameworks general handlers
core.on("web.request.start", _on_web_framework_start_request)
core.on("web.request.finish", _on_web_framework_finish_request)

core.on("test_visibility.enable", _on_test_visibility_enable)
core.on("test_visibility.disable", _on_test_visibility_disable)
core.on("test_visibility.is_enabled", _on_test_visibility_is_enabled, "is_enabled")
Expand All @@ -769,6 +812,14 @@ def listen():
core.on("rq.queue.enqueue_job", _propagate_context)

for context_name in (
# web frameworks
"aiohttp.request",
"bottle.request",
"cherrypy.request",
"falcon.request",
"molten.request",
"pyramid.request",
"sanic.request",
"flask.call",
"flask.jsonify",
"flask.render_template",
Expand All @@ -779,6 +830,7 @@ def listen():
"django.template.render",
"django.process_exception",
"django.func.wrapped",
# non web frameworks
"botocore.instrumented_api_call",
"botocore.instrumented_lib_function",
"botocore.patched_kinesis_api_call",
Expand Down
105 changes: 49 additions & 56 deletions ddtrace/contrib/internal/aiohttp/middlewares.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@
from aiohttp.web_urldispatcher import SystemRoute

from ddtrace import config
from ddtrace.constants import _ANALYTICS_SAMPLE_RATE_KEY
from ddtrace.constants import _SPAN_MEASURED_KEY
from ddtrace.constants import SPAN_KIND
from ddtrace.contrib import trace_utils
from ddtrace.contrib.asyncio import context_provider
from ddtrace.ext import SpanKind
from ddtrace.ext import SpanTypes
from ddtrace.ext import http
from ddtrace.internal.constants import COMPONENT
from ddtrace.internal import core
from ddtrace.internal.schema import schematize_url_operation
from ddtrace.internal.schema.span_attribute_schema import SpanDirection

Expand All @@ -35,47 +30,42 @@ async def attach_context(request):
# application configs
tracer = app[CONFIG_KEY]["tracer"]
service = app[CONFIG_KEY]["service"]
distributed_tracing = app[CONFIG_KEY]["distributed_tracing_enabled"]
# Create a new context based on the propagated information.
trace_utils.activate_distributed_headers(
tracer,
int_config=config.aiohttp,
request_headers=request.headers,
override=distributed_tracing,
)

# trace the handler
request_span = tracer.trace(
schematize_url_operation("aiohttp.request", protocol="http", direction=SpanDirection.INBOUND),
service=service,
span_type=SpanTypes.WEB,
)
request_span.set_tag(_SPAN_MEASURED_KEY)

request_span.set_tag_str(COMPONENT, config.aiohttp.integration_name)

# set span.kind tag equal to type of request
request_span.set_tag_str(SPAN_KIND, SpanKind.SERVER)

# Configure trace search sample rate
# DEV: aiohttp is special case maintains separate configuration from config api
analytics_enabled = app[CONFIG_KEY]["analytics_enabled"]
if (config._analytics_enabled and analytics_enabled is not False) or analytics_enabled is True:
request_span.set_tag(_ANALYTICS_SAMPLE_RATE_KEY, app[CONFIG_KEY].get("analytics_sample_rate", True))

# attach the context and the root span to the request; the Context
# may be freely used by the application code
request[REQUEST_CONTEXT_KEY] = request_span.context
request[REQUEST_SPAN_KEY] = request_span
request[REQUEST_CONFIG_KEY] = app[CONFIG_KEY]
try:
response = await handler(request)
if isinstance(response, web.StreamResponse):
request.task.add_done_callback(lambda _: finish_request_span(request, response))
return response
except Exception:
request_span.set_traceback()
raise
# Create a new context based on the propagated information.

with core.context_with_data(
"aiohttp.request",
span_name=schematize_url_operation("aiohttp.request", protocol="http", direction=SpanDirection.INBOUND),
span_type=SpanTypes.WEB,
service=service,
tags={},
tracer=tracer,
distributed_headers=request.headers,
distributed_headers_config=config.aiohttp,
distributed_headers_config_override=app[CONFIG_KEY]["distributed_tracing_enabled"],
headers_case_sensitive=True,
analytics_enabled=analytics_enabled,
analytics_sample_rate=app[CONFIG_KEY].get("analytics_sample_rate", True),
) as ctx:
req_span = ctx.span

ctx.set_item("req_span", req_span)
core.dispatch("web.request.start", (ctx, config.aiohttp))

# attach the context and the root span to the request; the Context
# may be freely used by the application code
request[REQUEST_CONTEXT_KEY] = req_span.context
request[REQUEST_SPAN_KEY] = req_span
request[REQUEST_CONFIG_KEY] = app[CONFIG_KEY]
try:
response = await handler(request)
if isinstance(response, web.StreamResponse):
request.task.add_done_callback(lambda _: finish_request_span(request, response))
return response
except Exception:
req_span.set_traceback()
raise

return attach_context

Expand Down Expand Up @@ -122,19 +112,22 @@ def finish_request_span(request, response):
# SystemRoute objects exist to throw HTTP errors and have no path
route = aiohttp_route.resource.canonical

trace_utils.set_http_meta(
request_span,
config.aiohttp,
method=request.method,
url=str(request.url), # DEV: request.url is a yarl's URL object
status_code=response.status,
request_headers=request.headers,
response_headers=response.headers,
route=route,
core.dispatch(
"web.request.finish",
(
request_span,
config.aiohttp,
request.method,
str(request.url), # DEV: request.url is a yarl's URL object
response.status,
None, # query arg = None
request.headers,
response.headers,
route,
True,
),
)

request_span.finish()


async def on_prepare(request, response):
"""
Expand Down
61 changes: 29 additions & 32 deletions ddtrace/contrib/internal/bottle/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@

import ddtrace
from ddtrace import config
from ddtrace.constants import _ANALYTICS_SAMPLE_RATE_KEY
from ddtrace.constants import _SPAN_MEASURED_KEY
from ddtrace.constants import SPAN_KIND
from ddtrace.contrib import trace_utils
from ddtrace.ext import SpanKind
from ddtrace.ext import SpanTypes
from ddtrace.internal.constants import COMPONENT
from ddtrace.internal import core
from ddtrace.internal.schema import schematize_url_operation
from ddtrace.internal.schema.span_attribute_schema import SpanDirection
from ddtrace.internal.utils.formats import asbool
Expand Down Expand Up @@ -42,24 +37,21 @@ def wrapped(*args, **kwargs):

resource = "{} {}".format(request.method, route.rule)

trace_utils.activate_distributed_headers(
self.tracer, int_config=config.bottle, request_headers=request.headers
)

with self.tracer.trace(
schematize_url_operation("bottle.request", protocol="http", direction=SpanDirection.INBOUND),
with core.context_with_data(
"bottle.request",
span_name=schematize_url_operation("bottle.request", protocol="http", direction=SpanDirection.INBOUND),
span_type=SpanTypes.WEB,
service=self.service,
resource=resource,
span_type=SpanTypes.WEB,
) as s:
s.set_tag_str(COMPONENT, config.bottle.integration_name)

# set span.kind to the type of request being performed
s.set_tag_str(SPAN_KIND, SpanKind.SERVER)

s.set_tag(_SPAN_MEASURED_KEY)
# set analytics sample rate with global config enabled
s.set_tag(_ANALYTICS_SAMPLE_RATE_KEY, config.bottle.get_analytics_sample_rate(use_global_config=True))
tags={},
tracer=self.tracer,
distributed_headers=request.headers,
distributed_headers_config=config.bottle,
headers_case_sensitive=True,
analytics_sample_rate=config.bottle.get_analytics_sample_rate(use_global_config=True),
) as ctx, ctx.span as req_span:
ctx.set_item("req_span", req_span)
core.dispatch("web.request.start", (ctx, config.bottle))

code = None
result = None
Expand Down Expand Up @@ -91,16 +83,21 @@ def wrapped(*args, **kwargs):
method = request.method
url = request.urlparts._replace(query="").geturl()
full_route = "/".join([request.script_name.rstrip("/"), route.rule.lstrip("/")])
trace_utils.set_http_meta(
s,
config.bottle,
method=method,
url=url,
status_code=response_code,
query=request.query_string,
request_headers=request.headers,
response_headers=response.headers,
route=full_route,

core.dispatch(
"web.request.finish",
(
req_span,
config.bottle,
method,
url,
response_code,
request.query_string,
request.headers,
response.headers,
full_route,
False,
),
)

return wrapped
53 changes: 29 additions & 24 deletions ddtrace/contrib/internal/cherrypy/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@
from ddtrace.constants import ERROR_MSG
from ddtrace.constants import ERROR_STACK
from ddtrace.constants import ERROR_TYPE
from ddtrace.constants import SPAN_KIND
from ddtrace.contrib import trace_utils
from ddtrace.ext import SpanKind
from ddtrace.ext import SpanTypes
from ddtrace.internal import compat
from ddtrace.internal.constants import COMPONENT
from ddtrace.internal import core
from ddtrace.internal.schema import SpanDirection
from ddtrace.internal.schema import schematize_service_name
from ddtrace.internal.schema import schematize_url_operation
Expand Down Expand Up @@ -77,20 +75,23 @@ def _setup(self):
cherrypy.request.hooks.attach("after_error_response", self._after_error_response, priority=5)

def _on_start_resource(self):
trace_utils.activate_distributed_headers(
self._tracer, int_config=config.cherrypy, request_headers=cherrypy.request.headers
)

cherrypy.request._datadog_span = self._tracer.trace(
SPAN_NAME,
service=trace_utils.int_service(None, config.cherrypy, default="cherrypy"),
with core.context_with_data(
"cherrypy.request",
span_name=SPAN_NAME,
span_type=SpanTypes.WEB,
)
service=trace_utils.int_service(None, config.cherrypy, default="cherrypy"),
tags={},
tracer=self._tracer,
distributed_headers=cherrypy.request.headers,
distributed_headers_config=config.cherrypy,
headers_case_sensitive=True,
) as ctx:
req_span = ctx.span

cherrypy.request._datadog_span.set_tag_str(COMPONENT, config.cherrypy.integration_name)
ctx.set_item("req_span", req_span)
core.dispatch("web.request.start", (ctx, config.cherrypy))

# set span.kind to the type of request being performed
cherrypy.request._datadog_span.set_tag_str(SPAN_KIND, SpanKind.SERVER)
cherrypy.request._datadog_span = req_span

def _after_error_response(self):
span = getattr(cherrypy.request, "_datadog_span", None)
Expand Down Expand Up @@ -135,18 +136,22 @@ def _close_span(self, span):
url = compat.to_unicode(cherrypy.request.base + cherrypy.request.path_info)
status_code, _, _ = valid_status(cherrypy.response.status)

trace_utils.set_http_meta(
span,
config.cherrypy,
method=cherrypy.request.method,
url=url,
status_code=status_code,
request_headers=cherrypy.request.headers,
response_headers=cherrypy.response.headers,
core.dispatch(
"web.request.finish",
(
span,
config.cherrypy,
cherrypy.request.method,
url,
status_code,
None,
cherrypy.request.headers,
cherrypy.response.headers,
None,
True,
),
)

span.finish()

# Clear our span just in case.
cherrypy.request._datadog_span = None

Expand Down
Loading

0 comments on commit 9271c33

Please sign in to comment.