django-lambda-tasks 0.11.0__tar.gz → 0.11.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.github/workflows/ci.yml +32 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/steering/product.md +16 -8
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/steering/structure.md +2 -2
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/steering/tech.md +1 -1
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/PKG-INFO +37 -12
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/README.md +36 -11
- django_lambda_tasks-0.11.1/lambda_tasks/sentry.py +186 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/pyproject.toml +2 -2
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_sentry.py +133 -15
- django_lambda_tasks-0.11.0/lambda_tasks/sentry.py +0 -143
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.github/dependabot.yml +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.github/workflows/release.yml +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.gitignore +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/async-local-execution/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/async-local-execution/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/async-local-execution/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/async-local-execution/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/deferred-task-enqueue/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/deferred-task-enqueue/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/deferred-task-enqueue/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/deferred-task-enqueue/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/eager-mode-example-app/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/eager-mode-example-app/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/eager-mode-example-app/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/eager-mode-example-app/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/ignore-errors-decorator-option/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/ignore-errors-decorator-option/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/ignore-errors-decorator-option/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/ignore-errors-decorator-option/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/import-string-task-resolution/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/import-string-task-resolution/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/import-string-task-resolution/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/import-string-task-resolution/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/retry-delay/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/retry-delay/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/retry-delay/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/retry-delay/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/rse-background-tasks/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/rse-background-tasks/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/rse-background-tasks/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/rse-background-tasks/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/rse-background-tasks-bugfix/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/rse-background-tasks-bugfix/bugfix.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/rse-background-tasks-bugfix/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/rse-background-tasks-bugfix/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/singleton-task/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/singleton-task/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/singleton-task/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/singleton-task/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/ssm-environment-loader/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/ssm-environment-loader/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/ssm-environment-loader/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/ssm-environment-loader/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/task-retry/.config.kiro +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/task-retry/design.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/task-retry/requirements.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.kiro/specs/task-retry/tasks.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.pre-commit-config.yaml +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/.vscode/settings.json +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/docker-compose.yml +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/README.md +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/docker-compose.yml +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_app/__init__.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_app/apps.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_app/tasks.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_app/urls.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_app/views.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_project/__init__.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_project/sentry.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_project/settings.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_project/urls.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/example_project/wsgi.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/example/manage.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/__init__.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/admin.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/apps.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/checks.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/decorators.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/environment_loader.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/handler.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/local_executor.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/logging.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/migrations/0001_initial.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/migrations/0002_alter_taskrecord_status.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/migrations/0003_taskrecord_taskrecord_kwargs_trgm.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/migrations/0004_remove_taskrecord_taskrecord_kwargs_trgm_and_more.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/migrations/__init__.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/models.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/secret_loader.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/settings.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/tasks.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/lambda_tasks/timeouts.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/conftest.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/settings.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_admin.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_batch.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_batch_worker.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_checks.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_decorator.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_decorators.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_deferred_enqueue.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_environment_loader.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_handler.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_kwargs_only.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_local_executor.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_logging.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_memory_limit.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_models.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_noop_execution.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_secret_loader.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_serializer.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_settings.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_tasks.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_timeout_validation.py +0 -0
- {django_lambda_tasks-0.11.0 → django_lambda_tasks-0.11.1}/tests/test_timeouts.py +0 -0
|
@@ -53,3 +53,35 @@ jobs:
|
|
|
53
53
|
- name: Run tests
|
|
54
54
|
run: |
|
|
55
55
|
uv run pytest --cov-branch --cov-report term-missing --cov=tests/ --cov=lambda_tasks/
|
|
56
|
+
|
|
57
|
+
sentry-prerelease:
|
|
58
|
+
# Exercise the Sentry integration against the latest 3.x pre-release of
|
|
59
|
+
# sentry-sdk (stream mode). The committed dev dependency is pinned to the
|
|
60
|
+
# stable 2.x line; this job overrides it at runtime via `uv run --with`
|
|
61
|
+
# without touching pyproject.toml, so regressions in the 3.x API are caught
|
|
62
|
+
# before the SDK goes stable.
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
services:
|
|
65
|
+
postgres:
|
|
66
|
+
image: postgres:17
|
|
67
|
+
env:
|
|
68
|
+
POSTGRES_DB: lambda_tasks_test
|
|
69
|
+
POSTGRES_USER: postgres
|
|
70
|
+
POSTGRES_PASSWORD: postgres
|
|
71
|
+
ports:
|
|
72
|
+
- 5432:5432
|
|
73
|
+
options: >-
|
|
74
|
+
--health-cmd "pg_isready -U postgres"
|
|
75
|
+
--health-interval 10s
|
|
76
|
+
--health-timeout 5s
|
|
77
|
+
--health-retries 5
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
80
|
+
- name: Install uv and set the python version
|
|
81
|
+
uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
82
|
+
with:
|
|
83
|
+
python-version: '3.14'
|
|
84
|
+
- name: Run tests against the latest sentry-sdk 3.x pre-release
|
|
85
|
+
run: |
|
|
86
|
+
uv run --prerelease=allow --with "sentry-sdk>=3.0.0b1" \
|
|
87
|
+
pytest --cov-branch --cov-report term-missing --cov=tests/ --cov=lambda_tasks/
|
|
@@ -315,21 +315,29 @@ def my_task(*, user_id: int) -> None:
|
|
|
315
315
|
|
|
316
316
|
## Sentry
|
|
317
317
|
|
|
318
|
-
The library does not call `sentry_sdk.init(...)` — the host application owns Sentry setup (typically `DjangoIntegration` + `AwsLambdaIntegration`). The problem those integrations leave is attribution: on Lambda, `AwsLambdaIntegration` names every event's transaction after the Lambda function, so all tasks
|
|
318
|
+
The library does not call `sentry_sdk.init(...)` — the host application owns Sentry setup (typically `DjangoIntegration` + `AwsLambdaIntegration`). The problem those integrations leave is attribution: on Lambda, `AwsLambdaIntegration` names every event's transaction after the Lambda function, so all tasks look alike; on AWS Batch there is no request and no Lambda integration, so the transaction/URL are `"--"`. Either way you cannot tell which task ran or raised.
|
|
319
319
|
|
|
320
|
-
`lambda_tasks.sentry.task_scope(*, task_name, message_id, n_retries)` fixes this. `execute_immediately` wraps the task body in it, which — when a Sentry client is active — opens an isolated `sentry_sdk.new_scope()`
|
|
320
|
+
`lambda_tasks.sentry.task_scope(*, task_name, message_id, n_retries)` fixes this. `execute_immediately` wraps the task body in it, which — when a Sentry client is active — opens an isolated `sentry_sdk.new_scope()` and traces the task. Trace shape depends on whether a trace is already active:
|
|
321
321
|
|
|
322
|
-
- transaction
|
|
323
|
-
-
|
|
322
|
+
- **Lambda** — `AwsLambdaIntegration` already started an invocation transaction, so the task is traced as a **child span** of it (not a second transaction). This is deliberate: a nested *transaction* per task inherited (rather than independently re-rolled) the invocation's sampling decision, doubling transaction volume and defeating `traces_sample_rate`. A child span is sent only if its parent is sampled, so sampling is respected. `_start_task_root` picks child-vs-root by checking `sentry_sdk.get_current_scope().transaction` (2.x only; on 3.0 `start_span` is automatically root-or-child).
|
|
323
|
+
- **Batch** (nothing active) — the task starts its **own root transaction** so it is still traced.
|
|
324
|
+
|
|
325
|
+
It records, both on the span and as scope tags:
|
|
326
|
+
|
|
327
|
+
- `lambda_task` = fully-qualified task name (e.g. `myapp.tasks.my_task`) — also the task span's name
|
|
324
328
|
- `lambda_task.message_id` = the `TaskRecord` primary key
|
|
325
329
|
- `lambda_task.n_retries` = the retry counter
|
|
326
|
-
- `lambda_task.runtime` = the
|
|
330
|
+
- `lambda_task.runtime` = the execution environment, from the AWS environment: `AWS_LAMBDA_FUNCTION_NAME` on Lambda or `AWS_BATCH_JQ_NAME` (Batch job queue name); omitted when neither is present
|
|
331
|
+
|
|
332
|
+
`task_scope` deliberately does **not** call `scope.set_transaction_name(task_name)`: on Lambda that would rename the shared invocation transaction (fighting across sibling records in a batch) and, on SDK 3.0 stream mode, corrupt the root-segment naming. Error attribution relies on the `lambda_task` **tag** instead, which is reliable on both SDKs and independent of sampling.
|
|
333
|
+
|
|
334
|
+
**Reporting task failures:** the executor catches task exceptions (recording `FAILED` and not re-raising), so `lambda_tasks.sentry.capture_task_exception()` is called on the two **terminal** failure branches in `models.py` (plain failure and max-retries-exceeded) to report the error via `sentry_sdk.capture_exception()`. This is essential on 3.0, where a merely-caught exception produces no error event. The **retry** branch deliberately does not capture — transient, to-be-re-enqueued failures would flood Sentry; only terminal failures are reported.
|
|
327
335
|
|
|
328
|
-
|
|
336
|
+
**Error-level log forwarding:** on sentry-sdk 2.x, `LoggingIntegration` defaulted to `event_level=ERROR`, so `task_logger.error(...)` became Sentry events automatically. On 3.0 the default is `None`, so hosts that want error logs reported must configure `LoggingIntegration(event_level=logging.ERROR)` explicitly (the example app does this). This is a host-init concern, not something the library sets.
|
|
329
337
|
|
|
330
|
-
**SDK version compatibility:** `task_scope` is future-proof across the sentry-sdk 2.x→3.0 boundary. On 2.x it uses `start_transaction` + `set_tag`
|
|
338
|
+
**SDK version compatibility:** `task_scope` is future-proof across the sentry-sdk 2.x→3.0 boundary and verified against both 2.74 and 3.0.0b4. On 2.x it uses `start_transaction` + `set_tag` for a root and `start_span` for a child; on 3.0 (stream mode) `start_span` is both root and child and metadata is attached via `set_attribute`. The correct API is detected at runtime via `hasattr`, so no code change is needed when the host upgrades the SDK.
|
|
331
339
|
|
|
332
|
-
`task_scope`
|
|
340
|
+
`task_scope` and `capture_task_exception` are no-ops when `sentry_sdk` is not installed or no client is initialised, so Sentry is not a dependency of the library. `sentry-sdk` is a dev dependency only (pinned `>=2,<3`), for the test suite. No custom Sentry integration is needed — per-task tracing at the one execution entry point covers both Lambda and Batch.
|
|
333
341
|
|
|
334
342
|
## Built-in Tasks
|
|
335
343
|
|
|
@@ -16,7 +16,7 @@ django-lambda-tasks/
|
|
|
16
16
|
│ ├── decorators.py # LambdaTaskWrapper, @lambda_task
|
|
17
17
|
│ ├── handler.py # AWS Lambda entry point + AWS Batch container entry point (main)
|
|
18
18
|
│ ├── logging.py # task_logger — invocation-scoped LoggerAdapter
|
|
19
|
-
│ ├── sentry.py # task_scope — per-task
|
|
19
|
+
│ ├── sentry.py # task_scope + capture_task_exception — per-task trace, tags, failure reporting
|
|
20
20
|
│ ├── models.py # TaskRecord, SQSLambdaTaskMessage, SQSLambdaTask
|
|
21
21
|
│ ├── settings.py # LambdaTasksSettings, constants, queue type helpers
|
|
22
22
|
│ ├── secret_loader.py # Resolves LAMBDA_TASKS_SECRET_* env vars at cold start
|
|
@@ -48,7 +48,7 @@ django-lambda-tasks/
|
|
|
48
48
|
- `environment_loader.py` — loads env vars from a Secrets Manager secret at cold start; validates flat JSON format; idempotent via `_loaded` sentinel
|
|
49
49
|
- `secret_loader.py` — resolves `LAMBDA_TASKS_SECRET_*` env vars from Secrets Manager before Django starts; validates format, detects conflicts, batches API calls; idempotent via `_loaded` sentinel
|
|
50
50
|
- `logging.py` — `task_logger` singleton; `message_id` set/cleared around each task execution
|
|
51
|
-
- `sentry.py` — `task_scope` context manager
|
|
51
|
+
- `sentry.py` — `task_scope` context manager + `capture_task_exception` helper. `task_scope` opens an isolated `new_scope` and traces the task: a **child span** of the invocation transaction on Lambda (where `AwsLambdaIntegration` already started one), or its **own root transaction** on Batch (nothing active). The child-span-on-Lambda behaviour avoids a second per-task transaction that would double trace volume and defeat `traces_sample_rate`. Records `lambda_task` / `lambda_task.message_id` / `lambda_task.n_retries` / `lambda_task.runtime` as span metadata and scope tags (runtime read from `AWS_LAMBDA_FUNCTION_NAME` or `AWS_BATCH_JQ_NAME`). Deliberately does not call `set_transaction_name` (would rename the shared invocation transaction and break 3.0 segment naming); error attribution rides on the `lambda_task` tag. `capture_task_exception` calls `sentry_sdk.capture_exception()` and is invoked on the terminal failure branches in `models.py` (plain failure + max-retries), since the executor catches exceptions without re-raising and 3.0 does not auto-report caught exceptions; the retry branch does not capture. Future-proof across the SDK 2.x→3.0 boundary (verified against 2.74 and 3.0.0b4): `start_transaction`/`set_tag`/child `start_span` on 2.x, `start_span`/`set_attribute` on 3.0, detected at runtime via `hasattr`. No-op when `sentry_sdk` is absent or uninitialised (`sentry-sdk` is a dev dependency only, pinned `>=2,<3`). Wrapped around the task body in `SQSLambdaTaskMessage.execute_immediately`. Note: `logger.error → Sentry event` needs `LoggingIntegration(event_level=logging.ERROR)` in the host init on 3.0 (default on 2.x); a host concern, demonstrated in the example app
|
|
52
52
|
- `settings.py` — constants (`MAX_DELAY`, `MAX_TIMEOUT`, `MAX_BATCH_TIMEOUT`); `_is_sqs_queue()` and `_is_batch_queue()` helpers; `LambdaTasksSettings` instantiated fresh per use (reads live Django settings); `queue_max_timeout()` method returns 900 or 3600 based on queue type
|
|
53
53
|
- `admin.py` — Django admin registration for `TaskRecord`
|
|
54
54
|
- `apps.py` — Django `AppConfig`; `ready()` imports `checks` to register deployment checks and installs the local-executor shutdown signal handlers when `LOCAL_WORKERS > 0`
|
|
@@ -23,7 +23,7 @@ inclusion: always
|
|
|
23
23
|
|---|---|---|
|
|
24
24
|
| `pytest` | `>= 9.0.2` | Test runner |
|
|
25
25
|
| `hypothesis` | present | Property-based testing (`.hypothesis/` directory exists) |
|
|
26
|
-
| `sentry-sdk` |
|
|
26
|
+
| `sentry-sdk` | `>=2,<3` | Testing the Sentry integration (`task_scope`, `capture_task_exception`); verified against 2.74 and 3.0.0b4. Not a runtime dependency |
|
|
27
27
|
|
|
28
28
|
## Package Manager
|
|
29
29
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: django-lambda-tasks
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.1
|
|
4
4
|
Summary: Run async tasks in a lambda function
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: awslambdaric
|
|
@@ -446,29 +446,54 @@ fields @timestamp, @message
|
|
|
446
446
|
|
|
447
447
|
### Sentry
|
|
448
448
|
|
|
449
|
-
If your application uses Sentry,
|
|
449
|
+
If your application uses Sentry, task errors are automatically attributed to the task that raised them. Without this, Sentry's `AwsLambdaIntegration` names every event's transaction after the Lambda function (so all tasks look the same), and on AWS Batch there is no transaction at all (it shows as `"--"`) — so you cannot tell which task failed.
|
|
450
450
|
|
|
451
|
-
|
|
451
|
+
Keep your existing `sentry_sdk.init(...)`. On **sentry-sdk 2.x** no change is needed. On **sentry-sdk 3.0+** one option changed default and must be set explicitly if you want error-level logs reported (see below):
|
|
452
452
|
|
|
453
453
|
```python
|
|
454
|
+
import logging
|
|
455
|
+
from sentry_sdk.integrations.logging import LoggingIntegration
|
|
456
|
+
|
|
454
457
|
sentry_sdk.init(
|
|
455
458
|
dsn=SENTRY_DSN,
|
|
456
|
-
integrations=[
|
|
457
|
-
|
|
459
|
+
integrations=[
|
|
460
|
+
DjangoIntegration(),
|
|
461
|
+
AwsLambdaIntegration(),
|
|
462
|
+
# Only needed on sentry-sdk 3.0+: its default event_level is None, so
|
|
463
|
+
# logger.error(...) no longer becomes a Sentry event. On 2.x this was
|
|
464
|
+
# the default and setting it is harmless.
|
|
465
|
+
LoggingIntegration(event_level=logging.ERROR),
|
|
466
|
+
],
|
|
467
|
+
traces_sample_rate=0.1,
|
|
458
468
|
)
|
|
459
469
|
```
|
|
460
470
|
|
|
461
|
-
|
|
471
|
+
#### What the library does per task
|
|
472
|
+
|
|
473
|
+
When a task runs, `execute_immediately()` opens an isolated Sentry scope and traces the task:
|
|
474
|
+
|
|
475
|
+
- **On Lambda**, the task is traced as a **child span** of the invocation transaction that `AwsLambdaIntegration` already started — not a second transaction. This keeps task tracing from doubling your transaction volume and respects `traces_sample_rate`.
|
|
476
|
+
- **On Batch** (no Lambda integration, nothing active), the task starts **its own root transaction** so it is still traced.
|
|
477
|
+
|
|
478
|
+
Either way it records, as both span metadata and scope tags:
|
|
479
|
+
|
|
480
|
+
- `lambda_task` — the fully-qualified task name (e.g. `myapp.tasks.send_welcome_email`). This is also the task span's name.
|
|
481
|
+
- `lambda_task.message_id`, `lambda_task.n_retries`
|
|
482
|
+
- `lambda_task.runtime` — the execution environment, from the AWS environment: `AWS_LAMBDA_FUNCTION_NAME` on Lambda or `AWS_BATCH_JQ_NAME` (the Batch job queue name) on Batch. Omitted when neither is set (local/eager).
|
|
483
|
+
|
|
484
|
+
Error attribution relies on the `lambda_task` **tag**, not on the transaction name, so it is reliable even when the trace is not sampled and regardless of whether the task ran as a child span (Lambda) or a root (Batch).
|
|
485
|
+
|
|
486
|
+
#### Task failures are reported
|
|
487
|
+
|
|
488
|
+
The executor catches a failing task's exception (recording a `FAILED` `TaskRecord`) rather than re-raising it. So the library calls `sentry_sdk.capture_exception()` itself on the **terminal failure paths** — a plain failure and max-retries-exceeded — to report the error to Sentry. This is essential on 3.0, where a merely-caught exception produces no error event at all.
|
|
462
489
|
|
|
463
|
-
|
|
464
|
-
- `lambda_task` (task name), `lambda_task.message_id`, and `lambda_task.n_retries`
|
|
465
|
-
- `lambda_task.runtime` — the physical execution environment, derived from the AWS environment: the Lambda function name (`AWS_LAMBDA_FUNCTION_NAME`) on Lambda, or the Batch job queue name (`AWS_BATCH_JQ_NAME`) on Batch. Omitted when neither is set (e.g. local/eager execution).
|
|
490
|
+
Transient, retryable failures (a task that will be re-enqueued) are **not** reported, to avoid flooding Sentry on every retry; only the final, terminal failure is captured.
|
|
466
491
|
|
|
467
|
-
|
|
492
|
+
#### SDK version compatibility
|
|
468
493
|
|
|
469
|
-
The integration is future-proof across the sentry-sdk 2.x→3.0 boundary
|
|
494
|
+
The integration is future-proof across the sentry-sdk 2.x→3.0 boundary and verified against both 2.74 and 3.0.0b4. It uses `start_transaction`/`set_tag` on 2.x and `start_span`/`set_attribute` on 3.0 (stream mode), chosen automatically at runtime — no change needed when you upgrade.
|
|
470
495
|
|
|
471
|
-
`sentry_sdk` is not a dependency of this library — if it is not installed or not initialised, the
|
|
496
|
+
`sentry_sdk` is not a dependency of this library — if it is not installed or not initialised, all of the above is a silent no-op.
|
|
472
497
|
|
|
473
498
|
---
|
|
474
499
|
|
|
@@ -434,29 +434,54 @@ fields @timestamp, @message
|
|
|
434
434
|
|
|
435
435
|
### Sentry
|
|
436
436
|
|
|
437
|
-
If your application uses Sentry,
|
|
437
|
+
If your application uses Sentry, task errors are automatically attributed to the task that raised them. Without this, Sentry's `AwsLambdaIntegration` names every event's transaction after the Lambda function (so all tasks look the same), and on AWS Batch there is no transaction at all (it shows as `"--"`) — so you cannot tell which task failed.
|
|
438
438
|
|
|
439
|
-
|
|
439
|
+
Keep your existing `sentry_sdk.init(...)`. On **sentry-sdk 2.x** no change is needed. On **sentry-sdk 3.0+** one option changed default and must be set explicitly if you want error-level logs reported (see below):
|
|
440
440
|
|
|
441
441
|
```python
|
|
442
|
+
import logging
|
|
443
|
+
from sentry_sdk.integrations.logging import LoggingIntegration
|
|
444
|
+
|
|
442
445
|
sentry_sdk.init(
|
|
443
446
|
dsn=SENTRY_DSN,
|
|
444
|
-
integrations=[
|
|
445
|
-
|
|
447
|
+
integrations=[
|
|
448
|
+
DjangoIntegration(),
|
|
449
|
+
AwsLambdaIntegration(),
|
|
450
|
+
# Only needed on sentry-sdk 3.0+: its default event_level is None, so
|
|
451
|
+
# logger.error(...) no longer becomes a Sentry event. On 2.x this was
|
|
452
|
+
# the default and setting it is harmless.
|
|
453
|
+
LoggingIntegration(event_level=logging.ERROR),
|
|
454
|
+
],
|
|
455
|
+
traces_sample_rate=0.1,
|
|
446
456
|
)
|
|
447
457
|
```
|
|
448
458
|
|
|
449
|
-
|
|
459
|
+
#### What the library does per task
|
|
460
|
+
|
|
461
|
+
When a task runs, `execute_immediately()` opens an isolated Sentry scope and traces the task:
|
|
462
|
+
|
|
463
|
+
- **On Lambda**, the task is traced as a **child span** of the invocation transaction that `AwsLambdaIntegration` already started — not a second transaction. This keeps task tracing from doubling your transaction volume and respects `traces_sample_rate`.
|
|
464
|
+
- **On Batch** (no Lambda integration, nothing active), the task starts **its own root transaction** so it is still traced.
|
|
465
|
+
|
|
466
|
+
Either way it records, as both span metadata and scope tags:
|
|
467
|
+
|
|
468
|
+
- `lambda_task` — the fully-qualified task name (e.g. `myapp.tasks.send_welcome_email`). This is also the task span's name.
|
|
469
|
+
- `lambda_task.message_id`, `lambda_task.n_retries`
|
|
470
|
+
- `lambda_task.runtime` — the execution environment, from the AWS environment: `AWS_LAMBDA_FUNCTION_NAME` on Lambda or `AWS_BATCH_JQ_NAME` (the Batch job queue name) on Batch. Omitted when neither is set (local/eager).
|
|
471
|
+
|
|
472
|
+
Error attribution relies on the `lambda_task` **tag**, not on the transaction name, so it is reliable even when the trace is not sampled and regardless of whether the task ran as a child span (Lambda) or a root (Batch).
|
|
473
|
+
|
|
474
|
+
#### Task failures are reported
|
|
475
|
+
|
|
476
|
+
The executor catches a failing task's exception (recording a `FAILED` `TaskRecord`) rather than re-raising it. So the library calls `sentry_sdk.capture_exception()` itself on the **terminal failure paths** — a plain failure and max-retries-exceeded — to report the error to Sentry. This is essential on 3.0, where a merely-caught exception produces no error event at all.
|
|
450
477
|
|
|
451
|
-
|
|
452
|
-
- `lambda_task` (task name), `lambda_task.message_id`, and `lambda_task.n_retries`
|
|
453
|
-
- `lambda_task.runtime` — the physical execution environment, derived from the AWS environment: the Lambda function name (`AWS_LAMBDA_FUNCTION_NAME`) on Lambda, or the Batch job queue name (`AWS_BATCH_JQ_NAME`) on Batch. Omitted when neither is set (e.g. local/eager execution).
|
|
478
|
+
Transient, retryable failures (a task that will be re-enqueued) are **not** reported, to avoid flooding Sentry on every retry; only the final, terminal failure is captured.
|
|
454
479
|
|
|
455
|
-
|
|
480
|
+
#### SDK version compatibility
|
|
456
481
|
|
|
457
|
-
The integration is future-proof across the sentry-sdk 2.x→3.0 boundary
|
|
482
|
+
The integration is future-proof across the sentry-sdk 2.x→3.0 boundary and verified against both 2.74 and 3.0.0b4. It uses `start_transaction`/`set_tag` on 2.x and `start_span`/`set_attribute` on 3.0 (stream mode), chosen automatically at runtime — no change needed when you upgrade.
|
|
458
483
|
|
|
459
|
-
`sentry_sdk` is not a dependency of this library — if it is not installed or not initialised, the
|
|
484
|
+
`sentry_sdk` is not a dependency of this library — if it is not installed or not initialised, all of the above is a silent no-op.
|
|
460
485
|
|
|
461
486
|
---
|
|
462
487
|
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Sentry tracing/scope helpers for task execution.
|
|
2
|
+
|
|
3
|
+
This library does not initialise Sentry itself — the host application owns
|
|
4
|
+
``sentry_sdk.init(...)``. These helpers trace each task and attribute errors to
|
|
5
|
+
the running task rather than to the Lambda function name (set by
|
|
6
|
+
``AwsLambdaIntegration``) or to ``"--"`` (on AWS Batch, where no request or
|
|
7
|
+
Lambda integration is active).
|
|
8
|
+
|
|
9
|
+
Trace shape depends on whether a trace is already active:
|
|
10
|
+
|
|
11
|
+
* **Lambda** — ``AwsLambdaIntegration`` starts a transaction per invocation, so
|
|
12
|
+
the task is traced as a **child span** of it. One transaction per invocation,
|
|
13
|
+
with the task span nested inside — not a second, competing transaction.
|
|
14
|
+
* **Batch** (and anywhere nothing is active) — the task starts its **own root
|
|
15
|
+
transaction**, so it is still traced on its own.
|
|
16
|
+
|
|
17
|
+
Why the child span matters: starting a nested *transaction* per task produced a
|
|
18
|
+
second transaction that inherited (rather than independently re-rolled) the
|
|
19
|
+
invocation's sampling decision, roughly doubling transaction volume and
|
|
20
|
+
defeating ``traces_sample_rate``. A child span is sent only if its parent
|
|
21
|
+
transaction is sampled, so sampling is respected.
|
|
22
|
+
|
|
23
|
+
Error attribution does not depend on the trace being sampled: the ``lambda_task``
|
|
24
|
+
scope tag carries the task name on every error event regardless.
|
|
25
|
+
|
|
26
|
+
Future-proofing across the SDK 2.x → 3.0 boundary
|
|
27
|
+
--------------------------------------------------
|
|
28
|
+
The tracing API changed in SDK 3.0 (stream mode):
|
|
29
|
+
|
|
30
|
+
* 2.x — a root is a *transaction* (``start_transaction``); a child uses
|
|
31
|
+
``start_span``. Metadata is attached with ``set_tag``; ``Span.set_attribute``
|
|
32
|
+
does not exist, and ``Scope`` has a ``transaction`` attribute.
|
|
33
|
+
* 3.0 — transaction mode is gone. ``start_span`` is both root (when nothing is
|
|
34
|
+
active) and child (when something is), metadata is attached via
|
|
35
|
+
``set_attribute``, and ``Scope`` has no ``transaction`` attribute.
|
|
36
|
+
|
|
37
|
+
We detect which API is present at runtime (via ``hasattr``) and use the correct
|
|
38
|
+
one, so this module keeps working unchanged after an SDK upgrade. Metadata is
|
|
39
|
+
attached with whichever setter the running span exposes (``set_attribute`` when
|
|
40
|
+
available, otherwise ``set_tag``).
|
|
41
|
+
|
|
42
|
+
Everything here degrades to a no-op when ``sentry_sdk`` is not installed or no
|
|
43
|
+
client is active, so the library has no hard dependency on Sentry.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
import contextlib
|
|
47
|
+
import os
|
|
48
|
+
from collections.abc import Iterator
|
|
49
|
+
from typing import Any
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
import sentry_sdk
|
|
53
|
+
except ImportError:
|
|
54
|
+
sentry_sdk = None # ty: ignore[invalid-assignment]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _runtime_environment() -> str | None:
|
|
58
|
+
"""Return the environment the task is executing in, or ``None``.
|
|
59
|
+
|
|
60
|
+
``AWS_LAMBDA_FUNCTION_NAME`` on Lambda, ``AWS_BATCH_JQ_NAME`` (the Batch job
|
|
61
|
+
queue the job was submitted to) on Batch. ``None`` when neither is present
|
|
62
|
+
(e.g. local or eager execution).
|
|
63
|
+
"""
|
|
64
|
+
return os.environ.get("AWS_LAMBDA_FUNCTION_NAME") or os.environ.get(
|
|
65
|
+
"AWS_BATCH_JQ_NAME"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _set_metadata(span: Any, *, key: str, value: Any) -> None:
|
|
70
|
+
"""Attach *key*/*value* to *span* using whichever setter the SDK exposes.
|
|
71
|
+
|
|
72
|
+
SDK 3.0 (stream mode) spans use ``set_attribute``; SDK 2.x transactions use
|
|
73
|
+
``set_tag``. Preferring ``set_attribute`` means the richer, future API is
|
|
74
|
+
used whenever it is available.
|
|
75
|
+
"""
|
|
76
|
+
if hasattr(span, "set_attribute"):
|
|
77
|
+
span.set_attribute(key, value)
|
|
78
|
+
else:
|
|
79
|
+
span.set_tag(key, value)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _start_task_root(*, task_name: str) -> Any:
|
|
83
|
+
"""Start and return the per-task span/transaction context manager.
|
|
84
|
+
|
|
85
|
+
Crucially, this starts a **child span** when a trace is already active
|
|
86
|
+
(e.g. ``AwsLambdaIntegration`` starts a transaction per Lambda invocation)
|
|
87
|
+
and only starts a **new transaction** when nothing is active (e.g. AWS
|
|
88
|
+
Batch, where there is no Lambda integration).
|
|
89
|
+
|
|
90
|
+
Why this matters for volume: starting a nested *transaction* under the
|
|
91
|
+
invocation transaction produced a *second* transaction per task that
|
|
92
|
+
inherited — rather than independently re-rolled — the parent's sampling
|
|
93
|
+
decision, roughly doubling transaction volume and defeating
|
|
94
|
+
``traces_sample_rate``. A child span carries no separate sampling decision:
|
|
95
|
+
it is sent only if its parent transaction is sampled, so one invocation
|
|
96
|
+
yields one transaction with the task span nested inside it.
|
|
97
|
+
|
|
98
|
+
The tracing API also differs across the SDK 2.x → 3.0 boundary:
|
|
99
|
+
|
|
100
|
+
* 2.x — ``start_transaction(name=, op=)`` for a root; ``start_span`` for a
|
|
101
|
+
child of the active transaction.
|
|
102
|
+
* 3.0 — transactions are gone; ``start_span(name=)`` is both the root (when
|
|
103
|
+
nothing is active) and a child (when something is), and no longer accepts
|
|
104
|
+
``op``.
|
|
105
|
+
"""
|
|
106
|
+
if not hasattr(sentry_sdk, "start_transaction"):
|
|
107
|
+
# SDK 3.0: start_span is automatically a root when nothing is active
|
|
108
|
+
# and a child otherwise — no manual active-trace check needed. (Scope
|
|
109
|
+
# has no ``transaction`` attribute on 3.0.)
|
|
110
|
+
return sentry_sdk.start_span(name=task_name)
|
|
111
|
+
else:
|
|
112
|
+
# SDK 2.x: a bare start_span with no active transaction is dropped,
|
|
113
|
+
# so pick child span vs. new transaction based on whether a trace is
|
|
114
|
+
# already active.
|
|
115
|
+
has_active_transaction = sentry_sdk.get_current_scope().transaction is not None
|
|
116
|
+
|
|
117
|
+
if has_active_transaction:
|
|
118
|
+
return sentry_sdk.start_span(op="task", name=task_name)
|
|
119
|
+
else:
|
|
120
|
+
return sentry_sdk.start_transaction(name=task_name, op="task")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def capture_task_exception() -> None:
|
|
124
|
+
"""Report the exception currently being handled to Sentry.
|
|
125
|
+
|
|
126
|
+
The executor catches task exceptions (recording a ``FAILED`` TaskRecord)
|
|
127
|
+
rather than re-raising, so the exception never propagates to an integration
|
|
128
|
+
that would report it. This matters most on SDK 3.0 (stream mode), where a
|
|
129
|
+
caught exception produces no error event at all — on 2.x the exception
|
|
130
|
+
happened to still be reported as it passed through the active span. Calling
|
|
131
|
+
this on the terminal failure path gives consistent error reporting on both
|
|
132
|
+
SDK versions.
|
|
133
|
+
|
|
134
|
+
Must be called from within an ``except`` block while the task's
|
|
135
|
+
:func:`task_scope` is still active, so the error carries the task name and
|
|
136
|
+
tags. A no-op when ``sentry_sdk`` is unavailable or uninitialised.
|
|
137
|
+
"""
|
|
138
|
+
if sentry_sdk and sentry_sdk.is_initialized():
|
|
139
|
+
sentry_sdk.capture_exception()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@contextlib.contextmanager
|
|
143
|
+
def task_scope(*, task_name: str, message_id: str, n_retries: int) -> Iterator[None]:
|
|
144
|
+
"""Run the enclosed task body inside a per-task Sentry trace.
|
|
145
|
+
|
|
146
|
+
Opens an isolated scope and traces the task — as a child span of the active
|
|
147
|
+
invocation transaction on Lambda, or as its own root transaction when
|
|
148
|
+
nothing is active (Batch). Records ``lambda_task``, ``lambda_task.message_id``,
|
|
149
|
+
``lambda_task.n_retries`` and (when derivable) ``lambda_task.runtime`` as
|
|
150
|
+
both span metadata and scope tags, so errors are attributed to the task even
|
|
151
|
+
when the trace is not sampled.
|
|
152
|
+
|
|
153
|
+
The scope is isolated via ``new_scope`` so records processed one after
|
|
154
|
+
another in the same Lambda invocation do not leak their task name into one
|
|
155
|
+
another.
|
|
156
|
+
|
|
157
|
+
A no-op when ``sentry_sdk`` is unavailable or uninitialised.
|
|
158
|
+
"""
|
|
159
|
+
if not (sentry_sdk and sentry_sdk.is_initialized()):
|
|
160
|
+
yield
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
runtime = _runtime_environment()
|
|
164
|
+
|
|
165
|
+
with sentry_sdk.new_scope() as scope:
|
|
166
|
+
# Attribute error events and the trace to the task via tags/attributes.
|
|
167
|
+
# We deliberately do NOT call scope.set_transaction_name(task_name):
|
|
168
|
+
# on Lambda the task runs inside the invocation transaction, and
|
|
169
|
+
# renaming it (a) fights across sibling records in a batch and (b) on
|
|
170
|
+
# SDK 3.0 stream mode corrupts the root-segment naming. The
|
|
171
|
+
# ``lambda_task`` tag below is the reliable, version-independent
|
|
172
|
+
# attribution; the task span itself is named after the task.
|
|
173
|
+
scope.set_tag("lambda_task", task_name)
|
|
174
|
+
scope.set_tag("lambda_task.message_id", message_id)
|
|
175
|
+
scope.set_tag("lambda_task.n_retries", n_retries)
|
|
176
|
+
if runtime:
|
|
177
|
+
scope.set_tag("lambda_task.runtime", runtime)
|
|
178
|
+
|
|
179
|
+
with _start_task_root(task_name=task_name) as span:
|
|
180
|
+
_set_metadata(span, key="lambda_task", value=task_name)
|
|
181
|
+
_set_metadata(span, key="lambda_task.message_id", value=message_id)
|
|
182
|
+
_set_metadata(span, key="lambda_task.n_retries", value=n_retries)
|
|
183
|
+
if runtime:
|
|
184
|
+
_set_metadata(span, key="lambda_task.runtime", value=runtime)
|
|
185
|
+
|
|
186
|
+
yield
|
|
@@ -7,7 +7,7 @@ packages = ["lambda_tasks"]
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "django-lambda-tasks"
|
|
10
|
-
version = "0.11.
|
|
10
|
+
version = "0.11.1"
|
|
11
11
|
description = "Run async tasks in a lambda function"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.11"
|
|
@@ -31,7 +31,7 @@ dev = [
|
|
|
31
31
|
"pytest-cov",
|
|
32
32
|
"ty",
|
|
33
33
|
"psycopg",
|
|
34
|
-
"sentry-sdk",
|
|
34
|
+
"sentry-sdk>=2,<3",
|
|
35
35
|
]
|
|
36
36
|
|
|
37
37
|
[tool.ty.rules]
|