KarenYYH Claude commited on
Commit
fcc1a30
·
1 Parent(s): 80d80f5

Add: Supabase 日志系统

Browse files

完整的结构化日志记录解决方案

新增文件:
1. supabase_schema_with_logs.sql - 数据库表结构
- api_access_logs: API 访问日志
- error_logs: 错误日志
- debug_logs: 调试日志
- model_performance_logs: 模型性能日志
- user_behavior_logs: 用户行为日志

2. services/supabase_logger.py - 日志记录器
- SupabaseLogger 类:统一的日志记录接口
- 装饰器:@log_api_access, @log_model_performance
- 上下文管理器:LogPerformance
- 简化函数:log_debug, log_error

3. services/supabase_logger_examples.py - 使用示例
- 基本日志记录
- 装饰器使用
- 上下文管理器使用
- FastAPI 集成
- 批量日志记录

功能特性:
- 自动记录 API 访问和性能指标
- 结构化错误追踪
- 模型性能监控
- 用户行为分析
- 自动日志清理
- 统计和查询函数

环境变量:
- ENABLE_SUPABASE_LOGGING=true 启用日志

Co-Authored-By: Claude <noreply@anthropic.com>

services/supabase_logger.py ADDED
@@ -0,0 +1,516 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Supabase 日志记录器
3
+
4
+ 提供结构化的日志记录功能,将日志存储到 Supabase
5
+ 支持:API 访问日志、错误日志、调试日志、模型性能日志、用户行为日志
6
+ """
7
+ import os
8
+ import json
9
+ import time
10
+ import traceback
11
+ from typing import Optional, Dict, Any, List
12
+ from datetime import datetime
13
+ from functools import wraps
14
+ import logging
15
+
16
+
17
+ class SupabaseLogger:
18
+ """
19
+ Supabase 日志记录器
20
+
21
+ 用法:
22
+ logger = SupabaseLogger()
23
+
24
+ # 记录 API 访问
25
+ logger.log_api_access(
26
+ endpoint="/api/v1/chat",
27
+ method="POST",
28
+ status_code=200,
29
+ response_time_ms=150
30
+ )
31
+
32
+ # 记录错误
33
+ logger.log_error(
34
+ error_type="model_load",
35
+ error_message="Failed to load model",
36
+ error_stack=traceback.format_exc()
37
+ )
38
+
39
+ # 记录调试信息
40
+ logger.log_debug(
41
+ level="INFO",
42
+ logger="intelligence_analyzer",
43
+ message="Scenario detected: policy_inquiry",
44
+ data={"scenario": "policy_inquiry", "confidence": 0.9}
45
+ )
46
+ """
47
+
48
+ def __init__(self):
49
+ """初始化日志记录器"""
50
+ self._client = None
51
+ self._enabled = None
52
+ self._fallback_logger = logging.getLogger(__name__)
53
+
54
+ @property
55
+ def client(self):
56
+ """延迟加载 Supabase 客户端"""
57
+ if self._client is None:
58
+ try:
59
+ from services.supabase_client import get_supabase_client
60
+ self._client = get_supabase_client()
61
+ except Exception as e:
62
+ self._fallback_logger.warning(f"Supabase client not available: {e}")
63
+ self._client = False # 标记为不可用
64
+ return self._client if self._client is not False else None
65
+
66
+ @property
67
+ def enabled(self) -> bool:
68
+ """检查是否启用日志记录"""
69
+ if self._enabled is None:
70
+ self._enabled = os.getenv("ENABLE_SUPABASE_LOGGING", "true").lower() == "true"
71
+ return self._enabled
72
+
73
+ def log_api_access(
74
+ self,
75
+ endpoint: str,
76
+ method: str,
77
+ path: Optional[str] = None,
78
+ query_params: Optional[Dict] = None,
79
+ client_ip: Optional[str] = None,
80
+ user_agent: Optional[str] = None,
81
+ session_id: Optional[str] = None,
82
+ response_time_ms: Optional[int] = None,
83
+ status_code: Optional[int] = None,
84
+ metadata: Optional[Dict] = None
85
+ ) -> bool:
86
+ """
87
+ 记录 API 访问日志
88
+
89
+ Args:
90
+ endpoint: API 端点(如 /api/v1/chat)
91
+ method: HTTP 方法
92
+ path: 请求路径
93
+ query_params: 查询参数
94
+ client_ip: 客户端 IP
95
+ user_agent: 用户代理
96
+ session_id: 会话 ID
97
+ response_time_ms: 响应时间(毫秒)
98
+ status_code: HTTP 状态码
99
+ metadata: 额外元数据
100
+
101
+ Returns:
102
+ 是否成功记录
103
+ """
104
+ if not self.enabled or not self.client:
105
+ return False
106
+
107
+ try:
108
+ data = {
109
+ "endpoint": endpoint,
110
+ "method": method,
111
+ "path": path,
112
+ "query_params": query_params,
113
+ "client_ip": client_ip,
114
+ "user_agent": user_agent,
115
+ "session_id": session_id,
116
+ "response_time_ms": response_time_ms,
117
+ "status_code": status_code,
118
+ "metadata": metadata or {}
119
+ }
120
+
121
+ self.client.table("api_access_logs").insert(data).execute()
122
+ return True
123
+ except Exception as e:
124
+ self._fallback_logger.error(f"Failed to log API access: {e}")
125
+ return False
126
+
127
+ def log_error(
128
+ self,
129
+ error_type: str,
130
+ error_message: str,
131
+ error_stack: Optional[str] = None,
132
+ endpoint: Optional[str] = None,
133
+ session_id: Optional[str] = None,
134
+ user_input: Optional[str] = None,
135
+ request_data: Optional[Dict] = None,
136
+ metadata: Optional[Dict] = None
137
+ ) -> bool:
138
+ """
139
+ 记录错误日志
140
+
141
+ Args:
142
+ error_type: 错误类型
143
+ error_message: 错误消息
144
+ error_stack: 错误堆栈
145
+ endpoint: 发生错误的端点
146
+ session_id: 会话 ID
147
+ user_input: 用户输入
148
+ request_data: 请求数据
149
+ metadata: 额外元数据
150
+
151
+ Returns:
152
+ 是否成功记录
153
+ """
154
+ if not self.enabled or not self.client:
155
+ return False
156
+
157
+ try:
158
+ data = {
159
+ "error_type": error_type,
160
+ "error_message": error_message,
161
+ "error_stack": error_stack,
162
+ "endpoint": endpoint,
163
+ "session_id": session_id,
164
+ "user_input": user_input,
165
+ "request_data": request_data,
166
+ "metadata": metadata or {}
167
+ }
168
+
169
+ self.client.table("error_logs").insert(data).execute()
170
+ return True
171
+ except Exception as e:
172
+ self._fallback_logger.error(f"Failed to log error: {e}")
173
+ return False
174
+
175
+ def log_debug(
176
+ self,
177
+ level: str,
178
+ logger: str,
179
+ message: str,
180
+ session_id: Optional[str] = None,
181
+ function_name: Optional[str] = None,
182
+ line_number: Optional[int] = None,
183
+ data: Optional[Dict] = None,
184
+ metadata: Optional[Dict] = None
185
+ ) -> bool:
186
+ """
187
+ 记录调试日志
188
+
189
+ Args:
190
+ level: 日志级别(DEBUG, INFO, WARNING, ERROR)
191
+ logger: 模块名称
192
+ message: 日志消息
193
+ session_id: 会话 ID
194
+ function_name: 函数名
195
+ line_number: 行号
196
+ data: 结构化数据
197
+ metadata: 额外元数据
198
+
199
+ Returns:
200
+ 是否成功记录
201
+ """
202
+ if not self.enabled or not self.client:
203
+ return False
204
+
205
+ # 只记录 INFO 及以上级别的日志
206
+ if level == "DEBUG" and os.getenv("LOG_LEVEL", "INFO").upper() != "DEBUG":
207
+ return False
208
+
209
+ try:
210
+ log_data = {
211
+ "level": level,
212
+ "logger": logger,
213
+ "message": message,
214
+ "session_id": session_id,
215
+ "function_name": function_name,
216
+ "line_number": line_number,
217
+ "data": data,
218
+ "metadata": metadata or {}
219
+ }
220
+
221
+ self.client.table("debug_logs").insert(log_data).execute()
222
+ return True
223
+ except Exception as e:
224
+ self._fallback_logger.error(f"Failed to log debug: {e}")
225
+ return False
226
+
227
+ def log_model_performance(
228
+ self,
229
+ model_name: str,
230
+ model_type: str,
231
+ load_time_ms: Optional[int] = None,
232
+ inference_time_ms: Optional[int] = None,
233
+ total_time_ms: Optional[int] = None,
234
+ input_text: Optional[str] = None,
235
+ output_summary: Optional[str] = None,
236
+ metadata: Optional[Dict] = None
237
+ ) -> bool:
238
+ """
239
+ 记录模型性能日志
240
+
241
+ Args:
242
+ model_name: 模型名称
243
+ model_type: 模型类型(sbert, sentiment, compliance, llm)
244
+ load_time_ms: 加载时间
245
+ inference_time_ms: 推理时间
246
+ total_time_ms: 总时间
247
+ input_text: 输入文本
248
+ output_summary: 输出摘要
249
+ metadata: 额外元数据
250
+
251
+ Returns:
252
+ 是否成功记录
253
+ """
254
+ if not self.enabled or not self.client:
255
+ return False
256
+
257
+ try:
258
+ data = {
259
+ "model_name": model_name,
260
+ "model_type": model_type,
261
+ "load_time_ms": load_time_ms,
262
+ "inference_time_ms": inference_time_ms,
263
+ "total_time_ms": total_time_ms,
264
+ "input_text": input_text[:500] if input_text else None, # 限制长度
265
+ "output_summary": output_summary[:500] if output_summary else None,
266
+ "metadata": metadata or {}
267
+ }
268
+
269
+ self.client.table("model_performance_logs").insert(data).execute()
270
+ return True
271
+ except Exception as e:
272
+ self._fallback_logger.error(f"Failed to log model performance: {e}")
273
+ return False
274
+
275
+ def log_user_behavior(
276
+ self,
277
+ session_id: str,
278
+ action_type: str,
279
+ action_detail: Optional[str] = None,
280
+ input_text: Optional[str] = None,
281
+ intent_detected: Optional[str] = None,
282
+ scenario_detected: Optional[str] = None,
283
+ success: Optional[bool] = None,
284
+ error_message: Optional[str] = None,
285
+ user_id: Optional[str] = None,
286
+ metadata: Optional[Dict] = None
287
+ ) -> bool:
288
+ """
289
+ 记录用户行为日志
290
+
291
+ Args:
292
+ session_id: 会话 ID
293
+ action_type: 动作类型(chat, evaluate, config_check, etc.)
294
+ action_detail: 动作详情
295
+ input_text: 用户输入
296
+ intent_detected: 检测到的意图
297
+ scenario_detected: 检测到的场景
298
+ success: 是否成功
299
+ error_message: 错误消息
300
+ user_id: 用户 ID
301
+ metadata: 额外元数据
302
+
303
+ Returns:
304
+ 是否成功记录
305
+ """
306
+ if not self.enabled or not self.client:
307
+ return False
308
+
309
+ try:
310
+ data = {
311
+ "session_id": session_id,
312
+ "user_id": user_id,
313
+ "action_type": action_type,
314
+ "action_detail": action_detail,
315
+ "input_text": input_text,
316
+ "intent_detected": intent_detected,
317
+ "scenario_detected": scenario_detected,
318
+ "success": success,
319
+ "error_message": error_message,
320
+ "metadata": metadata or {}
321
+ }
322
+
323
+ self.client.table("user_behavior_logs").insert(data).execute()
324
+ return True
325
+ except Exception as e:
326
+ self._fallback_logger.error(f"Failed to log user behavior: {e}")
327
+ return False
328
+
329
+
330
+ # 全局单例
331
+ _supabase_logger_instance = None
332
+
333
+
334
+ def get_supabase_logger() -> SupabaseLogger:
335
+ """获取 Supabase 日志记录器单例"""
336
+ global _supabase_logger_instance
337
+ if _supabase_logger_instance is None:
338
+ _supabase_logger_instance = SupabaseLogger()
339
+ return _supabase_logger_instance
340
+
341
+
342
+ # ============================================
343
+ # 装饰器
344
+ # ============================================
345
+
346
+ def log_api_access(endpoint: str = None):
347
+ """
348
+ API 访问日志装饰器
349
+
350
+ 用法:
351
+ @log_api_access("/api/v1/chat")
352
+ async def chat_endpoint(request):
353
+ ...
354
+ """
355
+ def decorator(func):
356
+ @wraps(func)
357
+ async def wrapper(*args, **kwargs):
358
+ logger = get_supabase_logger()
359
+ start_time = time.time()
360
+
361
+ # 获取端点名称
362
+ ep = endpoint or f"/{func.__name__}"
363
+
364
+ try:
365
+ result = await func(*args, **kwargs)
366
+ status_code = getattr(result, 'status_code', 200) if hasattr(result, 'status_code') else 200
367
+
368
+ # 记录成功的 API 调用
369
+ response_time = int((time.time() - start_time) * 1000)
370
+ logger.log_api_access(
371
+ endpoint=ep,
372
+ method="POST",
373
+ status_code=status_code,
374
+ response_time_ms=response_time
375
+ )
376
+
377
+ return result
378
+ except Exception as e:
379
+ # 记录失败的 API 调用
380
+ response_time = int((time.time() - start_time) * 1000)
381
+ logger.log_api_access(
382
+ endpoint=ep,
383
+ method="POST",
384
+ status_code=500,
385
+ response_time_ms=response_time
386
+ )
387
+
388
+ # 记录错误
389
+ logger.log_error(
390
+ error_type="api_error",
391
+ error_message=str(e),
392
+ error_stack=traceback.format_exc(),
393
+ endpoint=ep
394
+ )
395
+
396
+ raise
397
+
398
+ return wrapper
399
+ return decorator
400
+
401
+
402
+ def log_model_performance(model_name: str, model_type: str):
403
+ """
404
+ 模型性能日志装饰器
405
+
406
+ 用法:
407
+ @log_model_performance("sbert-hr-v2", "sbert")
408
+ def encode_text(text):
409
+ ...
410
+ """
411
+ def decorator(func):
412
+ @wraps(func)
413
+ def wrapper(*args, **kwargs):
414
+ logger = get_supabase_logger()
415
+ start_time = time.time()
416
+
417
+ try:
418
+ result = func(*args, **kwargs)
419
+ total_time = int((time.time() - start_time) * 1000)
420
+
421
+ # 记录性能
422
+ logger.log_model_performance(
423
+ model_name=model_name,
424
+ model_type=model_type,
425
+ total_time_ms=total_time,
426
+ input_text=str(args[0]) if args else None
427
+ )
428
+
429
+ return result
430
+ except Exception as e:
431
+ total_time = int((time.time() - start_time) * 1000)
432
+
433
+ # 记录错误
434
+ logger.log_error(
435
+ error_type="model_inference",
436
+ error_message=str(e),
437
+ error_stack=traceback.format_exc()
438
+ )
439
+
440
+ raise
441
+
442
+ return wrapper
443
+ return decorator
444
+
445
+
446
+ # ============================================
447
+ # 上下文管理器 - 用于性能追踪
448
+ # ============================================
449
+
450
+ class LogPerformance:
451
+ """
452
+ 性能日志上下文管理器
453
+
454
+ 用法:
455
+ with LogPerformance("chat_processing", "llm"):
456
+ # 执行耗时操作
457
+ result = process_chat(request)
458
+ """
459
+
460
+ def __init__(self, operation_name: str, model_name: str, model_type: str):
461
+ self.operation_name = operation_name
462
+ self.model_name = model_name
463
+ self.model_type = model_type
464
+ self.logger = get_supabase_logger()
465
+ self.start_time = None
466
+
467
+ def __enter__(self):
468
+ self.start_time = time.time()
469
+ return self
470
+
471
+ def __exit__(self, exc_type, exc_val, exc_tb):
472
+ total_time = int((time.time() - self.start_time) * 1000)
473
+
474
+ if exc_type is None:
475
+ # 成功完成
476
+ self.logger.log_model_performance(
477
+ model_name=self.model_name,
478
+ model_type=self.model_type,
479
+ total_time_ms=total_time
480
+ )
481
+ else:
482
+ # 发生错误
483
+ self.logger.log_error(
484
+ error_type="performance_error",
485
+ error_message=str(exc_val),
486
+ error_stack=traceback.format_exception(exc_type, exc_val, exc_tb),
487
+ metadata={
488
+ "operation": self.operation_name,
489
+ "duration_ms": total_time
490
+ }
491
+ )
492
+
493
+ return False # 不抑制异常
494
+
495
+
496
+ # ============================================
497
+ # 简化的日志函数
498
+ # ============================================
499
+
500
+ def log_debug(logger_name: str, message: str, level: str = "INFO", **kwargs):
501
+ """简化的调试日志记录"""
502
+ get_supabase_logger().log_debug(
503
+ level=level,
504
+ logger=logger_name,
505
+ message=message,
506
+ **kwargs
507
+ )
508
+
509
+
510
+ def log_error(error_type: str, error_message: str, **kwargs):
511
+ """简化的错误日志记录"""
512
+ get_supabase_logger().log_error(
513
+ error_type=error_type,
514
+ error_message=error_message,
515
+ **kwargs
516
+ )
services/supabase_logger_examples.py ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Supabase 日志使用示例
3
+
4
+ 演示如何在项目中使用 Supabase 日志记录器
5
+ """
6
+ from services.supabase_logger import (
7
+ get_supabase_logger,
8
+ log_api_access,
9
+ log_model_performance,
10
+ LogPerformance,
11
+ log_debug,
12
+ log_error
13
+ )
14
+ from functools import wraps
15
+ import time
16
+
17
+
18
+ # ============================================
19
+ # 示例 1: 基本日志记录
20
+ # ============================================
21
+
22
+ def example_basic_logging():
23
+ """基本日志记录示例"""
24
+ logger = get_supabase_logger()
25
+
26
+ # 记录 API 访问
27
+ logger.log_api_access(
28
+ endpoint="/api/v1/chat",
29
+ method="POST",
30
+ session_id="user_123",
31
+ response_time_ms=150,
32
+ status_code=200
33
+ )
34
+
35
+ # 记录调试信息
36
+ logger.log_debug(
37
+ level="INFO",
38
+ logger="intelligence_analyzer",
39
+ message="Scenario detected: policy_inquiry",
40
+ session_id="user_123",
41
+ data={
42
+ "scenario": "policy_inquiry",
43
+ "confidence": 0.95,
44
+ "keywords": ["年假", "多少"]
45
+ }
46
+ )
47
+
48
+ # 记录错误
49
+ logger.log_error(
50
+ error_type="model_load",
51
+ error_message="Failed to load sentiment model",
52
+ error_stack="Traceback...\n File ...",
53
+ endpoint="/api/v1/evaluate"
54
+ )
55
+
56
+
57
+ # ============================================
58
+ # 示例 2: 使用装饰器
59
+ # ============================================
60
+
61
+ @log_api_access("/api/v1/chat")
62
+ def process_chat_request(question: str, session_id: str):
63
+ """
64
+ 使用 API 日志装饰器
65
+ 自动记录 API 调用和响应时间
66
+ """
67
+ # 模拟处理
68
+ time.sleep(0.1)
69
+ return {"answer": "这是回复内容"}
70
+
71
+
72
+ @log_model_performance("sbert-hr-v2", "sbert")
73
+ def encode_with_sbert(text: str):
74
+ """
75
+ 使用模型性能装饰器
76
+ 自动记录模型推理性能
77
+ """
78
+ # 模拟编码
79
+ time.sleep(0.05)
80
+ return [0.1, 0.2, 0.3]
81
+
82
+
83
+ # ============================================
84
+ # 示例 3: 使用上下文管理器
85
+ # ============================================
86
+
87
+ def example_context_manager():
88
+ """使用上下文管理器追踪性能"""
89
+ with LogPerformance("chat_processing", "deepseek-chat", "llm"):
90
+ # 执行耗时操作
91
+ result = call_llm_api("年假有多少天?")
92
+ return result
93
+
94
+
95
+ def call_llm_api(prompt: str):
96
+ """模拟 LLM API 调用"""
97
+ time.sleep(0.2)
98
+ return "根据规定,年假天数与工龄挂钩..."
99
+
100
+
101
+ # ============================================
102
+ # 示例 4: 在 intelligence_analyzer 中集成
103
+ # ============================================
104
+
105
+ def analyze_with_logging(employee_input: str, session_id: str):
106
+ """
107
+ 在分析器中集成日志记录
108
+ """
109
+ logger = get_supabase_logger()
110
+
111
+ # 记录输入
112
+ logger.log_debug(
113
+ level="INFO",
114
+ logger="intelligence_analyzer",
115
+ message=f"Processing input: {employee_input[:50]}...",
116
+ session_id=session_id
117
+ )
118
+
119
+ try:
120
+ # 场景识别
121
+ start_time = time.time()
122
+ scenario = detect_scenario(employee_input)
123
+
124
+ logger.log_debug(
125
+ level="INFO",
126
+ logger="intelligence_analyzer",
127
+ message="Scenario detected",
128
+ session_id=session_id,
129
+ data={"scenario": scenario, "confidence": 0.9}
130
+ )
131
+
132
+ # 记录用户行为
133
+ logger.log_user_behavior(
134
+ session_id=session_id,
135
+ action_type="chat",
136
+ action_detail="scenario_detection",
137
+ input_text=employee_input,
138
+ scenario_detected=scenario,
139
+ success=True
140
+ )
141
+
142
+ return scenario
143
+
144
+ except Exception as e:
145
+ # 记录错误
146
+ logger.log_error(
147
+ error_type="analysis_error",
148
+ error_message=str(e),
149
+ session_id=session_id,
150
+ user_input=employee_input
151
+ )
152
+ raise
153
+
154
+
155
+ def detect_scenario(text: str) -> str:
156
+ """模拟场景检测"""
157
+ time.sleep(0.02)
158
+ return "policy_inquiry"
159
+
160
+
161
+ # ============================================
162
+ # 示例 5: 在 FastAPI 路由中集成
163
+ # ============================================
164
+
165
+ from fastapi import Request
166
+
167
+
168
+ async def log_request_middleware(request: Request, call_next):
169
+ """
170
+ FastAPI 中间件 - 记录所有请求
171
+ """
172
+ logger = get_supabase_logger()
173
+ start_time = time.time()
174
+
175
+ # 处理请求
176
+ response = await call_next(request)
177
+
178
+ # 计算响应时间
179
+ process_time = int((time.time() - start_time) * 1000)
180
+
181
+ # 记录日志
182
+ logger.log_api_access(
183
+ endpoint=request.url.path,
184
+ method=request.method,
185
+ client_ip=request.client.host if request.client else None,
186
+ user_agent=request.headers.get("user-agent"),
187
+ session_id=request.headers.get("x-session-id"),
188
+ response_time_ms=process_time,
189
+ status_code=response.status_code
190
+ )
191
+
192
+ # 添加响应头
193
+ response.headers["X-Process-Time"] = str(process_time)
194
+ return response
195
+
196
+
197
+ # ============================================
198
+ # 示例 6: 批量日志记录
199
+ # ============================================
200
+
201
+ def batch_log_examples():
202
+ """批量日志记录示例"""
203
+ logger = get_supabase_logger()
204
+
205
+ # 批量记录用户行为
206
+ behaviors = [
207
+ {
208
+ "session_id": "user_1",
209
+ "action_type": "chat",
210
+ "scenario_detected": "policy_inquiry",
211
+ "success": True
212
+ },
213
+ {
214
+ "session_id": "user_1",
215
+ "action_type": "chat",
216
+ "scenario_detected": "leave_application",
217
+ "success": True
218
+ },
219
+ {
220
+ "session_id": "user_2",
221
+ "action_type": "evaluate",
222
+ "success": False,
223
+ "error_message": "Invalid dialogue format"
224
+ }
225
+ ]
226
+
227
+ for behavior in behaviors:
228
+ logger.log_user_behavior(**behavior)
229
+
230
+
231
+ # ============================================
232
+ # 示例 7: 错误追踪
233
+ # ============================================
234
+
235
+ def example_with_error_tracking():
236
+ """带错误追踪的示例"""
237
+ logger = get_supabase_logger()
238
+
239
+ try:
240
+ # 模拟错误
241
+ result = 1 / 0
242
+ except Exception as e:
243
+ import traceback
244
+
245
+ # 记录详细错误信息
246
+ logger.log_error(
247
+ error_type="calculation_error",
248
+ error_message=str(e),
249
+ error_stack=traceback.format_exc(),
250
+ session_id="debug_session",
251
+ request_data={"operation": "division", "operands": [1, 0]}
252
+ )
253
+
254
+
255
+ # ============================================
256
+ # 示例 8: 模型性能追踪
257
+ # ============================================
258
+
259
+ def track_all_models():
260
+ """追踪所有模型的性能"""
261
+ logger = get_supabase_logger()
262
+
263
+ models = {
264
+ "sbert": "KarenYYH/sbert-hr-v2",
265
+ "sentiment": "KarenYYH/sentiment-hr",
266
+ "llm": "deepseek-chat"
267
+ }
268
+
269
+ for model_type, model_name in models.items():
270
+ with LogPerformance(f"load_{model_type}", model_name, model_type):
271
+ # 模拟模型加载
272
+ time.sleep(0.1)
273
+
274
+
275
+ # ============================================
276
+ # 简化的日志记录
277
+ # ============================================
278
+
279
+ def example_simplified_logging():
280
+ """使用简化函数记录日志"""
281
+ # 调试日志
282
+ log_debug("intelligence_analyzer", "处理用户输入", level="INFO",
283
+ session_id="user_123", data={"input_length": 20})
284
+
285
+ # 错误日志
286
+ log_error("api_error", "请求超时", endpoint="/api/v1/chat",
287
+ session_id="user_123", response_time_ms=5000)
288
+
289
+
290
+ # ============================================
291
+ # 运行所有示例
292
+ # ============================================
293
+
294
+ if __name__ == "__main__":
295
+ print("运行 Supabase 日志示例...")
296
+
297
+ example_basic_logging()
298
+ print("✓ 基本日志记录")
299
+
300
+ process_chat_request("年假有多少天?", "user_123")
301
+ print("✓ API 日志装饰器")
302
+
303
+ encode_with_sbert("测试文本")
304
+ print("✓ 模型性能装饰器")
305
+
306
+ example_context_manager()
307
+ print("✓ 上下文管理器")
308
+
309
+ analyze_with_logging("年假有多少天?", "user_456")
310
+ print("✓ 分析器集成")
311
+
312
+ batch_log_examples()
313
+ print("✓ 批量日志")
314
+
315
+ example_with_error_tracking()
316
+ print("✓ 错误追踪")
317
+
318
+ track_all_models()
319
+ print("✓ 模型性能追踪")
320
+
321
+ example_simplified_logging()
322
+ print("✓ 简化日志")
323
+
324
+ print("\n所有示例运行完成!")
supabase_schema_with_logs.sql ADDED
@@ -0,0 +1,431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- HR 对话质量评估系统 - Supabase 数据库表结构(含日志)
2
+ -- 在 Supabase SQL Editor 中执行此脚本
3
+
4
+ -- ============================================
5
+ -- 1. 对话历史表(已有)
6
+ -- ============================================
7
+ CREATE TABLE IF NOT EXISTS conversation_history (
8
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
9
+ session_id TEXT NOT NULL,
10
+ user_message TEXT NOT NULL,
11
+ assistant_message TEXT NOT NULL,
12
+ analysis_report JSONB,
13
+ context_summary JSONB,
14
+ metadata JSONB,
15
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
16
+ );
17
+
18
+ CREATE INDEX IF NOT EXISTS idx_conversation_history_session_id ON conversation_history(session_id);
19
+ CREATE INDEX IF NOT EXISTS idx_conversation_history_created_at ON conversation_history(created_at DESC);
20
+
21
+ -- ============================================
22
+ -- 2. API 访问日志表(新增)
23
+ -- ============================================
24
+ CREATE TABLE IF NOT EXISTS api_access_logs (
25
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
26
+ timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
27
+
28
+ -- 请求信息
29
+ endpoint TEXT NOT NULL,
30
+ method TEXT NOT NULL,
31
+ path TEXT,
32
+ query_params JSONB,
33
+
34
+ -- 客户端信息
35
+ client_ip TEXT,
36
+ user_agent TEXT,
37
+ session_id TEXT,
38
+
39
+ -- 性能指标
40
+ response_time_ms INTEGER,
41
+ status_code INTEGER,
42
+
43
+ -- 附加信息
44
+ metadata JSONB
45
+ );
46
+
47
+ -- 索引优化
48
+ CREATE INDEX IF NOT EXISTS idx_api_logs_timestamp ON api_access_logs(timestamp DESC);
49
+ CREATE INDEX IF NOT EXISTS idx_api_logs_endpoint ON api_access_logs(endpoint);
50
+ CREATE INDEX IF NOT EXISTS idx_api_logs_session_id ON api_access_logs(session_id);
51
+ CREATE INDEX IF NOT EXISTS idx_api_logs_status_code ON api_access_logs(status_code);
52
+
53
+ -- 分区(按月)- 可选,大数据量时使用
54
+ -- CREATE TABLE api_access_logs_y2025m01 PARTITION OF api_access_logs
55
+ -- FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
56
+
57
+ -- ============================================
58
+ -- 3. 错误日志表(新增)
59
+ -- ============================================
60
+ CREATE TABLE IF NOT EXISTS error_logs (
61
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
62
+ timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
63
+
64
+ -- 错误信息
65
+ error_type TEXT NOT NULL, -- 'validation', 'model_load', 'api_error', etc.
66
+ error_message TEXT NOT NULL,
67
+ error_stack TEXT,
68
+
69
+ -- 上下文
70
+ endpoint TEXT,
71
+ session_id TEXT,
72
+ user_input TEXT,
73
+
74
+ -- 请求详情
75
+ request_data JSONB,
76
+
77
+ -- 元数据
78
+ metadata JSONB
79
+ );
80
+
81
+ CREATE INDEX IF NOT EXISTS idx_error_logs_timestamp ON error_logs(timestamp DESC);
82
+ CREATE INDEX IF NOT EXISTS idx_error_logs_type ON error_logs(error_type);
83
+ CREATE INDEX IF NOT EXISTS idx_error_logs_session_id ON error_logs(session_id);
84
+
85
+ -- ============================================
86
+ -- 4. 模型性能日志表(新增)
87
+ -- ============================================
88
+ CREATE TABLE IF NOT EXISTS model_performance_logs (
89
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
90
+ timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
91
+
92
+ -- 模型信息
93
+ model_name TEXT NOT NULL,
94
+ model_type TEXT NOT NULL, -- 'sbert', 'sentiment', 'compliance', 'llm'
95
+
96
+ -- 性能指标
97
+ load_time_ms INTEGER,
98
+ inference_time_ms INTEGER,
99
+ total_time_ms INTEGER,
100
+
101
+ -- 输入输出
102
+ input_text TEXT,
103
+ output_summary TEXT,
104
+
105
+ -- 附加信息
106
+ metadata JSONB
107
+ );
108
+
109
+ CREATE INDEX IF NOT EXISTS idx_model_perf_timestamp ON model_performance_logs(timestamp DESC);
110
+ CREATE INDEX IF NOT EXISTS idx_model_perf_model_name ON model_performance_logs(model_name);
111
+ CREATE INDEX IF NOT EXISTS idx_model_perf_type ON model_performance_logs(model_type);
112
+
113
+ -- ============================================
114
+ -- 5. 调试日志表(新增)- 用于开发调试
115
+ -- ============================================
116
+ CREATE TABLE IF NOT EXISTS debug_logs (
117
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
118
+ timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
119
+
120
+ -- 日志信息
121
+ level TEXT NOT NULL, -- 'DEBUG', 'INFO', 'WARNING', 'ERROR'
122
+ logger TEXT NOT NULL, -- 模块名称,如 'intelligence_analyzer'
123
+ message TEXT NOT NULL,
124
+
125
+ -- 上下文
126
+ session_id TEXT,
127
+ function_name TEXT,
128
+ line_number INTEGER,
129
+
130
+ -- 结构化数据
131
+ data JSONB,
132
+
133
+ -- 附加信息
134
+ metadata JSONB
135
+ );
136
+
137
+ CREATE INDEX IF NOT EXISTS idx_debug_logs_timestamp ON debug_logs(timestamp DESC);
138
+ CREATE INDEX IF NOT EXISTS idx_debug_logs_level ON debug_logs(level);
139
+ CREATE INDEX IF NOT EXISTS idx_debug_logs_logger ON debug_logs(logger);
140
+ CREATE INDEX IF NOT EXISTS idx_debug_logs_session_id ON debug_logs(session_id);
141
+
142
+ -- ============================================
143
+ -- 6. 用户行为分析表(新增)
144
+ -- ============================================
145
+ CREATE TABLE IF NOT EXISTS user_behavior_logs (
146
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
147
+ timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
148
+
149
+ -- 用户标识
150
+ session_id TEXT NOT NULL,
151
+ user_id TEXT, -- 可选,如果有多用户系统
152
+
153
+ -- 行为类型
154
+ action_type TEXT NOT NULL, -- 'chat', 'evaluate', 'config_check', etc.
155
+ action_detail TEXT,
156
+
157
+ -- 内容
158
+ input_text TEXT,
159
+ intent_detected TEXT,
160
+ scenario_detected TEXT,
161
+
162
+ -- 结果
163
+ success BOOLEAN,
164
+ error_message TEXT,
165
+
166
+ -- 元数据
167
+ metadata JSONB
168
+ );
169
+
170
+ CREATE INDEX IF NOT EXISTS idx_user_behavior_timestamp ON user_behavior_logs(timestamp DESC);
171
+ CREATE INDEX IF NOT EXISTS idx_user_behavior_session_id ON user_behavior_logs(session_id);
172
+ CREATE INDEX IF NOT EXISTS idx_user_behavior_action_type ON user_behavior_logs(action_type);
173
+
174
+ -- ============================================
175
+ -- 启用行级安全性 (RLS)
176
+ -- ============================================
177
+ ALTER TABLE api_access_logs ENABLE ROW LEVEL SECURITY;
178
+ ALTER TABLE error_logs ENABLE ROW LEVEL SECURITY;
179
+ ALTER TABLE model_performance_logs ENABLE ROW LEVEL SECURITY;
180
+ ALTER TABLE debug_logs ENABLE ROW LEVEL SECURITY;
181
+ ALTER TABLE user_behavior_logs ENABLE ROW LEVEL SECURITY;
182
+
183
+ -- 允许所有操作(演示环境)
184
+ CREATE POLICY "Enable all access for api_access_logs" ON api_access_logs FOR ALL USING (true);
185
+ CREATE POLICY "Enable all access for error_logs" ON error_logs FOR ALL USING (true);
186
+ CREATE POLICY "Enable all access for model_performance_logs" ON model_performance_logs FOR ALL USING (true);
187
+ CREATE POLICY "Enable all access for debug_logs" ON debug_logs FOR ALL USING (true);
188
+ CREATE POLICY "Enable all access for user_behavior_logs" ON user_behavior_logs FOR ALL USING (true);
189
+
190
+ -- ============================================
191
+ -- 实用函数
192
+ -- ============================================
193
+
194
+ -- 清理旧日志(保留最近 N 天)
195
+ CREATE OR REPLACE FUNCTION cleanup_old_logs(days_to_keep INTEGER DEFAULT 30)
196
+ RETURNS TABLE (
197
+ api_logs_deleted BIGINT,
198
+ error_logs_deleted BIGINT,
199
+ debug_logs_deleted BIGINT,
200
+ model_perf_logs_deleted BIGINT
201
+ )
202
+ LANGUAGE plpgsql
203
+ AS $$
204
+ DECLARE
205
+ api_count BIGINT;
206
+ error_count BIGINT;
207
+ debug_count BIGINT;
208
+ model_count BIGINT;
209
+ BEGIN
210
+ -- 清理 API 日志
211
+ DELETE FROM api_access_logs
212
+ WHERE timestamp < NOW() - (days_to_keep || ' days')::INTERVAL;
213
+ GET DIAGNOSTICS api_count = ROW_COUNT;
214
+
215
+ -- 清理错误日志(保留更久)
216
+ DELETE FROM error_logs
217
+ WHERE timestamp < NOW() - ((days_to_keep * 2) || ' days')::INTERVAL;
218
+ GET DIAGNOSTICS error_count = ROW_COUNT;
219
+
220
+ -- 清理调试日志(保留更短时间)
221
+ DELETE FROM debug_logs
222
+ WHERE timestamp < NOW() - ((days_to_keep / 2) || ' days')::INTERVAL;
223
+ GET DIAGNOSTICS debug_count = ROW_COUNT;
224
+
225
+ -- 清理模型性能日志
226
+ DELETE FROM model_performance_logs
227
+ WHERE timestamp < NOW() - (days_to_keep || ' days')::INTERVAL;
228
+ GET DIAGNOSTICS model_count = ROW_COUNT;
229
+
230
+ RETURN QUERY SELECT api_count, error_count, debug_count, model_count;
231
+ END;
232
+ $$;
233
+
234
+ -- 获取 API 统计
235
+ CREATE OR REPLACE FUNCTION get_api_statistics(hours_ago INTEGER DEFAULT 24)
236
+ RETURNS TABLE (
237
+ endpoint TEXT,
238
+ request_count BIGINT,
239
+ avg_response_time NUMERIC,
240
+ error_rate NUMERIC,
241
+ p95_response_time NUMERIC
242
+ )
243
+ LANGUAGE plpgsql
244
+ AS $$
245
+ BEGIN
246
+ RETURN QUERY
247
+ SELECT
248
+ endpoint,
249
+ COUNT(*) as request_count,
250
+ AVG(response_time_ms) as avg_response_time,
251
+ COUNT(*) FILTER (WHERE status_code >= 400) * 100.0 / COUNT(*) as error_rate,
252
+ PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY response_time_ms) as p95_response_time
253
+ FROM api_access_logs
254
+ WHERE timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
255
+ GROUP BY endpoint
256
+ ORDER BY request_count DESC;
257
+ END;
258
+ $$;
259
+
260
+ -- 获取错误统计
261
+ CREATE OR REPLACE FUNCTION get_error_summary(hours_ago INTEGER DEFAULT 24)
262
+ RETURNS TABLE (
263
+ error_type TEXT,
264
+ error_count BIGINT,
265
+ most_common_message TEXT
266
+ )
267
+ LANGUAGE plpgsql
268
+ AS $$
269
+ BEGIN
270
+ RETURN QUERY
271
+ SELECT
272
+ error_type,
273
+ COUNT(*) as error_count,
274
+ mode() WITHIN GROUP (ORDER BY error_message) as most_common_message
275
+ FROM error_logs
276
+ WHERE timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
277
+ GROUP BY error_type
278
+ ORDER BY error_count DESC;
279
+ END;
280
+ $$;
281
+
282
+ -- 搜索日志
283
+ CREATE OR REPLACE FUNCTION search_logs(
284
+ search_term TEXT,
285
+ log_type TEXT DEFAULT 'all', -- 'api', 'error', 'debug', 'all'
286
+ hours_ago INTEGER DEFAULT 24,
287
+ limit_count INTEGER DEFAULT 100
288
+ )
289
+ RETURNS TABLE (
290
+ log_type TEXT,
291
+ log_id UUID,
292
+ timestamp TIMESTAMP WITH TIME ZONE,
293
+ summary TEXT
294
+ )
295
+ LANGUAGE plpgsql
296
+ AS $$
297
+ BEGIN
298
+ -- API 日志
299
+ IF log_type = 'api' OR log_type = 'all' THEN
300
+ RETURN QUERY
301
+ SELECT
302
+ 'api'::TEXT as log_type,
303
+ id as log_id,
304
+ timestamp,
305
+ endpoint || ' - ' || status_code::TEXT as summary
306
+ FROM api_access_logs
307
+ WHERE (
308
+ search_term ILIKE '%' || search_term || '%'
309
+ OR endpoint ILIKE '%' || search_term || '%'
310
+ OR path ILIKE '%' || search_term || '%'
311
+ )
312
+ AND timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
313
+ LIMIT limit_count;
314
+ END IF;
315
+
316
+ -- 错误日志
317
+ IF log_type = 'error' OR log_type = 'all' THEN
318
+ RETURN QUERY
319
+ SELECT
320
+ 'error'::TEXT as log_type,
321
+ id as log_id,
322
+ timestamp,
323
+ error_type || ' - ' || LEFT(error_message, 50) as summary
324
+ FROM error_logs
325
+ WHERE error_message ILIKE '%' || search_term || '%'
326
+ AND timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
327
+ LIMIT limit_count;
328
+ END IF;
329
+
330
+ -- 调试日志
331
+ IF log_type = 'debug' OR log_type = 'all' THEN
332
+ RETURN QUERY
333
+ SELECT
334
+ 'debug'::TEXT as log_type,
335
+ id as log_id,
336
+ timestamp,
337
+ level || ' - ' || logger || ' - ' || LEFT(message, 50) as summary
338
+ FROM debug_logs
339
+ WHERE message ILIKE '%' || search_term || '%'
340
+ AND timestamp >= NOW() - (hours_ago || ' hours')::INTERVAL
341
+ LIMIT limit_count;
342
+ END IF;
343
+ END;
344
+ $$;
345
+
346
+ -- ============================================
347
+ -- 定时清理任务(可选)
348
+ -- ============================================
349
+ -- 创建一个函数来定期清理日志
350
+ CREATE OR REPLACE FUNCTION auto_cleanup_logs()
351
+ RETURNS VOID
352
+ LANGUAGE plpgsql
353
+ AS $$
354
+ BEGIN
355
+ -- 清理 7 天前的调试日志
356
+ DELETE FROM debug_logs
357
+ WHERE timestamp < NOW() - INTERVAL '7 days';
358
+
359
+ -- 清理 30 天前的 API 日志
360
+ DELETE FROM api_access_logs
361
+ WHERE timestamp < NOW() - INTERVAL '30 days';
362
+
363
+ -- 清理 90 天前的模型性能日志
364
+ DELETE FROM model_performance_logs
365
+ WHERE timestamp < NOW() - INTERVAL '90 days';
366
+
367
+ -- 错误日志保留 180 天
368
+ DELETE FROM error_logs
369
+ WHERE timestamp < NOW() - INTERVAL '180 days';
370
+ END;
371
+ $$;
372
+
373
+ -- 在 Supabase Dashboard 中设置 cron job 来调用这个函数
374
+ -- 或者使用 pg_cron 扩展(如果可用)
375
+ -- SELECT cron.schedule('cleanup-logs', '0 2 * * *', 'SELECT auto_cleanup_logs()');
376
+
377
+ -- ============================================
378
+ -- 视图 - 方便查询
379
+ -- ============================================
380
+
381
+ -- 综合日志视图
382
+ CREATE OR REPLACE VIEW v_recent_logs AS
383
+ SELECT
384
+ 'api' as log_type,
385
+ timestamp,
386
+ endpoint as title,
387
+ status_code::TEXT as detail,
388
+ session_id
389
+ FROM api_access_logs
390
+ WHERE timestamp >= NOW() - INTERVAL '24 hours'
391
+
392
+ UNION ALL
393
+
394
+ SELECT
395
+ 'error' as log_type,
396
+ timestamp,
397
+ error_type as title,
398
+ LEFT(error_message, 50) as detail,
399
+ session_id
400
+ FROM error_logs
401
+ WHERE timestamp >= NOW() - INTERVAL '24 hours'
402
+
403
+ UNION ALL
404
+
405
+ SELECT
406
+ 'debug' as log_type,
407
+ timestamp,
408
+ logger as title,
409
+ level || ' - ' || LEFT(message, 30) as detail,
410
+ session_id
411
+ FROM debug_logs
412
+ WHERE timestamp >= NOW() - INTERVAL '24 hours'
413
+
414
+ ORDER BY timestamp DESC
415
+ LIMIT 100;
416
+
417
+ -- ============================================
418
+ -- 注释
419
+ -- ============================================
420
+ COMMENT ON TABLE api_access_logs IS 'API 访问日志';
421
+ COMMENT ON TABLE error_logs IS '错误日志';
422
+ COMMENT ON TABLE model_performance_logs IS '模型性能日志';
423
+ COMMENT ON TABLE debug_logs IS '调试日志';
424
+ COMMENT ON TABLE user_behavior_logs IS '用户行为日志';
425
+
426
+ COMMENT ON FUNCTION cleanup_old_logs IS '清理旧日志,默认保留 30 天';
427
+ COMMENT ON FUNCTION get_api_statistics IS '获取 API 统计信息';
428
+ COMMENT ON FUNCTION get_error_summary IS '获取错误摘要';
429
+ COMMENT ON FUNCTION search_logs IS '搜索日志内容';
430
+ COMMENT ON FUNCTION auto_cleanup_logs IS '自动清理日志';
431
+ COMMENT ON VIEW v_recent_logs IS '最近 24 小时的综合日志视图';