Skip to content

Commit

Permalink
Add middleware that apps can use to handle graceful timeout signal
Browse files Browse the repository at this point in the history
uwsgi 2.0.22+ offers option to send a user-defined signal at for a
"graceful" timeout before the real harakiri occurs.

gunicorn sends SIGABRT (signal 6) as graceful timeout signal before
sending SIGKILL.

Apps can use this middleware to log a traceback indicating what code was
executing when the graceful timeout signal was received.
  • Loading branch information
kdelee committed Aug 21, 2024
1 parent cdbea30 commit 56a9e2d
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 2 deletions.
4 changes: 2 additions & 2 deletions ansible_base/lib/middleware/logging/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from ansible_base.lib.middleware.logging.log_request import LogRequestMiddleware
from ansible_base.lib.middleware.logging.log_request import LogRequestMiddleware, LogTracebackMiddleware

__all__ = ('LogRequestMiddleware',)
__all__ = ('LogRequestMiddleware', 'LogTracebackMiddleware')
23 changes: 23 additions & 0 deletions ansible_base/lib/middleware/logging/log_request.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
import logging
import traceback
import uuid

from ansible_base.lib.logging import thread_local

logger = logging.getLogger(__name__)


class LogTracebackMiddleware:
transactions = {}

@classmethod
def handle_signal(cls, *args):
for t_id, request in LogTracebackMiddleware.transactions.items():
logger.error(f"Catching graceful timeout signal for {request.method} path: {request.path}")
logger.error(f"Received graceful timeout signal while in stack: {''.join(traceback.format_stack())}")

def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
t_id = str(uuid.uuid4())
LogTracebackMiddleware.transactions[t_id] = request
try:
return self.get_response(request)
finally:
LogTracebackMiddleware.transactions.pop(t_id)


class LogRequestMiddleware:
"""
Inject the request into the thread local so that it can be accessed by the logging filter.
Expand Down
8 changes: 8 additions & 0 deletions test_app/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import signal

from django.apps import AppConfig

from ansible_base.lib.middleware.logging import LogTracebackMiddleware


class TestAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'test_app'

def ready(self):
super().ready()
signal.signal(signal.SIGSYS, LogTracebackMiddleware.handle_signal)
1 change: 1 addition & 0 deletions test_app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'crum.CurrentRequestUserMiddleware',
'ansible_base.lib.middleware.logging.LogRequestMiddleware',
'ansible_base.lib.middleware.logging.LogTracebackMiddleware',
]

REST_FRAMEWORK = {
Expand Down

0 comments on commit 56a9e2d

Please sign in to comment.