v3.6.0 — add per-session ID (8-char hex) to all proxy log lines

This commit is contained in:
admin
2026-05-22 13:23:13 +04:00
Unverified
parent 0d34bb6301
commit d882b7df02
2 changed files with 26 additions and 23 deletions

Binary file not shown.

View File

@@ -3126,6 +3126,9 @@ class Handler(http.server.BaseHTTPRequestHandler):
except Exception as e: except Exception as e:
return self.send_json(400, {"error": {"message": f"Bad request: {e}"}}) return self.send_json(400, {"error": {"message": f"Bad request: {e}"}})
self._session_id = uuid.uuid4().hex[:8]
_sid = self._session_id
import datetime as _dt import datetime as _dt
_log_path = os.path.join(_LOG_DIR, "requests.log") _log_path = os.path.join(_LOG_DIR, "requests.log")
_ts = _dt.datetime.now().isoformat() _ts = _dt.datetime.now().isoformat()
@@ -3139,9 +3142,9 @@ class Handler(http.server.BaseHTTPRequestHandler):
raw_types = [i.get("type") for i in raw_input] if isinstance(raw_input, list) else "str" raw_types = [i.get("type") for i in raw_input] if isinstance(raw_input, list) else "str"
resolved_types = [i.get("type") for i in input_data] if isinstance(input_data, list) else "str" resolved_types = [i.get("type") for i in input_data] if isinstance(input_data, list) else "str"
print(f"[REQUEST] prev_id={prev_id} raw={raw_types} resolved={resolved_types}", file=sys.stderr) print(f"[{_sid}] prev_id={prev_id} raw={raw_types} resolved={resolved_types}", file=sys.stderr)
with open(_log_path, "a") as _lf: with open(_log_path, "a") as _lf:
_lf.write(f"\n{'='*60}\n{_ts} REQUEST {self.path}\n") _lf.write(f"\n{'='*60}\n{_ts} [session={_sid}] REQUEST {self.path}\n")
_lf.write(f" prev_id={prev_id}\n") _lf.write(f" prev_id={prev_id}\n")
_lf.write(f" raw_input_types={raw_types}\n") _lf.write(f" raw_input_types={raw_types}\n")
_lf.write(f" resolved_input_types={resolved_types}\n") _lf.write(f" resolved_input_types={resolved_types}\n")
@@ -3229,7 +3232,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": f"Bearer {effective_key}", "Authorization": f"Bearer {effective_key}",
}, browser_ua=True) }, browser_ua=True)
print(f"[translate-proxy] POST {target} model={model} stream={stream} items={len(input_data) if isinstance(input_data,list) else 1}", file=sys.stderr) print(f"[{self._session_id}] POST {target} model={model} stream={stream} items={len(input_data) if isinstance(input_data,list) else 1}", file=sys.stderr)
chat_body_b = json.dumps(chat_body).encode() chat_body_b = json.dumps(chat_body).encode()
max_retries = 3 max_retries = 3
for attempt in range(max_retries + 1): for attempt in range(max_retries + 1):
@@ -3247,14 +3250,14 @@ class Handler(http.server.BaseHTTPRequestHandler):
wait = min(2 ** (attempt + 1), 15) wait = min(2 ** (attempt + 1), 15)
else: else:
wait = min(2 ** (attempt + 1), 15) wait = min(2 ** (attempt + 1), 15)
print(f"[translate-proxy] HTTP {e.code} (attempt {attempt+1}/{max_retries}), retrying in {wait}s: {err_body[:150]}", file=sys.stderr) print(f"[{self._session_id}] HTTP {e.code} (attempt {attempt+1}/{max_retries}), retrying in {wait}s: {err_body[:150]}", file=sys.stderr)
time.sleep(wait) time.sleep(wait)
continue continue
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}}) return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}})
except (ConnectionResetError, ConnectionAbortedError, BrokenPipeError) as e: except (ConnectionResetError, ConnectionAbortedError, BrokenPipeError) as e:
if attempt < max_retries: if attempt < max_retries:
wait = min(2 ** (attempt + 1), 10) wait = min(2 ** (attempt + 1), 10)
print(f"[translate-proxy] connection error (attempt {attempt+1}/{max_retries}), retrying in {wait}s: {e}", file=sys.stderr) print(f"[{self._session_id}] connection error (attempt {attempt+1}/{max_retries}), retrying in {wait}s: {e}", file=sys.stderr)
time.sleep(wait) time.sleep(wait)
continue continue
return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}}) return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}})
@@ -3488,7 +3491,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
headers["X-Goog-Api-Client"] = "gl-node/22.17.0" headers["X-Goog-Api-Client"] = "gl-node/22.17.0"
headers["Client-Metadata"] = "ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI" headers["Client-Metadata"] = "ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI"
body_b = json.dumps(wrapped).encode() body_b = json.dumps(wrapped).encode()
print(f"[gemini-oauth] model={model} stream={stream} items={len(input_data) if isinstance(input_data, list) else 1} project={project_id}", file=sys.stderr) print(f"[{self._session_id}] model={model} stream={stream} items={len(input_data) if isinstance(input_data, list) else 1} project={project_id}", file=sys.stderr)
for ep in endpoints: for ep in endpoints:
target = f"{ep}/{url_suffix}" target = f"{ep}/{url_suffix}"
@@ -3503,17 +3506,17 @@ class Handler(http.server.BaseHTTPRequestHandler):
debug_path = os.path.join(_LOG_DIR, "gemini-last-400-request.json") debug_path = os.path.join(_LOG_DIR, "gemini-last-400-request.json")
with open(debug_path, "w") as dbg: with open(debug_path, "w") as dbg:
json.dump({"endpoint": ep, "model": model, "wrapped": wrapped, "error": err_body}, dbg, indent=2) json.dump({"endpoint": ep, "model": model, "wrapped": wrapped, "error": err_body}, dbg, indent=2)
print(f"[gemini-oauth] saved 400 debug request to {debug_path}", file=sys.stderr) print(f"[{self._session_id}] saved 400 debug request to {debug_path}", file=sys.stderr)
except Exception: except Exception:
pass pass
if e.code == 429 and ep != endpoints[-1]: if e.code == 429 and ep != endpoints[-1]:
print(f"[gemini-oauth] {ep} HTTP 429, trying next endpoint", file=sys.stderr) print(f"[{self._session_id}] {ep} HTTP 429, trying next endpoint", file=sys.stderr)
continue continue
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}}) return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}})
except Exception as e: except Exception as e:
if ep == endpoints[-1]: if ep == endpoints[-1]:
return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}}) return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}})
print(f"[gemini-oauth] {ep} failed: {e}, trying next", file=sys.stderr) print(f"[{self._session_id}] {ep} failed: {e}, trying next", file=sys.stderr)
continue continue
if stream: if stream:
@@ -3566,10 +3569,10 @@ class Handler(http.server.BaseHTTPRequestHandler):
candidates = chunk.get("response", chunk).get("candidates", []) candidates = chunk.get("response", chunk).get("candidates", [])
if not candidates: if not candidates:
if chunk.get("error"): if chunk.get("error"):
print(f"[gemini-oauth] stream error chunk: {str(chunk.get('error'))[:300]}", file=sys.stderr) print(f"[{self._session_id}] stream error chunk: {str(chunk.get('error'))[:300]}", file=sys.stderr)
continue continue
if candidates[0].get("finishReason") and not candidates[0].get("content", {}).get("parts"): if candidates[0].get("finishReason") and not candidates[0].get("content", {}).get("parts"):
print(f"[gemini-oauth] finish without parts: {candidates[0].get('finishReason')}", file=sys.stderr) print(f"[{self._session_id}] finish without parts: {candidates[0].get('finishReason')}", file=sys.stderr)
parts = candidates[0].get("content", {}).get("parts", []) parts = candidates[0].get("content", {}).get("parts", [])
for part in parts: for part in parts:
if part.get("thought"): if part.get("thought"):
@@ -3598,7 +3601,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
last_finish = candidates[0].get("finishReason", "") last_finish = candidates[0].get("finishReason", "")
if OAUTH_PROVIDER == "google-antigravity" and full_text and last_finish: if OAUTH_PROVIDER == "google-antigravity" and full_text and last_finish:
if last_finish == "MAX_TOKENS" and not current_tool_calls: if last_finish == "MAX_TOKENS" and not current_tool_calls:
print(f"[gemini-oauth] MAX_TOKENS hit ({len(full_text)} chars), auto-continuing...", file=sys.stderr) print(f"[{self._session_id}] MAX_TOKENS hit ({len(full_text)} chars), auto-continuing...", file=sys.stderr)
break break
stream_finished = True stream_finished = True
break break
@@ -3704,14 +3707,14 @@ class Handler(http.server.BaseHTTPRequestHandler):
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": f"Bearer {r_key}", "Authorization": f"Bearer {r_key}",
}, browser_ua=True) }, browser_ua=True)
print(f"[bgp] trying route '{route.get('name', r_url)}' model={r_model}", file=sys.stderr) print(f"[{self._session_id}] trying route '{route.get('name', r_url)}' model={r_model}", file=sys.stderr)
req = urllib.request.Request(target, data=json.dumps(chat_body).encode(), headers=fwd) req = urllib.request.Request(target, data=json.dumps(chat_body).encode(), headers=fwd)
t0_route = time.time() t0_route = time.time()
route_ok = False route_ok = False
for attempt in range(3): for attempt in range(3):
try: try:
upstream = urllib.request.urlopen(req, timeout=_upstream_timeout(body, stream)) upstream = urllib.request.urlopen(req, timeout=_upstream_timeout(body, stream))
print(f"[bgp] route '{route.get('name', r_url)}' connected OK", file=sys.stderr) print(f"[{self._session_id}] route '{route.get('name', r_url)}' connected OK", file=sys.stderr)
_update_route_stats(route, True, time.time() - t0_route) _update_route_stats(route, True, time.time() - t0_route)
self._forward_oa_compat(upstream, stream, r_model, chat_body, body, input_data, fwd, target) self._forward_oa_compat(upstream, stream, r_model, chat_body, body, input_data, fwd, target)
return return
@@ -3720,18 +3723,18 @@ class Handler(http.server.BaseHTTPRequestHandler):
if e.code in (429, 502, 503) and attempt < 2: if e.code in (429, 502, 503) and attempt < 2:
retry_after = e.headers.get("Retry-After") retry_after = e.headers.get("Retry-After")
wait = min(int(retry_after), 60) if retry_after and retry_after.isdigit() else min(2 ** (attempt + 1), 10) wait = min(int(retry_after), 60) if retry_after and retry_after.isdigit() else min(2 ** (attempt + 1), 10)
print(f"[bgp] route '{route.get('name', r_url)}' HTTP {e.code}, retry {attempt+1}/2 in {wait}s", file=sys.stderr) print(f"[{self._session_id}] route '{route.get('name', r_url)}' HTTP {e.code}, retry {attempt+1}/2 in {wait}s", file=sys.stderr)
time.sleep(wait) time.sleep(wait)
req = urllib.request.Request(target, data=json.dumps(chat_body).encode(), headers=fwd) req = urllib.request.Request(target, data=json.dumps(chat_body).encode(), headers=fwd)
continue continue
print(f"[bgp] route '{route.get('name', r_url)}' FAILED: HTTP {e.code}: {err[:200]}", file=sys.stderr) print(f"[{self._session_id}] route '{route.get('name', r_url)}' FAILED: HTTP {e.code}: {err[:200]}", file=sys.stderr)
_update_route_stats(route, False, time.time() - t0_route, http_code=e.code) _update_route_stats(route, False, time.time() - t0_route, http_code=e.code)
errors.append(f"{route.get('name','?')}: HTTP {e.code}") errors.append(f"{route.get('name','?')}: HTTP {e.code}")
break break
except (ConnectionResetError, ConnectionAbortedError, BrokenPipeError) as e: except (ConnectionResetError, ConnectionAbortedError, BrokenPipeError) as e:
if attempt < 2: if attempt < 2:
wait = min(2 ** (attempt + 1), 8) wait = min(2 ** (attempt + 1), 8)
print(f"[bgp] route '{route.get('name', r_url)}' conn error, retry {attempt+1}/2 in {wait}s: {e}", file=sys.stderr) print(f"[{self._session_id}] route '{route.get('name', r_url)}' conn error, retry {attempt+1}/2 in {wait}s: {e}", file=sys.stderr)
time.sleep(wait) time.sleep(wait)
req = urllib.request.Request(target, data=json.dumps(chat_body).encode(), headers=fwd) req = urllib.request.Request(target, data=json.dumps(chat_body).encode(), headers=fwd)
continue continue
@@ -3739,12 +3742,12 @@ class Handler(http.server.BaseHTTPRequestHandler):
errors.append(f"{route.get('name','?')}: {e}") errors.append(f"{route.get('name','?')}: {e}")
break break
except Exception as e: except Exception as e:
print(f"[bgp] route '{route.get('name', r_url)}' FAILED: {e}", file=sys.stderr) print(f"[{self._session_id}] route '{route.get('name', r_url)}' FAILED: {e}", file=sys.stderr)
_update_route_stats(route, False, time.time() - t0_route, error_type=str(e)) _update_route_stats(route, False, time.time() - t0_route, error_type=str(e))
errors.append(f"{route.get('name','?')}: {e}") errors.append(f"{route.get('name','?')}: {e}")
break break
print(f"[bgp] ALL ROUTES FAILED: {errors}", file=sys.stderr) print(f"[{self._session_id}] ALL ROUTES FAILED: {errors}", file=sys.stderr)
self.send_json(502, {"error": {"type": "bgp_all_routes_failed", "message": f"All BGP routes failed: {'; '.join(errors)}"}}) self.send_json(502, {"error": {"type": "bgp_all_routes_failed", "message": f"All BGP routes failed: {'; '.join(errors)}"}})
def _forward_oa_compat(self, upstream, stream, model, chat_body, body, input_data, fwd, target, tracker=None): def _forward_oa_compat(self, upstream, stream, model, chat_body, body, input_data, fwd, target, tracker=None):
@@ -4022,7 +4025,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
} }
fwd = forwarded_headers(self.headers, headers_extra, browser_ua=True) fwd = forwarded_headers(self.headers, headers_extra, browser_ua=True)
print(f"[translate-proxy] POST {target} model={model} stream={stream} attempt={attempt} [command-code]", file=sys.stderr) print(f"[{self._session_id}] POST {target} model={model} stream={stream} attempt={attempt} [command-code]", file=sys.stderr)
req = urllib.request.Request( req = urllib.request.Request(
target, target,
data=json.dumps(cc_body).encode(), data=json.dumps(cc_body).encode(),
@@ -4037,7 +4040,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
if attempt < max_retries: if attempt < max_retries:
hints = ErrorAnalyzer.analyze(err, schema) hints = ErrorAnalyzer.analyze(err, schema)
if hints: if hints:
print(f"[command-code] error analysis: {hints}", file=sys.stderr) print(f"[{self._session_id}] error analysis: {hints}", file=sys.stderr)
ErrorAnalyzer.merge_into_schema(hints, schema) ErrorAnalyzer.merge_into_schema(hints, schema)
_save_schema(schema, model=model) _save_schema(schema, model=model)
continue continue
@@ -4083,7 +4086,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
try: try:
self.stream_buffered_events(cc_stream_to_sse(upstream, model, body.get("request_id") or body.get("id")), on_event=on_event) self.stream_buffered_events(cc_stream_to_sse(upstream, model, body.get("request_id") or body.get("id")), on_event=on_event)
except Exception as e: except Exception as e:
print(f"[command-code] stream error: {e}", file=sys.stderr) print(f"[{self._session_id}] stream error: {e}", file=sys.stderr)
try: try:
err_event = 'data: ' + json.dumps({"type": "response.completed", err_event = 'data: ' + json.dumps({"type": "response.completed",
"response": {"id": body.get("request_id") or body.get("id") or uid("resp"), "response": {"id": body.get("request_id") or body.get("id") or uid("resp"),
@@ -4416,7 +4419,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
def log_message(self, fmt, *args): def log_message(self, fmt, *args):
msg = fmt % args if args else fmt msg = fmt % args if args else fmt
print(f"[translate-proxy] {BACKEND} {msg}", file=sys.stderr) print(f"[{self._session_id}] {BACKEND} {msg}", file=sys.stderr)
_SHUTDOWN_REQUESTED = False _SHUTDOWN_REQUESTED = False