Skip to content

Commit a9b4d47

Browse files
authored
Turn custom_sampling_context into span attrs in Celery integration (#3813)
1 parent 7cfe29a commit a9b4d47

File tree

3 files changed

+34
-16
lines changed

3 files changed

+34
-16
lines changed

MIGRATION_GUIDE.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ Looking to upgrade from Sentry SDK 2.x to 3.x? Here's a comprehensive list of wh
2121
- clickhouse-driver integration: The query is now available under the `db.query.text` span attribute (only if `send_default_pii` is `True`).
2222
- `sentry_sdk.init` now returns `None` instead of a context manager.
2323
- The `sampling_context` argument of `traces_sampler` now additionally contains all span attributes known at span start.
24+
- 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:
25+
26+
| Dictionary keys | Sampling context key |
27+
| ---------------------- | -------------------- |
28+
| `celery_job["args"]` | `celery.job.args` |
29+
| `celery_job["kwargs"]` | `celery.job.kwargs` |
30+
| `celery_job["task"]` | `celery.job.task` |
31+
32+
Note that all of these are serialized, i.e., not the original `args` and `kwargs` but rather OpenTelemetry-friendly span attributes.
33+
2434
- 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:
2535

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

74-
- 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:
84+
- 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:
7585

7686
| RQ property | Sampling context key(s) |
7787
| --------------- | ---------------------------- |
7888
| `rq_job.args` | `rq.job.args` |
7989
| `rq_job.kwargs` | `rq.job.kwargs` |
8090
| `rq_job.func` | `rq.job.func` |
8191
| `queue.name` | `messaging.destination.name` |
82-
| `job.id` | `messaging.message.id` |
92+
| `rq_job.id` | `messaging.message.id` |
8393

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

sentry_sdk/integrations/celery/__init__.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
ensure_integration_enabled,
2121
event_from_exception,
2222
reraise,
23+
_serialize_span_attribute,
2324
)
2425

2526
from typing import TYPE_CHECKING
@@ -318,15 +319,9 @@ def _inner(*args, **kwargs):
318319
name=task.name,
319320
source=TRANSACTION_SOURCE_TASK,
320321
origin=CeleryIntegration.origin,
321-
custom_sampling_context={
322-
"celery_job": {
323-
"task": task.name,
324-
# for some reason, args[1] is a list if non-empty but a
325-
# tuple if empty
326-
"args": list(args[1]),
327-
"kwargs": args[2],
328-
}
329-
},
322+
# for some reason, args[1] is a list if non-empty but a
323+
# tuple if empty
324+
attributes=_prepopulate_attributes(task, list(args[1]), args[2]),
330325
) as transaction:
331326
transaction.set_status(SPANSTATUS.OK)
332327
return f(*args, **kwargs)
@@ -516,3 +511,12 @@ def sentry_publish(self, *args, **kwargs):
516511
return original_publish(self, *args, **kwargs)
517512

518513
Producer.publish = sentry_publish
514+
515+
516+
def _prepopulate_attributes(task, args, kwargs):
517+
attributes = {
518+
"celery.job.task": task.name,
519+
"celery.job.args": _serialize_span_attribute(args),
520+
"celery.job.kwargs": _serialize_span_attribute(kwargs),
521+
}
522+
return attributes

tests/integrations/celery/test_celery.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
_wrap_task_run,
1414
)
1515
from sentry_sdk.integrations.celery.beat import _get_headers
16+
from sentry_sdk.utils import _serialize_span_attribute
1617
from tests.conftest import ApproxDict
1718

1819

@@ -430,7 +431,7 @@ def dummy_task(self, x, y):
430431

431432

432433
def test_traces_sampler_gets_task_info_in_sampling_context(
433-
init_celery, celery_invocation, DictionaryContaining # noqa:N803
434+
init_celery, celery_invocation
434435
):
435436
traces_sampler = mock.Mock()
436437
celery = init_celery(traces_sampler=traces_sampler)
@@ -445,10 +446,13 @@ def walk_dogs(x, y):
445446
walk_dogs, [["Maisey", "Charlie", "Bodhi", "Cory"], "Dog park round trip"], 1
446447
)
447448

448-
traces_sampler.assert_any_call(
449-
# depending on the iteration of celery_invocation, the data might be
450-
# passed as args or as kwargs, so make this generic
451-
DictionaryContaining({"celery_job": dict(task="dog_walk", **args_kwargs)})
449+
sampling_context = traces_sampler.call_args_list[1][0][0]
450+
assert sampling_context["celery.job.task"] == "dog_walk"
451+
assert sampling_context["celery.job.args"] == _serialize_span_attribute(
452+
args_kwargs["args"]
453+
)
454+
assert sampling_context["celery.job.kwargs"] == _serialize_span_attribute(
455+
args_kwargs["kwargs"]
452456
)
453457

454458

0 commit comments

Comments
 (0)