runsignal 0.2.0__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.
@@ -0,0 +1,495 @@
1
+ ## Ignore Visual Studio temporary files, build results, and
2
+ ## files generated by popular Visual Studio add-ons.
3
+ ##
4
+ ## Get latest from `dotnet new gitignore`
5
+
6
+ # dotenv files
7
+ .env
8
+
9
+ # User-specific files
10
+ *.rsuser
11
+ *.suo
12
+ *.user
13
+ *.userosscache
14
+ *.sln.docstates
15
+
16
+ # User-specific files (MonoDevelop/Xamarin Studio)
17
+ *.userprefs
18
+
19
+ # Mono auto generated files
20
+ mono_crash.*
21
+
22
+ # Build results
23
+ [Dd]ebug/
24
+ [Dd]ebugPublic/
25
+ [Rr]elease/
26
+ [Rr]eleases/
27
+ x64/
28
+ x86/
29
+ [Ww][Ii][Nn]32/
30
+ [Aa][Rr][Mm]/
31
+ [Aa][Rr][Mm]64/
32
+ bld/
33
+ [Bb]in/
34
+ [Oo]bj/
35
+ [Ll]og/
36
+ [Ll]ogs/
37
+
38
+ # Visual Studio 2015/2017 cache/options directory
39
+ .vs/
40
+ # Uncomment if you have tasks that create the project's static files in wwwroot
41
+ #wwwroot/
42
+
43
+ # Visual Studio 2017 auto generated files
44
+ Generated\ Files/
45
+
46
+ # MSTest test Results
47
+ [Tt]est[Rr]esult*/
48
+ [Bb]uild[Ll]og.*
49
+
50
+ # NUnit
51
+ *.VisualState.xml
52
+ TestResult.xml
53
+ nunit-*.xml
54
+
55
+ # Build Results of an ATL Project
56
+ [Dd]ebugPS/
57
+ [Rr]eleasePS/
58
+ dlldata.c
59
+
60
+ # Benchmark Results
61
+ BenchmarkDotNet.Artifacts/
62
+
63
+ # .NET
64
+ project.lock.json
65
+ project.fragment.lock.json
66
+ artifacts/
67
+
68
+ # Tye
69
+ .tye/
70
+
71
+ # ASP.NET Scaffolding
72
+ ScaffoldingReadMe.txt
73
+
74
+ # StyleCop
75
+ StyleCopReport.xml
76
+
77
+ # Files built by Visual Studio
78
+ *_i.c
79
+ *_p.c
80
+ *_h.h
81
+ *.ilk
82
+ *.meta
83
+ *.obj
84
+ *.iobj
85
+ *.pch
86
+ *.pdb
87
+ *.ipdb
88
+ *.pgc
89
+ *.pgd
90
+ *.rsp
91
+ *.sbr
92
+ *.tlb
93
+ *.tli
94
+ *.tlh
95
+ *.tmp
96
+ *.tmp_proj
97
+ *_wpftmp.csproj
98
+ *.log
99
+ *.tlog
100
+ *.vspscc
101
+ *.vssscc
102
+ .builds
103
+ *.pidb
104
+ *.svclog
105
+ *.scc
106
+
107
+ # Chutzpah Test files
108
+ _Chutzpah*
109
+
110
+ # Visual C++ cache files
111
+ ipch/
112
+ *.aps
113
+ *.ncb
114
+ *.opendb
115
+ *.opensdf
116
+ *.sdf
117
+ *.cachefile
118
+ *.VC.db
119
+ *.VC.VC.opendb
120
+
121
+ # Visual Studio profiler
122
+ *.psess
123
+ *.vsp
124
+ *.vspx
125
+ *.sap
126
+
127
+ # Visual Studio Trace Files
128
+ *.e2e
129
+
130
+ # TFS 2012 Local Workspace
131
+ $tf/
132
+
133
+ # Guidance Automation Toolkit
134
+ *.gpState
135
+
136
+ # ReSharper is a .NET coding add-in
137
+ _ReSharper*/
138
+ *.[Rr]e[Ss]harper
139
+ *.DotSettings.user
140
+
141
+ # TeamCity is a build add-in
142
+ _TeamCity*
143
+
144
+ # DotCover is a Code Coverage Tool
145
+ *.dotCover
146
+
147
+ # AxoCover is a Code Coverage Tool
148
+ .axoCover/*
149
+ !.axoCover/settings.json
150
+
151
+ # Coverlet is a free, cross platform Code Coverage Tool
152
+ coverage*.json
153
+ coverage*.xml
154
+ coverage*.info
155
+
156
+ # Visual Studio code coverage results
157
+ *.coverage
158
+ *.coveragexml
159
+
160
+ # NCrunch
161
+ _NCrunch_*
162
+ .*crunch*.local.xml
163
+ nCrunchTemp_*
164
+
165
+ # MightyMoose
166
+ *.mm.*
167
+ AutoTest.Net/
168
+
169
+ # Web workbench (sass)
170
+ .sass-cache/
171
+
172
+ # Installshield output folder
173
+ [Ee]xpress/
174
+
175
+ # DocProject is a documentation generator add-in
176
+ DocProject/buildhelp/
177
+ DocProject/Help/*.HxT
178
+ DocProject/Help/*.HxC
179
+ DocProject/Help/*.hhc
180
+ DocProject/Help/*.hhk
181
+ DocProject/Help/*.hhp
182
+ DocProject/Help/Html2
183
+ DocProject/Help/html
184
+
185
+ # Click-Once directory
186
+ publish/
187
+
188
+ # Publish Web Output
189
+ *.[Pp]ublish.xml
190
+ *.azurePubxml
191
+ # Note: Comment the next line if you want to checkin your web deploy settings,
192
+ # but database connection strings (with potential passwords) will be unencrypted
193
+ *.pubxml
194
+ *.publishproj
195
+
196
+ # Microsoft Azure Web App publish settings. Comment the next line if you want to
197
+ # checkin your Azure Web App publish settings, but sensitive information contained
198
+ # in these scripts will be unencrypted
199
+ PublishScripts/
200
+
201
+ # NuGet Packages
202
+ *.nupkg
203
+ # NuGet Symbol Packages
204
+ *.snupkg
205
+ # The packages folder can be ignored because of Package Restore
206
+ **/[Pp]ackages/*
207
+ # except build/, which is used as an MSBuild target.
208
+ !**/[Pp]ackages/build/
209
+ # Uncomment if necessary however generally it will be regenerated when needed
210
+ #!**/[Pp]ackages/repositories.config
211
+ # NuGet v3's project.json files produces more ignorable files
212
+ *.nuget.props
213
+ *.nuget.targets
214
+
215
+ # Microsoft Azure Build Output
216
+ csx/
217
+ *.build.csdef
218
+
219
+ # Microsoft Azure Emulator
220
+ ecf/
221
+ rcf/
222
+
223
+ # Windows Store app package directories and files
224
+ AppPackages/
225
+ BundleArtifacts/
226
+ Package.StoreAssociation.xml
227
+ _pkginfo.txt
228
+ *.appx
229
+ *.appxbundle
230
+ *.appxupload
231
+
232
+ # Visual Studio cache files
233
+ # files ending in .cache can be ignored
234
+ *.[Cc]ache
235
+ # but keep track of directories ending in .cache
236
+ !?*.[Cc]ache/
237
+
238
+ # Others
239
+ ClientBin/
240
+ ~$*
241
+ *~
242
+ *.dbmdl
243
+ *.dbproj.schemaview
244
+ *.jfm
245
+ *.pfx
246
+ *.publishsettings
247
+ orleans.codegen.cs
248
+
249
+ # Including strong name files can present a security risk
250
+ # (https://github.com/github/gitignore/pull/2483#issue-259490424)
251
+ #*.snk
252
+
253
+ # Since there are multiple workflows, uncomment next line to ignore bower_components
254
+ # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
255
+ #bower_components/
256
+
257
+ # RIA/Silverlight projects
258
+ Generated_Code/
259
+
260
+ # Backup & report files from converting an old project file
261
+ # to a newer Visual Studio version. Backup files are not needed,
262
+ # because we have git ;-)
263
+ _UpgradeReport_Files/
264
+ Backup*/
265
+ UpgradeLog*.XML
266
+ UpgradeLog*.htm
267
+ ServiceFabricBackup/
268
+ *.rptproj.bak
269
+
270
+ # SQL Server files
271
+ *.mdf
272
+ *.ldf
273
+ *.ndf
274
+
275
+ # Business Intelligence projects
276
+ *.rdl.data
277
+ *.bim.layout
278
+ *.bim_*.settings
279
+ *.rptproj.rsuser
280
+ *- [Bb]ackup.rdl
281
+ *- [Bb]ackup ([0-9]).rdl
282
+ *- [Bb]ackup ([0-9][0-9]).rdl
283
+
284
+ # Microsoft Fakes
285
+ FakesAssemblies/
286
+
287
+ # GhostDoc plugin setting file
288
+ *.GhostDoc.xml
289
+
290
+ # Node.js Tools for Visual Studio
291
+ .ntvs_analysis.dat
292
+ node_modules/
293
+
294
+ # Visual Studio 6 build log
295
+ *.plg
296
+
297
+ # Visual Studio 6 workspace options file
298
+ *.opt
299
+
300
+ # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
301
+ *.vbw
302
+
303
+ # Visual Studio 6 auto-generated project file (contains which files were open etc.)
304
+ *.vbp
305
+
306
+ # Visual Studio 6 workspace and project file (working project files containing files to include in project)
307
+ *.dsw
308
+ *.dsp
309
+
310
+ # Visual Studio 6 technical files
311
+ *.ncb
312
+ *.aps
313
+
314
+ # Visual Studio LightSwitch build output
315
+ **/*.HTMLClient/GeneratedArtifacts
316
+ **/*.DesktopClient/GeneratedArtifacts
317
+ **/*.DesktopClient/ModelManifest.xml
318
+ **/*.Server/GeneratedArtifacts
319
+ **/*.Server/ModelManifest.xml
320
+ _Pvt_Extensions
321
+
322
+ # Paket dependency manager
323
+ .paket/paket.exe
324
+ paket-files/
325
+
326
+ # FAKE - F# Make
327
+ .fake/
328
+
329
+ # CodeRush personal settings
330
+ .cr/personal
331
+
332
+ # Python Tools for Visual Studio (PTVS)
333
+ __pycache__/
334
+ *.pyc
335
+
336
+ # Cake - Uncomment if you are using it
337
+ # tools/**
338
+ # !tools/packages.config
339
+
340
+ # Tabs Studio
341
+ *.tss
342
+
343
+ # Telerik's JustMock configuration file
344
+ *.jmconfig
345
+
346
+ # BizTalk build output
347
+ *.btp.cs
348
+ *.btm.cs
349
+ *.odx.cs
350
+ *.xsd.cs
351
+
352
+ # OpenCover UI analysis results
353
+ OpenCover/
354
+
355
+ # Azure Stream Analytics local run output
356
+ ASALocalRun/
357
+
358
+ # MSBuild Binary and Structured Log
359
+ *.binlog
360
+
361
+ # NVidia Nsight GPU debugger configuration file
362
+ *.nvuser
363
+
364
+ # MFractors (Xamarin productivity tool) working folder
365
+ .mfractor/
366
+
367
+ # Local History for Visual Studio
368
+ .localhistory/
369
+
370
+ # Visual Studio History (VSHistory) files
371
+ .vshistory/
372
+
373
+ # BeatPulse healthcheck temp database
374
+ healthchecksdb
375
+
376
+ # Backup folder for Package Reference Convert tool in Visual Studio 2017
377
+ MigrationBackup/
378
+
379
+ # Ionide (cross platform F# VS Code tools) working folder
380
+ .ionide/
381
+
382
+ # Fody - auto-generated XML schema
383
+ FodyWeavers.xsd
384
+
385
+ # VS Code files for those working on multiple tools
386
+ .vscode/*
387
+ !.vscode/settings.json
388
+ !.vscode/tasks.json
389
+ !.vscode/launch.json
390
+ !.vscode/extensions.json
391
+ *.code-workspace
392
+
393
+ # Local History for Visual Studio Code
394
+ .history/
395
+
396
+ # Windows Installer files from build outputs
397
+ *.cab
398
+ *.msi
399
+ *.msix
400
+ *.msm
401
+ *.msp
402
+
403
+ # JetBrains Rider
404
+ *.sln.iml
405
+ .idea/
406
+
407
+ ##
408
+ ## Visual studio for Mac
409
+ ##
410
+
411
+
412
+ # globs
413
+ Makefile.in
414
+ *.userprefs
415
+ *.usertasks
416
+ config.make
417
+ config.status
418
+ aclocal.m4
419
+ install-sh
420
+ autom4te.cache/
421
+ *.tar.gz
422
+ tarballs/
423
+ test-results/
424
+
425
+ # Mac bundle stuff
426
+ *.dmg
427
+ *.app
428
+
429
+ # content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore
430
+ # General
431
+ .DS_Store
432
+ .AppleDouble
433
+ .LSOverride
434
+
435
+ # Icon must end with two \r
436
+ Icon
437
+
438
+
439
+ # Thumbnails
440
+ ._*
441
+
442
+ # Files that might appear in the root of a volume
443
+ .DocumentRevisions-V100
444
+ .fseventsd
445
+ .Spotlight-V100
446
+ .TemporaryItems
447
+ .Trashes
448
+ .VolumeIcon.icns
449
+ .com.apple.timemachine.donotpresent
450
+
451
+ # Directories potentially created on remote AFP share
452
+ .AppleDB
453
+ .AppleDesktop
454
+ Network Trash Folder
455
+ Temporary Items
456
+ .apdisk
457
+
458
+ # content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore
459
+ # Windows thumbnail cache files
460
+ Thumbs.db
461
+ ehthumbs.db
462
+ ehthumbs_vista.db
463
+
464
+ # Dump file
465
+ *.stackdump
466
+
467
+ # Folder config file
468
+ [Dd]esktop.ini
469
+
470
+ # Recycle Bin used on file shares
471
+ $RECYCLE.BIN/
472
+
473
+ # Windows Installer files
474
+ *.cab
475
+ *.msi
476
+ *.msix
477
+ *.msm
478
+ *.msp
479
+
480
+ # Windows shortcuts
481
+ *.lnk
482
+
483
+ # Vim temporary swap files
484
+ *.swp
485
+ BenchmarkDotNet.Artifacts/
486
+
487
+ # Go
488
+ *.test
489
+ *.out
490
+
491
+ # Python SDK
492
+ .venv/
493
+ *.egg-info/
494
+ src/RunSignal.Python/dist/
495
+ .pytest_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gokhan Ertogan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,205 @@
1
+ Metadata-Version: 2.5
2
+ Name: runsignal
3
+ Version: 0.2.0
4
+ Summary: Cron job and background job monitoring for Python with OpenTelemetry: alert when a Celery task, APScheduler job or cron script is missed, fails, overruns, stalls or processes nothing, in New Relic, Prometheus and Grafana.
5
+ Project-URL: Homepage, https://github.com/gokhanertogan/RunSignal
6
+ Project-URL: Documentation, https://github.com/gokhanertogan/RunSignal/tree/main/src/RunSignal.Python#readme
7
+ Project-URL: Repository, https://github.com/gokhanertogan/RunSignal
8
+ Project-URL: Issues, https://github.com/gokhanertogan/RunSignal/issues
9
+ Project-URL: Changelog, https://github.com/gokhanertogan/RunSignal/blob/main/CHANGELOG.md
10
+ Author: Gokhan Ertogan
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: alerting,apscheduler,background-jobs,celery,celery-beat,cron,cron-monitoring,dead-mans-switch,grafana,healthcheck,heartbeat,job-monitoring,monitoring,newrelic,observability,opentelemetry,otel,prometheus,scheduled-jobs,worker
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Framework :: Celery
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: System Administrators
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Topic :: System :: Logging
22
+ Classifier: Topic :: System :: Monitoring
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: opentelemetry-api>=1.27
26
+ Provides-Extra: apscheduler
27
+ Requires-Dist: apscheduler<4,>=3.10; extra == 'apscheduler'
28
+ Provides-Extra: celery
29
+ Requires-Dist: celery>=5.3; extra == 'celery'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # RunSignal for Python
33
+
34
+ [![PyPI](https://img.shields.io/pypi/v/runsignal)](https://pypi.org/project/runsignal/)
35
+ [![Python versions](https://img.shields.io/pypi/pyversions/runsignal)](https://pypi.org/project/runsignal/)
36
+ [![license](https://img.shields.io/pypi/l/runsignal)](https://github.com/gokhanertogan/RunSignal/blob/main/LICENSE)
37
+
38
+ **Cron job and background job monitoring for Python, on OpenTelemetry.** Get an alert when a Celery task or
39
+ Celery beat schedule, an APScheduler job or a cron script is missed, fails, runs too long or processes nothing,
40
+ and when a consumer or worker loop stops making progress: the failures no HTTP status code describes. No hosted
41
+ service and no ping URLs: the alerts live in the backend you already use.
42
+
43
+ RunSignal records runs, items and heartbeats as OpenTelemetry metrics, with the same names as every other
44
+ RunSignal SDK, so the `runsignal` CLI provisions the same alerts for Python services as for .NET, Go, Java,
45
+ Node.js and Rust ones: New Relic alerts and dashboards, or Prometheus rules and a Grafana dashboard. The
46
+ [main README](https://github.com/gokhanertogan/RunSignal#readme) covers the concepts, the alerts and the
47
+ backends.
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ pip install runsignal # core, depends only on opentelemetry-api
53
+ pip install "runsignal[celery]" # with Celery 5.3+
54
+ pip install "runsignal[apscheduler]" # with APScheduler 3.10+
55
+ ```
56
+
57
+ Requires Python 3.9 or later. Install and configure the OpenTelemetry SDK and an exporter as usual
58
+ (`opentelemetry-sdk`, `opentelemetry-exporter-otlp`).
59
+
60
+ ## Quick start
61
+
62
+ ```python
63
+ from runsignal import default_registry
64
+
65
+ invoices = default_registry.workload(
66
+ "nightly-invoice",
67
+ schedule="0 2 * * *",
68
+ max_duration="30m", # or seconds, or a timedelta
69
+ min_processed=100,
70
+ severity="critical",
71
+ )
72
+
73
+ with invoices.run():
74
+ for order in unbilled_orders():
75
+ create_invoice(order)
76
+ invoices.processed()
77
+ ```
78
+
79
+ Or as a decorator, for sync and async functions:
80
+
81
+ ```python
82
+ from runsignal import monitor
83
+
84
+ @monitor("nightly-invoice", schedule="0 2 * * *", max_duration="30m")
85
+ def generate_invoices():
86
+ for order in unbilled_orders():
87
+ create_invoice(order)
88
+ generate_invoices.workload.processed()
89
+ ```
90
+
91
+ `default_registry` records on the global OpenTelemetry providers, which forward to the SDK once it is configured,
92
+ so it works even when it is created by an import first. For explicit providers, use
93
+ `WorkloadRegistry(meter_provider=..., tracer_provider=...)`.
94
+
95
+ ## Recording work
96
+
97
+ | Call | Records |
98
+ |---|---|
99
+ | `with w.run():` / `async with w.run():` | One run. It succeeds when the block completes, is cancelled on `asyncio.CancelledError`, `KeyboardInterrupt` or `SystemExit`, and fails on any other exception, which propagates. The run's span is current inside the block. |
100
+ | `@w.monitor` / `@monitor(name, **expectations)` | One run per call, for sync and async functions. |
101
+ | `run = w.start()` then `run.succeed()` / `fail(exc)` / `fail_code(code, description)` / `cancel()` | A run ended by hand. Later calls are ignored. |
102
+ | `w.processed(n)` / `w.item_failed(n)` | Items handled. |
103
+ | `w.beat()` | A heartbeat from a long-running loop. |
104
+
105
+ `error.type` is the exception's `error_type` attribute if it has one (such as `"statement_missing"`), otherwise
106
+ its fully qualified class name (`ValueError`, `requests.exceptions.ConnectionError`).
107
+
108
+ ## Celery
109
+
110
+ ```python
111
+ from celery import Celery
112
+ from celery.schedules import crontab
113
+ from runsignal.celery import instrument, workload_of
114
+
115
+ app = Celery("billing")
116
+ app.conf.beat_schedule = {
117
+ "nightly-invoice": {"task": "billing.tasks.generate_invoices", "schedule": crontab(hour=2, minute=0)},
118
+ }
119
+ instrument(app, workloads={"billing.tasks.generate_invoices": {"max_duration": "30m", "severity": "critical"}})
120
+
121
+ @app.task(bind=True)
122
+ def generate_invoices(self):
123
+ for order in unbilled_orders():
124
+ create_invoice(order)
125
+ workload_of(self).processed()
126
+ ```
127
+
128
+ - **What is recorded:** every task execution, with no code in your tasks: success, failure with the exception's type (each retry counts as a failed run), cancellation when a task is revoked, and duration. Celery's own `celery.*` tasks are skipped.
129
+ - **Naming:** tasks are named after their task name (`billing.tasks.generate_invoices`).
130
+ - **Celery beat:** `instrument(app)` registers every task in `beat_schedule` with its `crontab` as the schedule, so a periodic task that never runs gets a `missed` alert. Interval schedules get no schedule.
131
+ - **Manifest:** the worker writes it when it is ready if `RUNSIGNAL_MANIFEST_PATH` is set. In CI, import the app and call `default_registry.write_manifest_from_env("billing")` instead; no broker is needed.
132
+ - **Prefork workers:** configure the OpenTelemetry SDK in `worker_process_init`, as for any OpenTelemetry instrumentation of Celery.
133
+
134
+ ## APScheduler
135
+
136
+ ```python
137
+ from apscheduler.schedulers.background import BackgroundScheduler
138
+ from runsignal.apscheduler import instrument
139
+
140
+ scheduler = BackgroundScheduler()
141
+ scheduler.add_job(generate_invoices, "cron", hour=2, minute=0, id="nightly-invoice")
142
+ instrument(scheduler, workloads={"nightly-invoice": {"max_duration": "30m", "severity": "critical"}})
143
+ scheduler.start()
144
+ ```
145
+
146
+ - **What is recorded:** every run of every job, including jobs added after `instrument`, for all schedulers (`BackgroundScheduler`, `BlockingScheduler`, `AsyncIOScheduler`, ...).
147
+ - **Naming:** jobs are named after the `id` you gave them, or their `name` when APScheduler generated the id.
148
+ - **Schedules:** cron triggers become the workload's schedule, with APScheduler's Monday = 0 converted to cron's numbering. Triggers that use `year`, `week` or days such as `1st mon` get no schedule, with a warning.
149
+ - **Persistent job stores** (SQLAlchemy, Redis, MongoDB) cannot store a wrapped function: decorate the job function with `@default_registry.monitor("nightly-invoice")` instead. `instrument` still registers the schedule.
150
+
151
+ ## Cron scripts and loops
152
+
153
+ A script run by cron, a Kubernetes CronJob or systemd timer is one run per invocation:
154
+
155
+ ```python
156
+ with default_registry.workload("nightly-invoice", schedule="0 2 * * *").run():
157
+ main()
158
+ ```
159
+
160
+ The `missed` alert needs a long-running process to report `since_success`, so for short-lived scripts declare
161
+ the workload in the service that exports continuously, or rely on the `failed`, `overrun` and `low output`
162
+ alerts. Long-running loops send heartbeats:
163
+
164
+ ```python
165
+ sync = default_registry.workload("stock-sync", expect_every="5m")
166
+ while True:
167
+ sync_stock()
168
+ sync.beat()
169
+ ```
170
+
171
+ ## Manifest
172
+
173
+ ```python
174
+ default_registry.write_manifest_from_env("billing-service") # writes to RUNSIGNAL_MANIFEST_PATH when it is set
175
+ ```
176
+
177
+ Then provision the alerts from CI with the [`runsignal` CLI](https://github.com/gokhanertogan/RunSignal#cli).
178
+
179
+ ## Exporting
180
+
181
+ **New Relic** needs delta temporality. With cumulative, a stopped workload never looks stopped:
182
+
183
+ ```bash
184
+ export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.eu01.nr-data.net:4317 # US: https://otlp.nr-data.net:4317
185
+ export OTEL_EXPORTER_OTLP_HEADERS=api-key=<your license key>
186
+ export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=delta
187
+ ```
188
+
189
+ **Prometheus 3** receives OTLP at `/api/v1/otlp/v1/metrics` with the default (cumulative) temporality. Generate
190
+ its rules with `runsignal export prometheus` and a dashboard with `runsignal export grafana`.
191
+
192
+ ## Development
193
+
194
+ ```bash
195
+ python -m venv .venv && .venv/bin/pip install -e ".[celery,apscheduler]" opentelemetry-sdk pytest
196
+ .venv/bin/pytest
197
+ ```
198
+
199
+ The tests include the cross-language conformance vectors in `spec/conformance`.
200
+
201
+ ## Links
202
+
203
+ [Specification](https://github.com/gokhanertogan/RunSignal/blob/main/spec/README.md) ·
204
+ [Changelog](https://github.com/gokhanertogan/RunSignal/blob/main/CHANGELOG.md) ·
205
+ [Other languages](https://github.com/gokhanertogan/RunSignal#quick-start)