Adds slack webhook for error logging
This commit is contained in:
parent
434970647f
commit
f28dc70181
4 changed files with 48 additions and 6 deletions
|
@ -3,10 +3,13 @@ import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
import signal
|
import signal
|
||||||
import time
|
import time
|
||||||
|
import queue
|
||||||
|
|
||||||
|
|
||||||
import aiojobs
|
import aiojobs
|
||||||
import aiojobs.aiohttp
|
import aiojobs.aiohttp
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import aiohttp
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
|
||||||
from src.database.queries import setup_database, backup_database
|
from src.database.queries import setup_database, backup_database
|
||||||
|
@ -35,6 +38,24 @@ async def database_backup_routine(app):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
async def post_errors_to_slack(client, app):
|
||||||
|
while app['errors'].qsize() > 0:
|
||||||
|
msg = app['errors'].get_nowait()
|
||||||
|
if 'slack_webhook' in app['config']:
|
||||||
|
await client.post(app['config']['slack_webhook'], json=msg)
|
||||||
|
|
||||||
|
|
||||||
|
async def report_errors_to_slack_webhook(app):
|
||||||
|
async with aiohttp.ClientSession() as client:
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
await asyncio.shield(post_errors_to_slack(client, app))
|
||||||
|
await asyncio.sleep(300)
|
||||||
|
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
await asyncio.shield(post_errors_to_slack(client, app))
|
||||||
|
|
||||||
|
|
||||||
async def start_background_tasks(app):
|
async def start_background_tasks(app):
|
||||||
app['reader'] = obtain_connection(app['db_path'], True)
|
app['reader'] = obtain_connection(app['db_path'], True)
|
||||||
app['waitful_backup'] = asyncio.create_task(database_backup_routine(app))
|
app['waitful_backup'] = asyncio.create_task(database_backup_routine(app))
|
||||||
|
@ -42,6 +63,9 @@ async def start_background_tasks(app):
|
||||||
app['db_writer'] = DatabaseWriter(app['db_path'])
|
app['db_writer'] = DatabaseWriter(app['db_path'])
|
||||||
app['writer'] = app['db_writer'].connection
|
app['writer'] = app['db_writer'].connection
|
||||||
|
|
||||||
|
app['errors'] = queue.Queue()
|
||||||
|
app['slack_webhook'] = asyncio.create_task(report_errors_to_slack_webhook(app))
|
||||||
|
|
||||||
|
|
||||||
async def close_database_connections(app):
|
async def close_database_connections(app):
|
||||||
logger.info('Ending background backup loop')
|
logger.info('Ending background backup loop')
|
||||||
|
@ -52,6 +76,11 @@ async def close_database_connections(app):
|
||||||
app['db_writer'].cleanup()
|
app['db_writer'].cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
async def stop_reporting_errors(app):
|
||||||
|
app['slack_webhook'].cancel()
|
||||||
|
await app['slack_webhook']
|
||||||
|
|
||||||
|
|
||||||
async def close_comment_scheduler(app):
|
async def close_comment_scheduler(app):
|
||||||
logger.info('Closing comment_scheduler')
|
logger.info('Closing comment_scheduler')
|
||||||
await app['comment_scheduler'].close()
|
await app['comment_scheduler'].close()
|
||||||
|
@ -72,6 +101,7 @@ class CommentDaemon:
|
||||||
app.on_startup.append(start_background_tasks)
|
app.on_startup.append(start_background_tasks)
|
||||||
app.on_shutdown.append(close_comment_scheduler)
|
app.on_shutdown.append(close_comment_scheduler)
|
||||||
app.on_cleanup.append(close_database_connections)
|
app.on_cleanup.append(close_database_connections)
|
||||||
|
app.on_cleanup.append(stop_reporting_errors)
|
||||||
aiojobs.aiohttp.setup(app, **kwargs)
|
aiojobs.aiohttp.setup(app, **kwargs)
|
||||||
app.add_routes([
|
app.add_routes([
|
||||||
web.post('/api', api_endpoint),
|
web.post('/api', api_endpoint),
|
||||||
|
|
|
@ -91,11 +91,12 @@ async def process_json(app, body: dict) -> dict:
|
||||||
result = METHODS[method](app, params)
|
result = METHODS[method](app, params)
|
||||||
response['result'] = result
|
response['result'] = result
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.exception(f'Got {type(err).__name__}: {err}')
|
logger.exception(f'Got {type(err).__name__}:')
|
||||||
if type(err) in (ValueError, TypeError):
|
if type(err) in (ValueError, TypeError):
|
||||||
response['error'] = make_error('INVALID_PARAMS', err)
|
response['error'] = make_error('INVALID_PARAMS', err, app)
|
||||||
else:
|
else:
|
||||||
response['error'] = make_error('INTERNAL', err)
|
response['error'] = make_error('INTERNAL', err, app)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
end = time.time()
|
end = time.time()
|
||||||
logger.debug(f'Time taken to process {method}: {end - start} secs')
|
logger.debug(f'Time taken to process {method}: {end - start} secs')
|
||||||
|
@ -122,7 +123,7 @@ async def api_endpoint(request: web.Request):
|
||||||
else:
|
else:
|
||||||
return web.json_response(await process_json(request.app, body))
|
return web.json_response(await process_json(request.app, body))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return make_error('INVALID_REQUEST', e)
|
return make_error('INVALID_REQUEST', e, request.app)
|
||||||
|
|
||||||
|
|
||||||
async def get_api_endpoint(request: web.Request):
|
async def get_api_endpoint(request: web.Request):
|
||||||
|
|
|
@ -31,11 +31,18 @@ ERRORS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def make_error(error, exc=None) -> dict:
|
def make_error(error, exc=None, app=None) -> dict:
|
||||||
body = ERRORS[error] if error in ERRORS else ERRORS['INTERNAL']
|
body = ERRORS[error] if error in ERRORS else ERRORS['INTERNAL']
|
||||||
try:
|
try:
|
||||||
if exc:
|
if exc:
|
||||||
body.update({type(exc).__name__: str(exc)})
|
exc_name = type(exc).__name__
|
||||||
|
body.update({exc_name: str(exc)})
|
||||||
|
|
||||||
|
if app:
|
||||||
|
app['errors'].put_nowait({
|
||||||
|
"text": f"Got `{exc_name}`: ```\n{exc}```"
|
||||||
|
})
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,10 @@ from src.server import app
|
||||||
from test.testcase import AsyncioTestCase
|
from test.testcase import AsyncioTestCase
|
||||||
|
|
||||||
|
|
||||||
|
if 'slack_webhook' in config:
|
||||||
|
config.pop('slack_webhook')
|
||||||
|
|
||||||
|
|
||||||
fake = faker.Faker()
|
fake = faker.Faker()
|
||||||
fake.add_provider(internet)
|
fake.add_provider(internet)
|
||||||
fake.add_provider(lorem)
|
fake.add_provider(lorem)
|
||||||
|
|
Loading…
Reference in a new issue