Skip to content

Commit

Permalink
Add origin to spans and transactions (#3133)
Browse files Browse the repository at this point in the history
API for adding origin to spans and transactions. Updating all our integrations to send a origin.
  • Loading branch information
antonpirker authored Jun 24, 2024
1 parent 24a5457 commit 87f6037
Show file tree
Hide file tree
Showing 102 changed files with 1,899 additions and 135 deletions.
8 changes: 5 additions & 3 deletions sentry_sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,13 @@ def get_baggage():
return None


def continue_trace(environ_or_headers, op=None, name=None, source=None):
# type: (Dict[str, Any], Optional[str], Optional[str], Optional[str]) -> Transaction
def continue_trace(
environ_or_headers, op=None, name=None, source=None, origin="manual"
):
# type: (Dict[str, Any], Optional[str], Optional[str], Optional[str], str) -> Transaction
"""
Sets the propagation context from environment or headers and returns a transaction.
"""
return Scope.get_isolation_scope().continue_trace(
environ_or_headers, op, name, source
environ_or_headers, op, name, source, origin
)
3 changes: 3 additions & 0 deletions sentry_sdk/integrations/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@

class AioHttpIntegration(Integration):
identifier = "aiohttp"
origin = f"auto.http.{identifier}"

def __init__(self, transaction_style="handler_name"):
# type: (str) -> None
Expand Down Expand Up @@ -120,6 +121,7 @@ async def sentry_app_handle(self, request, *args, **kwargs):
# URL resolver did not find a route or died trying.
name="generic AIOHTTP request",
source=TRANSACTION_SOURCE_ROUTE,
origin=AioHttpIntegration.origin,
)
with sentry_sdk.start_transaction(
transaction,
Expand Down Expand Up @@ -206,6 +208,7 @@ async def on_request_start(session, trace_config_ctx, params):
op=OP.HTTP_CLIENT,
description="%s %s"
% (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
origin=AioHttpIntegration.origin,
)
span.set_data(SPANDATA.HTTP_METHOD, method)
if parsed_url is not None:
Expand Down
5 changes: 4 additions & 1 deletion sentry_sdk/integrations/anthropic.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

class AnthropicIntegration(Integration):
identifier = "anthropic"
origin = f"auto.ai.{identifier}"

def __init__(self, include_prompts=True):
# type: (AnthropicIntegration, bool) -> None
Expand Down Expand Up @@ -92,7 +93,9 @@ def _sentry_patched_create(*args, **kwargs):
model = kwargs.get("model")

span = sentry_sdk.start_span(
op=OP.ANTHROPIC_MESSAGES_CREATE, description="Anthropic messages create"
op=OP.ANTHROPIC_MESSAGES_CREATE,
description="Anthropic messages create",
origin=AnthropicIntegration.origin,
)
span.__enter__()

Expand Down
6 changes: 5 additions & 1 deletion sentry_sdk/integrations/arq.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

class ArqIntegration(Integration):
identifier = "arq"
origin = f"auto.queue.{identifier}"

@staticmethod
def setup_once():
Expand Down Expand Up @@ -76,7 +77,9 @@ async def _sentry_enqueue_job(self, function, *args, **kwargs):
if integration is None:
return await old_enqueue_job(self, function, *args, **kwargs)

with sentry_sdk.start_span(op=OP.QUEUE_SUBMIT_ARQ, description=function):
with sentry_sdk.start_span(
op=OP.QUEUE_SUBMIT_ARQ, description=function, origin=ArqIntegration.origin
):
return await old_enqueue_job(self, function, *args, **kwargs)

ArqRedis.enqueue_job = _sentry_enqueue_job
Expand All @@ -101,6 +104,7 @@ async def _sentry_run_job(self, job_id, score):
status="ok",
op=OP.QUEUE_TASK_ARQ,
source=TRANSACTION_SOURCE_TASK,
origin=ArqIntegration.origin,
)

with sentry_sdk.start_transaction(transaction):
Expand Down
17 changes: 14 additions & 3 deletions sentry_sdk/integrations/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,23 @@ def _looks_like_asgi3(app):


class SentryAsgiMiddleware:
__slots__ = ("app", "__call__", "transaction_style", "mechanism_type")
__slots__ = (
"app",
"__call__",
"transaction_style",
"mechanism_type",
"span_origin",
)

def __init__(
self,
app,
unsafe_context_data=False,
transaction_style="endpoint",
mechanism_type="asgi",
span_origin="manual",
):
# type: (Any, bool, str, str) -> None
# type: (Any, bool, str, str, str) -> None
"""
Instrument an ASGI application with Sentry. Provides HTTP/websocket
data to sent events and basic handling for exceptions bubbling up
Expand Down Expand Up @@ -124,6 +131,7 @@ def __init__(

self.transaction_style = transaction_style
self.mechanism_type = mechanism_type
self.span_origin = span_origin
self.app = app

if _looks_like_asgi3(app):
Expand Down Expand Up @@ -182,6 +190,7 @@ async def _run_app(self, scope, receive, send, asgi_version):
op="{}.server".format(ty),
name=transaction_name,
source=transaction_source,
origin=self.span_origin,
)
logger.debug(
"[ASGI] Created transaction (continuing trace): %s",
Expand All @@ -192,6 +201,7 @@ async def _run_app(self, scope, receive, send, asgi_version):
op=OP.HTTP_SERVER,
name=transaction_name,
source=transaction_source,
origin=self.span_origin,
)
logger.debug(
"[ASGI] Created transaction (new): %s", transaction
Expand All @@ -205,7 +215,8 @@ async def _run_app(self, scope, receive, send, asgi_version):
)

with sentry_sdk.start_transaction(
transaction, custom_sampling_context={"asgi_scope": scope}
transaction,
custom_sampling_context={"asgi_scope": scope},
):
logger.debug("[ASGI] Started transaction: %s", transaction)
try:
Expand Down
5 changes: 4 additions & 1 deletion sentry_sdk/integrations/asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ async def _coro_creating_hub_and_span():

with sentry_sdk.isolation_scope():
with sentry_sdk.start_span(
op=OP.FUNCTION, description=get_name(coro)
op=OP.FUNCTION,
description=get_name(coro),
origin=AsyncioIntegration.origin,
):
try:
result = await coro
Expand Down Expand Up @@ -97,6 +99,7 @@ def _capture_exception():

class AsyncioIntegration(Integration):
identifier = "asyncio"
origin = f"auto.function.{identifier}"

@staticmethod
def setup_once():
Expand Down
25 changes: 19 additions & 6 deletions sentry_sdk/integrations/asyncpg.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

class AsyncPGIntegration(Integration):
identifier = "asyncpg"
origin = f"auto.db.{identifier}"
_record_params = False

def __init__(self, *, record_params: bool = False):
Expand Down Expand Up @@ -69,7 +70,14 @@ async def _inner(*args: Any, **kwargs: Any) -> T:
return await f(*args, **kwargs)

query = args[1]
with record_sql_queries(None, query, None, None, executemany=False) as span:
with record_sql_queries(
cursor=None,
query=query,
params_list=None,
paramstyle=None,
executemany=False,
span_origin=AsyncPGIntegration.origin,
) as span:
res = await f(*args, **kwargs)

with capture_internal_exceptions():
Expand Down Expand Up @@ -98,12 +106,13 @@ def _record(
param_style = "pyformat" if params_list else None

with record_sql_queries(
cursor,
query,
params_list,
param_style,
cursor=cursor,
query=query,
params_list=params_list,
paramstyle=param_style,
executemany=executemany,
record_cursor_repr=cursor is not None,
span_origin=AsyncPGIntegration.origin,
) as span:
yield span

Expand Down Expand Up @@ -154,7 +163,11 @@ async def _inner(*args: Any, **kwargs: Any) -> T:
user = kwargs["params"].user
database = kwargs["params"].database

with sentry_sdk.start_span(op=OP.DB, description="connect") as span:
with sentry_sdk.start_span(
op=OP.DB,
description="connect",
origin=AsyncPGIntegration.origin,
) as span:
span.set_data(SPANDATA.DB_SYSTEM, "postgresql")
addr = kwargs.get("addr")
if addr:
Expand Down
2 changes: 2 additions & 0 deletions sentry_sdk/integrations/aws_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def sentry_handler(aws_event, aws_context, *args, **kwargs):
op=OP.FUNCTION_AWS,
name=aws_context.function_name,
source=TRANSACTION_SOURCE_COMPONENT,
origin=AwsLambdaIntegration.origin,
)
with sentry_sdk.start_transaction(
transaction,
Expand Down Expand Up @@ -178,6 +179,7 @@ def _drain_queue():

class AwsLambdaIntegration(Integration):
identifier = "aws_lambda"
origin = f"auto.function.{identifier}"

def __init__(self, timeout_warning=False):
# type: (bool) -> None
Expand Down
3 changes: 3 additions & 0 deletions sentry_sdk/integrations/boto3.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

class Boto3Integration(Integration):
identifier = "boto3"
origin = f"auto.http.{identifier}"

@staticmethod
def setup_once():
Expand Down Expand Up @@ -69,6 +70,7 @@ def _sentry_request_created(service_id, request, operation_name, **kwargs):
span = sentry_sdk.start_span(
op=OP.HTTP_CLIENT,
description=description,
origin=Boto3Integration.origin,
)

with capture_internal_exceptions():
Expand Down Expand Up @@ -106,6 +108,7 @@ def _sentry_after_call(context, parsed, **kwargs):
streaming_span = span.start_child(
op=OP.HTTP_CLIENT_STREAM,
description=span.description,
origin=Boto3Integration.origin,
)

orig_read = body.read
Expand Down
8 changes: 6 additions & 2 deletions sentry_sdk/integrations/bottle.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

class BottleIntegration(Integration):
identifier = "bottle"
origin = f"auto.http.{identifier}"

transaction_style = ""

Expand Down Expand Up @@ -69,10 +70,13 @@ def setup_once():
@ensure_integration_enabled(BottleIntegration, old_app)
def sentry_patched_wsgi_app(self, environ, start_response):
# type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse
return SentryWsgiMiddleware(lambda *a, **kw: old_app(self, *a, **kw))(
environ, start_response
middleware = SentryWsgiMiddleware(
lambda *a, **kw: old_app(self, *a, **kw),
span_origin=BottleIntegration.origin,
)

return middleware(environ, start_response)

Bottle.__call__ = sentry_patched_wsgi_app

old_handle = Bottle._handle
Expand Down
18 changes: 15 additions & 3 deletions sentry_sdk/integrations/celery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@

class CeleryIntegration(Integration):
identifier = "celery"
origin = f"auto.queue.{identifier}"

def __init__(
self,
Expand Down Expand Up @@ -266,7 +267,11 @@ def apply_async(*args, **kwargs):
)

span_mgr = (
sentry_sdk.start_span(op=OP.QUEUE_SUBMIT_CELERY, description=task.name)
sentry_sdk.start_span(
op=OP.QUEUE_SUBMIT_CELERY,
description=task.name,
origin=CeleryIntegration.origin,
)
if not task_started_from_beat
else NoOpMgr()
) # type: Union[Span, NoOpMgr]
Expand Down Expand Up @@ -309,6 +314,7 @@ def _inner(*args, **kwargs):
op=OP.QUEUE_TASK_CELERY,
name="unknown celery task",
source=TRANSACTION_SOURCE_TASK,
origin=CeleryIntegration.origin,
)
transaction.name = task.name
transaction.set_status("ok")
Expand Down Expand Up @@ -362,7 +368,9 @@ def _inner(*args, **kwargs):
# type: (*Any, **Any) -> Any
try:
with sentry_sdk.start_span(
op=OP.QUEUE_PROCESS, description=task.name
op=OP.QUEUE_PROCESS,
description=task.name,
origin=CeleryIntegration.origin,
) as span:
_set_messaging_destination_name(task, span)

Expand Down Expand Up @@ -483,7 +491,11 @@ def sentry_publish(self, *args, **kwargs):
routing_key = kwargs.get("routing_key")
exchange = kwargs.get("exchange")

with sentry_sdk.start_span(op=OP.QUEUE_PUBLISH, description=task_name) as span:
with sentry_sdk.start_span(
op=OP.QUEUE_PUBLISH,
description=task_name,
origin=CeleryIntegration.origin,
) as span:
if task_id is not None:
span.set_data(SPANDATA.MESSAGING_MESSAGE_ID, task_id)

Expand Down
7 changes: 6 additions & 1 deletion sentry_sdk/integrations/clickhouse_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def __getitem__(self, _):

class ClickhouseDriverIntegration(Integration):
identifier = "clickhouse_driver"
origin = f"auto.db.{identifier}"

@staticmethod
def setup_once() -> None:
Expand Down Expand Up @@ -81,7 +82,11 @@ def _inner(*args: P.args, **kwargs: P.kwargs) -> T:
query_id = args[2] if len(args) > 2 else kwargs.get("query_id")
params = args[3] if len(args) > 3 else kwargs.get("params")

span = sentry_sdk.start_span(op=OP.DB, description=query)
span = sentry_sdk.start_span(
op=OP.DB,
description=query,
origin=ClickhouseDriverIntegration.origin,
)

connection._sentry_span = span # type: ignore[attr-defined]

Expand Down
3 changes: 3 additions & 0 deletions sentry_sdk/integrations/cohere.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@

class CohereIntegration(Integration):
identifier = "cohere"
origin = f"auto.ai.{identifier}"

def __init__(self, include_prompts=True):
# type: (CohereIntegration, bool) -> None
Expand Down Expand Up @@ -141,6 +142,7 @@ def new_chat(*args, **kwargs):
span = sentry_sdk.start_span(
op=consts.OP.COHERE_CHAT_COMPLETIONS_CREATE,
description="cohere.client.Chat",
origin=CohereIntegration.origin,
)
span.__enter__()
try:
Expand Down Expand Up @@ -225,6 +227,7 @@ def new_embed(*args, **kwargs):
with sentry_sdk.start_span(
op=consts.OP.COHERE_EMBEDDINGS_CREATE,
description="Cohere Embedding Creation",
origin=CohereIntegration.origin,
) as span:
integration = sentry_sdk.get_client().get_integration(CohereIntegration)
if "texts" in kwargs and (
Expand Down
Loading

0 comments on commit 87f6037

Please sign in to comment.