Skip to content

Commit 9761126

Browse files
committed
feat: implement graceful shutdown and enhance request logging in main.py
1 parent e7dca4b commit 9761126

1 file changed

Lines changed: 91 additions & 27 deletions

File tree

src/python_response_time/main.py

Lines changed: 91 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,51 @@
1-
"""Main entry point for the Python Response Time application."""
1+
"""Python Response Time Benchmark (production-safe, Docker/K8s friendly)."""
22

3+
import signal
4+
import threading
35
import time
46

57
import requests
8+
from loguru import logger
69
from rich.console import Console
710

811
from python_response_time.core import app_settings, setup_logger
912

13+
setup_logger(app_settings.LOG_LEVEL)
1014
console = Console()
1115

16+
shutdown_event = threading.Event()
17+
18+
19+
def handle_shutdown(*_):
20+
"""Handle SIGTERM / SIGINT."""
21+
shutdown_event.set()
22+
console.print("\n[yellow]Shutdown signal received... stopping gracefully[/yellow]")
23+
24+
25+
def register_signals():
26+
"""Register signal handlers for graceful shutdown."""
27+
signal.signal(signal.SIGTERM, handle_shutdown)
28+
signal.signal(signal.SIGINT, handle_shutdown)
29+
30+
31+
def sleep_interruptible(seconds: float):
32+
"""Sleep in small increments, checking for shutdown signal."""
33+
step = 0.1
34+
elapsed = 0.0
35+
36+
while elapsed < seconds:
37+
if shutdown_event.is_set():
38+
return
39+
try:
40+
time.sleep(step)
41+
except KeyboardInterrupt:
42+
shutdown_event.set()
43+
return
44+
elapsed += step
45+
1246

1347
def run_app():
14-
"""Run HTTP benchmark and display results via Rich."""
15-
logger = setup_logger(app_settings.LOG_LEVEL)
48+
"""Run the main application logic."""
1649
console.print("[bold cyan]HTTP Benchmark Starting...[/bold cyan]")
1750
console.print(f"Target: {app_settings.TARGET_URL}")
1851
console.print(f"Requests: {app_settings.NUM_REQUESTS}")
@@ -23,31 +56,62 @@ def run_app():
2356

2457
session = requests.Session()
2558

26-
console.print("[bold green]Running benchmark...[/bold green]\n")
27-
28-
for i in range(app_settings.NUM_REQUESTS):
29-
start_time = time.perf_counter()
30-
try:
31-
response = session.get(
32-
str(app_settings.TARGET_URL),
33-
timeout=app_settings.TIMEOUT,
34-
verify=app_settings.VERIFY_SSL,
35-
)
36-
elapsed_ms = (time.perf_counter() - start_time) * 1000
37-
logger.info(
38-
f"Request {i + 1}: {response.status_code} in {elapsed_ms:.2f} ms"
39-
)
40-
41-
except requests.exceptions.SSLError as e:
42-
console.print(f"[red]Request {i + 1} SSL error: {e}[/red]")
43-
44-
except requests.RequestException as e:
45-
console.print(f"[red]Request {i + 1} failed: {e}[/red]")
46-
47-
finally:
59+
try:
60+
for i in range(app_settings.NUM_REQUESTS):
61+
if shutdown_event.is_set():
62+
break
63+
start_time = time.perf_counter()
64+
try:
65+
response = session.get(
66+
str(app_settings.TARGET_URL),
67+
timeout=app_settings.TIMEOUT,
68+
verify=app_settings.VERIFY_SSL,
69+
)
70+
elapsed_ms = (time.perf_counter() - start_time) * 1000
71+
console.print(
72+
f"{i + 1:>4} | {response.status_code} | {elapsed_ms:.2f} ms"
73+
)
74+
logger.info(
75+
{
76+
"request": i + 1,
77+
"status": response.status_code,
78+
"response_time_ms": elapsed_ms,
79+
"url": str(app_settings.TARGET_URL),
80+
}
81+
)
82+
except requests.exceptions.SSLError as e:
83+
console.print(f"{i + 1:>4} | SSL ERROR")
84+
logger.error(
85+
{
86+
"request": i + 1,
87+
"error": "ssl_error",
88+
"details": str(e),
89+
}
90+
)
91+
except requests.RequestException as e:
92+
console.print(f"{i + 1:>4} | ERROR")
93+
logger.error(
94+
{
95+
"request": i + 1,
96+
"error": "request_error",
97+
"details": str(e),
98+
}
99+
)
48100
if app_settings.REQUEST_DELAY > 0:
49-
time.sleep(app_settings.REQUEST_DELAY)
101+
sleep_interruptible(app_settings.REQUEST_DELAY)
102+
finally:
103+
session.close()
104+
console.print("\n[green]Benchmark stopped gracefully[/green]")
50105

51106

52107
if __name__ == "__main__":
53-
run_app()
108+
"""Run the application."""
109+
register_signals()
110+
try:
111+
run_app()
112+
except KeyboardInterrupt:
113+
shutdown_event.set()
114+
console.print("\n[yellow]Interrupted (Ctrl+C). Exiting cleanly...[/yellow]")
115+
finally:
116+
shutdown_event.set()
117+
console.print("[green]Cleanup complete[/green]")

0 commit comments

Comments
 (0)