Skip to content

Commit

Permalink
Ref #697 - Add traceback logging on SIGUSR1 (#721)
Browse files Browse the repository at this point in the history
  • Loading branch information
mxsasha authored Jan 6, 2023
1 parent 94c4b5c commit 466849a
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 5 deletions.
3 changes: 2 additions & 1 deletion docs/admins/deployment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ Useful options:
``log.logfile_path`` is not set, this also shows all log output
in the terminal.

IRRd can be stopped by sending a SIGTERM signal.
IRRd can be stopped by sending a SIGTERM signal. A SIGUSR1 will log a
traceback of all threads in a specific IRRd process.


.. _deployment-https:
Expand Down
7 changes: 7 additions & 0 deletions docs/releases/4.3.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,10 @@ Other dependency versions
IRRd now requires Redis 5 or newer. PostgreSQL 11 or newer is strongly
recommended before upgrading, as it makes database migrations
significantly faster.


Debugging info on SIGUSR1
-------------------------
IRRd processes will now log a traceback of all their threads when
receiving a SIGUSR1 signal. This can be helpful when debugging
hanging workers or other complex issues.
5 changes: 3 additions & 2 deletions irrd/daemon/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/usr/bin/env python
# flake8: noqa: E402
import argparse
import grp
import logging
import multiprocessing
import os
Expand All @@ -13,14 +12,15 @@
from typing import Tuple, Optional

import daemon
import grp
import psutil
from daemon.daemon import change_process_owner
from pid import PidFile, PidFileError

logger = logging.getLogger(__name__)
sys.path.append(str(Path(__file__).resolve().parents[2]))

from irrd.utils.process_support import ExceptionLoggingProcess
from irrd.utils.process_support import ExceptionLoggingProcess, set_traceback_handler
from irrd.storage.preload import PreloadStoreManager
from irrd.server.whois.server import start_whois_server
from irrd.server.http.server import run_http_server
Expand Down Expand Up @@ -99,6 +99,7 @@ def main():
def run_irrd(mirror_frequency: int, config_file_path: str, uid: Optional[int], gid: Optional[int]):
terminated = False
os.environ[ENV_MAIN_PROCESS_PID] = str(os.getpid())
set_traceback_handler()

whois_process = ExceptionLoggingProcess(
target=start_whois_server,
Expand Down
4 changes: 2 additions & 2 deletions irrd/server/http/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
from irrd.server.http.event_stream import EventStreamEndpoint, EventStreamInitialDownloadEndpoint
from irrd.storage.database_handler import DatabaseHandler
from irrd.storage.preload import Preloader
from irrd.utils.process_support import memory_trim

from irrd.utils.process_support import memory_trim, set_traceback_handler

logger = logging.getLogger(__name__)

Expand All @@ -46,6 +45,7 @@ async def startup():
is read from the environment.
"""
setproctitle("irrd-http-server-listener")
set_traceback_handler()
global app
config_path = os.getenv(ENV_UVICORN_WORKER_CONFIG_PATH)
config_init(config_path)
Expand Down
23 changes: 23 additions & 0 deletions irrd/utils/process_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@
import logging
import os
import signal
import sys
import threading
import traceback
from multiprocessing import Process

from setproctitle import getproctitle

logger = logging.getLogger(__name__)


Expand All @@ -26,3 +31,21 @@ def memory_trim(): # pragma: no cover
ctypes.CDLL(None).malloc_trim(0)
except Exception:
pass


def set_traceback_handler(): # pragma: no cover
"""
Log a traceback of all threads when receiving SIGUSR1.
This is inherited by child processes, so only set twice:
in the main process, and in the uvicorn app startup.
"""
def sigusr1_handler(signal, frame):
thread_names = {th.ident: th.name for th in threading.enumerate()}
code = [f"Traceback follows for all threads of process {os.getpid()} ({getproctitle()}):"]
for thread_id, stack in sys._current_frames().items():
thread_name = thread_names.get(thread_id, "")
code.append(f"\n## Thread: {thread_name}({thread_id}) ##\n")
code += traceback.format_list(traceback.extract_stack(stack))
logger.info("".join(code))

signal.signal(signal.SIGUSR1, sigusr1_handler)

0 comments on commit 466849a

Please sign in to comment.