Skip to content

Commit 8805137

Browse files
scripts: add set log level script
1 parent a109ad6 commit 8805137

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed

scripts/set_log_level.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import argparse
2+
import subprocess
3+
import sys
4+
import time
5+
from typing import List
6+
7+
import requests
8+
import signal
9+
import socket
10+
11+
SLEEP_INTERVAL = 0.4
12+
13+
14+
def parse_args(args: List[str]) -> argparse.Namespace:
15+
parser = argparse.ArgumentParser(
16+
description="Set the log level for a module or crate",
17+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
18+
)
19+
20+
# Add port-forwarding arguments
21+
add_port_forward_args(parser)
22+
23+
parser.add_argument(
24+
"--target",
25+
type=str,
26+
help="Crate or module name whose log level should be inspected or updated",
27+
)
28+
parser.add_argument("--log_level", type=str, help="The log level to set for the module/crate")
29+
parser.add_argument(
30+
"--method",
31+
type=str,
32+
choices=["get", "post"],
33+
default="post",
34+
help="HTTP method to use: 'get' to read current log level, 'post' to set a log level",
35+
)
36+
return parser.parse_args(args)
37+
38+
39+
def add_port_forward_args(parser: argparse.ArgumentParser) -> None:
40+
"""Add port-forwarding related CLI options to the parser."""
41+
42+
pf_group = parser.add_argument_group("port-forwarding options")
43+
44+
pf_group.add_argument(
45+
"--setup_port_forwarding",
46+
action="store_true",
47+
help="Establish the port-forward tunnel before sending the request",
48+
)
49+
50+
pf_group.add_argument(
51+
"--pod_name",
52+
type=str,
53+
help="Pod to port-forward to; required when --setup_port_forwarding is used",
54+
)
55+
56+
pf_group.add_argument(
57+
"--local_port",
58+
type=int,
59+
default=8082,
60+
help="Local port to bind the port-forward to",
61+
)
62+
63+
pf_group.add_argument(
64+
"--monitoring_port",
65+
type=int,
66+
default=8082,
67+
help="Monitoring endpoint port",
68+
)
69+
70+
71+
def port_forward(
72+
pod_name: str,
73+
local_port: int,
74+
remote_port: int,
75+
max_attempts: int = 5,
76+
) -> subprocess.Popen:
77+
"""Start a kubectl port-forward and wait until it is ready.
78+
79+
Returns the Popen handle so the caller can terminate it later.
80+
Raises RuntimeError if the local port is still unreachable after
81+
`max_attempts` connection checks.
82+
"""
83+
84+
cmd = ["kubectl", "port-forward", pod_name, f"{local_port}:{remote_port}"]
85+
print("Starting port-forward:", " ".join(cmd))
86+
proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
87+
88+
for _attempt in range(1, max_attempts + 1):
89+
try:
90+
with socket.create_connection(("localhost", local_port), timeout=1):
91+
print(
92+
f"✅ Port-forward to {pod_name}:{remote_port} is ready on localhost:{local_port}"
93+
)
94+
return proc
95+
except OSError:
96+
time.sleep(SLEEP_INTERVAL)
97+
98+
proc.terminate()
99+
proc.wait(timeout=5)
100+
raise RuntimeError(
101+
f"❌ Port-forward to {pod_name}:{remote_port} failed after {max_attempts} attempts."
102+
)
103+
104+
105+
def main():
106+
args = parse_args(sys.argv[1:])
107+
108+
# If a pod name is supplied, establish a port-forward before making the request
109+
port_forward_proc = None
110+
111+
target_port = args.monitoring_port
112+
base_port = args.local_port if args.setup_port_forwarding else target_port
113+
114+
if args.setup_port_forwarding:
115+
if not args.pod_name:
116+
print("--pod_name is required when --setup_port_forwarding is supplied")
117+
sys.exit(1)
118+
119+
try:
120+
port_forward_proc = port_forward(args.pod_name, args.local_port, args.monitoring_port)
121+
except RuntimeError as err:
122+
print(err)
123+
sys.exit(1)
124+
125+
try:
126+
if args.method == "get":
127+
full_url = f"http://localhost:{base_port}/monitoring/logLevel"
128+
print(f"Fetching current log level from {full_url}")
129+
response = requests.get(full_url, timeout=5)
130+
131+
if response.status_code != 200:
132+
print(f"Failed to fetch log level: {response.status_code} {response.text}")
133+
sys.exit(1)
134+
135+
print("Current log level response:\n", response.text)
136+
elif args.method == "post":
137+
# Validate required arguments
138+
if not args.target or not args.log_level:
139+
print("--target and --log_level are required when --method=post")
140+
sys.exit(1)
141+
142+
base_url = f"http://localhost:{base_port}/monitoring/setLogLevel"
143+
full_url = f"{base_url}/{args.target}/{args.log_level}"
144+
145+
print(f"Setting log level for {args.target} to {args.log_level} at {full_url}")
146+
147+
response = requests.post(full_url, timeout=5)
148+
149+
if response.status_code != 200:
150+
print(
151+
f"❌ Failed to set log level for {args.target} to {args.log_level}: {response.text}"
152+
)
153+
sys.exit(1)
154+
155+
print(f"✅ Successfully set log level for {args.target} to {args.log_level}")
156+
157+
finally:
158+
# Clean up the port-forward process if we started one
159+
if port_forward_proc:
160+
port_forward_proc.send_signal(signal.SIGINT)
161+
try:
162+
port_forward_proc.wait(timeout=5)
163+
except subprocess.TimeoutExpired:
164+
port_forward_proc.kill()
165+
port_forward_proc.wait()
166+
167+
168+
if __name__ == "__main__":
169+
main()

0 commit comments

Comments
 (0)