pytbox 0.1.5__py3-none-any.whl → 0.1.7__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.
- pytbox/alert/ping.py +0 -1
- pytbox/alicloud/sls.py +9 -6
- pytbox/base.py +28 -1
- pytbox/database/victoriametrics.py +169 -6
- pytbox/log/logger.py +22 -6
- pytbox/utils/cronjob.py +79 -0
- {pytbox-0.1.5.dist-info → pytbox-0.1.7.dist-info}/METADATA +1 -1
- {pytbox-0.1.5.dist-info → pytbox-0.1.7.dist-info}/RECORD +11 -11
- pytbox/utils/ping_checker.py +0 -1
- {pytbox-0.1.5.dist-info → pytbox-0.1.7.dist-info}/WHEEL +0 -0
- {pytbox-0.1.5.dist-info → pytbox-0.1.7.dist-info}/entry_points.txt +0 -0
- {pytbox-0.1.5.dist-info → pytbox-0.1.7.dist-info}/top_level.txt +0 -0
pytbox/alert/ping.py
CHANGED
pytbox/alicloud/sls.py
CHANGED
|
@@ -5,19 +5,21 @@ from typing import Literal
|
|
|
5
5
|
from aliyun.log import GetLogsRequest, LogItem, PutLogsRequest
|
|
6
6
|
from aliyun.log import LogClient as SlsLogClient
|
|
7
7
|
from aliyun.log.auth import AUTH_VERSION_4
|
|
8
|
-
from ..utils.env import check_env
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
class AliCloudSls:
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
'''
|
|
13
|
+
pip install -U aliyun-log-python-sdk
|
|
14
|
+
'''
|
|
15
|
+
def __init__(self, access_key_id: str=None, access_key_secret: str=None, project: str=None, logstore: str=None, env: str='prod'):
|
|
15
16
|
# 日志服务的服务接入点
|
|
16
17
|
self.endpoint = "cn-shanghai.log.aliyuncs.com"
|
|
17
18
|
# 创建 LogClient 实例,使用 V4 签名,根据实际情况填写 region,这里以杭州为例
|
|
18
19
|
self.client = SlsLogClient(self.endpoint, access_key_id, access_key_secret, auth_version=AUTH_VERSION_4, region='cn-shanghai')
|
|
19
20
|
self.project = project
|
|
20
21
|
self.logstore = logstore
|
|
22
|
+
self.env = env
|
|
21
23
|
|
|
22
24
|
def get_logs(self, project_name, logstore_name, query, from_time, to_time):
|
|
23
25
|
logstore_index = {'line': {
|
|
@@ -57,7 +59,7 @@ class AliCloudSls:
|
|
|
57
59
|
log_group = []
|
|
58
60
|
log_item = LogItem()
|
|
59
61
|
contents = [
|
|
60
|
-
('env',
|
|
62
|
+
('env', self.env),
|
|
61
63
|
('level', level),
|
|
62
64
|
('app', app),
|
|
63
65
|
('msg', msg),
|
|
@@ -69,8 +71,9 @@ class AliCloudSls:
|
|
|
69
71
|
log_item.set_contents(contents)
|
|
70
72
|
log_group.append(log_item)
|
|
71
73
|
request = PutLogsRequest(self.project, self.logstore, topic, "", log_group, compress=False)
|
|
72
|
-
self.client.put_logs(request)
|
|
73
|
-
|
|
74
|
+
r = self.client.put_logs(request)
|
|
75
|
+
return r
|
|
76
|
+
|
|
74
77
|
def put_logs_for_meraki(self, alert):
|
|
75
78
|
log_group = []
|
|
76
79
|
log_item = LogItem()
|
pytbox/base.py
CHANGED
|
@@ -15,6 +15,9 @@ from pytbox.vmware import VMwareClient
|
|
|
15
15
|
from pytbox.pyjira import PyJira
|
|
16
16
|
from pytbox.mail.client import MailClient
|
|
17
17
|
from pytbox.mail.alimail import AliMail
|
|
18
|
+
from pytbox.alicloud.sls import AliCloudSls
|
|
19
|
+
from pytbox.utils.cronjob import cronjob_counter
|
|
20
|
+
|
|
18
21
|
|
|
19
22
|
config = load_config_by_file(path='/workspaces/pytbox/tests/alert/config_dev.toml', oc_vault_id=os.environ.get('oc_vault_id'))
|
|
20
23
|
|
|
@@ -53,6 +56,19 @@ def get_logger(app):
|
|
|
53
56
|
mongo=get_mongo('alert_program')
|
|
54
57
|
)
|
|
55
58
|
|
|
59
|
+
def get_logger_sls(app):
|
|
60
|
+
return AppLogger(
|
|
61
|
+
app_name=app,
|
|
62
|
+
enable_sls=True,
|
|
63
|
+
feishu=feishu,
|
|
64
|
+
dida=dida,
|
|
65
|
+
mongo=get_mongo('alert_program'),
|
|
66
|
+
sls_access_key_id=config['alicloud']['account1']['access_key_id'],
|
|
67
|
+
sls_access_key_secret=config['alicloud']['account1']['access_key_secret'],
|
|
68
|
+
sls_project=config['alicloud']['account1']['project'],
|
|
69
|
+
sls_logstore=config['alicloud']['account1']['logstore']
|
|
70
|
+
)
|
|
71
|
+
|
|
56
72
|
# ad_dev = ADClient(
|
|
57
73
|
# server=config['ad']['dev']['AD_SERVER'],
|
|
58
74
|
# base_dn=config['ad']['dev']['BASE_DN'],
|
|
@@ -85,4 +101,15 @@ pyjira = PyJira(
|
|
|
85
101
|
|
|
86
102
|
mail_163 = MailClient(mail_address=config['mail']['163']['mail_address'], password=config['mail']['163']['password'])
|
|
87
103
|
mail_qq = MailClient(mail_address=config['mail']['qq']['mail_address'], password=config['mail']['qq']['password'])
|
|
88
|
-
ali_mail = AliMail(mail_address=config['mail']['aliyun']['mail_address'], client_id=config['mail']['aliyun']['client_id'], client_secret=config['mail']['aliyun']['client_secret'])
|
|
104
|
+
ali_mail = AliMail(mail_address=config['mail']['aliyun']['mail_address'], client_id=config['mail']['aliyun']['client_id'], client_secret=config['mail']['aliyun']['client_secret'])
|
|
105
|
+
|
|
106
|
+
sls = AliCloudSls(
|
|
107
|
+
access_key_id=config['alicloud']['account1']['access_key_id'],
|
|
108
|
+
access_key_secret=config['alicloud']['account1']['access_key_secret'],
|
|
109
|
+
project=config['alicloud']['account1']['project'],
|
|
110
|
+
logstore=config['alicloud']['account1']['logstore']
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_cronjob_counter(app_type='', app='', comment=None, schedule_interval=None, schedule_cron=None):
|
|
115
|
+
return cronjob_counter(vm=vm, log=get_logger('cronjob_counter'), app_type=app_type, app=app, comment=comment, schedule_interval=schedule_interval, schedule_cron=schedule_cron)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
+
import time
|
|
3
4
|
import json
|
|
4
|
-
from typing import Literal, Optional
|
|
5
|
+
from typing import Literal, Optional, Dict, List
|
|
5
6
|
import requests
|
|
6
7
|
from ..utils.response import ReturnResponse
|
|
7
8
|
from ..utils.load_vm_devfile import load_dev_file
|
|
@@ -9,7 +10,7 @@ from ..utils.load_vm_devfile import load_dev_file
|
|
|
9
10
|
|
|
10
11
|
class VictoriaMetrics:
|
|
11
12
|
|
|
12
|
-
def __init__(self, url: str='', timeout: int=3) -> None:
|
|
13
|
+
def __init__(self, url: str='', timeout: int=3, env: str='prod') -> None:
|
|
13
14
|
self.url = url
|
|
14
15
|
self.timeout = timeout
|
|
15
16
|
self.session = requests.Session()
|
|
@@ -17,8 +18,45 @@ class VictoriaMetrics:
|
|
|
17
18
|
'Content-Type': 'application/json',
|
|
18
19
|
'Accept': 'application/json'
|
|
19
20
|
})
|
|
21
|
+
self.env = env
|
|
22
|
+
|
|
23
|
+
def insert(self, metric_name: str = '', labels: Dict[str, str] = None,
|
|
24
|
+
value: List[float] = None, timestamp: int = None) -> ReturnResponse:
|
|
25
|
+
"""插入指标数据。
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
Args:
|
|
28
|
+
metric_name: 指标名称
|
|
29
|
+
labels: 标签字典
|
|
30
|
+
value: 值列表
|
|
31
|
+
timestamp: 时间戳(毫秒),默认为当前时间
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
requests.RequestException: 当请求失败时抛出
|
|
35
|
+
"""
|
|
36
|
+
if labels is None:
|
|
37
|
+
labels = {}
|
|
38
|
+
if value is None:
|
|
39
|
+
value = 1
|
|
40
|
+
if timestamp is None:
|
|
41
|
+
timestamp = int(time.time() * 1000)
|
|
42
|
+
|
|
43
|
+
url = f"{self.url}/api/v1/import"
|
|
44
|
+
data = {
|
|
45
|
+
"metric": {
|
|
46
|
+
"__name__": metric_name,
|
|
47
|
+
**labels
|
|
48
|
+
},
|
|
49
|
+
"values": [value],
|
|
50
|
+
"timestamps": [timestamp]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
response = requests.post(url, json=data, timeout=self.timeout)
|
|
55
|
+
return ReturnResponse(code=0, msg=f"数据插入成功,状态码: {response.status_code}, metric_name: {metric_name}, labels: {labels}, value: {value}, timestamp: {timestamp}")
|
|
56
|
+
except requests.RequestException as e:
|
|
57
|
+
return ReturnResponse(code=1, msg=f"数据插入失败: {e}")
|
|
58
|
+
|
|
59
|
+
def query(self, query: str=None, output_format: Literal['json']=None) -> ReturnResponse:
|
|
22
60
|
'''
|
|
23
61
|
查询指标数据
|
|
24
62
|
|
|
@@ -61,6 +99,52 @@ class VictoriaMetrics:
|
|
|
61
99
|
else:
|
|
62
100
|
return resp
|
|
63
101
|
|
|
102
|
+
def query_range(self, query):
|
|
103
|
+
'''
|
|
104
|
+
查询指标数据
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
query (str): 查询语句
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
dict: 查询结果
|
|
111
|
+
'''
|
|
112
|
+
url = f"{self.url}/prometheus/api/v1/query_range"
|
|
113
|
+
|
|
114
|
+
data = {
|
|
115
|
+
'query': query,
|
|
116
|
+
'start': '-1d',
|
|
117
|
+
'step': '1h'
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
r = requests.post(url, data=data, timeout=self.timeout)
|
|
121
|
+
res_json = r.json()
|
|
122
|
+
print(res_json)
|
|
123
|
+
# status = res_json.get("status")
|
|
124
|
+
# result = res_json.get("data", {}).get("result", [])
|
|
125
|
+
# is_json = output_format == 'json'
|
|
126
|
+
|
|
127
|
+
# if status == "success":
|
|
128
|
+
# if result:
|
|
129
|
+
# code = 0
|
|
130
|
+
# msg = f"[{query}] 查询成功!"
|
|
131
|
+
# data = result
|
|
132
|
+
# else:
|
|
133
|
+
# code = 2
|
|
134
|
+
# msg = f"[{query}] 没有查询到结果"
|
|
135
|
+
# data = res_json
|
|
136
|
+
# else:
|
|
137
|
+
# code = 1
|
|
138
|
+
# msg = f"[{query}] 查询失败: {res_json.get('error')}"
|
|
139
|
+
# data = res_json
|
|
140
|
+
|
|
141
|
+
# resp = ReturnResponse(code=code, msg=msg, data=data)
|
|
142
|
+
|
|
143
|
+
# if is_json:
|
|
144
|
+
# json_result = json.dumps(resp.__dict__, ensure_ascii=False)
|
|
145
|
+
# return json_result
|
|
146
|
+
# else:
|
|
147
|
+
# return resp
|
|
64
148
|
def get_labels(self, metric_name: str) -> ReturnResponse:
|
|
65
149
|
url = f"{self.url}/api/v1/series?match[]={metric_name}"
|
|
66
150
|
response = requests.get(url, timeout=self.timeout)
|
|
@@ -104,7 +188,6 @@ class VictoriaMetrics:
|
|
|
104
188
|
code = 0
|
|
105
189
|
msg = f"已检查 {target} 最近 {last_minute} 分钟是正常的!"
|
|
106
190
|
else:
|
|
107
|
-
|
|
108
191
|
if all(str(item[1]) == "1" for item in values):
|
|
109
192
|
code = 1
|
|
110
193
|
msg = f"已检查 {target} 最近 {last_minute} 分钟是异常的!"
|
|
@@ -114,7 +197,6 @@ class VictoriaMetrics:
|
|
|
114
197
|
elif r.code == 2:
|
|
115
198
|
code = 2
|
|
116
199
|
msg = f"没有查询到 {target} 最近 {last_minute} 分钟的ping结果!"
|
|
117
|
-
|
|
118
200
|
try:
|
|
119
201
|
data = r.data[0]
|
|
120
202
|
except KeyError:
|
|
@@ -146,4 +228,85 @@ class VictoriaMetrics:
|
|
|
146
228
|
query = f'(rate(snmp_interface_ifHCOutOctets{{sysName="{sysName}", ifName="{ifName}"}}[{last_minutes}m])) * 8 / 1000000'
|
|
147
229
|
r = self.query(query)
|
|
148
230
|
rate = r.data[0]['value'][1]
|
|
149
|
-
return int(float(rate))
|
|
231
|
+
return int(float(rate))
|
|
232
|
+
|
|
233
|
+
def check_snmp_port_status(self, sysname: str=None, if_name: str=None, last_minute: int=5) -> ReturnResponse:
|
|
234
|
+
'''
|
|
235
|
+
查询端口状态
|
|
236
|
+
status code 可参考 SNMP 文件 https://mibbrowser.online/mibdb_search.php?mib=IF-MIB
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
sysname (_type_): 设备名称
|
|
240
|
+
if_name (_type_): _description_
|
|
241
|
+
last_minute (_type_): _description_
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
ReturnResponse:
|
|
245
|
+
code: 0, msg: , data: up,down
|
|
246
|
+
'''
|
|
247
|
+
q = f"""avg_over_time(snmp_interface_ifOperStatus{{sysName="{sysname}", ifName="{if_name}"}}[{last_minute}m])"""
|
|
248
|
+
r = self.query(query=q)
|
|
249
|
+
if r.code == 0:
|
|
250
|
+
status_code = r.data[0]['value'][1]
|
|
251
|
+
if status_code == 1:
|
|
252
|
+
status = 'up'
|
|
253
|
+
else:
|
|
254
|
+
status = 'down'
|
|
255
|
+
return ReturnResponse(code=0, msg=f"{sysname} {if_name} 最近 {last_minute} 分钟端口状态为 {status}", data=status)
|
|
256
|
+
else:
|
|
257
|
+
return r
|
|
258
|
+
|
|
259
|
+
def insert_cronjob_run_status(self,
|
|
260
|
+
app_type: Literal['alert', 'meraki', 'other']='other',
|
|
261
|
+
app: str='',
|
|
262
|
+
status_code: Literal[0, 1]=1,
|
|
263
|
+
comment: str=None,
|
|
264
|
+
schedule_interval: str=None,
|
|
265
|
+
schedule_cron: str=None
|
|
266
|
+
) -> ReturnResponse:
|
|
267
|
+
labels = {
|
|
268
|
+
"app": app,
|
|
269
|
+
"env": self.env,
|
|
270
|
+
}
|
|
271
|
+
if app_type:
|
|
272
|
+
labels['app_type'] = app_type
|
|
273
|
+
if comment:
|
|
274
|
+
labels['comment'] = comment
|
|
275
|
+
|
|
276
|
+
if schedule_interval:
|
|
277
|
+
labels['schedule_type'] = 'interval'
|
|
278
|
+
labels['schedule_interval'] = schedule_interval
|
|
279
|
+
|
|
280
|
+
if schedule_cron:
|
|
281
|
+
labels['schedule_type'] = 'cron'
|
|
282
|
+
labels['schedule_cron'] = schedule_cron
|
|
283
|
+
|
|
284
|
+
r = self.insert(metric_name="cronjob_run_status", labels=labels, value=status_code)
|
|
285
|
+
return r
|
|
286
|
+
|
|
287
|
+
def insert_cronjob_duration_seconds(self,
|
|
288
|
+
app_type: Literal['alert', 'meraki', 'other']='other',
|
|
289
|
+
app: str='',
|
|
290
|
+
duration_seconds: float=None,
|
|
291
|
+
comment: str=None,
|
|
292
|
+
schedule_interval: str=None,
|
|
293
|
+
schedule_cron: str=None
|
|
294
|
+
) -> ReturnResponse:
|
|
295
|
+
labels = {
|
|
296
|
+
"app": app,
|
|
297
|
+
"env": self.env
|
|
298
|
+
}
|
|
299
|
+
if app_type:
|
|
300
|
+
labels['app_type'] = app_type
|
|
301
|
+
if comment:
|
|
302
|
+
labels['comment'] = comment
|
|
303
|
+
|
|
304
|
+
if schedule_interval:
|
|
305
|
+
labels['schedule_type'] = 'interval'
|
|
306
|
+
labels['schedule_interval'] = schedule_interval
|
|
307
|
+
|
|
308
|
+
if schedule_cron:
|
|
309
|
+
labels['schedule_type'] = 'cron'
|
|
310
|
+
labels['schedule_cron'] = schedule_cron
|
|
311
|
+
r = self.insert(metric_name="cronjob_run_duration_seconds", labels=labels, value=duration_seconds)
|
|
312
|
+
return r
|
pytbox/log/logger.py
CHANGED
|
@@ -9,7 +9,7 @@ from ..database.mongo import Mongo
|
|
|
9
9
|
from ..feishu.client import Client as FeishuClient
|
|
10
10
|
from ..utils.timeutils import TimeUtils
|
|
11
11
|
from ..dida365 import Dida365
|
|
12
|
-
|
|
12
|
+
from ..alicloud.sls import AliCloudSls
|
|
13
13
|
|
|
14
14
|
logger.remove()
|
|
15
15
|
logger.add(sys.stdout, colorize=True, format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <level>{message}</level>")
|
|
@@ -31,7 +31,6 @@ class AppLogger:
|
|
|
31
31
|
feishu: FeishuClient=None,
|
|
32
32
|
dida: Dida365=None,
|
|
33
33
|
enable_sls: bool=False,
|
|
34
|
-
sls_url: str=None,
|
|
35
34
|
sls_access_key_id: str=None,
|
|
36
35
|
sls_access_key_secret: str=None,
|
|
37
36
|
sls_project: str=None,
|
|
@@ -49,10 +48,17 @@ class AppLogger:
|
|
|
49
48
|
self.stream = stream
|
|
50
49
|
self.victorialog = Victorialog(url=victorialog_url)
|
|
51
50
|
self.enable_victorialog = enable_victorialog
|
|
51
|
+
self.enable_sls = enable_sls
|
|
52
52
|
self.mongo = mongo
|
|
53
53
|
self.feishu = feishu
|
|
54
54
|
self.dida = dida
|
|
55
|
-
|
|
55
|
+
self.sls = AliCloudSls(
|
|
56
|
+
access_key_id=sls_access_key_id,
|
|
57
|
+
access_key_secret=sls_access_key_secret,
|
|
58
|
+
project=sls_project,
|
|
59
|
+
logstore=sls_logstore
|
|
60
|
+
)
|
|
61
|
+
|
|
56
62
|
def _get_caller_info(self) -> tuple[str, int, str]:
|
|
57
63
|
"""
|
|
58
64
|
获取调用者信息
|
|
@@ -78,6 +84,9 @@ class AppLogger:
|
|
|
78
84
|
logger.debug(f"[{caller_filename}:{caller_lineno}:{caller_function}] {message}")
|
|
79
85
|
if self.enable_victorialog:
|
|
80
86
|
self.victorialog.send_program_log(stream=self.stream, level="DEBUG", message=message, app_name=self.app_name, file_name=call_full_filename, line_number=caller_lineno, function_name=caller_function)
|
|
87
|
+
if self.enable_sls:
|
|
88
|
+
self.sls.put_logs(level="DEBUG", msg=message, app=self.app_name, caller_filename=caller_filename, caller_lineno=caller_lineno, caller_function=caller_function, call_full_filename=call_full_filename)
|
|
89
|
+
|
|
81
90
|
|
|
82
91
|
def info(self, message: str='', feishu_notify: bool=False):
|
|
83
92
|
"""记录信息级别日志"""
|
|
@@ -85,22 +94,28 @@ class AppLogger:
|
|
|
85
94
|
logger.info(f"[{caller_filename}:{caller_lineno}:{caller_function}] {message}")
|
|
86
95
|
if self.enable_victorialog:
|
|
87
96
|
r = self.victorialog.send_program_log(stream=self.stream, level="INFO", message=message, app_name=self.app_name, file_name=call_full_filename, line_number=caller_lineno, function_name=caller_function)
|
|
97
|
+
if self.enable_sls:
|
|
98
|
+
self.sls.put_logs(level="INFO", msg=message, app=self.app_name, caller_filename=caller_filename, caller_lineno=caller_lineno, caller_function=caller_function, call_full_filename=call_full_filename)
|
|
88
99
|
if feishu_notify:
|
|
89
100
|
self.feishu(message)
|
|
90
|
-
|
|
101
|
+
|
|
91
102
|
def warning(self, message: str):
|
|
92
103
|
"""记录警告级别日志"""
|
|
93
104
|
caller_filename, caller_lineno, caller_function, call_full_filename = self._get_caller_info()
|
|
94
105
|
logger.warning(f"[{caller_filename}:{caller_lineno}:{caller_function}] {message}")
|
|
95
106
|
if self.enable_victorialog:
|
|
96
107
|
self.victorialog.send_program_log(stream=self.stream, level="WARN", message=message, app_name=self.app_name, file_name=call_full_filename, line_number=caller_lineno, function_name=caller_function)
|
|
97
|
-
|
|
108
|
+
if self.enable_sls:
|
|
109
|
+
self.sls.put_logs(level="WARN", msg=message, app=self.app_name, caller_filename=caller_filename, caller_lineno=caller_lineno, caller_function=caller_function, call_full_filename=call_full_filename)
|
|
110
|
+
|
|
98
111
|
def error(self, message: str):
|
|
99
112
|
"""记录错误级别日志"""
|
|
100
113
|
caller_filename, caller_lineno, caller_function, call_full_filename = self._get_caller_info()
|
|
101
114
|
logger.error(f"[{caller_filename}:{caller_lineno}:{caller_function}] {message}")
|
|
102
115
|
if self.enable_victorialog:
|
|
103
116
|
self.victorialog.send_program_log(stream=self.stream, level="ERROR", message=message, app_name=self.app_name, file_name=call_full_filename, line_number=caller_lineno, function_name=caller_function)
|
|
117
|
+
if self.enable_sls:
|
|
118
|
+
self.sls.put_logs(level="ERROR", msg=message, app=self.app_name, caller_filename=caller_filename, caller_lineno=caller_lineno, caller_function=caller_function, call_full_filename=call_full_filename)
|
|
104
119
|
|
|
105
120
|
if self.feishu:
|
|
106
121
|
existing_message = self.mongo.collection.find_one({"message": message}, sort=[("time", -1)])
|
|
@@ -150,7 +165,8 @@ class AppLogger:
|
|
|
150
165
|
logger.critical(f"[{caller_filename}:{caller_lineno}:{caller_function}] {message}")
|
|
151
166
|
if self.enable_victorialog:
|
|
152
167
|
self.victorialog.send_program_log(stream=self.stream, level="CRITICAL", message=message, app_name=self.app_name, file_name=call_full_filename, line_number=caller_lineno, function_name=caller_function)
|
|
153
|
-
|
|
168
|
+
if self.enable_sls:
|
|
169
|
+
self.sls.put_logs(level="CRITICAL", msg=message, app=self.app_name, caller_filename=caller_filename, caller_lineno=caller_lineno, caller_function=caller_function, call_full_filename=call_full_filename)
|
|
154
170
|
|
|
155
171
|
# 使用示例
|
|
156
172
|
if __name__ == "__main__":
|
pytbox/utils/cronjob.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from functools import wraps
|
|
5
|
+
from typing import Literal
|
|
6
|
+
from ..database.victoriametrics import VictoriaMetrics
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def cronjob_counter(vm: VictoriaMetrics=None, log: str=None, app_type: Literal['']='', app='', comment=None, schedule_interval=None, schedule_cron=None):
|
|
10
|
+
"""计算函数运行时间的装饰器,支持记录到 VictoriaMetrics
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
app_type: 应用类型 ('alert', 'meraki', 'other')
|
|
14
|
+
app: 应用名称
|
|
15
|
+
comment: 备注信息
|
|
16
|
+
schedule_interval: 定时任务间隔(如 '1m', '5m')
|
|
17
|
+
schedule_cron: cron 表达式(如 '0 */5 * * *')
|
|
18
|
+
"""
|
|
19
|
+
def decorator(func):
|
|
20
|
+
@wraps(func)
|
|
21
|
+
def wrapper(*args, **kwargs):
|
|
22
|
+
start_time = time.time()
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
result = func(*args, **kwargs)
|
|
26
|
+
end_time = time.time()
|
|
27
|
+
elapsed_time = end_time - start_time
|
|
28
|
+
|
|
29
|
+
# 记录任务成功完成状态
|
|
30
|
+
vm.insert_cronjob_run_status(
|
|
31
|
+
app_type=app_type,
|
|
32
|
+
app=app,
|
|
33
|
+
status_code=1, # 0 表示成功完成
|
|
34
|
+
comment=f"成功完成: {comment}" if comment else "任务成功完成",
|
|
35
|
+
schedule_interval=schedule_interval,
|
|
36
|
+
schedule_cron=schedule_cron
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# 记录执行耗时
|
|
40
|
+
vm.insert_cronjob_duration_seconds(
|
|
41
|
+
app_type=app_type,
|
|
42
|
+
app=app,
|
|
43
|
+
duration_seconds=elapsed_time,
|
|
44
|
+
comment=comment,
|
|
45
|
+
schedule_interval=schedule_interval,
|
|
46
|
+
schedule_cron=schedule_cron
|
|
47
|
+
)
|
|
48
|
+
log.info(f"{app} 任务成功完成, 耗时 {elapsed_time:.2f} 秒")
|
|
49
|
+
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
except Exception as e:
|
|
53
|
+
end_time = time.time()
|
|
54
|
+
elapsed_time = end_time - start_time
|
|
55
|
+
|
|
56
|
+
# 记录任务失败状态
|
|
57
|
+
error_comment = f"执行出错: {str(e)}" if not comment else f"{comment} (出错: {str(e)})"
|
|
58
|
+
vm.insert_cronjob_run_status(
|
|
59
|
+
app_type=app_type,
|
|
60
|
+
app=app,
|
|
61
|
+
status_code=0, # 1 表示失败
|
|
62
|
+
comment=error_comment,
|
|
63
|
+
schedule_interval=schedule_interval,
|
|
64
|
+
schedule_cron=schedule_cron
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# 即使出错也记录耗时
|
|
68
|
+
vm.insert_cronjob_duration_seconds(
|
|
69
|
+
app_type=app_type,
|
|
70
|
+
app=app,
|
|
71
|
+
duration_seconds=elapsed_time,
|
|
72
|
+
comment=error_comment,
|
|
73
|
+
schedule_interval=schedule_interval,
|
|
74
|
+
schedule_cron=schedule_cron
|
|
75
|
+
)
|
|
76
|
+
log.error(f"任务失败: {error_comment}")
|
|
77
|
+
raise
|
|
78
|
+
return wrapper
|
|
79
|
+
return decorator
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
pytbox/base.py,sha256=
|
|
1
|
+
pytbox/base.py,sha256=uzrv0vlHVPOr2JjpUGd8R2Wr-iRf6SysnrAYOalKXYY,4283
|
|
2
2
|
pytbox/cli.py,sha256=N775a0GK80IT2lQC2KRYtkZpIiu9UjavZmaxgNUgJhQ,160
|
|
3
3
|
pytbox/dida365.py,sha256=pUMPB9AyLZpTTbaz2LbtzdEpyjvuGf4YlRrCvM5sbJo,10545
|
|
4
4
|
pytbox/excel.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -7,8 +7,8 @@ pytbox/onepassword_sa.py,sha256=08iUcYud3aEHuQcUsem9bWNxdXKgaxFbMy9yvtr-DZQ,6995
|
|
|
7
7
|
pytbox/pyjira.py,sha256=TMy34Rtu7OXRA8wpUuLsFeyIQfRNUn2ed2no00L8YSE,22470
|
|
8
8
|
pytbox/vmware.py,sha256=WiH67_3-VCBjXJuh3UueOc31BdZDItiZhkeuPzoRhw4,3975
|
|
9
9
|
pytbox/alert/alert_handler.py,sha256=FePPQS4LyGphSJ0QMv0_pLWaXxEqsRlcTKMfUjtsNfk,5048
|
|
10
|
-
pytbox/alert/ping.py,sha256=
|
|
11
|
-
pytbox/alicloud/sls.py,sha256
|
|
10
|
+
pytbox/alert/ping.py,sha256=KEnnXdIRJHvR_rEHPWLBt0wz4cGwmA29Lenlak3Z_1Y,778
|
|
11
|
+
pytbox/alicloud/sls.py,sha256=-r6rbCwDUQ4jwAgSVSNu7B2h1MNg20CrRIKnlXVeY9w,4159
|
|
12
12
|
pytbox/categraf/build_config.py,sha256=9G85rLqkz3lchpH7ef0LbvckYHl0nRA6mHVLeUfs9Mw,6308
|
|
13
13
|
pytbox/categraf/instances.toml,sha256=jcJyEaqhohUcECczWArxUK4t0-rdk4vmrX21kxZlSLA,1254
|
|
14
14
|
pytbox/categraf/jinja2/__init__.py,sha256=Epm01j8Oujeg4Sk5GgHMvgKIZ6h3BTx-MGmuMgIjUMo,150
|
|
@@ -43,28 +43,28 @@ pytbox/cli/formatters/__init__.py,sha256=4o85w4j-A-O1oBLvuE9q8AFiJ2C9rvB3MIKsy5V
|
|
|
43
43
|
pytbox/cli/formatters/output.py,sha256=h5WhZlQk1rjmxEj88Jy5ODLcv6L5zfGUhks_3AWIkKU,5455
|
|
44
44
|
pytbox/common/__init__.py,sha256=3JWfgCQZKZuSH5NCE7OCzKwq82pkyop9l7sH5YSNyfU,122
|
|
45
45
|
pytbox/database/mongo.py,sha256=CSpHC7iR-M0BaVxXz5j6iXjMKPgXJX_G7MrjCj5Gm8Q,3478
|
|
46
|
-
pytbox/database/victoriametrics.py,sha256=
|
|
46
|
+
pytbox/database/victoriametrics.py,sha256=3lLbcqDADojkFH52OcJaz2ApxWnAD_pnqkMQR8fditI,11295
|
|
47
47
|
pytbox/feishu/client.py,sha256=kwGLseGT_iQUFmSqpuS2_77WmxtHstD64nXvktuQ3B4,5865
|
|
48
48
|
pytbox/feishu/endpoints.py,sha256=z3nPOZPC2JGDJlO7SusWBpRA33hZZ4Z-GBhI6F8L_u4,40240
|
|
49
49
|
pytbox/feishu/errors.py,sha256=79qFAHZw7jDj3gnWAjI1-W4tB0q1_aSfdjee4xzXeuI,1179
|
|
50
50
|
pytbox/feishu/helpers.py,sha256=jhSkHiUw4822QBXx2Jw8AksogZdakZ-3QqvC3lB3qEI,201
|
|
51
51
|
pytbox/feishu/typing.py,sha256=3hWkJgOi-v2bt9viMxkyvNHsPgrbAa0aZOxsZYg2vdM,122
|
|
52
|
-
pytbox/log/logger.py,sha256=
|
|
52
|
+
pytbox/log/logger.py,sha256=tfqRc7VcDyI_U-NiiK4euFe8O2ro9yeXVohPmzAC_hY,8867
|
|
53
53
|
pytbox/log/victorialog.py,sha256=gffEiq38adv9sC5oZeMcyKghd3SGfRuqtZOFuqHQF6E,4139
|
|
54
54
|
pytbox/mail/alimail.py,sha256=njKA3PUbIaiKFaxKvUObmklmEEHg2YA-O5rpgsgT5_w,5147
|
|
55
55
|
pytbox/mail/client.py,sha256=6HeKpChHGjTCYGBgQcfAhWlU_wh9wtO-bjP6TU38pGM,6120
|
|
56
56
|
pytbox/mail/mail_detail.py,sha256=6u8DK-7WzYPSuX6TdicSCh2Os_9Ou6Rn9xc6WRvv85M,699
|
|
57
57
|
pytbox/network/meraki.py,sha256=054E3C5KzAuXs9aPalvdAOUo6Hc5aOKZSWUaVbPquy4,6112
|
|
58
|
+
pytbox/utils/cronjob.py,sha256=b17CY1fmaFTdQojicXAXHliov_JZdmT7cZhtO4v5sCo,3080
|
|
58
59
|
pytbox/utils/env.py,sha256=gD2-NyL3K3Vg1B1eGeD1hRtlSHPGgF8Oi9mchuQL6_o,646
|
|
59
60
|
pytbox/utils/load_config.py,sha256=R4pGerBinbewsym41hQ8Z-I5I7gepuEKODjIrli4C08,5043
|
|
60
61
|
pytbox/utils/load_vm_devfile.py,sha256=GVbB-FvGb0l586SDaraz__ZaXyDWd1WxcXVw5xGfXWw,1328
|
|
61
|
-
pytbox/utils/ping_checker.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
62
62
|
pytbox/utils/response.py,sha256=kXjlwt0WVmLRam2eu1shzX2cQ7ux4cCQryaPGYwle5g,1247
|
|
63
63
|
pytbox/utils/richutils.py,sha256=OT9_q2Q1bthzB0g1GlhZVvM4ZAepJRKL6a_Vsr6vEqo,487
|
|
64
64
|
pytbox/utils/timeutils.py,sha256=uSKgwt20mVcgIGKLsH2tNum8v3rcpzgmBibPvyPQFgM,20433
|
|
65
65
|
pytbox/win/ad.py,sha256=-3pWfL3dElz-XoO4j4M9lrgu3KJtlhrS9gCWJBpafAU,1147
|
|
66
|
-
pytbox-0.1.
|
|
67
|
-
pytbox-0.1.
|
|
68
|
-
pytbox-0.1.
|
|
69
|
-
pytbox-0.1.
|
|
70
|
-
pytbox-0.1.
|
|
66
|
+
pytbox-0.1.7.dist-info/METADATA,sha256=F1iAjEaYwI8n3pUxQn22V4Zcavu_W0Toz4scTHxwg0c,6319
|
|
67
|
+
pytbox-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
68
|
+
pytbox-0.1.7.dist-info/entry_points.txt,sha256=YaTOJ2oPjOiv2SZwY0UC-UA9QS2phRH1oMvxGnxO0Js,43
|
|
69
|
+
pytbox-0.1.7.dist-info/top_level.txt,sha256=YADgWue-Oe128ptN3J2hS3GB0Ncc5uZaSUM3e1rwswE,7
|
|
70
|
+
pytbox-0.1.7.dist-info/RECORD,,
|
pytbox/utils/ping_checker.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|