Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new optional parameter on the job() wrapper to sync monitor attr… #35

Merged
merged 6 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,21 @@ def send_invoices_task(*args, **kwargs):
...
```

#### You can provide monitor attributes that will be synced when your app starts

To sync attributes, provide an API key with monitor:write privileges.

```python
import cronitor

# Copy your SDK Integration key from https://cronitor.io/settings/api
cronitor.api_key = 'apiKey123'

@cronitor.job('send-invoices', attributes={'schedule': '0 8 * * *', 'notify': ['devops-alerts']})
def send_invoices_task(*args, **kwargs):
...
```

## Sending Telemetry Events

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.
Expand Down
30 changes: 28 additions & 2 deletions cronitor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from datetime import datetime
from functools import wraps
import sys
import requests
import yaml
from yaml.loader import SafeLoader
import time
import atexit
import threading

from .monitor import Monitor, YAML

Expand All @@ -23,6 +25,9 @@

celerybeat_only = False

# monitor attributes can be synced at process startup
monitor_attributes = []

# this is a pointer to the module object instance itself.
this = sys.modules[__name__]
if this.config:
Expand Down Expand Up @@ -50,7 +55,12 @@ class State(object):
FAIL = 'fail'

# include_output is deprecated in favor of log_output and can be removed in 5.0 release
def job(key, env=None, log_output=True, include_output=True):
def job(key, env=None, log_output=True, include_output=True, attributes=None):

if type(attributes) is dict:
attributes['key'] = key
monitor_attributes.append(attributes)

def wrapper(func):
@wraps(func)
def wrapped(*args, **kwargs):
Expand Down Expand Up @@ -108,3 +118,19 @@ def read_config(path=None, output=False):
data = yaml.load(conf, Loader=SafeLoader)
if output:
return data

def sync_monitors(wait=1):
global monitor_attributes
if wait > 0:
time.sleep(wait)

if len(monitor_attributes):
Monitor.put(monitor_attributes)
monitor_attributes = []

try:
sync
except NameError:
sync = threading.Thread(target=sync_monitors)
sync.start()
atexit.register(sync.join)
29 changes: 29 additions & 0 deletions cronitor/tests/test_00.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import yaml
import cronitor
import unittest
from unittest.mock import call, patch, ANY
import time
import cronitor

FAKE_API_KEY = 'cb54ac4fd16142469f2d84fc1bbebd84XXXDEADXXX'
YAML_PATH = './cronitor/tests/cronitor.yaml'

cronitor.api_key = FAKE_API_KEY
cronitor.timeout = 10

class SyncTests(unittest.TestCase):

def setUp(self):
return super().setUp()

def test_00_monitor_attributes_are_put(self):
# This test will run first, test that attributes are synced correctly, and then undo the global mock

with patch('cronitor.Monitor.put') as mock_put:
time.sleep(2)
calls = [call([{'key': 'ping-decorator-test', 'name': 'Ping Decorator Test'}])]
mock_put.assert_has_calls(calls)

@cronitor.job('ping-decorator-test', attributes={'name': 'Ping Decorator Test'})
def function_call_with_attributes(self):
return
4 changes: 3 additions & 1 deletion cronitor/tests/test_pings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import unittest
from unittest.mock import patch, ANY, call
from unittest.mock import MagicMock

import cronitor
import pytest

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


@patch('cronitor.Monitor.ping')
@patch('cronitor.Monitor.__init__')
def test_ping_with_non_default_env(self, mocked_monitor, mocked_ping):
Expand All @@ -104,3 +105,4 @@ def staging_env_function_call(self):