sycommon-python-lib 0.1.56b2__py3-none-any.whl → 0.1.56b3__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.
File without changes
@@ -0,0 +1,195 @@
1
+ from datetime import datetime
2
+ import sys
3
+ import traceback
4
+ import aiohttp
5
+ import asyncio
6
+ import json
7
+ from typing import Optional
8
+ from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
9
+ from sycommon.config.Config import Config
10
+
11
+
12
+ async def send_wechat_markdown_msg(
13
+ content: str,
14
+ webhook: str = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=b9992872-ea66-494f-8683-f43411b7713a"
15
+ ) -> Optional[dict]:
16
+ """
17
+ 异步发送企业微信Markdown格式的WebHook消息
18
+
19
+ Args:
20
+ content: Markdown格式的消息内容(支持企业微信支持的markdown语法)
21
+ webhook: 完整的企业微信WebHook URL(默认值包含key)
22
+
23
+ Returns:
24
+ 接口返回的JSON数据(dict),失败返回None
25
+ """
26
+ # 设置请求头
27
+ headers = {
28
+ "Content-Type": "application/json; charset=utf-8"
29
+ }
30
+
31
+ # 构造请求体(Markdown格式)
32
+ payload = {
33
+ "msgtype": "markdown",
34
+ "markdown": {
35
+ "content": content
36
+ }
37
+ }
38
+
39
+ try:
40
+ async with aiohttp.ClientSession() as session:
41
+ async with session.post(
42
+ url=webhook,
43
+ data=json.dumps(payload, ensure_ascii=False),
44
+ headers=headers,
45
+ timeout=aiohttp.ClientTimeout(total=10)
46
+ ) as response:
47
+ status = response.status
48
+ response_text = await response.text()
49
+ response_data = json.loads(
50
+ response_text) if response_text else {}
51
+
52
+ if status == 200 and response_data.get("errcode") == 0:
53
+ print(f"消息发送成功: {response_data}")
54
+ return response_data
55
+ else:
56
+ print(f"消息发送失败 - 状态码: {status}, 响应: {response_data}")
57
+ return None
58
+ except Exception as e:
59
+ print(f"错误:未知异常 - {str(e)}")
60
+ return None
61
+
62
+
63
+ async def send_webhook(error_info: dict = None, webhook: str = None):
64
+ """
65
+ 发送服务启动结果的企业微信通知
66
+ Args:
67
+ error_info: 错误信息字典(启动失败时必填),包含:error_type, error_msg, stack_trace, elapsed_time
68
+ webhook: 完整的企业微信WebHook URL(覆盖默认值)
69
+ """
70
+ # 获取服务名和环境(兼容配置读取失败)
71
+ try:
72
+ service_name = Config().config.get('Name', "未知服务")
73
+ env = Config().config.get('Nacos', {}).get('namespaceId', '未知环境')
74
+ except Exception as e:
75
+ service_name = "未知服务"
76
+ env = "未知环境"
77
+ print(f"读取配置失败: {str(e)}")
78
+
79
+ start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
80
+
81
+ # 启动失败的通知内容(包含详细错误信息)
82
+ error_type = error_info.get("error_type", "未知错误")
83
+ error_msg = error_info.get("error_msg", "无错误信息")
84
+ stack_trace = error_info.get("stack_trace", "无堆栈信息")[:500] # 限制长度避免超限
85
+ elapsed_time = error_info.get("elapsed_time", 0)
86
+
87
+ markdown_content = f"""### {service_name}服务启动失败告警 ⚠️
88
+ > 环境: <font color="warning">{env}</font>
89
+ > 启动时间: <font color="comment">{start_time}</font>
90
+ > 耗时: <font color="comment">{elapsed_time:.2f}秒</font>
91
+ > 错误类型: <font color="danger">{error_type}</font>
92
+ > 错误信息: <font color="danger">{error_msg}</font>
93
+ > 错误堆栈: {stack_trace}"""
94
+
95
+ # 发送消息(优先使用传入的webhook,否则用默认值)
96
+ default_webhook = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=b9992872-ea66-494f-8683-f43411b7713a"
97
+ result = await send_wechat_markdown_msg(
98
+ content=markdown_content,
99
+ webhook=webhook if webhook else default_webhook
100
+ )
101
+ print(f"通知发送结果: {result}")
102
+
103
+
104
+ def run(*args, webhook: str = None, **kwargs):
105
+ """
106
+ 带企业微信告警的Uvicorn启动监控
107
+ 调用方式1(默认配置):uvicorn_monitor.run("app:app", **app.state.config)
108
+ 调用方式2(自定义webhook):uvicorn_monitor.run("app:app", webhook="完整URL", **app.state.config)
109
+
110
+ Args:
111
+ *args: 传递给uvicorn.run的位置参数(如"app:app")
112
+ webhook: 完整的企业微信WebHook URL(可选,覆盖默认值)
113
+ **kwargs: 传递给uvicorn.run的关键字参数(如app.state.config)
114
+ """
115
+ # 判断环境
116
+ env = Config().config.get('Nacos', {}).get('namespaceId', '未知环境')
117
+ if env == "prod":
118
+ import uvicorn
119
+ uvicorn.run(*args, **kwargs)
120
+ return
121
+
122
+ # 记录启动开始时间
123
+ start_time = datetime.now()
124
+
125
+ if webhook:
126
+ # 脱敏展示webhook(隐藏key的后半部分)
127
+ parsed = urlparse(webhook)
128
+ query = parse_qs(parsed.query)
129
+ if 'key' in query and query['key'][0]:
130
+ key = query['key'][0]
131
+ masked_key = key[:8] + "****" if len(key) > 8 else key + "****"
132
+ query['key'] = [masked_key]
133
+ masked_query = urlencode(query, doseq=True)
134
+ masked_webhook = urlunparse(
135
+ (parsed.scheme, parsed.netloc, parsed.path, parsed.params, masked_query, parsed.fragment))
136
+ print(f"自定义企业微信WebHook: {masked_webhook}")
137
+
138
+ # 初始化错误信息
139
+ error_info = None
140
+
141
+ try:
142
+ import uvicorn
143
+ # 执行启动(如果启动成功,此方法会阻塞,不会执行后续except)
144
+ uvicorn.run(*args, **kwargs)
145
+
146
+ except KeyboardInterrupt:
147
+ # 处理用户手动中断(不算启动失败)
148
+ elapsed = (datetime.now() - start_time).total_seconds()
149
+ print(f"\n{'='*50}")
150
+ print(f"ℹ️ 应用被用户手动中断")
151
+ print(f"启动耗时: {elapsed:.2f} 秒")
152
+ print(f"{'='*50}\n")
153
+ sys.exit(0)
154
+
155
+ except Exception as e:
156
+ # 捕获启动失败异常,收集错误信息
157
+ elapsed = (datetime.now() - start_time).total_seconds()
158
+ # 捕获堆栈信息
159
+ stack_trace = traceback.format_exc()
160
+
161
+ # 构造错误信息字典
162
+ error_info = {
163
+ "error_type": type(e).__name__,
164
+ "error_msg": str(e),
165
+ "stack_trace": stack_trace,
166
+ "elapsed_time": elapsed
167
+ }
168
+
169
+ # 打印错误信息
170
+ print(f"\n{'='*50}")
171
+ print(f"🚨 应用启动失败!")
172
+ print(f"失败时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
173
+ print(f"错误类型: {type(e).__name__}")
174
+ print(f"错误信息: {str(e)}")
175
+ print(f"\n📝 错误堆栈(关键):")
176
+ print(f"-"*50)
177
+ traceback.print_exc(file=sys.stdout)
178
+ print(f"\n⏱️ 启动耗时: {elapsed:.2f} 秒")
179
+ print(f"{'='*50}\n")
180
+
181
+ finally:
182
+ # 运行异步通知函数,传递自定义的webhook参数
183
+ try:
184
+ asyncio.run(send_webhook(
185
+ error_info=error_info,
186
+ webhook=webhook
187
+ ))
188
+ except Exception as e:
189
+ print(f"错误:异步通知失败 - {str(e)}")
190
+ # 启动失败时退出程序
191
+ sys.exit(1)
192
+
193
+
194
+ # 兼容旧调用方式(可选)
195
+ run_uvicorn_with_monitor = run
@@ -1,4 +1,3 @@
1
- import logging
2
1
  import threading
3
2
  import json
4
3
  from typing import Callable, Dict, List, Optional
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sycommon-python-lib
3
- Version: 0.1.56b2
3
+ Version: 0.1.56b3
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -43,6 +43,8 @@ sycommon/models/mqlistener_config.py,sha256=vXp2uMmd0XQ5B9noSRXWHewTy-juQ2y7IsWt
43
43
  sycommon/models/mqmsg_model.py,sha256=cxn0M5b0utQK6crMYmL-1waeGYHvK3AlGaRy23clqTE,277
44
44
  sycommon/models/mqsend_config.py,sha256=NQX9dc8PpuquMG36GCVhJe8omAW1KVXXqr6lSRU6D7I,268
45
45
  sycommon/models/sso_user.py,sha256=i1WAN6k5sPcPApQEdtjpWDy7VrzWLpOrOQewGLGoGIw,2702
46
+ sycommon/notice/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ sycommon/notice/uvicorn_monitor.py,sha256=NRQVWs3jzZq9IY0M55s7LvOKtacsNpfCGGzH5071dGw,7181
46
48
  sycommon/rabbitmq/rabbitmq_client.py,sha256=GkuYILMZJnvgZs4ID46I-w_UGzzI28YUydKgkTIDhIs,20226
47
49
  sycommon/rabbitmq/rabbitmq_pool.py,sha256=QtUcK4HuepRqRmy5XkUQo8gDgj74fr77CX7T5rN0y4I,15640
48
50
  sycommon/rabbitmq/rabbitmq_service.py,sha256=wpEJynP0gzbnmMyB02sfR9nTWB4hfTTP00xQxDJu644,38119
@@ -54,15 +56,15 @@ sycommon/synacos/example.py,sha256=61XL03tU8WTNOo3FUduf93F2fAwah1S0lbH1ufhRhRk,5
54
56
  sycommon/synacos/example2.py,sha256=adUaru3Hy482KrOA17DfaC4nwvLj8etIDS_KrWLWmCU,4811
55
57
  sycommon/synacos/feign.py,sha256=frB3D5LeFDtT3pJLFOwFzEOrNAJKeQNGk-BzUg9T3WM,8295
56
58
  sycommon/synacos/feign_client.py,sha256=ExO7Pd5B3eFKDjXqBRc260K1jkI49IYguLwJJaD2R-o,16166
57
- sycommon/synacos/nacos_service.py,sha256=cvmjd9dJIzaOFm6IXGjy2BCq1gHTHRxLYdgt5kBguUw,35976
59
+ sycommon/synacos/nacos_service.py,sha256=QPdCRFPXWUUoHL72VCjBAImdIo175q2plDO1T0xrPAg,35961
58
60
  sycommon/synacos/param.py,sha256=KcfSkxnXOa0TGmCjY8hdzU9pzUsA8-4PeyBKWI2-568,1765
59
61
  sycommon/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
62
  sycommon/tools/docs.py,sha256=OPj2ETheuWjXLyaXtaZPbwmJKfJaYXV5s4XMVAUNrms,1607
61
63
  sycommon/tools/merge_headers.py,sha256=HV_i52Q-9se3SP8qh7ZGYl8bP7Fxtal4CGVkyMwEdM8,4373
62
64
  sycommon/tools/snowflake.py,sha256=lVEe5mNCOgz5OqGQpf5_nXaGnRJlI2STX2s-ppTtanA,11947
63
65
  sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
64
- sycommon_python_lib-0.1.56b2.dist-info/METADATA,sha256=HzNJOWrTsJrz8BkzZwJRiq6r9ugYormfdmCEFPM_e_g,7226
65
- sycommon_python_lib-0.1.56b2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
66
- sycommon_python_lib-0.1.56b2.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
67
- sycommon_python_lib-0.1.56b2.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
68
- sycommon_python_lib-0.1.56b2.dist-info/RECORD,,
66
+ sycommon_python_lib-0.1.56b3.dist-info/METADATA,sha256=J7AWcuKaJxgpLHHVQs9qYPokB_-oge0eOD6q8tGIrXo,7226
67
+ sycommon_python_lib-0.1.56b3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
68
+ sycommon_python_lib-0.1.56b3.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
69
+ sycommon_python_lib-0.1.56b3.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
70
+ sycommon_python_lib-0.1.56b3.dist-info/RECORD,,