feat(ops): add endpoint/model/request_type fields to error log structs + safeUpstreamURL
This commit is contained in:
@@ -62,6 +62,12 @@ type OpsErrorLog struct {
|
||||
ClientIP *string `json:"client_ip"`
|
||||
RequestPath string `json:"request_path"`
|
||||
Stream bool `json:"stream"`
|
||||
|
||||
InboundEndpoint string `json:"inbound_endpoint"`
|
||||
UpstreamEndpoint string `json:"upstream_endpoint"`
|
||||
RequestedModel string `json:"requested_model"`
|
||||
UpstreamModel string `json:"upstream_model"`
|
||||
RequestType *int16 `json:"request_type"`
|
||||
}
|
||||
|
||||
type OpsErrorLogDetail struct {
|
||||
|
||||
@@ -79,6 +79,17 @@ type OpsInsertErrorLogInput struct {
|
||||
Model string
|
||||
RequestPath string
|
||||
Stream bool
|
||||
// InboundEndpoint is the normalized client-facing API endpoint path, e.g. /v1/chat/completions.
|
||||
InboundEndpoint string
|
||||
// UpstreamEndpoint is the normalized upstream endpoint path, e.g. /v1/responses.
|
||||
UpstreamEndpoint string
|
||||
// RequestedModel is the client-requested model name before mapping.
|
||||
RequestedModel string
|
||||
// UpstreamModel is the actual model sent to upstream after mapping. Empty means no mapping.
|
||||
UpstreamModel string
|
||||
// RequestType is the granular request type: 0=unknown, 1=sync, 2=stream, 3=ws_v2.
|
||||
// Matches service.RequestType enum semantics from usage_log.go.
|
||||
RequestType *int16
|
||||
UserAgent string
|
||||
|
||||
ErrorPhase string
|
||||
|
||||
@@ -93,6 +93,10 @@ type OpsUpstreamErrorEvent struct {
|
||||
UpstreamStatusCode int `json:"upstream_status_code,omitempty"`
|
||||
UpstreamRequestID string `json:"upstream_request_id,omitempty"`
|
||||
|
||||
// UpstreamURL is the actual upstream URL that was called (host + path, query/fragment stripped).
|
||||
// Helps debug 404/routing errors by showing which endpoint was targeted.
|
||||
UpstreamURL string `json:"upstream_url,omitempty"`
|
||||
|
||||
// Best-effort upstream request capture (sanitized+trimmed).
|
||||
// Required for retrying a specific upstream attempt.
|
||||
UpstreamRequestBody string `json:"upstream_request_body,omitempty"`
|
||||
@@ -119,6 +123,7 @@ func appendOpsUpstreamError(c *gin.Context, ev OpsUpstreamErrorEvent) {
|
||||
ev.UpstreamRequestBody = strings.TrimSpace(ev.UpstreamRequestBody)
|
||||
ev.UpstreamResponseBody = strings.TrimSpace(ev.UpstreamResponseBody)
|
||||
ev.Kind = strings.TrimSpace(ev.Kind)
|
||||
ev.UpstreamURL = strings.TrimSpace(ev.UpstreamURL)
|
||||
ev.Message = strings.TrimSpace(ev.Message)
|
||||
ev.Detail = strings.TrimSpace(ev.Detail)
|
||||
if ev.Message != "" {
|
||||
@@ -205,3 +210,19 @@ func ParseOpsUpstreamErrors(raw string) ([]*OpsUpstreamErrorEvent, error) {
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// safeUpstreamURL returns scheme + host + path from a URL, stripping query/fragment
|
||||
// to avoid leaking sensitive query parameters (e.g. OAuth tokens).
|
||||
func safeUpstreamURL(rawURL string) string {
|
||||
rawURL = strings.TrimSpace(rawURL)
|
||||
if rawURL == "" {
|
||||
return ""
|
||||
}
|
||||
if idx := strings.IndexByte(rawURL, '?'); idx >= 0 {
|
||||
rawURL = rawURL[:idx]
|
||||
}
|
||||
if idx := strings.IndexByte(rawURL, '#'); idx >= 0 {
|
||||
rawURL = rawURL[:idx]
|
||||
}
|
||||
return rawURL
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
-- Ops error logs: add endpoint, model mapping, and request_type fields
|
||||
-- to match usage_logs observability coverage.
|
||||
--
|
||||
-- All columns are nullable with no default to preserve backward compatibility
|
||||
-- with existing rows.
|
||||
|
||||
SET LOCAL lock_timeout = '5s';
|
||||
SET LOCAL statement_timeout = '10min';
|
||||
|
||||
-- 1) Standardized endpoint paths (analogous to usage_logs.inbound_endpoint / upstream_endpoint)
|
||||
ALTER TABLE ops_error_logs
|
||||
ADD COLUMN IF NOT EXISTS inbound_endpoint VARCHAR(256),
|
||||
ADD COLUMN IF NOT EXISTS upstream_endpoint VARCHAR(256);
|
||||
|
||||
-- 2) Model mapping fields (analogous to usage_logs.requested_model / upstream_model)
|
||||
ALTER TABLE ops_error_logs
|
||||
ADD COLUMN IF NOT EXISTS requested_model VARCHAR(100),
|
||||
ADD COLUMN IF NOT EXISTS upstream_model VARCHAR(100);
|
||||
|
||||
-- 3) Granular request type enum (analogous to usage_logs.request_type: 0=unknown, 1=sync, 2=stream, 3=ws_v2)
|
||||
ALTER TABLE ops_error_logs
|
||||
ADD COLUMN IF NOT EXISTS request_type SMALLINT;
|
||||
|
||||
COMMENT ON COLUMN ops_error_logs.inbound_endpoint IS 'Normalized client-facing API endpoint path, e.g. /v1/chat/completions. Populated from InboundEndpointMiddleware.';
|
||||
COMMENT ON COLUMN ops_error_logs.upstream_endpoint IS 'Normalized upstream endpoint path derived from platform, e.g. /v1/responses.';
|
||||
COMMENT ON COLUMN ops_error_logs.requested_model IS 'Client-requested model name before mapping (raw from request body).';
|
||||
COMMENT ON COLUMN ops_error_logs.upstream_model IS 'Actual model sent to upstream provider after mapping. NULL means no mapping applied.';
|
||||
COMMENT ON COLUMN ops_error_logs.request_type IS 'Request type enum: 0=unknown, 1=sync, 2=stream, 3=ws_v2. Matches usage_logs.request_type semantics.';
|
||||
Reference in New Issue
Block a user