mirror of
https://github.com/ep1cman/unifi-protect-backup.git
synced 2026-01-04 17:43:54 +00:00
Add the ability to send logging output to apprise
This commit is contained in:
@@ -24,7 +24,7 @@ def _parse_detection_types(ctx, param, value):
|
||||
return types
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.command(context_settings=dict(max_content_width=100))
|
||||
@click.version_option(__version__)
|
||||
@click.option('--address', required=True, envvar='UFP_ADDRESS', help='Address of Unifi Protect instance')
|
||||
@click.option('--port', default=443, envvar='UFP_PORT', show_default=True, help='Port of Unifi Protect instance')
|
||||
@@ -134,6 +134,25 @@ all warnings, and websocket data
|
||||
help="How frequently to check for file to purge.\n\nNOTE: Can create a lot of API calls, so be careful if "
|
||||
"your cloud provider charges you per api call",
|
||||
)
|
||||
@click.option(
|
||||
'--apprise-notifier',
|
||||
'apprise_notifiers',
|
||||
multiple=True,
|
||||
envvar="APPRISE_NOTIFIERS",
|
||||
help="""\b
|
||||
Apprise URL for sending notifications.
|
||||
E.g: ERROR,WARNING=tgram://[BOT KEY]/[CHAT ID]
|
||||
|
||||
You can use this parameter multiple times to use more than one notification platform.
|
||||
|
||||
The following notification tags are available (corresponding to the respective logging levels):
|
||||
|
||||
ERROR, WARNING, INFO, DEBUG, EXTRA_DEBUG, WEBSOCKET_DATA
|
||||
|
||||
If no tags are specified, it defaults to ERROR
|
||||
|
||||
More details about supported platforms can be found here: https://github.com/caronc/apprise""",
|
||||
)
|
||||
def main(**kwargs):
|
||||
"""A Python based tool for backing up Unifi Protect event clips as they occur."""
|
||||
event_listener = UnifiProtectBackup(**kwargs)
|
||||
|
||||
15
unifi_protect_backup/notifications.py
Normal file
15
unifi_protect_backup/notifications.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import apprise
|
||||
|
||||
notifier = apprise.Apprise()
|
||||
|
||||
|
||||
def add_notification_service(url):
|
||||
config = apprise.AppriseConfig()
|
||||
config.add_config(url, format='text')
|
||||
|
||||
# If not tags are specified, default to errors otherwise ALL logging will
|
||||
# be spammed to the notification service
|
||||
if not config.servers()[0].tags:
|
||||
config.servers()[0].tags = {'ERROR'}
|
||||
|
||||
notifier.add(config)
|
||||
@@ -28,6 +28,7 @@ from unifi_protect_backup.utils import (
|
||||
human_readable_size,
|
||||
VideoQueue,
|
||||
)
|
||||
from unifi_protect_backup.notifications import notifier
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -67,6 +68,7 @@ class UnifiProtectBackup:
|
||||
verbose: int,
|
||||
download_buffer_size: int,
|
||||
purge_interval: str,
|
||||
apprise_notifiers: str,
|
||||
sqlite_path: str = "events.sqlite",
|
||||
color_logging=False,
|
||||
port: int = 443,
|
||||
@@ -94,7 +96,7 @@ class UnifiProtectBackup:
|
||||
sqlite_path (str): Path where to find/create sqlite database
|
||||
purge_interval (str): How often to check for files to delete
|
||||
"""
|
||||
setup_logging(verbose, color_logging)
|
||||
setup_logging(verbose, color_logging, apprise_notifiers)
|
||||
|
||||
logger.debug("Config:")
|
||||
logger.debug(f" {address=}")
|
||||
@@ -116,6 +118,7 @@ class UnifiProtectBackup:
|
||||
logger.debug(f" {sqlite_path=}")
|
||||
logger.debug(f" download_buffer_size={human_readable_size(download_buffer_size)}")
|
||||
logger.debug(f" {purge_interval=}")
|
||||
logger.debug(f" {apprise_notifiers=}")
|
||||
|
||||
self.rclone_destination = rclone_destination
|
||||
self.retention = parse_rclone_retention(retention)
|
||||
@@ -154,6 +157,7 @@ class UnifiProtectBackup:
|
||||
"""
|
||||
try:
|
||||
logger.info("Starting...")
|
||||
await notifier.async_notify("Starting UniFi Protect Backup")
|
||||
|
||||
# Ensure `rclone` is installed and properly configured
|
||||
logger.info("Checking rclone configuration...")
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import logging
|
||||
import re
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from apprise import NotifyType
|
||||
|
||||
from unifi_protect_backup import notifications
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -118,7 +121,41 @@ def create_logging_handler(format):
|
||||
return sh
|
||||
|
||||
|
||||
def setup_logging(verbosity: int, color_logging: bool = False) -> None:
|
||||
def patch_logger_notifications(logger):
|
||||
"""
|
||||
Patches the core logging function to also send apprise notifications
|
||||
"""
|
||||
original_log = logger._log
|
||||
|
||||
logging_map = {
|
||||
logging.ERROR: NotifyType.FAILURE,
|
||||
logging.WARNING: NotifyType.WARNING,
|
||||
logging.INFO: NotifyType.INFO,
|
||||
logging.DEBUG: NotifyType.INFO,
|
||||
logging.EXTRA_DEBUG: NotifyType.INFO,
|
||||
logging.WEBSOCKET_DATA: NotifyType.INFO,
|
||||
}
|
||||
|
||||
def new_log(self, level, msg, *args, **kwargs):
|
||||
original_log(level, msg, *args, **kwargs)
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
if not loop.is_closed():
|
||||
level_name = logging.getLevelName(level)
|
||||
coro = notifications.notifier.async_notify(
|
||||
body=msg, title=level_name, notify_type=logging_map[level], tag=[level_name]
|
||||
)
|
||||
|
||||
if loop.is_running():
|
||||
asyncio.create_task(coro)
|
||||
else:
|
||||
loop.run_until_complete(coro)
|
||||
|
||||
logger.__class__._log = new_log
|
||||
|
||||
|
||||
def setup_logging(verbosity: int, color_logging: bool = False, apprise_notifiers: List[str] = []) -> None:
|
||||
"""Configures loggers to provided the desired level of verbosity.
|
||||
|
||||
Verbosity 0: Only log info messages created by `unifi-protect-backup`, and all warnings
|
||||
@@ -135,6 +172,7 @@ def setup_logging(verbosity: int, color_logging: bool = False) -> None:
|
||||
Args:
|
||||
verbosity (int): The desired level of verbosity
|
||||
color_logging (bool): If colors should be used in the log (default=False)
|
||||
apprise_notifiers (List[str]): Notification services to hook into the logger
|
||||
|
||||
"""
|
||||
globals()['color_logging'] = color_logging
|
||||
@@ -174,6 +212,13 @@ def setup_logging(verbosity: int, color_logging: bool = False) -> None:
|
||||
logging.basicConfig(level=logging.DEBUG, handlers=[sh])
|
||||
logger.setLevel(logging.WEBSOCKET_DATA) # type: ignore
|
||||
|
||||
for notifier in apprise_notifiers:
|
||||
notifications.add_notification_service(notifier)
|
||||
|
||||
# Only send logs to notification service if it is enabled
|
||||
if notifications.notifier.servers:
|
||||
patch_logger_notifications(logger)
|
||||
|
||||
|
||||
def setup_event_logger(logger):
|
||||
format = "{asctime} [{levelname:^11s}] {name:<42} :{event} {message}"
|
||||
|
||||
Reference in New Issue
Block a user