From 7f481c47edb28ffa76ff8eef2eea4fc9747db48b Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Thu, 9 Jun 2022 12:11:48 +0100 Subject: [PATCH] Add Prometheus metrics --- requirements.txt | 3 ++- setup.cfg | 1 + smsbot/telegram.py | 17 +++++++++++++---- smsbot/webhook_handler.py | 23 ++++++++++++++++++++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index e0f2822..776b059 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ flask waitress twilio -python-telegram-bot \ No newline at end of file +python-telegram-bot +prometheus_client \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 06c8808..8773565 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,6 +20,7 @@ install_requires = waitress twilio python-telegram-bot + prometheus_client [options.entry_points] console_scripts = diff --git a/smsbot/telegram.py b/smsbot/telegram.py index 0771b5b..93d372f 100644 --- a/smsbot/telegram.py +++ b/smsbot/telegram.py @@ -4,24 +4,27 @@ from setuptools import Command from telegram.ext import CommandHandler, Updater from smsbot.utils import get_smsbot_version +from prometheus_client import make_wsgi_app, Counter, Summary +REQUEST_TIME = Summary('telegram_request_processing_seconds', 'Time spent processing request') +COMMAND_COUNT = Counter('telegram_command_count', 'Total number of commands processed') class TelegramSmsBot(object): - def __init__(self, token, allow_subscribing=False, owner=None, subscribers=None): + def __init__(self, telegram_token, allow_subscribing=False, owner=None, subscribers=None): self.logger = logging.getLogger(self.__class__.__name__) - self.bot_token = token + self.bot_token = telegram_token self.subscriber_ids = subscribers or [] self.set_owner(owner) self.updater = Updater(self.bot_token, use_context=True) self.updater.dispatcher.add_handler(CommandHandler('help', self.help_handler)) self.updater.dispatcher.add_handler(CommandHandler('start', self.help_handler)) - + if allow_subscribing: self.updater.dispatcher.add_handler(CommandHandler('subscribe', self.subscribe_handler)) self.updater.dispatcher.add_handler(CommandHandler('unsubscribe', self.unsubscribe_handler)) - + self.updater.dispatcher.add_error_handler(self.error_handler) def start(self): @@ -33,6 +36,7 @@ class TelegramSmsBot(object): def stop(self): self.updater.stop() + @REQUEST_TIME.time() def help_handler(self, update, context): """Send a message when the command /help is issued.""" self.logger.info('/help command received in chat: %s', update.message.chat) @@ -42,7 +46,9 @@ class TelegramSmsBot(object): commands.extend(['/{0}'.format(x) for x in command.command]) update.message.reply_markdown('Smsbot v{0}\n\n{1}'.format(get_smsbot_version(), '\n'.join(commands))) + COMMAND_COUNT.inc() + @REQUEST_TIME.time() def subscribe_handler(self, update, context): self.logger.info('/subscribe command received') if update.message.chat['id'] not in self.subscriber_ids: @@ -52,7 +58,9 @@ class TelegramSmsBot(object): update.message.reply_markdown('You have been subscribed to SMS notifications') else: update.message.reply_markdown('You are already subscribed to SMS notifications') + COMMAND_COUNT.inc() + @REQUEST_TIME.time() def unsubscribe_handler(self, update, context): self.logger.info('/unsubscribe command received') if update.message.chat['id'] in self.subscriber_ids: @@ -62,6 +70,7 @@ class TelegramSmsBot(object): update.message.reply_markdown('You have been unsubscribed to SMS notifications') else: update.message.reply_markdown('You are not subscribed to SMS notifications') + COMMAND_COUNT.inc() def error_handler(self, update, context): """Log Errors caused by Updates.""" diff --git a/smsbot/webhook_handler.py b/smsbot/webhook_handler.py index 6942560..7c19a51 100644 --- a/smsbot/webhook_handler.py +++ b/smsbot/webhook_handler.py @@ -8,6 +8,13 @@ from waitress import serve from smsbot.utils import get_smsbot_version +from werkzeug.middleware.dispatcher import DispatcherMiddleware +from prometheus_client import make_wsgi_app, Counter, Summary + +REQUEST_TIME = Summary('webhook_request_processing_seconds', 'Time spent processing request') +MESSAGE_COUNT = Counter('webhook_message_count', 'Total number of messages processed') +CALL_COUNT = Counter('webhook_call_count', 'Total number of calls processed') + def validate_twilio_request(func): """Validates that incoming requests genuinely originated from Twilio""" @@ -47,15 +54,26 @@ class TwilioWebhookHandler(object): self.app.add_url_rule('/message', 'message', self.message, methods=['POST']) self.app.add_url_rule('/call', 'call', self.call, methods=['POST']) + # Add prometheus wsgi middleware to route /metrics requests + self.app.wsgi_app = DispatcherMiddleware(self.app.wsgi_app, { + '/metrics': make_wsgi_app(), + }) + def set_bot(self, bot): # noqa: WPS615 self.bot = bot def index(self): return '' + @REQUEST_TIME.time() def health(self): - return '

Smsbot v{0}

Owner: {1}

Subscribers: {2}

'.format(get_smsbot_version(), self.bot.owner_id, self.bot.subscriber_ids) + return { + 'version': get_smsbot_version(), + 'owner': self.bot.owner_id, + 'subscribers': self.bot.subscriber_ids, + } + @REQUEST_TIME.time() @validate_twilio_request def message(self): current_app.logger.info('Received SMS from {From}: {Body}'.format(**request.values.to_dict())) @@ -64,14 +82,17 @@ class TwilioWebhookHandler(object): self.bot.send_subscribers(message) # Return a blank response + MESSAGE_COUNT.inc() return '' + @REQUEST_TIME.time() @validate_twilio_request def call(self): current_app.logger.info('Received Call from {From}'.format(**request.values.to_dict())) self.bot.send_subscribers('Received Call from {From}, rejecting.'.format(**request.values.to_dict())) # Always reject calls + CALL_COUNT.inc() return '' def serve(self, host='0.0.0.0', port=80, debug=False):