Skip to content

Commit 72c98b0

Browse files
authored
Add new optional parameter on the job() wrapper to sync monitor attr… (#35)
* Add new optional parameter on the job() wrapper to sync monitor attributes at startup * Update readme * Add a test that will always run first to test the auto-sync that happens on module import
1 parent bf3114b commit 72c98b0

4 files changed

Lines changed: 75 additions & 3 deletions

File tree

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,21 @@ def send_invoices_task(*args, **kwargs):
8080
...
8181
```
8282

83+
#### You can provide monitor attributes that will be synced when your app starts
84+
85+
To sync attributes, provide an API key with monitor:write privileges.
86+
87+
```python
88+
import cronitor
89+
90+
# Copy your SDK Integration key from https://cronitor.io/settings/api
91+
cronitor.api_key = 'apiKey123'
92+
93+
@cronitor.job('send-invoices', attributes={'schedule': '0 8 * * *', 'notify': ['devops-alerts']})
94+
def send_invoices_task(*args, **kwargs):
95+
...
96+
```
97+
8398
## Sending Telemetry Events
8499

85100
If you want to send a heartbeat events, or want finer control over when/how [telemetry events](https://cronitor.io/docs/telemetry-api) are sent for your jobs, you can create a monitor instance and call the `.ping` method.

cronitor/__init__.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
from datetime import datetime
44
from functools import wraps
55
import sys
6-
import requests
76
import yaml
87
from yaml.loader import SafeLoader
8+
import time
9+
import atexit
10+
import threading
911

1012
from .monitor import Monitor, YAML
1113

@@ -23,6 +25,9 @@
2325

2426
celerybeat_only = False
2527

28+
# monitor attributes can be synced at process startup
29+
monitor_attributes = []
30+
2631
# this is a pointer to the module object instance itself.
2732
this = sys.modules[__name__]
2833
if this.config:
@@ -50,7 +55,12 @@ class State(object):
5055
FAIL = 'fail'
5156

5257
# include_output is deprecated in favor of log_output and can be removed in 5.0 release
53-
def job(key, env=None, log_output=True, include_output=True):
58+
def job(key, env=None, log_output=True, include_output=True, attributes=None):
59+
60+
if type(attributes) is dict:
61+
attributes['key'] = key
62+
monitor_attributes.append(attributes)
63+
5464
def wrapper(func):
5565
@wraps(func)
5666
def wrapped(*args, **kwargs):
@@ -108,3 +118,19 @@ def read_config(path=None, output=False):
108118
data = yaml.load(conf, Loader=SafeLoader)
109119
if output:
110120
return data
121+
122+
def sync_monitors(wait=1):
123+
global monitor_attributes
124+
if wait > 0:
125+
time.sleep(wait)
126+
127+
if len(monitor_attributes):
128+
Monitor.put(monitor_attributes)
129+
monitor_attributes = []
130+
131+
try:
132+
sync
133+
except NameError:
134+
sync = threading.Thread(target=sync_monitors)
135+
sync.start()
136+
atexit.register(sync.join)

cronitor/tests/test_00.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import yaml
2+
import cronitor
3+
import unittest
4+
from unittest.mock import call, patch, ANY
5+
import time
6+
import cronitor
7+
8+
FAKE_API_KEY = 'cb54ac4fd16142469f2d84fc1bbebd84XXXDEADXXX'
9+
YAML_PATH = './cronitor/tests/cronitor.yaml'
10+
11+
cronitor.api_key = FAKE_API_KEY
12+
cronitor.timeout = 10
13+
14+
class SyncTests(unittest.TestCase):
15+
16+
def setUp(self):
17+
return super().setUp()
18+
19+
def test_00_monitor_attributes_are_put(self):
20+
# This test will run first, test that attributes are synced correctly, and then undo the global mock
21+
22+
with patch('cronitor.Monitor.put') as mock_put:
23+
time.sleep(2)
24+
calls = [call([{'key': 'ping-decorator-test', 'name': 'Ping Decorator Test'}])]
25+
mock_put.assert_has_calls(calls)
26+
27+
@cronitor.job('ping-decorator-test', attributes={'name': 'Ping Decorator Test'})
28+
def function_call_with_attributes(self):
29+
return

cronitor/tests/test_pings.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import unittest
33
from unittest.mock import patch, ANY, call
44
from unittest.mock import MagicMock
5-
65
import cronitor
6+
import pytest
77

88
# a reserved monitorkey for running integration tests against cronitor.link
99
FAKE_KEY = 'd3x0c1'
@@ -83,6 +83,7 @@ def test_ping_wraps_function_raises_exception(self, mocked_ping):
8383
self.assertRaises(Exception, lambda: self.error_function_call())
8484
mocked_ping.assert_has_calls(calls)
8585

86+
8687
@patch('cronitor.Monitor.ping')
8788
@patch('cronitor.Monitor.__init__')
8889
def test_ping_with_non_default_env(self, mocked_monitor, mocked_ping):
@@ -104,3 +105,4 @@ def staging_env_function_call(self):
104105

105106

106107

108+

0 commit comments

Comments
 (0)