Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.2.x] Ref #697 - Add traceback logging on SIGUSR1 (backport #721) #728

Merged
merged 1 commit into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/admins/deployment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,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
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 os
import pwd
Expand All @@ -12,14 +11,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 @@ -94,6 +94,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
6 changes: 3 additions & 3 deletions irrd/server/http/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
from irrd.server.http.endpoints import StatusEndpoint, WhoisQueryEndpoint, ObjectSubmissionEndpoint
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 @@ -38,7 +37,8 @@ async def startup():
As these are run in a separate process, the config file
is read from the environment.
"""
setproctitle('irrd-http-server-listener')
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)