Skip to content
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

Turn custom_sampling_context into span attrs in Celery integration #3813

Merged
merged 2 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh
- clickhouse-driver integration: The query is now available under the `db.query.text` span attribute (only if `send_default_pii` is `True`).
- `sentry_sdk.init` now returns `None` instead of a context manager.
- The `sampling_context` argument of `traces_sampler` now additionally contains all span attributes known at span start.
- If you're using the Celery integration, the `sampling_context` argument of `traces_sampler` doesn't contain the `celery_job` dictionary anymore. Instead, the individual keys are now available as:

| Dictionary keys | Sampling context key |
| ---------------------- | -------------------- |
| `celery_job["args"]` | `celery.job.args` |
| `celery_job["kwargs"]` | `celery.job.kwargs` |
| `celery_job["task"]` | `celery.job.task` |

Note that all of these are serialized, i.e., not the original `args` and `kwargs` but rather OpenTelemetry-friendly span attributes.

- If you're using the AIOHTTP integration, the `sampling_context` argument of `traces_sampler` doesn't contain the `aiohttp_request` object anymore. Instead, some of the individual properties of the request are accessible, if available, as follows:

| Request property | Sampling context key(s) |
Expand Down Expand Up @@ -71,15 +81,15 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh
| `client` | `client.address`, `client.port` |
| full URL | `url.full` |

- If you're using the RQ integration, the `sampling_context` argument of `traces_sampler` doesn't contain the `rq_job` object anymore. Instead, the individual properties of the scope, if available, are accessible as follows:
- If you're using the RQ integration, the `sampling_context` argument of `traces_sampler` doesn't contain the `rq_job` object anymore. Instead, the individual properties of the job and the queue, if available, are accessible as follows:

| RQ property | Sampling context key(s) |
| --------------- | ---------------------------- |
| `rq_job.args` | `rq.job.args` |
| `rq_job.kwargs` | `rq.job.kwargs` |
| `rq_job.func` | `rq.job.func` |
| `queue.name` | `messaging.destination.name` |
| `job.id` | `messaging.message.id` |
| `rq_job.id` | `messaging.message.id` |

Note that `rq.job.args`, `rq.job.kwargs`, and `rq.job.func` are serialized and not the actual objects on the job.

Expand Down
22 changes: 13 additions & 9 deletions sentry_sdk/integrations/celery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ensure_integration_enabled,
event_from_exception,
reraise,
_serialize_span_attribute,
)

from typing import TYPE_CHECKING
Expand Down Expand Up @@ -318,15 +319,9 @@ def _inner(*args, **kwargs):
name=task.name,
source=TRANSACTION_SOURCE_TASK,
origin=CeleryIntegration.origin,
custom_sampling_context={
"celery_job": {
"task": task.name,
# for some reason, args[1] is a list if non-empty but a
# tuple if empty
"args": list(args[1]),
"kwargs": args[2],
}
},
# for some reason, args[1] is a list if non-empty but a
# tuple if empty
attributes=_prepopulate_attributes(task, list(args[1]), args[2]),
) as transaction:
transaction.set_status(SPANSTATUS.OK)
return f(*args, **kwargs)
Expand Down Expand Up @@ -516,3 +511,12 @@ def sentry_publish(self, *args, **kwargs):
return original_publish(self, *args, **kwargs)

Producer.publish = sentry_publish


def _prepopulate_attributes(task, args, kwargs):
attributes = {
"celery.job.task": task.name,
"celery.job.args": _serialize_span_attribute(args),
"celery.job.kwargs": _serialize_span_attribute(kwargs),
}
return attributes
14 changes: 9 additions & 5 deletions tests/integrations/celery/test_celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
_wrap_task_run,
)
from sentry_sdk.integrations.celery.beat import _get_headers
from sentry_sdk.utils import _serialize_span_attribute
from tests.conftest import ApproxDict


Expand Down Expand Up @@ -430,7 +431,7 @@ def dummy_task(self, x, y):


def test_traces_sampler_gets_task_info_in_sampling_context(
init_celery, celery_invocation, DictionaryContaining # noqa:N803
init_celery, celery_invocation
):
traces_sampler = mock.Mock()
celery = init_celery(traces_sampler=traces_sampler)
Expand All @@ -445,10 +446,13 @@ def walk_dogs(x, y):
walk_dogs, [["Maisey", "Charlie", "Bodhi", "Cory"], "Dog park round trip"], 1
)

traces_sampler.assert_any_call(
# depending on the iteration of celery_invocation, the data might be
# passed as args or as kwargs, so make this generic
DictionaryContaining({"celery_job": dict(task="dog_walk", **args_kwargs)})
sampling_context = traces_sampler.call_args_list[1][0][0]
assert sampling_context["celery.job.task"] == "dog_walk"
assert sampling_context["celery.job.args"] == _serialize_span_attribute(
args_kwargs["args"]
)
assert sampling_context["celery.job.kwargs"] == _serialize_span_attribute(
args_kwargs["kwargs"]
)


Expand Down
Loading