v3.6.0 — add per-session ID (8-char hex) to all proxy log lines
This commit is contained in:
Binary file not shown.
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user