Notification error log handler (#403)
* Add a notifications debug/error log interface (Link available under the notification URLs list)
This commit is contained in:
@@ -70,6 +70,7 @@ app.config['LOGIN_DISABLED'] = False
|
|||||||
# Disables caching of the templates
|
# Disables caching of the templates
|
||||||
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
||||||
|
|
||||||
|
notification_debug_log=[]
|
||||||
|
|
||||||
def init_app_secret(datastore_path):
|
def init_app_secret(datastore_path):
|
||||||
secret = ""
|
secret = ""
|
||||||
@@ -529,6 +530,7 @@ def changedetection_app(config=None, datastore_o=None):
|
|||||||
'notification_title': form.notification_title.data,
|
'notification_title': form.notification_title.data,
|
||||||
'notification_body': form.notification_body.data,
|
'notification_body': form.notification_body.data,
|
||||||
'notification_format': form.notification_format.data,
|
'notification_format': form.notification_format.data,
|
||||||
|
'uuid': uuid
|
||||||
}
|
}
|
||||||
notification_q.put(n_object)
|
notification_q.put(n_object)
|
||||||
flash('Test notification queued.')
|
flash('Test notification queued.')
|
||||||
@@ -765,6 +767,14 @@ def changedetection_app(config=None, datastore_o=None):
|
|||||||
uuid=uuid)
|
uuid=uuid)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
@app.route("/settings/notification-logs", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def notification_logs():
|
||||||
|
global notification_debug_log
|
||||||
|
output = render_template("notification-log.html",
|
||||||
|
logs=notification_debug_log if len(notification_debug_log) else ["No errors or warnings detected"])
|
||||||
|
|
||||||
|
return output
|
||||||
@app.route("/api/<string:uuid>/snapshot/current", methods=['GET'])
|
@app.route("/api/<string:uuid>/snapshot/current", methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
def api_snapshot(uuid):
|
def api_snapshot(uuid):
|
||||||
@@ -997,6 +1007,7 @@ def check_for_new_version():
|
|||||||
app.config.exit.wait(86400)
|
app.config.exit.wait(86400)
|
||||||
|
|
||||||
def notification_runner():
|
def notification_runner():
|
||||||
|
global notification_debug_log
|
||||||
while not app.config.exit.is_set():
|
while not app.config.exit.is_set():
|
||||||
try:
|
try:
|
||||||
# At the moment only one thread runs (single runner)
|
# At the moment only one thread runs (single runner)
|
||||||
@@ -1011,8 +1022,18 @@ def notification_runner():
|
|||||||
notification.process_notification(n_object, datastore)
|
notification.process_notification(n_object, datastore)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Watch URL: {} Error {}".format(n_object['watch_url'], e))
|
print("Watch URL: {} Error {}".format(n_object['watch_url'], str(e)))
|
||||||
datastore.update_watch(uuid=n_object['uuid'], update_obj={'last_error': "Notification error: " + str(e)})
|
|
||||||
|
# UUID wont be present when we submit a 'test' from the global settings
|
||||||
|
if 'uuid' in n_object:
|
||||||
|
datastore.update_watch(uuid=n_object['uuid'], update_obj={'last_error': "Notification error detected, please see logs."})
|
||||||
|
|
||||||
|
log_lines = str(e).splitlines()
|
||||||
|
notification_debug_log += log_lines
|
||||||
|
|
||||||
|
# Trim the log length
|
||||||
|
notification_debug_log = notification_debug_log[-100:]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Thread runner to check every minute, look for new watches to feed into the Queue.
|
# Thread runner to check every minute, look for new watches to feed into the Queue.
|
||||||
|
|||||||
@@ -25,9 +25,7 @@ default_notification_body = '{watch_url} had a change.\n---\n{diff}\n---\n'
|
|||||||
default_notification_title = 'ChangeDetection.io Notification - {watch_url}'
|
default_notification_title = 'ChangeDetection.io Notification - {watch_url}'
|
||||||
|
|
||||||
def process_notification(n_object, datastore):
|
def process_notification(n_object, datastore):
|
||||||
import logging
|
|
||||||
log = logging.getLogger('apprise')
|
|
||||||
log.setLevel('TRACE')
|
|
||||||
apobj = apprise.Apprise(debug=True)
|
apobj = apprise.Apprise(debug=True)
|
||||||
|
|
||||||
for url in n_object['notification_urls']:
|
for url in n_object['notification_urls']:
|
||||||
@@ -53,11 +51,22 @@ def process_notification(n_object, datastore):
|
|||||||
n_title = n_title.replace(token, val)
|
n_title = n_title.replace(token, val)
|
||||||
n_body = n_body.replace(token, val)
|
n_body = n_body.replace(token, val)
|
||||||
|
|
||||||
apobj.notify(
|
# https://github.com/caronc/apprise/wiki/Development_LogCapture
|
||||||
|
# Anything higher than or equal to WARNING (which covers things like Connection errors)
|
||||||
|
# raise it as an exception
|
||||||
|
|
||||||
|
with apprise.LogCapture(level=apprise.logging.DEBUG) as logs:
|
||||||
|
apobj.notify(
|
||||||
body=n_body,
|
body=n_body,
|
||||||
title=n_title,
|
title=n_title,
|
||||||
body_format=n_format,
|
body_format=n_format)
|
||||||
)
|
|
||||||
|
# Returns empty string if nothing found, multi-line string otherwise
|
||||||
|
log_value = logs.getvalue()
|
||||||
|
if log_value and 'WARNING' in log_value or 'ERROR' in log_value:
|
||||||
|
raise Exception(log_value)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Notification title + body content parameters get created here.
|
# Notification title + body content parameters get created here.
|
||||||
def create_notification_parameters(n_object, datastore):
|
def create_notification_parameters(n_object, datastore):
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<li>Use <a target=_new href="https://github.com/caronc/apprise">AppRise URLs</a> for notification to just about any service! <i><a target=_new href="https://github.com/dgtlmoon/changedetection.io/wiki/Notification-configuration-notes">Please read the notification services wiki here for important configuration notes</a></i>.</li>
|
<li>Use <a target=_new href="https://github.com/caronc/apprise">AppRise URLs</a> for notification to just about any service! <i><a target=_new href="https://github.com/dgtlmoon/changedetection.io/wiki/Notification-configuration-notes">Please read the notification services wiki here for important configuration notes</a></i>.</li>
|
||||||
<li><code>discord://</code> will silently fail if the total message length is more than 2000 chars.</li>
|
<li><code>discord://</code> will silently fail if the total message length is more than 2000 chars.</li>
|
||||||
<li><code>tgram://</code> bots cant send messages to other bots, so you should specify chat ID of non-bot user.</li>
|
<li><code>tgram://</code> bots cant send messages to other bots, so you should specify chat ID of non-bot user.</li>
|
||||||
|
<li>Go here for <a href="{{url_for('notification_logs')}}">Notification debug logs</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
19
changedetectionio/templates/notification-log.html
Normal file
19
changedetectionio/templates/notification-log.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="edit-form">
|
||||||
|
<div class="inner">
|
||||||
|
|
||||||
|
<h4 style="margin-top: 0px;">The following issues were detected when sending notifications</h4>
|
||||||
|
<div id="notification-customisation">
|
||||||
|
<ul style="font-size: 80%; margin:0px; padding: 0 0 0 7px">
|
||||||
|
{% for log in logs|reverse %}
|
||||||
|
<li>{{log}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -59,6 +59,8 @@
|
|||||||
{{ render_common_settings_form(form, current_base_url) }}
|
{{ render_common_settings_form(form, current_base_url) }}
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<a href="{{url_for('notification_logs')}}">Notification debug logs</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="fetching">
|
<div class="tab-pane-inner" id="fetching">
|
||||||
|
|||||||
@@ -226,3 +226,32 @@ def test_check_notification(client, live_server):
|
|||||||
)
|
)
|
||||||
assert b"Notification Body and Title is required when a Notification URL is used" in res.data
|
assert b"Notification Body and Title is required when a Notification URL is used" in res.data
|
||||||
|
|
||||||
|
|
||||||
|
# Check we capture the failure, we can just use trigger_check = y here
|
||||||
|
res = client.post(
|
||||||
|
url_for("edit_page", uuid="first"),
|
||||||
|
data={"notification_urls": "jsons://broken-url.changedetection.io/test",
|
||||||
|
"notification_title": "xxx",
|
||||||
|
"notification_body": "xxxxx",
|
||||||
|
"notification_format": "Text",
|
||||||
|
"url": test_url,
|
||||||
|
"tag": "my tag",
|
||||||
|
"title": "my title",
|
||||||
|
"headers": "",
|
||||||
|
"fetch_backend": "html_requests",
|
||||||
|
"trigger_check": "y"},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
# The error should show in the notification logs
|
||||||
|
res = client.get(
|
||||||
|
url_for("notification_logs"))
|
||||||
|
assert bytes("Name or service not known".encode('utf-8')) in res.data
|
||||||
|
|
||||||
|
# And it should be listed on the watch overview
|
||||||
|
res = client.get(
|
||||||
|
url_for("index"))
|
||||||
|
assert bytes("Notification error detected".encode('utf-8')) in res.data
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user