mm-qa-mcp 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,608 @@
1
+ from minimax_qa_mcp.utils.logger import logger
2
+ import os
3
+ import re
4
+ import pandas as pd
5
+ import json
6
+ from datetime import datetime
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from minimax_qa_mcp.src.grafana.service import GetFromGrafana
9
+ from concurrent.futures import as_completed
10
+ import requests
11
+
12
+ class CaseGrafanaService(GetFromGrafana):
13
+ """
14
+ 继承自GetFromGrafana类,用于获取案例相关的grafana日志
15
+ """
16
+ def __init__(self, scene, psm="", from_time=None, to_time=None):
17
+ """
18
+ 初始化CaseGrafanaService
19
+
20
+ Args:
21
+ scene: 枚举值,可选为[ ]
22
+ psm: 服务名,可选
23
+ from_time: 日志筛选时间区间-开始时间
24
+ to_time: 日志筛选时间区间-结束时间
25
+ """
26
+
27
+ # 调用父类初始化方法
28
+ self.scene = scene
29
+ if scene in ['xingye_http_prod','talkie_http_prod']:
30
+ self.psm = 'ingress'
31
+ else:
32
+ self.psm = psm
33
+ self.from_time = from_time
34
+ self.to_time = to_time
35
+ super().__init__(scene=self.scene, psm=psm, from_time=from_time, to_time=to_time)
36
+
37
+ def get_case_logs(self,msgs):
38
+ """
39
+ 获取案例相关的日志
40
+
41
+ Returns:
42
+ 处理后的案例日志信息
43
+ """
44
+ try:
45
+ # 调用父类方法获取原始日志
46
+ raw_logs = self.post_grafana(msgs)
47
+
48
+ # 处理日志数据
49
+ processed_logs = self.process_logs(raw_logs)
50
+
51
+ return processed_logs
52
+ except Exception as e:
53
+ logger.error(f"获取日志时发生错误: {e},业务:{self.scene},服务:{self.psm}")
54
+ return []
55
+
56
+ def process_logs(self, logs):
57
+ """
58
+ 处理原始日志数据,提取消息内容
59
+
60
+ Args:
61
+ logs: 原始日志数据列表
62
+
63
+ Returns:
64
+ 处理后的日志消息列表或结构化日志数据
65
+ """
66
+ # 处理logs为空的情况
67
+ if logs is None:
68
+ logger.warning(f"接收到的日志为None,业务:{self.scene},服务:{self.psm}")
69
+ return []
70
+ try:
71
+ # 提取消息内容
72
+ messages = [item.get('msg', '') for item in logs if isinstance(item, dict) and item.get('msg')]
73
+
74
+ # 如果没有提取到任何消息
75
+ if not messages:
76
+ logger.warning(f"接收到的日志为None,业务:{self.scene},服务:{self.psm}")
77
+ return []
78
+
79
+ return messages
80
+ except Exception as e:
81
+ logger.error(f"处理日志时发生错误: {e},业务:{self.scene},服务:{self.psm}")
82
+ # 返回空列表作为兜底
83
+ return []
84
+ def extract_req_details(self, response, match_field):
85
+ """
86
+ 从日志内容中提取请求的详细信息
87
+
88
+ :param response: 日志内容字符串
89
+ :param match_field: 匹配的字段,例如 "req: http://hailuoai.video" 或 "req: http://hailuoai.video/v3/api/multimodal/video/like"
90
+ :return: 请求体字典
91
+ """
92
+ try:
93
+ # 使用正则表达式匹配请求URL和请求体
94
+ domain_path_match = re.search(r'(req: http?://[^/]+)(/.*)?', match_field)
95
+ if not domain_path_match:
96
+ logger.error(f"无法解析match_field: {match_field}")
97
+ return {}
98
+
99
+ domain_part = domain_path_match.group(1)
100
+ path_part = domain_path_match.group(2) or ""
101
+
102
+ # 构建用于匹配的模式,直接匹配到响应体部分
103
+ pattern = rf'{re.escape(domain_part + path_part)}.*?:(.*?)$'
104
+
105
+ match = re.search(pattern, response)
106
+
107
+ if match:
108
+ # 提取请求体部分
109
+ body_str = match.group(1).strip()
110
+
111
+ # 解析请求体
112
+ try:
113
+ body_params = json.loads(body_str) if body_str else {}
114
+ return body_params
115
+ except json.JSONDecodeError:
116
+ logger.error(f"无法解析请求体JSON字符串,请求信息: {match_field},对应的服务:{self.psm}")
117
+ return {}
118
+ else:
119
+ logger.error(f"未找到匹配的请求信息: {match_field},对应的服务:{self.psm},完整日志: {response[:100]}...")
120
+ return {}
121
+
122
+ except Exception as e:
123
+ logger.error(f'提取请求详细信息失败,错误:{e},match_field: {match_field}')
124
+ return {}
125
+
126
+ def extract_resp_details(self, response, match_field):
127
+ """
128
+ 从日志内容中提取响应的详细信息
129
+
130
+ :param response: 日志内容字符串
131
+ :param match_field: 匹配的字段,例如 "resp: http://hailuoai.video" 或 "resp: http://hailuoai.video/v3/api/multimodal/video/like"
132
+ :return: 元组 (trace_id, path, query_params, resp_body),分别是跟踪ID、路径、查询参数和响应体
133
+ """
134
+ try:
135
+ # 提取trace_id
136
+ trace_id_match = re.search(r'INFO 1 1 (\w+)', response)
137
+ trace_id = trace_id_match.group(1) if trace_id_match else ""
138
+
139
+ # 使用正则表达式匹配响应URL和响应体
140
+ # 提取域名部分和路径部分
141
+ domain_path_match = re.search(r'(resp: http?://[^/]+)(/.*)?', response.split('?')[0])
142
+ if not domain_path_match:
143
+ logger.error(f"无法解析response: {response}")
144
+ return trace_id, "", "", {}
145
+
146
+ domain_part = domain_path_match.group(1)
147
+ path_part = domain_path_match.group(2) or ""
148
+
149
+ # 提取路径
150
+ path = path_part.split('?')[0] if '?' in path_part else path_part
151
+ if path != match_field[1] and path != '/' + match_field[1]: raise Exception(f"路径不匹配,期望:{match_field[1]},实际:{path}")
152
+
153
+ # 构建用于匹配的模式
154
+ if path_part:
155
+ # 如果response包含路径部分,精确匹配这个路径后面的查询参数和响应体
156
+ pattern = rf'{re.escape(domain_part + path_part)}\?([^:]*):(.*)$'
157
+ else:
158
+ # 如果response只包含域名,匹配任何路径及其后面的查询参数和响应体
159
+ pattern = rf'{re.escape(domain_part)}([^?]*\?[^:]*):(.*)$'
160
+
161
+ match = re.search(pattern, response)
162
+
163
+ if match:
164
+ if path_part:
165
+ # 如果match_field包含路径,匹配的是查询参数和响应体
166
+ query_params = match.group(1).strip()
167
+ resp_body_str = match.group(2).strip()
168
+ else:
169
+ # 如果match_field只有域名,匹配的是路径+查询参数和响应体
170
+ path_query = match.group(1).strip()
171
+ resp_body_str = match.group(2).strip()
172
+
173
+ # 分离路径和查询参数
174
+ if '?' in path_query:
175
+ path, query_params = path_query.split('?', 1)
176
+ else:
177
+ path = path_query
178
+ query_params = ""
179
+
180
+ # 过滤掉指定的字段
181
+ filtered_params = []
182
+ unwanted_fields = [
183
+ 'yy_platform', 'device_type', 'brand', 'biz_id',
184
+ 'device_brand', 'os_version', 'channel',
185
+ 'version_name', 'device_id', 'sys_region',
186
+ 'lang', 'unix', 'server_version',"uuid","os_name","browser_name","browser_platform","screen_width","screen_height","device_memory","cpu_core_num","browser_language"
187
+ ]
188
+
189
+ if query_params:
190
+ param_pairs = query_params.split('&')
191
+ for pair in param_pairs:
192
+ key_value = pair.split('=', 1)
193
+ if len(key_value) > 0 and key_value[0] not in unwanted_fields:
194
+ filtered_params.append(pair)
195
+
196
+ # 重新构建查询参数字符串
197
+ filtered_query_params = '&'.join(filtered_params)
198
+
199
+ # 解析响应体
200
+ try:
201
+ resp_body = json.loads(resp_body_str) if resp_body_str else {}
202
+ except json.JSONDecodeError:
203
+ logger.error(f"无法解析响应体JSON字符串,响应信息: {match_field},对应的服务:{self.psm}")
204
+ resp_body = {}
205
+
206
+ return trace_id, path, filtered_query_params, resp_body
207
+ else:
208
+ logger.error(f"未找到匹配的响应信息: {match_field},对应的服务:{self.psm},完整日志: {response[:100]}...")
209
+ return trace_id, "", "", {}
210
+
211
+ except Exception as e:
212
+ logger.error(f'提取响应详细信息失败,错误:{e},match_field: {match_field}')
213
+ return "", "", "", {}
214
+
215
+ def process_logs_extract_resp_details(self, match_field=None):
216
+ """
217
+ 获取日志并遍历调用extract_resp_details函数提取响应详情
218
+
219
+ Args:
220
+ match_field: 匹配的字段,默认为None时会使用self.msg的第一个元素
221
+
222
+ Returns:
223
+ 处理后的响应详情列表,每个元素为(trace_id, path, query_params, resp_body)元组
224
+ """
225
+ try:
226
+ if not match_field:
227
+ logger.error("未提供匹配字段,无法提取响应详情")
228
+ return []
229
+
230
+ # 确保match_field以"resp: http"开头
231
+ if not match_field[0].startswith("resp"):
232
+ logger.error(f"匹配字段不包含'resp',无法提取响应详情: {match_field}")
233
+ return []
234
+
235
+ # 获取日志
236
+ logs = self.get_case_logs(match_field)
237
+
238
+ if not logs:
239
+ logger.warning(f"未获取到日志,无法提取响应详情,业务:{self.scene},服务:{self.psm}")
240
+ return []
241
+
242
+ # 存储提取的响应详情
243
+ resp_details = []
244
+
245
+ # 遍历日志,提取响应详情
246
+ for log in logs:
247
+ try:
248
+ # 调用extract_resp_details函数提取响应详情
249
+ trace_id, path, query_params, resp_body = self.extract_resp_details(log, match_field)
250
+
251
+ # 如果成功提取到查询参数或响应体,则添加到结果列表
252
+ if query_params or resp_body:
253
+ resp_details.append((trace_id, path, query_params, resp_body))
254
+ except Exception as inner_e:
255
+ logger.error(f"处理单条日志时出错: {inner_e}")
256
+ continue
257
+
258
+ # 去重(使用查询参数作为唯一键)
259
+ unique_details = []
260
+ seen = set()
261
+
262
+ for detail in resp_details:
263
+ key = detail[2] # query_params
264
+ if key not in seen:
265
+ seen.add(key)
266
+ unique_details.append(detail)
267
+
268
+ logger.info(f"从 {len(logs)} 条日志中提取了 {len(resp_details)} 条响应详情,去重后剩余 {len(unique_details)} 条")
269
+ return unique_details
270
+
271
+ except Exception as e:
272
+ logger.error(f"遍历日志提取响应详情时出错: {e}")
273
+ return []
274
+
275
+ def get_req_by_traceid(self, trace_id, api_path):
276
+ """
277
+ 根据trace_id和API路径获取请求详情
278
+
279
+ Args:
280
+ trace_id: 跟踪ID
281
+ api_path: API路径
282
+
283
+ Returns:
284
+ 请求体字典
285
+ """
286
+ try:
287
+ # 构建匹配字段
288
+ if self.scene == "hailuo_video_cn_pre":
289
+ match_field = f"req: http://hailuo-pre.xaminim.com{api_path}"
290
+ elif self.scene == "hailuo_video_cn_prod":
291
+ match_field = f"req: http://hailuoai.com{api_path}"
292
+ elif self.scene == "hailuo_video_us_test":
293
+ match_field = f"req: http://hailuoai-video-test.xaminim.com{api_path}"
294
+ elif self.scene == "hailuo_video_us_prod":
295
+ match_field = f"req: http://hailuoai.video{api_path}"
296
+ else:
297
+ # 保留原来的默认行为
298
+ match_field = f"req: http://hailuoai.video{api_path}"
299
+
300
+
301
+ # 获取日志
302
+ logs = self.get_case_logs([match_field, trace_id])
303
+
304
+ if not logs:
305
+ logger.warning(f"未获取到日志,无法根据trace_id获取请求详情,业务:{self.scene},服务:{self.psm},trace_id:{trace_id}")
306
+ return {}
307
+
308
+ # 遍历日志,直接提取请求体
309
+ for log in logs:
310
+ try:
311
+ # 确认日志包含trace_id
312
+ if trace_id in log:
313
+ # 提取请求体部分
314
+ req_body_match = re.search(rf'{re.escape(match_field)}.*?:(.*?)$', log)
315
+ if req_body_match:
316
+ req_body_str = req_body_match.group(1).strip()
317
+ try:
318
+ # 直接解析请求体JSON
319
+ return json.loads(req_body_str) if req_body_str else {}
320
+ except json.JSONDecodeError:
321
+ logger.error(f"无法解析请求体JSON字符串,trace_id:{trace_id}")
322
+ return {}
323
+ except Exception as inner_e:
324
+ logger.error(f"处理单条日志时出错: {inner_e}")
325
+ continue
326
+
327
+ logger.warning(f"未找到包含trace_id为{trace_id}的请求详情,业务:{self.scene},服务:{self.psm}")
328
+ return {}
329
+
330
+ except Exception as e:
331
+ logger.error(f"根据trace_id获取请求详情时出错: {e}")
332
+ return {}
333
+
334
+ def process_logs_extract_req_details(self, match_field=None):
335
+ """
336
+ 获取日志并遍历调用extract_req_details函数提取请求详情
337
+
338
+ Args:
339
+ match_field: 匹配的字段,默认为None时会使用self.msg的第一个元素
340
+
341
+ Returns:
342
+ 处理后的请求详情列表,每个元素为请求体字典
343
+ """
344
+ try:
345
+ # 获取日志
346
+ logs = self.get_case_logs(match_field)
347
+
348
+ if not logs:
349
+ logger.warning(f"未获取到日志,无法提取请求详情,业务:{self.scene},服务:{self.psm}")
350
+ return []
351
+
352
+ # 存储提取的请求详情
353
+ req_details = []
354
+
355
+ # 遍历日志,提取请求详情
356
+ for log in logs:
357
+ try:
358
+ # match_field为uri原文,做一层全匹配校验,确保值接口一致
359
+ uri = re.search(r'"uri":\s*"([^\"]+)"', log).group(1)
360
+ if match_field[0] != uri and f"\\{match_field[0]}" != uri:
361
+ break
362
+
363
+ request_body = re.search(r'\"request_body\":\s*\"({.*?})"', log).group(1)
364
+ args = re.search(r'"args":\s*"([^\"]+)"', log).group(1)
365
+ req_details.append((uri, args, request_body,{},"" ))
366
+
367
+ except Exception as inner_e:
368
+ logger.error(f"处理单条日志时出错: {inner_e}")
369
+ continue
370
+
371
+ logger.info(f"从 {len(logs)} 条日志中提取了 {len(req_details)} 条请求详情")
372
+ return req_details
373
+
374
+ except Exception as e:
375
+ logger.error(f"遍历日志提取请求详情时出错: {e}")
376
+ return []
377
+
378
+ def process_api_path_with_service(self, api_path):
379
+ """
380
+ 处理单个API路径的辅助方法
381
+
382
+ Args:
383
+ api_path: API路径
384
+
385
+ Returns:
386
+ 元组 (api_path, results),其中results是请求详情列表
387
+ """
388
+ complete_results = []
389
+ try:
390
+ # if self.scene == "hailuo_video_cn_pre":
391
+ # match_field = f"resp: http://hailuo-pre.xaminim.com/{api_path}"
392
+ # elif self.scene == "hailuo_video_cn_prod":
393
+ # match_field = f"resp: http://hailuoai.com/{api_path}"
394
+ # elif self.scene == "hailuo_video_us_test":
395
+ # match_field = f"resp: http://hailuoai-video-test.xaminim.com/{api_path}"
396
+ # elif self.scene == "hailuo_video_us_prod":
397
+ # match_field = f"resp: http://hailuoai.video/{api_path}"
398
+ # else:
399
+ # # 保留原来的默认行为或者添加错误处理
400
+ # match_field = f"resp: http://hailuoai.video/{api_path}"
401
+
402
+ #TX业务目前只能获取req
403
+ if self.scene in ["xingye_http_prod","talkie_http_prod"]:
404
+ complete_results = self.process_logs_extract_req_details([api_path])
405
+ #海螺业务目前可以获得所有req、resp
406
+ else:
407
+ # 获取响应详情
408
+ resp_results = self.process_logs_extract_resp_details(["resp: http",api_path])
409
+ with ThreadPoolExecutor(max_workers=10) as executor:
410
+ # 创建任务列表
411
+ future_to_data = {}
412
+ for trace_id, path, query_params, resp_body in resp_results:
413
+ future = executor.submit(
414
+ self.get_req_by_traceid,
415
+ trace_id=trace_id,
416
+ api_path=path
417
+ )
418
+ future_to_data[future] = (trace_id, path, query_params, resp_body)
419
+
420
+ # 收集所有完成的任务结果
421
+ for future in as_completed(future_to_data):
422
+ try:
423
+ req_body = future.result()
424
+ trace_id, path, query_params, resp_body = future_to_data[future]
425
+ complete_results.append((path, query_params, req_body, resp_body, trace_id))
426
+ except Exception as e:
427
+ logger.error(f"获取请求体时出错: {e}")
428
+
429
+ except Exception as e:
430
+ logger.error(f"处理API路径时出错: {e}, api_path: {api_path}")
431
+
432
+ # 存入文件
433
+ return complete_results
434
+ # self.write_results_to_excel_pandas(complete_results, filename)
435
+
436
+ def process_qps_file(self, api_path=""):
437
+ """
438
+ 处理HTTP接口路径
439
+
440
+ Args:
441
+ from_time: 开始时间,可选
442
+ to_time: 结束时间,可选
443
+
444
+ Returns:
445
+ 字典,键为API路径,值为该路径的请求详情列表
446
+ """
447
+ try:
448
+ if api_path is None or len(api_path) <=0:
449
+ return {"write_result":False,"msg":"api_path is null"}
450
+ logger.info(f"待处理path {api_path}")
451
+
452
+ # 保存到文件
453
+ current_dir = os.path.abspath(os.path.dirname(__file__))
454
+
455
+ # 确保输出目录存在
456
+ output_dir = current_dir
457
+ os.makedirs(output_dir, exist_ok=True)
458
+
459
+ # 处理API路径,替换斜杠为下划线,使其成为有效的文件名
460
+ safe_api_path = api_path.replace('/', '_')
461
+
462
+ # 创建完整的文件路径
463
+ filename = os.path.join(output_dir, f"{self.scene}_{safe_api_path}.csv")
464
+
465
+ complete_results = self.process_api_path_with_service(api_path)
466
+ self.write_results_to_excel_pandas(complete_results, filename)
467
+
468
+ # 上传文件到COS
469
+ files = {
470
+ 'file': (os.path.basename(filename), open(filename, 'rb'), 'text/csv')
471
+ }
472
+ cos_path = f'/http_api_logs/{datetime.now().strftime("%Y%m%d")}/'
473
+ data = {
474
+ 'cos_path': cos_path
475
+ }
476
+ res = requests.post(
477
+ url="http://swing.xaminim.com/save/cos",
478
+ data=data,
479
+ files=files
480
+ )
481
+ if res.status_code == 200:
482
+ return f"https://qa-tool-1315599187.cos.ap-shanghai.myqcloud.com/{cos_path}/{self.scene}_{safe_api_path}.csv"
483
+ else:
484
+ logger.error(f"上传文件到COS失败,错误信息: {res.text}")
485
+ return ""
486
+
487
+ except Exception as e:
488
+ logger.error(f"处理API路径{api_path}时出错: {e}")
489
+ return ""
490
+
491
+
492
+
493
+ def write_results_to_excel_pandas(self, results, filename):
494
+ """
495
+ 使用pandas将结果写入Excel文件
496
+
497
+ Args:
498
+ results: 处理结果字典,键为API路径,值为该路径的请求详情列表
499
+ filename: 存入文件名
500
+
501
+ Returns:
502
+ None
503
+ """
504
+ try:
505
+ # 准备数据
506
+ all_req_info = []
507
+
508
+ for result in results:
509
+ # 解包结果元组,确保获取正确的字段
510
+ if len(result) >= 5:
511
+ path, query_params, req_body, resp_body, trace_id = result
512
+ else:
513
+ continue
514
+
515
+ # 判断HTTP方法
516
+ method = "post" if req_body else "get"
517
+
518
+ # 构建参数字符串
519
+ params_str = f"{query_params}"
520
+
521
+ # 添加到列表
522
+ all_req_info.append({
523
+ "path": path,
524
+ "method_for": method,
525
+ "params": params_str,
526
+ "req": json.dumps(req_body) if req_body else "{}",
527
+ "resp": json.dumps(resp_body) if resp_body else "{}"
528
+ })
529
+
530
+ # 创建新数据的DataFrame
531
+ new_df = pd.DataFrame(all_req_info)
532
+
533
+ # 如果DataFrame为空,添加列名
534
+ if new_df.empty:
535
+ new_df = pd.DataFrame(columns=["path", "params", "req", "resp", "trace"])
536
+
537
+ # 检查文件是否已存在
538
+ if os.path.exists(filename):
539
+ try:
540
+ # 读取现有文件
541
+ existing_df = pd.read_csv(filename)
542
+
543
+ # 合并现有数据和新数据
544
+ combined_df = pd.concat([existing_df, new_df], ignore_index=True)
545
+
546
+ # 写入合并后的数据
547
+ combined_df.to_csv(filename, index=False)
548
+ logger.info(f"已将结果追加到现有文件 {filename}")
549
+ except Exception as read_error:
550
+ logger.error(f"读取现有文件出错,将覆盖写入: {read_error}")
551
+ new_df.to_csv(filename, index=False)
552
+ logger.info(f"已将结果保存到 {filename}(覆盖模式)")
553
+ else:
554
+ # 文件不存在,直接写入
555
+ new_df.to_csv(filename, index=False)
556
+ logger.info(f"已将结果保存到新文件 {filename}")
557
+
558
+ except Exception as e:
559
+ logger.error(f"写入CSV文件时出错: {e}")
560
+
561
+
562
+ if __name__ == "__main__":
563
+ to_time_str = '2025-03-25T19:57:57.567593+08:00'
564
+ from_time_str = '2025-03-25T19:00:57.567593+08:00'
565
+
566
+ print(CaseGrafanaService(scene="hailuo_video_us_prod", psm="moss-gateway",from_time=from_time_str, to_time=to_time_str).process_qps_file(api_path="v3/api/multimodal/video/detail"))
567
+ # print(CaseGrafanaService(scene="xingye_http_prod", psm="",from_time=from_time_str, to_time=to_time_str).process_qps_file(api_path="/weaver/api/v1/relation/get_item_list"))
568
+
569
+ # # 测试响应日志处理功能
570
+ # print("测试3: 响应日志处理")
571
+ # resp_service = CaseGrafanaService(
572
+ # scene="hailuo_video_us_prod",
573
+ # psm="moss-gateway",
574
+ # msg=["resp: http://hailuoai.video/v3/api/multimodal/video/like"],
575
+ # from_time=from_time_str,
576
+ # to_time=to_time_str
577
+ # )
578
+ #
579
+ # # 测试场景3:响应日志解析
580
+ # resp_log = '2025-03-19 17:26:59.474 INFO 1 1 475e33d2fb07fcbbfaaf4bf6d3e85bcf /app/common/log_middileware.go:24 {} : [100.127.128.118] resp: http://hailuoai.video/v3/api/multimodal/video/like?device_platform=web&app_id=3001&version_code=22202&biz_id=0&lang=en&uuid=1c90e957-0060-4a7c-8e74-23a0e23f010e&device_id=300764012298178569&os_name=Mac&browser_name=chrome&device_memory=8&cpu_core_num=8&browser_language=zh-CN&browser_platform=MacIntel&screen_width=1152&screen_height=2048&unix=1742376419000: {"data":{},"statusInfo":{"code":0,"httpCode":0,"message":"Success","serviceTime":1742376419,"requestID":"bd306209-dcde-4dbd-80bb-c7ae34c78cb4","debugInfo":"Success","serverAlert":0}}'
581
+ # resp_result = resp_service.extract_resp_details(resp_log, "resp: http://hailuoai.video/v3/api/multimodal/video/like")
582
+ # print(f"响应日志处理结果: {resp_result}\n")
583
+
584
+ # 测试请求日志处理功能
585
+ # print("测试4: 请求日志处理")
586
+ # req_service = CaseGrafanaService(
587
+ # scene="hailuo_video_us_prod",
588
+ # psm="moss-gateway",
589
+ # msg=["req: http://hailuoai.video/v3/api/multimodal/video/like"],
590
+ # from_time=from_time_str,
591
+ # to_time=to_time_str
592
+ # )
593
+ #
594
+ # # 测试场景4:请求日志解析
595
+ # req_log = '2025-03-18 15:25:17.546 INFO 1 1 74bc348ca6bf4e8d4b447f4ee3fea4c6 /app/common/log_middileware.go:21 {} : [100.127.128.113] req: http://hailuoai.video/v3/api/multimodal/video/like?device_platform=web&app_id=3001&version_code=22202&biz_id=0&lang=en&uuid=cde11186-0f9b-4c0f-9809-9be1a1b98a01&device_id=357566914493747208&os_name=Android&browser_name=chrome&device_memory=2&cpu_core_num=4&browser_language=en-BD&browser_platform=Linux+armv7l&screen_width=360&screen_height=800&unix=1742282716000: {"id":"dsadasada"}'
596
+ # req_result = req_service.extract_req_details(req_log, "req: http://hailuoai.video/v3/api/multimodal/video/like")
597
+ # print(f"请求日志处理结果: {req_result}\n")
598
+ #
599
+ # # 测试跟踪ID获取请求体功能
600
+ # print("测试5: 通过trace_id获取请求体")
601
+ # req_body = resp_service.get_req_by_traceid("475e33d2fb07fcbbfaaf4bf6d3e85bcf", "/v3/api/multimodal/video/like")
602
+ # print(f"通过trace_id获取的请求体: {req_body}\n")
603
+ #
604
+ # 测试完整流程
605
+ print("测试6: 完整流程测试")
606
+
607
+ # 打印结果
608
+ print("\n所有测试完成!")
@@ -0,0 +1,6 @@
1
+ """
2
+ coding:utf-8
3
+ @Software: PyCharm
4
+ @Time: 2025/4/9 17:53
5
+ @Author: xingyun
6
+ """