diff --git a/docs/docs.go b/docs/docs.go index 489bf98..8f4fc4e 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -861,7 +861,7 @@ const docTemplate = `{ "AdminAuth": [] } ], - "description": "List request logs with basic filtering/pagination", + "description": "List request logs with basic filtering/pagination. Returns full log records including request_body.", "produces": [ "application/json" ], @@ -923,7 +923,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/internal_api.ListMasterLogsResponse" + "$ref": "#/definitions/internal_api.ListLogsResponse" } }, "500": { @@ -991,7 +991,7 @@ const docTemplate = `{ "AdminAuth": [] } ], - "description": "Aggregate log stats with basic filtering", + "description": "Aggregate log stats with basic filtering. Use group_by param for grouped statistics (model/day/month). Without group_by returns LogStatsResponse; with group_by returns GroupedStatsResponse.", "produces": [ "application/json" ], @@ -1011,13 +1011,24 @@ const docTemplate = `{ "description": "unix seconds", "name": "until", "in": "query" + }, + { + "enum": [ + "model", + "day", + "month" + ], + "type": "string", + "description": "group by dimension: model, day, month. Returns GroupedStatsResponse when specified.", + "name": "group_by", + "in": "query" } ], "responses": { "200": { - "description": "OK", + "description": "Grouped stats (when group_by is specified)", "schema": { - "$ref": "#/definitions/internal_api.LogStatsResponse" + "$ref": "#/definitions/internal_api.GroupedStatsResponse" } }, "500": { @@ -4175,6 +4186,46 @@ const docTemplate = `{ } } }, + "internal_api.GroupedStatsItem": { + "type": "object", + "properties": { + "avg_latency_ms": { + "type": "number" + }, + "count": { + "type": "integer" + }, + "date": { + "description": "For group_by=day", + "type": "string" + }, + "model": { + "description": "For group_by=model", + "type": "string" + }, + "month": { + "description": "For group_by=month", + "type": "string" + }, + "tokens_in": { + "type": "integer" + }, + "tokens_out": { + "type": "integer" + } + } + }, + "internal_api.GroupedStatsResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_api.GroupedStatsItem" + } + } + } + }, "internal_api.IssueChildKeyRequest": { "type": "object", "properties": { @@ -4215,6 +4266,26 @@ const docTemplate = `{ } } }, + "internal_api.ListLogsResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_api.LogView" + } + }, + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, "internal_api.ListMasterLogsResponse": { "type": "object", "properties": { @@ -4259,6 +4330,65 @@ const docTemplate = `{ } } }, + "internal_api.LogView": { + "type": "object", + "properties": { + "audit_reason": { + "type": "string" + }, + "client_ip": { + "type": "string" + }, + "created_at": { + "type": "integer" + }, + "error_message": { + "type": "string" + }, + "group": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "key_id": { + "type": "integer" + }, + "latency_ms": { + "type": "integer" + }, + "model": { + "type": "string" + }, + "provider_id": { + "type": "integer" + }, + "provider_name": { + "type": "string" + }, + "provider_type": { + "type": "string" + }, + "request_body": { + "type": "string" + }, + "request_size": { + "type": "integer" + }, + "response_size": { + "type": "integer" + }, + "status_code": { + "type": "integer" + }, + "tokens_in": { + "type": "integer" + }, + "tokens_out": { + "type": "integer" + } + } + }, "internal_api.ManageMasterRequest": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index cda71cc..66f8bc5 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -855,7 +855,7 @@ "AdminAuth": [] } ], - "description": "List request logs with basic filtering/pagination", + "description": "List request logs with basic filtering/pagination. Returns full log records including request_body.", "produces": [ "application/json" ], @@ -917,7 +917,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/internal_api.ListMasterLogsResponse" + "$ref": "#/definitions/internal_api.ListLogsResponse" } }, "500": { @@ -985,7 +985,7 @@ "AdminAuth": [] } ], - "description": "Aggregate log stats with basic filtering", + "description": "Aggregate log stats with basic filtering. Use group_by param for grouped statistics (model/day/month). Without group_by returns LogStatsResponse; with group_by returns GroupedStatsResponse.", "produces": [ "application/json" ], @@ -1005,13 +1005,24 @@ "description": "unix seconds", "name": "until", "in": "query" + }, + { + "enum": [ + "model", + "day", + "month" + ], + "type": "string", + "description": "group by dimension: model, day, month. Returns GroupedStatsResponse when specified.", + "name": "group_by", + "in": "query" } ], "responses": { "200": { - "description": "OK", + "description": "Grouped stats (when group_by is specified)", "schema": { - "$ref": "#/definitions/internal_api.LogStatsResponse" + "$ref": "#/definitions/internal_api.GroupedStatsResponse" } }, "500": { @@ -4169,6 +4180,46 @@ } } }, + "internal_api.GroupedStatsItem": { + "type": "object", + "properties": { + "avg_latency_ms": { + "type": "number" + }, + "count": { + "type": "integer" + }, + "date": { + "description": "For group_by=day", + "type": "string" + }, + "model": { + "description": "For group_by=model", + "type": "string" + }, + "month": { + "description": "For group_by=month", + "type": "string" + }, + "tokens_in": { + "type": "integer" + }, + "tokens_out": { + "type": "integer" + } + } + }, + "internal_api.GroupedStatsResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_api.GroupedStatsItem" + } + } + } + }, "internal_api.IssueChildKeyRequest": { "type": "object", "properties": { @@ -4209,6 +4260,26 @@ } } }, + "internal_api.ListLogsResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_api.LogView" + } + }, + "limit": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, "internal_api.ListMasterLogsResponse": { "type": "object", "properties": { @@ -4253,6 +4324,65 @@ } } }, + "internal_api.LogView": { + "type": "object", + "properties": { + "audit_reason": { + "type": "string" + }, + "client_ip": { + "type": "string" + }, + "created_at": { + "type": "integer" + }, + "error_message": { + "type": "string" + }, + "group": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "key_id": { + "type": "integer" + }, + "latency_ms": { + "type": "integer" + }, + "model": { + "type": "string" + }, + "provider_id": { + "type": "integer" + }, + "provider_name": { + "type": "string" + }, + "provider_type": { + "type": "string" + }, + "request_body": { + "type": "string" + }, + "request_size": { + "type": "integer" + }, + "response_size": { + "type": "integer" + }, + "status_code": { + "type": "integer" + }, + "tokens_in": { + "type": "integer" + }, + "tokens_out": { + "type": "integer" + } + } + }, "internal_api.ManageMasterRequest": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 1078e4a..d21f473 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -417,6 +417,33 @@ definitions: deleted_count: type: integer type: object + internal_api.GroupedStatsItem: + properties: + avg_latency_ms: + type: number + count: + type: integer + date: + description: For group_by=day + type: string + model: + description: For group_by=model + type: string + month: + description: For group_by=month + type: string + tokens_in: + type: integer + tokens_out: + type: integer + type: object + internal_api.GroupedStatsResponse: + properties: + items: + items: + $ref: '#/definitions/internal_api.GroupedStatsItem' + type: array + type: object internal_api.IssueChildKeyRequest: properties: allow_ips: @@ -443,6 +470,19 @@ definitions: tokens: type: integer type: object + internal_api.ListLogsResponse: + properties: + items: + items: + $ref: '#/definitions/internal_api.LogView' + type: array + limit: + type: integer + offset: + type: integer + total: + type: integer + type: object internal_api.ListMasterLogsResponse: properties: items: @@ -472,6 +512,45 @@ definitions: total: type: integer type: object + internal_api.LogView: + properties: + audit_reason: + type: string + client_ip: + type: string + created_at: + type: integer + error_message: + type: string + group: + type: string + id: + type: integer + key_id: + type: integer + latency_ms: + type: integer + model: + type: string + provider_id: + type: integer + provider_name: + type: string + provider_type: + type: string + request_body: + type: string + request_size: + type: integer + response_size: + type: integer + status_code: + type: integer + tokens_in: + type: integer + tokens_out: + type: integer + type: object internal_api.ManageMasterRequest: properties: action: @@ -1426,7 +1505,8 @@ paths: tags: - admin get: - description: List request logs with basic filtering/pagination + description: List request logs with basic filtering/pagination. Returns full + log records including request_body. parameters: - description: limit (default 50, max 200) in: query @@ -1466,7 +1546,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/internal_api.ListMasterLogsResponse' + $ref: '#/definitions/internal_api.ListLogsResponse' "500": description: Internal Server Error schema: @@ -1478,7 +1558,9 @@ paths: - admin /admin/logs/stats: get: - description: Aggregate log stats with basic filtering + description: Aggregate log stats with basic filtering. Use group_by param for + grouped statistics (model/day/month). Without group_by returns LogStatsResponse; + with group_by returns GroupedStatsResponse. parameters: - description: unix seconds in: query @@ -1488,13 +1570,22 @@ paths: in: query name: until type: integer + - description: 'group by dimension: model, day, month. Returns GroupedStatsResponse + when specified.' + enum: + - model + - day + - month + in: query + name: group_by + type: string produces: - application/json responses: "200": - description: OK + description: Grouped stats (when group_by is specified) schema: - $ref: '#/definitions/internal_api.LogStatsResponse' + $ref: '#/definitions/internal_api.GroupedStatsResponse' "500": description: Internal Server Error schema: diff --git a/internal/api/log_handler.go b/internal/api/log_handler.go index 6a4bba8..8fb0d29 100644 --- a/internal/api/log_handler.go +++ b/internal/api/log_handler.go @@ -155,7 +155,7 @@ func (h *MasterHandler) masterLogBase(masterID uint) (*gorm.DB, error) { // ListLogs godoc // @Summary List logs (admin) -// @Description List request logs with basic filtering/pagination +// @Description List request logs with basic filtering/pagination. Returns full log records including request_body. // @Tags admin // @Produce json // @Security AdminAuth @@ -167,7 +167,7 @@ func (h *MasterHandler) masterLogBase(masterID uint) (*gorm.DB, error) { // @Param group query string false "route group" // @Param model query string false "model" // @Param status_code query int false "status code" -// @Success 200 {object} ListMasterLogsResponse +// @Success 200 {object} ListLogsResponse // @Failure 500 {object} gin.H // @Router /admin/logs [get] func (h *Handler) ListLogs(c *gin.Context) { @@ -346,14 +346,15 @@ func (h *Handler) deleteLogsBefore(cutoff time.Time, keyID uint, modelName strin // LogStats godoc // @Summary Log stats (admin) -// @Description Aggregate log stats with basic filtering. Use group_by param for grouped statistics. +// @Description Aggregate log stats with basic filtering. Use group_by param for grouped statistics (model/day/month). Without group_by returns LogStatsResponse; with group_by returns GroupedStatsResponse. // @Tags admin // @Produce json // @Security AdminAuth // @Param since query int false "unix seconds" // @Param until query int false "unix seconds" -// @Param group_by query string false "group by dimension: model, day, month" -// @Success 200 {object} LogStatsResponse +// @Param group_by query string false "group by dimension: model, day, month. Returns GroupedStatsResponse when specified." Enums(model, day, month) +// @Success 200 {object} LogStatsResponse "Default aggregated stats (when group_by is not specified)" +// @Success 200 {object} GroupedStatsResponse "Grouped stats (when group_by is specified)" // @Failure 500 {object} gin.H // @Router /admin/logs/stats [get] func (h *Handler) LogStats(c *gin.Context) {