@danieljvdm/dev-kit 1.0.2 → 2.0.0
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.
- package/README.md +6 -6
- package/package.json +1 -1
- package/skill-sources.jsonc +2 -33
- package/skill-sources.lock.json +15 -117
- package/skills/animate/LICENSE +21 -0
- package/skills/animate/NOTICE +10 -0
- package/skills/animate/SKILL.md +34 -0
- package/skills/animate/references/gestures.md +27 -0
- package/skills/animate/references/glossary.md +136 -0
- package/skills/animate/references/implementation.md +22 -0
- package/skills/animate/references/opportunities.md +18 -0
- package/skills/animate/references/principles.md +48 -0
- package/skills/animate/references/review.md +19 -0
- package/skills/cloudflare-workers/LICENSE +202 -0
- package/skills/cloudflare-workers/NOTICE +9 -0
- package/skills/cloudflare-workers/SKILL.md +26 -0
- package/skills/cloudflare-workers/references/durable-objects.md +68 -0
- package/skills/cloudflare-workers/references/sandbox.md +48 -0
- package/skills/cloudflare-workers/references/workers.md +63 -0
- package/skills/cloudflare-workers/references/wrangler.md +56 -0
- package/skills/design-ui/LICENSE +21 -0
- package/skills/design-ui/NOTICE +8 -0
- package/skills/design-ui/SKILL.md +29 -0
- package/skills/design-ui/references/appearance.md +22 -0
- package/skills/design-ui/references/components.md +33 -0
- package/skills/design-ui/references/exploration.md +50 -0
- package/skills/design-ui/references/libraries.md +39 -0
- package/skills/design-ui/references/themes.md +23 -0
- package/skills/dev-kit/SKILL.md +19 -38
- package/skills/dev-kit/references/cloudflare-worker-api.md +1 -1
- package/skills/dev-kit/references/skills.md +2 -2
- package/skills/effect-development/SKILL.md +27 -0
- package/skills/effect-development/agents/openai.yaml +4 -0
- package/skills/effect-development/references/api/index.md +32 -0
- package/skills/effect-development/references/api/schema-codecs.md +15 -0
- package/skills/{build-effect-apis/references → effect-development/references/api}/verification.md +10 -5
- package/skills/{effect-architecture-audit/SKILL.md → effect-development/references/architecture/index.md} +1 -6
- package/skills/{effect-atom-state/references → effect-development/references/atom}/effect-atom-client.md +8 -6
- package/skills/{effect-atom-state/references → effect-development/references/atom}/effect-atom-testing.md +7 -2
- package/skills/effect-development/references/atom/index.md +40 -0
- package/skills/{build-effect-clis/references → effect-development/references/cli}/entrypoints-and-testing.md +6 -2
- package/skills/effect-development/references/cli/index.md +39 -0
- package/skills/effect-development/references/setup.md +15 -0
- package/skills/open-pull-request/SKILL.md +10 -70
- package/skills/open-pull-request/references/evidence.md +22 -0
- package/skills/open-pull-request/references/explanation.md +21 -0
- package/skills/open-pull-request/references/publication.md +21 -0
- package/skills/sentry/LICENSE +202 -0
- package/skills/sentry/LICENSE-UPSTREAM +21 -0
- package/skills/sentry/NOTICE +14 -0
- package/skills/sentry/SKILL.md +33 -0
- package/skills/sentry/references/alert-payloads.md +105 -0
- package/skills/sentry/references/alerts.md +17 -0
- package/skills/sentry/references/artifacts.md +25 -0
- package/skills/sentry/references/auth-token.md +65 -0
- package/skills/sentry/references/concepts/ai-monitoring.md +131 -0
- package/skills/sentry/references/concepts/choosing-a-signal.md +78 -0
- package/skills/sentry/references/concepts/crons.md +50 -0
- package/skills/sentry/references/concepts/data-scrubbing.md +43 -0
- package/skills/sentry/references/concepts/errors.md +40 -0
- package/skills/sentry/references/concepts/logging.md +85 -0
- package/skills/sentry/references/concepts/metrics.md +49 -0
- package/skills/sentry/references/concepts/monitors.md +78 -0
- package/skills/sentry/references/concepts/profiling.md +38 -0
- package/skills/sentry/references/concepts/reduce-volume.md +49 -0
- package/skills/sentry/references/concepts/releases.md +43 -0
- package/skills/sentry/references/concepts/session-replay.md +35 -0
- package/skills/sentry/references/concepts/tracing.md +54 -0
- package/skills/sentry/references/concepts/user-feedback.md +36 -0
- package/skills/sentry/references/debug-artifacts/android.md +68 -0
- package/skills/sentry/references/debug-artifacts/apple.md +96 -0
- package/skills/sentry/references/debug-artifacts/dotnet.md +70 -0
- package/skills/sentry/references/debug-artifacts/flutter.md +65 -0
- package/skills/sentry/references/debug-artifacts/index.md +75 -0
- package/skills/sentry/references/debug-artifacts/javascript.md +94 -0
- package/skills/sentry/references/debug-artifacts/matching.md +73 -0
- package/skills/sentry/references/debug-artifacts/react-native.md +75 -0
- package/skills/sentry/references/first-error-setup.md +25 -0
- package/skills/sentry/references/instrumentation.md +30 -0
- package/skills/sentry/references/issues.md +32 -0
- package/skills/sentry/references/new-project.md +17 -0
- package/skills/sentry/references/otel-exporter.md +30 -0
- package/skills/sentry/references/release-workflow.md +31 -0
- package/skills/sentry/references/releases/ci-pipeline.md +214 -0
- package/skills/sentry/references/releases/index.md +93 -0
- package/skills/sentry/references/releases/suspect-commits.md +155 -0
- package/skills/sentry/references/releases/tagging.md +131 -0
- package/skills/sentry/references/releases/troubleshooting.md +67 -0
- package/skills/sentry/references/sdks/android/crons.md +305 -0
- package/skills/sentry/references/sdks/android/error-monitoring.md +800 -0
- package/skills/sentry/references/sdks/android/index.md +748 -0
- package/skills/sentry/references/sdks/android/integrations.md +48 -0
- package/skills/sentry/references/sdks/android/logging.md +374 -0
- package/skills/sentry/references/sdks/android/metrics.md +288 -0
- package/skills/sentry/references/sdks/android/profiling.md +397 -0
- package/skills/sentry/references/sdks/android/session-replay.md +406 -0
- package/skills/sentry/references/sdks/android/tracing.md +861 -0
- package/skills/sentry/references/sdks/browser/error-monitoring.md +861 -0
- package/skills/sentry/references/sdks/browser/index.md +714 -0
- package/skills/sentry/references/sdks/browser/logging.md +315 -0
- package/skills/sentry/references/sdks/browser/profiling.md +236 -0
- package/skills/sentry/references/sdks/browser/session-replay.md +586 -0
- package/skills/sentry/references/sdks/browser/tracing.md +561 -0
- package/skills/sentry/references/sdks/browser/user-feedback.md +354 -0
- package/skills/sentry/references/sdks/cloudflare/ai-monitoring.md +661 -0
- package/skills/sentry/references/sdks/cloudflare/crons.md +231 -0
- package/skills/sentry/references/sdks/cloudflare/durable-objects.md +381 -0
- package/skills/sentry/references/sdks/cloudflare/error-monitoring.md +329 -0
- package/skills/sentry/references/sdks/cloudflare/index.md +698 -0
- package/skills/sentry/references/sdks/cloudflare/logging.md +169 -0
- package/skills/sentry/references/sdks/cloudflare/nodejs-compat.md +134 -0
- package/skills/sentry/references/sdks/cloudflare/tracing.md +475 -0
- package/skills/sentry/references/sdks/cocoa/error-monitoring.md +394 -0
- package/skills/sentry/references/sdks/cocoa/index.md +497 -0
- package/skills/sentry/references/sdks/cocoa/logging.md +209 -0
- package/skills/sentry/references/sdks/cocoa/metrics.md +136 -0
- package/skills/sentry/references/sdks/cocoa/profiling.md +223 -0
- package/skills/sentry/references/sdks/cocoa/session-replay.md +257 -0
- package/skills/sentry/references/sdks/cocoa/tracing.md +524 -0
- package/skills/sentry/references/sdks/cocoa/user-feedback.md +310 -0
- package/skills/sentry/references/sdks/dotnet/crons.md +465 -0
- package/skills/sentry/references/sdks/dotnet/error-monitoring.md +1100 -0
- package/skills/sentry/references/sdks/dotnet/index.md +614 -0
- package/skills/sentry/references/sdks/dotnet/logging.md +556 -0
- package/skills/sentry/references/sdks/dotnet/metrics.md +229 -0
- package/skills/sentry/references/sdks/dotnet/profiling.md +296 -0
- package/skills/sentry/references/sdks/dotnet/tracing.md +897 -0
- package/skills/sentry/references/sdks/elixir/crons.md +371 -0
- package/skills/sentry/references/sdks/elixir/error-monitoring.md +335 -0
- package/skills/sentry/references/sdks/elixir/index.md +383 -0
- package/skills/sentry/references/sdks/elixir/logging.md +239 -0
- package/skills/sentry/references/sdks/elixir/tracing.md +233 -0
- package/skills/sentry/references/sdks/flutter/ecosystem-integrations.md +70 -0
- package/skills/sentry/references/sdks/flutter/error-monitoring.md +312 -0
- package/skills/sentry/references/sdks/flutter/index.md +728 -0
- package/skills/sentry/references/sdks/flutter/logging.md +387 -0
- package/skills/sentry/references/sdks/flutter/metrics.md +310 -0
- package/skills/sentry/references/sdks/flutter/profiling.md +214 -0
- package/skills/sentry/references/sdks/flutter/session-replay.md +308 -0
- package/skills/sentry/references/sdks/flutter/tracing.md +502 -0
- package/skills/sentry/references/sdks/go/crons.md +270 -0
- package/skills/sentry/references/sdks/go/error-monitoring.md +361 -0
- package/skills/sentry/references/sdks/go/index.md +324 -0
- package/skills/sentry/references/sdks/go/logging.md +354 -0
- package/skills/sentry/references/sdks/go/metrics.md +273 -0
- package/skills/sentry/references/sdks/go/profiling.md +46 -0
- package/skills/sentry/references/sdks/go/tracing.md +366 -0
- package/skills/sentry/references/sdks/index.md +41 -0
- package/skills/sentry/references/sdks/nestjs/ai-monitoring.md +410 -0
- package/skills/sentry/references/sdks/nestjs/crons.md +268 -0
- package/skills/sentry/references/sdks/nestjs/error-monitoring.md +729 -0
- package/skills/sentry/references/sdks/nestjs/index.md +699 -0
- package/skills/sentry/references/sdks/nestjs/logging.md +174 -0
- package/skills/sentry/references/sdks/nestjs/metrics.md +243 -0
- package/skills/sentry/references/sdks/nestjs/profiling.md +127 -0
- package/skills/sentry/references/sdks/nestjs/tracing.md +762 -0
- package/skills/sentry/references/sdks/nextjs/ai-monitoring.md +494 -0
- package/skills/sentry/references/sdks/nextjs/crons.md +383 -0
- package/skills/sentry/references/sdks/nextjs/error-monitoring.md +974 -0
- package/skills/sentry/references/sdks/nextjs/index.md +491 -0
- package/skills/sentry/references/sdks/nextjs/logging.md +381 -0
- package/skills/sentry/references/sdks/nextjs/metrics.md +411 -0
- package/skills/sentry/references/sdks/nextjs/profiling.md +403 -0
- package/skills/sentry/references/sdks/nextjs/session-replay.md +462 -0
- package/skills/sentry/references/sdks/nextjs/tracing.md +628 -0
- package/skills/sentry/references/sdks/node/ai-monitoring.md +303 -0
- package/skills/sentry/references/sdks/node/crons.md +432 -0
- package/skills/sentry/references/sdks/node/error-monitoring.md +1125 -0
- package/skills/sentry/references/sdks/node/index.md +942 -0
- package/skills/sentry/references/sdks/node/logging.md +380 -0
- package/skills/sentry/references/sdks/node/metrics.md +278 -0
- package/skills/sentry/references/sdks/node/profiling.md +373 -0
- package/skills/sentry/references/sdks/node/tracing.md +878 -0
- package/skills/sentry/references/sdks/php/ai-monitoring.md +310 -0
- package/skills/sentry/references/sdks/php/crons.md +265 -0
- package/skills/sentry/references/sdks/php/error-monitoring.md +626 -0
- package/skills/sentry/references/sdks/php/index.md +362 -0
- package/skills/sentry/references/sdks/php/laravel.md +542 -0
- package/skills/sentry/references/sdks/php/logging.md +242 -0
- package/skills/sentry/references/sdks/php/metrics.md +193 -0
- package/skills/sentry/references/sdks/php/profiling.md +115 -0
- package/skills/sentry/references/sdks/php/symfony.md +552 -0
- package/skills/sentry/references/sdks/php/tracing.md +516 -0
- package/skills/sentry/references/sdks/python/ai-monitoring.md +351 -0
- package/skills/sentry/references/sdks/python/crons.md +218 -0
- package/skills/sentry/references/sdks/python/error-monitoring.md +292 -0
- package/skills/sentry/references/sdks/python/index.md +361 -0
- package/skills/sentry/references/sdks/python/logging.md +234 -0
- package/skills/sentry/references/sdks/python/metrics.md +154 -0
- package/skills/sentry/references/sdks/python/profiling.md +120 -0
- package/skills/sentry/references/sdks/python/tracing.md +364 -0
- package/skills/sentry/references/sdks/react/error-monitoring.md +1607 -0
- package/skills/sentry/references/sdks/react/index.md +492 -0
- package/skills/sentry/references/sdks/react/logging.md +364 -0
- package/skills/sentry/references/sdks/react/profiling.md +322 -0
- package/skills/sentry/references/sdks/react/react-features.md +1272 -0
- package/skills/sentry/references/sdks/react/session-replay.md +1193 -0
- package/skills/sentry/references/sdks/react/tracing.md +1415 -0
- package/skills/sentry/references/sdks/react-native/error-monitoring.md +1481 -0
- package/skills/sentry/references/sdks/react-native/expo-config-plugin.md +45 -0
- package/skills/sentry/references/sdks/react-native/index.md +1030 -0
- package/skills/sentry/references/sdks/react-native/logging.md +444 -0
- package/skills/sentry/references/sdks/react-native/profiling.md +386 -0
- package/skills/sentry/references/sdks/react-native/session-replay.md +704 -0
- package/skills/sentry/references/sdks/react-native/tracing.md +1086 -0
- package/skills/sentry/references/sdks/react-native/user-feedback.md +815 -0
- package/skills/sentry/references/sdks/react-router-framework/error-monitoring.md +127 -0
- package/skills/sentry/references/sdks/react-router-framework/index.md +411 -0
- package/skills/sentry/references/sdks/react-router-framework/logging.md +66 -0
- package/skills/sentry/references/sdks/react-router-framework/profiling.md +57 -0
- package/skills/sentry/references/sdks/react-router-framework/react-router-framework-features.md +110 -0
- package/skills/sentry/references/sdks/react-router-framework/session-replay.md +67 -0
- package/skills/sentry/references/sdks/react-router-framework/tracing.md +96 -0
- package/skills/sentry/references/sdks/react-router-framework/user-feedback.md +71 -0
- package/skills/sentry/references/sdks/ruby/crons.md +184 -0
- package/skills/sentry/references/sdks/ruby/error-monitoring.md +248 -0
- package/skills/sentry/references/sdks/ruby/index.md +297 -0
- package/skills/sentry/references/sdks/ruby/logging.md +168 -0
- package/skills/sentry/references/sdks/ruby/metrics.md +196 -0
- package/skills/sentry/references/sdks/ruby/migration.md +321 -0
- package/skills/sentry/references/sdks/ruby/profiling.md +107 -0
- package/skills/sentry/references/sdks/ruby/tracing.md +388 -0
- package/skills/sentry/references/sdks/svelte/error-monitoring.md +456 -0
- package/skills/sentry/references/sdks/svelte/index.md +467 -0
- package/skills/sentry/references/sdks/svelte/logging.md +344 -0
- package/skills/sentry/references/sdks/svelte/session-replay.md +369 -0
- package/skills/sentry/references/sdks/svelte/tracing.md +406 -0
- package/skills/sentry/references/sdks/tanstack-start/error-monitoring.md +149 -0
- package/skills/sentry/references/sdks/tanstack-start/index.md +381 -0
- package/skills/sentry/references/sdks/tanstack-start/logging.md +74 -0
- package/skills/sentry/references/sdks/tanstack-start/session-replay.md +76 -0
- package/skills/sentry/references/sdks/tanstack-start/tanstackstart-features.md +128 -0
- package/skills/sentry/references/sdks/tanstack-start/tracing.md +123 -0
- package/skills/sentry/references/sdks/tanstack-start/user-feedback.md +81 -0
- package/skills/sentry/references/search-query-language.md +226 -0
- package/skills/sentry/references/semantics/angular.md +7 -0
- package/skills/sentry/references/semantics/app.md +28 -0
- package/skills/sentry/references/semantics/art.md +17 -0
- package/skills/sentry/references/semantics/aws.md +43 -0
- package/skills/sentry/references/semantics/browser.md +34 -0
- package/skills/sentry/references/semantics/cache.md +12 -0
- package/skills/sentry/references/semantics/client.md +8 -0
- package/skills/sentry/references/semantics/cloud.md +12 -0
- package/skills/sentry/references/semantics/cloudflare.md +23 -0
- package/skills/sentry/references/semantics/code.md +11 -0
- package/skills/sentry/references/semantics/culture.md +11 -0
- package/skills/sentry/references/semantics/db.md +21 -0
- package/skills/sentry/references/semantics/device.md +43 -0
- package/skills/sentry/references/semantics/error.md +7 -0
- package/skills/sentry/references/semantics/event.md +8 -0
- package/skills/sentry/references/semantics/exception.md +10 -0
- package/skills/sentry/references/semantics/faas.md +19 -0
- package/skills/sentry/references/semantics/file.md +8 -0
- package/skills/sentry/references/semantics/flag.md +7 -0
- package/skills/sentry/references/semantics/gcp.md +16 -0
- package/skills/sentry/references/semantics/gen_ai.md +53 -0
- package/skills/sentry/references/semantics/general.md +11 -0
- package/skills/sentry/references/semantics/graphql.md +9 -0
- package/skills/sentry/references/semantics/grpc.md +19 -0
- package/skills/sentry/references/semantics/http.md +34 -0
- package/skills/sentry/references/semantics/index.md +63 -0
- package/skills/sentry/references/semantics/jsonrpc.md +8 -0
- package/skills/sentry/references/semantics/jvm.md +12 -0
- package/skills/sentry/references/semantics/koa.md +7 -0
- package/skills/sentry/references/semantics/logger.md +7 -0
- package/skills/sentry/references/semantics/mcp.md +37 -0
- package/skills/sentry/references/semantics/mdc.md +7 -0
- package/skills/sentry/references/semantics/messaging.md +23 -0
- package/skills/sentry/references/semantics/middleware.md +7 -0
- package/skills/sentry/references/semantics/navigation.md +9 -0
- package/skills/sentry/references/semantics/nel.md +11 -0
- package/skills/sentry/references/semantics/network.md +17 -0
- package/skills/sentry/references/semantics/os.md +15 -0
- package/skills/sentry/references/semantics/otel.md +10 -0
- package/skills/sentry/references/semantics/params.md +7 -0
- package/skills/sentry/references/semantics/process.md +14 -0
- package/skills/sentry/references/semantics/react.md +7 -0
- package/skills/sentry/references/semantics/remix.md +7 -0
- package/skills/sentry/references/semantics/resource.md +7 -0
- package/skills/sentry/references/semantics/rpc.md +10 -0
- package/skills/sentry/references/semantics/score.md +10 -0
- package/skills/sentry/references/semantics/sentry.md +69 -0
- package/skills/sentry/references/semantics/server.md +8 -0
- package/skills/sentry/references/semantics/service.md +8 -0
- package/skills/sentry/references/semantics/session.md +7 -0
- package/skills/sentry/references/semantics/state.md +7 -0
- package/skills/sentry/references/semantics/thread.md +8 -0
- package/skills/sentry/references/semantics/timber.md +7 -0
- package/skills/sentry/references/semantics/trpc.md +8 -0
- package/skills/sentry/references/semantics/ui.md +18 -0
- package/skills/sentry/references/semantics/url.md +15 -0
- package/skills/sentry/references/semantics/user.md +17 -0
- package/skills/sentry/references/semantics/user_agent.md +7 -0
- package/skills/sentry/references/semantics/vercel.md +42 -0
- package/skills/sentry/references/setup-verification.md +39 -0
- package/skills/sentry/references/snapshots/github-actions-fanout.md +206 -0
- package/skills/sentry/references/snapshots/github-actions-simple.md +102 -0
- package/skills/sentry/references/snapshots/github-actions-swift-snapshot-testing.md +118 -0
- package/skills/sentry/references/snapshots/index.md +19 -0
- package/skills/sentry/references/snapshots/snapshot-previews.md +100 -0
- package/skills/sentry/references/snapshots/snapshots.md +123 -0
- package/skills/sentry/references/snapshots/wizard-setup.md +134 -0
- package/skills/testing/LICENSE +21 -0
- package/skills/testing/NOTICE +7 -0
- package/skills/testing/SKILL.md +13 -19
- package/skills/testing/agents/openai.yaml +1 -2
- package/skills/testing/references/selection.md +23 -0
- package/skills/testing/references/tdd.md +19 -0
- package/skills/testing/references/test-design.md +17 -0
- package/src/catalog.ts +1 -7
- package/skills/build-effect-apis/SKILL.md +0 -81
- package/skills/build-effect-apis/agents/openai.yaml +0 -4
- package/skills/build-effect-clis/SKILL.md +0 -92
- package/skills/build-effect-clis/agents/openai.yaml +0 -4
- package/skills/effect-architecture-audit/agents/openai.yaml +0 -4
- package/skills/effect-atom-state/SKILL.md +0 -97
- package/skills/effect-atom-state/agents/openai.yaml +0 -4
- package/skills/effect-ts/SKILL.md +0 -35
- package/skills/effect-ts/agents/openai.yaml +0 -4
- /package/skills/{build-effect-apis/references → effect-development/references/api}/cloudflare-workers.md +0 -0
- /package/skills/{build-effect-apis/references → effect-development/references/api}/runtime-assembly.md +0 -0
- /package/skills/{build-effect-apis/references → effect-development/references/api}/server-and-middleware.md +0 -0
- /package/skills/{build-effect-apis/references → effect-development/references/api}/shared-contracts.md +0 -0
- /package/skills/{effect-architecture-audit/references → effect-development/references/architecture}/service-and-boundary-audit.md +0 -0
- /package/skills/{effect-atom-state/references → effect-development/references/atom}/effect-atom-lifecycle.md +0 -0
- /package/skills/{effect-atom-state/references → effect-development/references/atom}/effect-atom-workflows.md +0 -0
- /package/skills/{effect-atom-state/references → effect-development/references/atom}/tanstack-start.md +0 -0
- /package/skills/{build-effect-clis/references → effect-development/references/cli}/command-design.md +0 -0
- /package/skills/{build-effect-clis/references → effect-development/references/cli}/processes-and-platform.md +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# Crons — Sentry Elixir SDK
|
|
2
|
+
|
|
3
|
+
> Minimum SDK: `sentry` v10.2.0+
|
|
4
|
+
|
|
5
|
+
Sentry Cron Monitoring detects when scheduled jobs fail silently — they don’t error, but
|
|
6
|
+
they stop running or take too long.
|
|
7
|
+
The SDK provides three integration paths: manual check-ins (any scheduler), Oban (job
|
|
8
|
+
queue), and Quantum (cron scheduler).
|
|
9
|
+
|
|
10
|
+
## Manual Check-Ins
|
|
11
|
+
|
|
12
|
+
Use `Sentry.capture_check_in/1` with any periodic task — `GenServer`, `Task`, or custom
|
|
13
|
+
scheduler.
|
|
14
|
+
|
|
15
|
+
### Basic pattern: start → work → complete/error
|
|
16
|
+
|
|
17
|
+
```elixir
|
|
18
|
+
defmodule MyApp.ReportWorker do
|
|
19
|
+
use GenServer
|
|
20
|
+
|
|
21
|
+
def handle_info(:run, state) do
|
|
22
|
+
# 1. Signal job started
|
|
23
|
+
{:ok, check_in_id} = Sentry.capture_check_in(
|
|
24
|
+
status: :in_progress,
|
|
25
|
+
monitor_slug: "daily-report"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# 2. Do the work
|
|
29
|
+
result = MyApp.Reports.generate_daily_report()
|
|
30
|
+
|
|
31
|
+
# 3. Signal completion or failure
|
|
32
|
+
case result do
|
|
33
|
+
{:ok, _report} ->
|
|
34
|
+
Sentry.capture_check_in(
|
|
35
|
+
check_in_id: check_in_id,
|
|
36
|
+
status: :ok,
|
|
37
|
+
monitor_slug: "daily-report"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
{:error, reason} ->
|
|
41
|
+
Sentry.capture_check_in(
|
|
42
|
+
check_in_id: check_in_id,
|
|
43
|
+
status: :error,
|
|
44
|
+
monitor_slug: "daily-report"
|
|
45
|
+
)
|
|
46
|
+
Logger.error("Report generation failed: #{inspect(reason)}")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
{:noreply, state}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### With monitor configuration (upsert)
|
|
55
|
+
|
|
56
|
+
Providing `monitor_config` creates or updates the monitor definition in Sentry on first
|
|
57
|
+
check-in. This eliminates the need to create monitors in the Sentry UI manually:
|
|
58
|
+
|
|
59
|
+
```elixir
|
|
60
|
+
Sentry.capture_check_in(
|
|
61
|
+
status: :in_progress,
|
|
62
|
+
monitor_slug: "hourly-sync",
|
|
63
|
+
monitor_config: [
|
|
64
|
+
schedule: [type: :crontab, value: "0 * * * *"], # runs every hour at :00
|
|
65
|
+
timezone: "America/New_York",
|
|
66
|
+
checkin_margin: 5, # minutes before Sentry considers the check-in missed
|
|
67
|
+
max_runtime: 30, # minutes before Sentry considers the job failed
|
|
68
|
+
failure_issue_threshold: 2,
|
|
69
|
+
recovery_threshold: 2,
|
|
70
|
+
owner: "platform-team" # since v10.10.0
|
|
71
|
+
]
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Interval schedule
|
|
76
|
+
|
|
77
|
+
For jobs that run every N minutes/hours rather than on a crontab:
|
|
78
|
+
|
|
79
|
+
```elixir
|
|
80
|
+
Sentry.capture_check_in(
|
|
81
|
+
status: :in_progress,
|
|
82
|
+
monitor_slug: "health-probe",
|
|
83
|
+
monitor_config: [
|
|
84
|
+
schedule: [type: :interval, value: 15, unit: :minute],
|
|
85
|
+
# unit options: :year | :month | :week | :day | :hour | :minute
|
|
86
|
+
checkin_margin: 3,
|
|
87
|
+
max_runtime: 5
|
|
88
|
+
]
|
|
89
|
+
)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### `capture_check_in/1` options
|
|
93
|
+
|
|
94
|
+
| Option | Type | Required | Description |
|
|
95
|
+
| ----------------- | ------------------------------- | ------------- | ---------------------------------------------------------------- |
|
|
96
|
+
| `:status` | `:in_progress \| :ok \| :error` | Yes | Current job status |
|
|
97
|
+
| `:monitor_slug` | `string` | Yes | Unique identifier for the monitor (slug format) |
|
|
98
|
+
| `:check_in_id` | `string` | On completion | ID from the initial `:in_progress` call |
|
|
99
|
+
| `:duration` | `number` | No | Job duration in seconds (auto-calculated when using check_in_id) |
|
|
100
|
+
| `:monitor_config` | `keyword` | No | Monitor definition; upserted on each call |
|
|
101
|
+
| `:environment` | `string` | No | Override default environment for this check-in |
|
|
102
|
+
|
|
103
|
+
`capture_check_in/1` returns:
|
|
104
|
+
|
|
105
|
+
- `{:ok, check_in_id}` — check-in ID string; pass it to subsequent calls
|
|
106
|
+
- `:ignored` — not sent (no DSN, wrong environment, etc.)
|
|
107
|
+
- `{:error, ClientError.t()}` — HTTP error
|
|
108
|
+
|
|
109
|
+
### Helper wrapper pattern
|
|
110
|
+
|
|
111
|
+
For clean code, wrap the check-in lifecycle in a helper:
|
|
112
|
+
|
|
113
|
+
```elixir
|
|
114
|
+
defmodule MyApp.Crons do
|
|
115
|
+
@doc """
|
|
116
|
+
Runs a function as a Sentry-monitored cron job.
|
|
117
|
+
Returns {:ok, result} or {:error, exception}.
|
|
118
|
+
"""
|
|
119
|
+
def monitor(slug, fun, monitor_config \\ []) do
|
|
120
|
+
# capture_check_in/1 returns {:ok, id}, :ignored (no DSN / filtered env),
|
|
121
|
+
# or {:error, _} (HTTP failure). Match all three so a non-:ok result never
|
|
122
|
+
# raises a MatchError and crashes the job.
|
|
123
|
+
check_in_id =
|
|
124
|
+
case Sentry.capture_check_in(
|
|
125
|
+
status: :in_progress,
|
|
126
|
+
monitor_slug: slug,
|
|
127
|
+
monitor_config: monitor_config
|
|
128
|
+
) do
|
|
129
|
+
{:ok, id} -> id
|
|
130
|
+
:ignored -> nil
|
|
131
|
+
{:error, _reason} -> nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
try do
|
|
135
|
+
result = fun.()
|
|
136
|
+
|
|
137
|
+
if check_in_id do
|
|
138
|
+
Sentry.capture_check_in(
|
|
139
|
+
check_in_id: check_in_id,
|
|
140
|
+
status: :ok,
|
|
141
|
+
monitor_slug: slug
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
{:ok, result}
|
|
146
|
+
rescue
|
|
147
|
+
exception ->
|
|
148
|
+
if check_in_id do
|
|
149
|
+
Sentry.capture_check_in(
|
|
150
|
+
check_in_id: check_in_id,
|
|
151
|
+
status: :error,
|
|
152
|
+
monitor_slug: slug
|
|
153
|
+
)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
Sentry.capture_exception(exception, stacktrace: __STACKTRACE__)
|
|
157
|
+
{:error, exception}
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Usage
|
|
163
|
+
MyApp.Crons.monitor("nightly-cleanup", fn ->
|
|
164
|
+
MyApp.Cleanup.run()
|
|
165
|
+
end, schedule: [type: :crontab, value: "0 3 * * *"])
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Oban Integration
|
|
171
|
+
|
|
172
|
+
Requires: Oban v2.17.6+ or Oban Pro v0.14+
|
|
173
|
+
|
|
174
|
+
### Error capture (since v10.9.0)
|
|
175
|
+
|
|
176
|
+
Report failed Oban job errors to Sentry automatically:
|
|
177
|
+
|
|
178
|
+
```elixir
|
|
179
|
+
# config/config.exs
|
|
180
|
+
config :sentry,
|
|
181
|
+
integrations: [
|
|
182
|
+
oban: [
|
|
183
|
+
capture_errors: true
|
|
184
|
+
]
|
|
185
|
+
]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Errors from all Oban workers are captured with job context included.
|
|
189
|
+
|
|
190
|
+
### Cron monitoring (since v10.2.0)
|
|
191
|
+
|
|
192
|
+
Monitor scheduled Oban jobs automatically.
|
|
193
|
+
The integration reads cron metadata set by Oban Pro (or manually via job meta):
|
|
194
|
+
|
|
195
|
+
```elixir
|
|
196
|
+
# config/config.exs
|
|
197
|
+
config :sentry,
|
|
198
|
+
integrations: [
|
|
199
|
+
oban: [
|
|
200
|
+
capture_errors: true,
|
|
201
|
+
cron: [
|
|
202
|
+
enabled: true
|
|
203
|
+
]
|
|
204
|
+
]
|
|
205
|
+
]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Jobs are only monitored if their meta contains `"cron" => true`. Oban Pro sets this
|
|
209
|
+
automatically. For standard Oban, add it manually:
|
|
210
|
+
|
|
211
|
+
```elixir
|
|
212
|
+
# Scheduling a cron job with Oban (standard, not Pro)
|
|
213
|
+
Oban.insert(%Oban.Job{
|
|
214
|
+
worker: MyApp.DailyReportWorker,
|
|
215
|
+
meta: %{"cron" => true, "cron_expr" => "0 8 * * *"}
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Per-worker monitor configuration (since v10.9.0)
|
|
220
|
+
|
|
221
|
+
Override monitor settings per worker using the `Sentry.Integrations.Oban.Cron` callback:
|
|
222
|
+
|
|
223
|
+
```elixir
|
|
224
|
+
defmodule MyApp.DailyReportWorker do
|
|
225
|
+
use Oban.Worker, queue: :reports
|
|
226
|
+
|
|
227
|
+
@behaviour Sentry.Integrations.Oban.Cron # optional callback
|
|
228
|
+
|
|
229
|
+
@impl Sentry.Integrations.Oban.Cron
|
|
230
|
+
def sentry_check_in_configuration(_job) do
|
|
231
|
+
[
|
|
232
|
+
monitor_config: [
|
|
233
|
+
timezone: "America/New_York",
|
|
234
|
+
checkin_margin: 10,
|
|
235
|
+
max_runtime: 60
|
|
236
|
+
]
|
|
237
|
+
]
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
@impl Oban.Worker
|
|
241
|
+
def perform(%Oban.Job{} = job) do
|
|
242
|
+
# ... job logic
|
|
243
|
+
:ok
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Custom error reporting filter (since v12.0.0)
|
|
249
|
+
|
|
250
|
+
Suppress reporting for specific noisy workers:
|
|
251
|
+
|
|
252
|
+
```elixir
|
|
253
|
+
config :sentry,
|
|
254
|
+
integrations: [
|
|
255
|
+
oban: [
|
|
256
|
+
capture_errors: true,
|
|
257
|
+
should_report_error_callback: fn worker, _job ->
|
|
258
|
+
worker not in [MyApp.NoisyWorker, MyApp.ExpectedFailureWorker]
|
|
259
|
+
end
|
|
260
|
+
]
|
|
261
|
+
]
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Custom monitor slug generator
|
|
265
|
+
|
|
266
|
+
```elixir
|
|
267
|
+
defmodule MyApp.ObanSlugger do
|
|
268
|
+
def generate_slug(%Oban.Job{worker: worker}) do
|
|
269
|
+
worker
|
|
270
|
+
|> Module.split()
|
|
271
|
+
|> Enum.map(&Macro.underscore/1)
|
|
272
|
+
|> Enum.join("-")
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
config :sentry,
|
|
277
|
+
integrations: [
|
|
278
|
+
oban: [
|
|
279
|
+
cron: [
|
|
280
|
+
enabled: true,
|
|
281
|
+
monitor_slug_generator: {MyApp.ObanSlugger, :generate_slug}
|
|
282
|
+
]
|
|
283
|
+
]
|
|
284
|
+
]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Quantum Integration
|
|
290
|
+
|
|
291
|
+
Requires: Quantum v3.0+
|
|
292
|
+
|
|
293
|
+
### Enable cron monitoring
|
|
294
|
+
|
|
295
|
+
```elixir
|
|
296
|
+
# mix.exs
|
|
297
|
+
{:quantum, "~> 3.0"}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
```elixir
|
|
301
|
+
# config/config.exs
|
|
302
|
+
config :sentry,
|
|
303
|
+
integrations: [
|
|
304
|
+
quantum: [
|
|
305
|
+
cron: [
|
|
306
|
+
enabled: true
|
|
307
|
+
]
|
|
308
|
+
]
|
|
309
|
+
]
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The Quantum integration automatically reads cron expressions from your Quantum scheduler
|
|
313
|
+
configuration and creates monitors for each job.
|
|
314
|
+
The monitor slug is derived from the job name.
|
|
315
|
+
|
|
316
|
+
```elixir
|
|
317
|
+
# lib/my_app/scheduler.ex
|
|
318
|
+
defmodule MyApp.Scheduler do
|
|
319
|
+
use Quantum, otp_app: :my_app
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# config/config.exs
|
|
323
|
+
config :my_app, MyApp.Scheduler,
|
|
324
|
+
jobs: [
|
|
325
|
+
{"0 * * * *", {MyApp.HourlyTask, :run, []}}, # monitored as "hourly_task"
|
|
326
|
+
{"@daily", {MyApp.DailyReport, :run, []}}, # monitored as "daily_report"
|
|
327
|
+
]
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
> **Note:** Quantum jobs using `@reboot` are not monitored (no equivalent schedule type
|
|
331
|
+
> in Sentry Crons).
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Monitor Configuration Reference
|
|
336
|
+
|
|
337
|
+
| Option | Type | Description |
|
|
338
|
+
| ------------------------- | ------------------------------------------------------ | -------------------------------------------------- |
|
|
339
|
+
| `schedule.type` | `:crontab \| :interval` | Schedule type |
|
|
340
|
+
| `schedule.value` | `string` (crontab) or `integer` (interval) | Crontab expression or interval count |
|
|
341
|
+
| `schedule.unit` | `:minute \| :hour \| :day \| :week \| :month \| :year` | Interval unit (`:interval` type only) |
|
|
342
|
+
| `timezone` | `string` | IANA timezone (e.g., `"America/New_York"`) |
|
|
343
|
+
| `checkin_margin` | `integer` | Minutes after expected start before marking missed |
|
|
344
|
+
| `max_runtime` | `integer` | Minutes from start before marking timed out |
|
|
345
|
+
| `failure_issue_threshold` | `integer` | Consecutive failures before opening an issue |
|
|
346
|
+
| `recovery_threshold` | `integer` | Consecutive successes before closing the issue |
|
|
347
|
+
| `owner` | `string` | Team or user slug (since v10.10.0) |
|
|
348
|
+
|
|
349
|
+
## Best Practices
|
|
350
|
+
|
|
351
|
+
- Always call `Sentry.capture_check_in/1` with `:in_progress` before starting work and
|
|
352
|
+
`:ok` or `:error` on completion — sending only `:ok` at the end still works but loses
|
|
353
|
+
duration tracking
|
|
354
|
+
- Wrap job logic in a try/rescue so failures also send the `:error` status (see helper
|
|
355
|
+
wrapper pattern above)
|
|
356
|
+
- Use `monitor_config` on the first check-in of a new job to create the monitor
|
|
357
|
+
automatically — no need to set it on every subsequent check-in
|
|
358
|
+
- Set `checkin_margin` to a reasonable buffer (e.g., 5 minutes for hourly jobs) to avoid
|
|
359
|
+
false alarms from minor scheduling jitter
|
|
360
|
+
- For Oban, prefer the built-in integration over manual check-ins — it handles the
|
|
361
|
+
check-in lifecycle and job context enrichment automatically
|
|
362
|
+
|
|
363
|
+
## Troubleshooting
|
|
364
|
+
|
|
365
|
+
| Issue | Solution |
|
|
366
|
+
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
367
|
+
| Monitor not appearing in Sentry | Send at least one `:in_progress` check-in with `monitor_config` to create the monitor |
|
|
368
|
+
| Oban integration not monitoring crons | Requires Oban v2.17.6+ or Oban Pro; job meta must contain `"cron" => true` |
|
|
369
|
+
| `capture_check_in/1` returns `:ignored` | DSN is not set or `:environment_name` is excluded in your Sentry alert filters |
|
|
370
|
+
| Quantum `@reboot` jobs not monitored | Expected — `@reboot` has no crontab/interval equivalent; use manual check-ins for one-time startup jobs |
|
|
371
|
+
| Missed check-ins on deploy | If the app restarts during a cron window, the check-in is missed; increase `checkin_margin` to account for deploy time |
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# Error Monitoring — Sentry Elixir SDK
|
|
2
|
+
|
|
3
|
+
> Minimum SDK: `sentry` v8.0.0+
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
Key config options for error monitoring:
|
|
8
|
+
|
|
9
|
+
| Option | Type | Default | Purpose |
|
|
10
|
+
| ----------------------------- | -------------------------------------------- | --------------------------- | -------------------------------------------------------- |
|
|
11
|
+
| `:dsn` | `string \| nil` | `nil` | SDK disabled if nil |
|
|
12
|
+
| `:sample_rate` | `float` | `1.0` | Error event sample rate (0.0–1.0) |
|
|
13
|
+
| `:before_send` | `(event -> event \| nil \| false) \| {m, f}` | `nil` | Mutate or drop error events before sending |
|
|
14
|
+
| `:after_send_event` | `(event, result -> any) \| {m, f}` | `nil` | Called after sending; return value ignored |
|
|
15
|
+
| `:filter` | `module` | `Sentry.DefaultEventFilter` | Module implementing `Sentry.EventFilter` |
|
|
16
|
+
| `:max_breadcrumbs` | `integer` | `100` | Max breadcrumbs stored per process |
|
|
17
|
+
| `:dedup_events` | `boolean` | `true` | Deduplicate identical events within ~30 seconds |
|
|
18
|
+
| `:tags` | `map` | `%{}` | Global tags sent with every event |
|
|
19
|
+
| `:enable_source_code_context` | `boolean` | `false` | Include source lines around errors |
|
|
20
|
+
| `:root_source_code_paths` | `[path]` | `[]` | Required when source context is enabled |
|
|
21
|
+
| `:in_app_otp_apps` | `[atom]` | `[]` | OTP apps whose modules appear as “in-app” in stacktraces |
|
|
22
|
+
|
|
23
|
+
## Code Examples
|
|
24
|
+
|
|
25
|
+
### Basic setup
|
|
26
|
+
|
|
27
|
+
```elixir
|
|
28
|
+
# config/config.exs
|
|
29
|
+
config :sentry,
|
|
30
|
+
dsn: System.get_env("SENTRY_DSN"),
|
|
31
|
+
environment_name: config_env(),
|
|
32
|
+
enable_source_code_context: true,
|
|
33
|
+
root_source_code_paths: [File.cwd!()],
|
|
34
|
+
in_app_otp_apps: [:my_app]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Capturing exceptions
|
|
38
|
+
|
|
39
|
+
Always pass `stacktrace: __STACKTRACE__` in rescue blocks — Elixir only populates
|
|
40
|
+
`__STACKTRACE__` inside a `rescue` or `catch` clause:
|
|
41
|
+
|
|
42
|
+
```elixir
|
|
43
|
+
try do
|
|
44
|
+
perform_risky_operation()
|
|
45
|
+
rescue
|
|
46
|
+
exception ->
|
|
47
|
+
Sentry.capture_exception(exception, stacktrace: __STACKTRACE__)
|
|
48
|
+
reraise exception, __STACKTRACE__
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
With extra context:
|
|
53
|
+
|
|
54
|
+
```elixir
|
|
55
|
+
try do
|
|
56
|
+
process_order(order_id)
|
|
57
|
+
rescue
|
|
58
|
+
exception ->
|
|
59
|
+
Sentry.capture_exception(exception,
|
|
60
|
+
stacktrace: __STACKTRACE__,
|
|
61
|
+
extra: %{order_id: order_id},
|
|
62
|
+
tags: %{region: "us-east-1"},
|
|
63
|
+
level: :error
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Capturing messages
|
|
69
|
+
|
|
70
|
+
```elixir
|
|
71
|
+
# Simple message
|
|
72
|
+
Sentry.capture_message("Payment gateway timeout")
|
|
73
|
+
|
|
74
|
+
# With context
|
|
75
|
+
Sentry.capture_message("Queue depth exceeded threshold",
|
|
76
|
+
extra: %{depth: 5000, limit: 1000},
|
|
77
|
+
tags: %{queue: "payments"},
|
|
78
|
+
level: :warning
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# With interpolation (since v10.1.0)
|
|
82
|
+
Sentry.capture_message("Failed to process user %s after %d attempts",
|
|
83
|
+
interpolation_parameters: [user_id, attempt_count]
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Context enrichment with `Sentry.Context`
|
|
88
|
+
|
|
89
|
+
Context is stored per-process (in Logger metadata).
|
|
90
|
+
Set it early in request handling — in a Plug, LiveView mount, or GenServer handler:
|
|
91
|
+
|
|
92
|
+
```elixir
|
|
93
|
+
# Set user identity
|
|
94
|
+
Sentry.Context.set_user_context(%{
|
|
95
|
+
id: current_user.id,
|
|
96
|
+
username: current_user.username,
|
|
97
|
+
email: current_user.email,
|
|
98
|
+
ip_address: "{{auto}}" # Sentry infers from request headers
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
# Set tags (searchable in Sentry)
|
|
102
|
+
Sentry.Context.set_tags_context(%{
|
|
103
|
+
subscription_tier: "pro",
|
|
104
|
+
region: "us-east-1"
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
# Set extra context (not searchable — use tags for filtering)
|
|
108
|
+
Sentry.Context.set_extra_context(%{
|
|
109
|
+
order_id: "abc-123",
|
|
110
|
+
items_count: 5
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
# Set request context (URL, method, headers)
|
|
114
|
+
Sentry.Context.set_request_context(%{
|
|
115
|
+
url: conn.request_path,
|
|
116
|
+
method: conn.method,
|
|
117
|
+
headers: Enum.into(conn.req_headers, %{})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
# Add a breadcrumb
|
|
121
|
+
Sentry.Context.add_breadcrumb(%{
|
|
122
|
+
category: "auth",
|
|
123
|
+
message: "User authenticated",
|
|
124
|
+
level: :info,
|
|
125
|
+
data: %{method: "oauth2", provider: "github"}
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
> **Important:** `Sentry.Context` data is scoped to the current process only.
|
|
130
|
+
> It is NOT automatically propagated to spawned `Task` or `GenServer` processes.
|
|
131
|
+
> For async work, pass context values explicitly.
|
|
132
|
+
|
|
133
|
+
### Context in a Phoenix controller (via Plug)
|
|
134
|
+
|
|
135
|
+
```elixir
|
|
136
|
+
defmodule MyAppWeb.Plugs.SentryContext do
|
|
137
|
+
@behaviour Plug
|
|
138
|
+
|
|
139
|
+
def init(opts), do: opts
|
|
140
|
+
|
|
141
|
+
def call(conn, _opts) do
|
|
142
|
+
if user = conn.assigns[:current_user] do
|
|
143
|
+
Sentry.Context.set_user_context(%{
|
|
144
|
+
id: user.id,
|
|
145
|
+
username: user.username,
|
|
146
|
+
email: user.email
|
|
147
|
+
})
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
Sentry.Context.set_request_context(%{
|
|
151
|
+
url: Phoenix.Controller.current_url(conn),
|
|
152
|
+
method: conn.method,
|
|
153
|
+
headers: Enum.into(conn.req_headers, %{})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
conn
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# In your router pipeline:
|
|
161
|
+
pipeline :browser do
|
|
162
|
+
plug :accepts, ["html"]
|
|
163
|
+
plug :fetch_session
|
|
164
|
+
plug :put_secure_browser_headers
|
|
165
|
+
plug MyAppWeb.Plugs.SentryContext
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Breadcrumbs
|
|
170
|
+
|
|
171
|
+
```elixir
|
|
172
|
+
Sentry.Context.add_breadcrumb(%{
|
|
173
|
+
type: "http",
|
|
174
|
+
category: "http",
|
|
175
|
+
message: "GET https://api.stripe.com/v1/charges",
|
|
176
|
+
level: :info,
|
|
177
|
+
data: %{
|
|
178
|
+
url: "https://api.stripe.com/v1/charges",
|
|
179
|
+
method: "GET",
|
|
180
|
+
status_code: 200
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
Sentry.Context.add_breadcrumb(%{
|
|
185
|
+
category: "db.query",
|
|
186
|
+
message: "SELECT * FROM orders WHERE id = ?",
|
|
187
|
+
level: :debug,
|
|
188
|
+
data: %{duration_ms: 42}
|
|
189
|
+
})
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Before-send hook
|
|
193
|
+
|
|
194
|
+
Use `:before_send` to mutate or drop events before they are sent:
|
|
195
|
+
|
|
196
|
+
```elixir
|
|
197
|
+
defmodule MyApp.SentryHooks do
|
|
198
|
+
def before_send(event) do
|
|
199
|
+
# Drop events from test/health endpoints
|
|
200
|
+
if get_in(event, [:request, :url]) |> String.contains?("/health") do
|
|
201
|
+
nil # return nil or false to drop the event
|
|
202
|
+
else
|
|
203
|
+
# Scrub PII from request headers
|
|
204
|
+
event = put_in(event, [:request, :headers, "authorization"], "[FILTERED]")
|
|
205
|
+
# Enrich with deployment metadata
|
|
206
|
+
put_in(event, [:extra, :deploy_sha], System.get_env("GIT_SHA"))
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# config/config.exs
|
|
212
|
+
config :sentry,
|
|
213
|
+
before_send: {MyApp.SentryHooks, :before_send}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Custom event filter
|
|
217
|
+
|
|
218
|
+
`Sentry.EventFilter` is called before `before_send` and before sampling.
|
|
219
|
+
Returning `true` from `exclude_exception?/2` silently drops the event:
|
|
220
|
+
|
|
221
|
+
```elixir
|
|
222
|
+
defmodule MyApp.SentryFilter do
|
|
223
|
+
@behaviour Sentry.EventFilter
|
|
224
|
+
|
|
225
|
+
@impl Sentry.EventFilter
|
|
226
|
+
def exclude_exception?(%MyApp.NotFoundError{}, _source), do: true
|
|
227
|
+
def exclude_exception?(%MyApp.ValidationError{}, _source), do: true
|
|
228
|
+
def exclude_exception?(exception, source) do
|
|
229
|
+
# Delegate other exceptions to the default filter
|
|
230
|
+
Sentry.DefaultEventFilter.exclude_exception?(exception, source)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# config/config.exs
|
|
235
|
+
config :sentry, filter: MyApp.SentryFilter
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`Sentry.DefaultEventFilter` already excludes common Phoenix/Plug noise:
|
|
239
|
+
|
|
240
|
+
- `Ecto.NoResultsError`
|
|
241
|
+
- `Phoenix.Router.NoRouteError`
|
|
242
|
+
- `Plug.Parsers.BadEncodingError`, `ParseError`, `RequestTooLargeError`,
|
|
243
|
+
`UnsupportedMediaTypeError`
|
|
244
|
+
|
|
245
|
+
### Fingerprinting and custom grouping
|
|
246
|
+
|
|
247
|
+
Override Sentry’s default grouping algorithm via `before_send`:
|
|
248
|
+
|
|
249
|
+
```elixir
|
|
250
|
+
def before_send(%{exception: [%{type: "MyApp.DatabaseError"} | _]} = event) do
|
|
251
|
+
%{event | fingerprint: ["database-connection", event.extra[:db_host]]}
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def before_send(event) do
|
|
255
|
+
# Extend default grouping with additional discriminators
|
|
256
|
+
if event.exception != [] do
|
|
257
|
+
%{event | fingerprint: ["{{ default }}", System.get_env("RELEASE_NODE")]}
|
|
258
|
+
else
|
|
259
|
+
event
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Attachments (since v10.1.0)
|
|
265
|
+
|
|
266
|
+
```elixir
|
|
267
|
+
Sentry.Context.add_attachment(%Sentry.Attachment{
|
|
268
|
+
filename: "debug.log",
|
|
269
|
+
data: File.read!("debug.log")
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
# Then capture the exception as usual
|
|
273
|
+
Sentry.capture_exception(exception, stacktrace: __STACKTRACE__)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Flush pending events
|
|
277
|
+
|
|
278
|
+
```elixir
|
|
279
|
+
# Default 5-second timeout
|
|
280
|
+
Sentry.flush()
|
|
281
|
+
|
|
282
|
+
# Custom timeout
|
|
283
|
+
Sentry.flush(timeout: 10_000)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Source Code Context in Production
|
|
287
|
+
|
|
288
|
+
Without packaging, production releases strip source code — Sentry cannot show the lines
|
|
289
|
+
around an error. Package your source before building:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Run before mix release
|
|
293
|
+
mix sentry.package_source_code
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
OTP 28 compatibility — use string patterns instead of compiled regexps:
|
|
297
|
+
|
|
298
|
+
```elixir
|
|
299
|
+
# config/config.exs
|
|
300
|
+
config :sentry,
|
|
301
|
+
source_code_exclude_patterns: ["/_build/", "/deps/", "/priv/", "/test/"]
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## `send_result` Types
|
|
305
|
+
|
|
306
|
+
| Value | Description |
|
|
307
|
+
| --------------------------- | ---------------------------------------------------- |
|
|
308
|
+
| `{:ok, event_id}` | Event sent successfully (`:send_result: :sync` only) |
|
|
309
|
+
| `:ignored` | Not sent (no DSN, filtered, sampled out) |
|
|
310
|
+
| `:excluded` | Dropped by `EventFilter` |
|
|
311
|
+
| `{:error, ClientError.t()}` | HTTP-level send error |
|
|
312
|
+
|
|
313
|
+
## Best Practices
|
|
314
|
+
|
|
315
|
+
- Always pass `stacktrace: __STACKTRACE__` in rescue blocks — stacktraces are not
|
|
316
|
+
automatically captured in Elixir
|
|
317
|
+
- Set `in_app_otp_apps: [:my_app]` to distinguish your code from library code in
|
|
318
|
+
Sentry’s issue grouping
|
|
319
|
+
- Use `Sentry.Context.set_user_context/1` early in the request lifecycle (e.g., in a
|
|
320
|
+
Plug) so every event from that process includes user identity
|
|
321
|
+
- Use `Sentry.EventFilter` for structural filtering (known non-errors), and
|
|
322
|
+
`before_send` for event mutation or conditional dropping
|
|
323
|
+
- Run `mix sentry.package_source_code` as part of your release build process so
|
|
324
|
+
production stacktraces show source lines
|
|
325
|
+
|
|
326
|
+
## Troubleshooting
|
|
327
|
+
|
|
328
|
+
| Issue | Solution |
|
|
329
|
+
| ----------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
330
|
+
| No stack trace on captured exception | Add `stacktrace: __STACKTRACE__` to `capture_exception/2` inside the rescue block |
|
|
331
|
+
| Events not appearing | Set `log_level: :debug`; check DSN; call `Sentry.flush/1` before process exit |
|
|
332
|
+
| All events showing “in-app: false” | Set `in_app_otp_apps: [:my_app]` in config to mark your app’s modules as in-app |
|
|
333
|
+
| Context missing from async events | `Sentry.Context` is process-scoped — pass data explicitly to spawned tasks/GenServers |
|
|
334
|
+
| `before_send` not dropping events | Ensure function returns `nil` or `false` (not an empty map) to drop the event |
|
|
335
|
+
| Duplicate events (Cowboy + LoggerHandler) | Set `excluded_domains: [:cowboy]` in `LoggerHandler` config (default behavior) |
|