gomyck-tools 1.3.1__py3-none-any.whl → 1.3.2__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.
Files changed (64) hide show
  1. ctools/__init__.py +0 -0
  2. ctools/aes_tools.py +35 -0
  3. ctools/api_result.py +55 -0
  4. ctools/application.py +386 -0
  5. ctools/b64.py +7 -0
  6. ctools/bashPath.py +13 -0
  7. ctools/bottle_web_base.py +169 -0
  8. ctools/bottle_webserver.py +143 -0
  9. ctools/bottle_websocket.py +75 -0
  10. ctools/browser_element_tools.py +314 -0
  11. ctools/call.py +71 -0
  12. ctools/cftp.py +74 -0
  13. ctools/cjson.py +54 -0
  14. ctools/ckafka.py +159 -0
  15. ctools/compile_tools.py +18 -0
  16. ctools/console.py +55 -0
  17. ctools/coord_trans.py +127 -0
  18. ctools/credis.py +111 -0
  19. ctools/cron_lite.py +252 -0
  20. ctools/ctoken.py +34 -0
  21. ctools/cword.py +30 -0
  22. ctools/czip.py +130 -0
  23. ctools/database.py +185 -0
  24. ctools/date_utils.py +43 -0
  25. ctools/dict_wrapper.py +20 -0
  26. ctools/douglas_rarefy.py +136 -0
  27. ctools/download_tools.py +57 -0
  28. ctools/enums.py +4 -0
  29. ctools/ex.py +31 -0
  30. ctools/excelOpt.py +36 -0
  31. ctools/html_soup.py +35 -0
  32. ctools/http_utils.py +24 -0
  33. ctools/images_tools.py +27 -0
  34. ctools/imgDialog.py +44 -0
  35. ctools/metrics.py +131 -0
  36. ctools/mqtt_utils.py +289 -0
  37. ctools/obj.py +20 -0
  38. ctools/pacth.py +74 -0
  39. ctools/plan_area_tools.py +97 -0
  40. ctools/process_pool.py +36 -0
  41. ctools/pty_tools.py +72 -0
  42. ctools/resource_bundle_tools.py +121 -0
  43. ctools/rsa.py +70 -0
  44. ctools/screenshot_tools.py +127 -0
  45. ctools/sign.py +20 -0
  46. ctools/sm_tools.py +49 -0
  47. ctools/snow_id.py +76 -0
  48. ctools/str_diff.py +20 -0
  49. ctools/string_tools.py +85 -0
  50. ctools/sys_info.py +157 -0
  51. ctools/sys_log.py +89 -0
  52. ctools/thread_pool.py +35 -0
  53. ctools/upload_tools.py +40 -0
  54. ctools/win_canvas.py +83 -0
  55. ctools/win_control.py +106 -0
  56. ctools/word_fill.py +562 -0
  57. ctools/word_fill_entity.py +46 -0
  58. ctools/work_path.py +69 -0
  59. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/METADATA +1 -1
  60. gomyck_tools-1.3.2.dist-info/RECORD +62 -0
  61. gomyck_tools-1.3.2.dist-info/top_level.txt +1 -0
  62. gomyck_tools-1.3.1.dist-info/RECORD +0 -4
  63. gomyck_tools-1.3.1.dist-info/top_level.txt +0 -1
  64. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2024/9/19 14:02'
5
+
6
+ import math
7
+
8
+ from jsonpath_ng import parser
9
+
10
+ from ctools import cjson
11
+ from ctools.sys_log import flog as log
12
+
13
+ """
14
+ douglas_rarefy.DouglasRarefy(res, level=3).sparse_points()
15
+ """
16
+
17
+ class THIN_LEVEL:
18
+ L1 = 0.00001
19
+ L2 = 0.00003
20
+ L3 = 0.00009
21
+ L4 = 0.0002
22
+ L5 = 0.0004
23
+ L6 = 0.0007
24
+ L7 = 0.0011
25
+ L8 = 0.0017
26
+ L9 = 0.0022
27
+
28
+ class Point:
29
+ def __init__(self, lng, lat, origin_data):
30
+ self.lng = lng
31
+ self.lat = lat
32
+ self.origin_data = origin_data
33
+
34
+ def _get_line_by_point(xy1, xy2):
35
+ """
36
+ 根据两个点求直线方程 ax + by + c = 0
37
+ :param xy1: 点1, 例如 {'lat': 1, 'lng': 1}
38
+ :param xy2: 点2, 例如 {'lat': 2, 'lng': 2}
39
+ :return: 直线方程的三个参数 [a, b, c]
40
+ """
41
+ x1 = xy1.lng
42
+ y1 = xy1.lat
43
+ x2 = xy2.lng
44
+ y2 = xy2.lat
45
+ a = y2 - y1
46
+ b = x1 - x2
47
+ c = (y1 - y2) * x1 - y1 * (x1 - x2)
48
+ return [a, b, c]
49
+
50
+
51
+ def _get_distance_from_point_to_line(a, b, c, xy):
52
+ """
53
+ 点到直线的距离,直线方程为 ax + by + c = 0
54
+ :param a: 直线参数a
55
+ :param b: 直线参数b
56
+ :param c: 直线参数c
57
+ :param xy: 点坐标,例如 {'lat': 2, 'lng': 2}
58
+ :return: 距离
59
+ """
60
+ x = xy.lng
61
+ y = xy.lat
62
+ return abs((a * x + b * y + c) / math.sqrt(a * a + b * b))
63
+
64
+
65
+ class DouglasRarefy:
66
+ """
67
+ DouglasRarefy Use Guide:
68
+ points must be arrays, element can be dict or arrays, when element is arrays, index 0 must be lng, index 1 must be lat, and element can be use max column num is 2 (lng, lat) in ret_tpl
69
+ level default is L2, this level can be hold most of the points detail
70
+ ret_tpl is the result tpl, can be arrays and json or some can be json loads, exp: [{lng}, {lat}] OR {{"lng": {lng}, "lat": {lat}}}
71
+ """
72
+ def __init__(self, points:[], level=THIN_LEVEL.L2, ret_tpl=None, get_lng=None, get_lat=None):
73
+ if not isinstance(points, list): raise Exception('points must be list obj !!')
74
+ if len(points) < 3: raise Exception('points length must be gt 2 !!')
75
+ self.points = points
76
+ self.threshold = THIN_LEVEL.L2 if level is None else (getattr(THIN_LEVEL, "L{}".format(int(level))) if int(level) >= 1 else level)
77
+ log.debug("threshold is: {}".format(self.threshold))
78
+ self.is_json = isinstance(points[0], dict)
79
+ self.get_lng = get_lng
80
+ self.get_lat = get_lat
81
+ if self.is_json:
82
+ if not self.get_lng: self.get_lng = '$.lng'
83
+ if not self.get_lat: self.get_lat = '$.lat'
84
+ else:
85
+ if not self.get_lng: self.get_lng = '$.[0]'
86
+ if not self.get_lat: self.get_lat = '$.[1]'
87
+ log.debug("get_lng is: {}, get_lat is: {}".format(self.get_lng, self.get_lat))
88
+ self.lng_parser = parser.parse(self.get_lng)
89
+ self.lat_parser = parser.parse(self.get_lat)
90
+ log.debug("is_json is: {}".format(self.is_json))
91
+ self.ret_tpl = ret_tpl
92
+ log.debug("ret_tpl is: {}".format(self.ret_tpl))
93
+ self.data = [Point(self.lng_parser.find(p)[0].value, self.lat_parser.find(p)[0].value, p) for p in self.points]
94
+
95
+ def _sparse_points(self, points):
96
+ """
97
+ 点位压缩
98
+ :return: 稀疏后的点集
99
+ """
100
+ if len(points) < 3:
101
+ if not self.ret_tpl:
102
+ return [points[0].origin_data, points[-1].origin_data]
103
+ else:
104
+ if self.is_json:
105
+ return [cjson.loads(self.ret_tpl.format(**points[0].origin_data)), cjson.loads(self.ret_tpl.format(**points[-1].origin_data))]
106
+ else:
107
+ return [cjson.loads(self.ret_tpl.format(lng=points[0].lng, lat=points[0].lat)), cjson.loads(self.ret_tpl.format(lng=points[-1].lng, lat=points[-1].lat))]
108
+
109
+ xy_first = points[0] # 第一个点
110
+ xy_end = points[-1] # 最后一个点
111
+ a, b, c = _get_line_by_point(xy_first, xy_end) # 获取直线方程的 a, b, c 值
112
+ d_max = 0 # 记录点到直线的最大距离
113
+ split = 0 # 分割位置
114
+ for i in range(1, len(points) - 1):
115
+ d = _get_distance_from_point_to_line(a, b, c, points[i])
116
+ if d > d_max:
117
+ split = i
118
+ d_max = d
119
+ if d_max > self.threshold:
120
+ # 如果存在点到首位点连成直线的距离大于 max_distance 的, 即需要再次划分
121
+ child_left = self._sparse_points(points[:split + 1]) # 递归处理左边部分
122
+ child_right = self._sparse_points(points[split:]) # 递归处理右边部分
123
+ # 合并结果,避免重复
124
+ return child_left + child_right[1:]
125
+ else:
126
+ if not self.ret_tpl:
127
+ return [points[0].origin_data, points[-1].origin_data]
128
+ else:
129
+ if self.is_json:
130
+ return [cjson.loads(self.ret_tpl.format(**points[0].origin_data)), cjson.loads(self.ret_tpl.format(**points[-1].origin_data))]
131
+ else:
132
+ return [cjson.loads(self.ret_tpl.format(lng=points[0].lng, lat=points[0].lat)), cjson.loads(self.ret_tpl.format(lng=points[-1].lng, lat=points[-1].lat))]
133
+
134
+ def sparse_points(self):
135
+ return self._sparse_points(self.data)
136
+
@@ -0,0 +1,57 @@
1
+ import os
2
+ from urllib.parse import urlencode
3
+
4
+ from bottle import static_file, HTTPResponse
5
+
6
+ from ctools import sys_log, http_utils
7
+
8
+ log = sys_log.flog
9
+
10
+ """
11
+ 文件下载服务
12
+ """
13
+
14
+
15
+ def download(file_path: str, download_name:str=None):
16
+ """
17
+ 文件下载
18
+ :param file_path: 静态文件路径
19
+ :param download_name: 下载文件名
20
+ :return:
21
+ """
22
+ if os.path.exists(file_path):
23
+ root_path = os.path.split(file_path)[0]
24
+ file_name = os.path.split(file_path)[1]
25
+ download_filename = urlencode({'filename': download_name or file_name}).split("=")[-1] # 对文件名进行URL编码
26
+ response = static_file(file_name, root=root_path, download=True)
27
+ # 设置响应头,告诉浏览器这是一个文件下载
28
+ response.headers['Content-Type'] = 'application/octet-stream;charset=utf-8'
29
+ response.headers['Content-Disposition'] = f'attachment; filename={download_filename}'
30
+ log.debug(f"下载文件成功, file_path: {file_path}, file_name: {file_name}, download_name: {download_name}")
31
+ else:
32
+ response = None
33
+ log.info("下载文件失败, 此文件不存在, file_path: %s" % file_path)
34
+ return response
35
+
36
+
37
+ def download_bytes(file_bytes: bytes, download_name: str):
38
+ """
39
+ 文件下载
40
+ :param file_bytes: file_bytes
41
+ :param download_name: download_name
42
+ :return:
43
+ """
44
+ download_filename = urlencode({'filename': download_name}).split("=")[-1] # 对文件名进行URL编码
45
+ # 设置响应头,告诉浏览器这是一个文件下载
46
+ headers = {"Accept-Ranges": "bytes", "Content-Length": len(file_bytes),
47
+ 'Content-Type': 'application/octet-stream;charset=utf-8',
48
+ 'Content-Disposition': f'attachment; filename={download_filename}'}
49
+ return HTTPResponse(file_bytes, **headers)
50
+
51
+
52
+ def download_url(url: str, save_path: str):
53
+ content = http_utils.get(url)
54
+ if content:
55
+ with open(save_path, "wb") as f:
56
+ f.write(content)
57
+ return save_path
ctools/enums.py ADDED
@@ -0,0 +1,4 @@
1
+ def value_of(e, v):
2
+ for member_name, member in e.__members__.items():
3
+ if member.value == v:
4
+ return member
ctools/ex.py ADDED
@@ -0,0 +1,31 @@
1
+ import time
2
+ import traceback
3
+ from functools import wraps
4
+
5
+ # annotation
6
+ def exception_handler(fail_return, retry_num=0, delay=3, catch_e=Exception, print_exc=False):
7
+ def decorator(func):
8
+ @wraps(func)
9
+ def wrapper(*args, **kwargs):
10
+ try:
11
+ return func(*args, **kwargs)
12
+ except catch_e as e:
13
+ print(f"{func.__name__} runtime exception: {str(e)}")
14
+ if print_exc: traceback.print_exc()
15
+ nonlocal retry_num
16
+ renum = retry_num
17
+ if renum == 0:
18
+ return fail_return
19
+ else:
20
+ while renum > 0:
21
+ time.sleep(delay)
22
+ renum -= 1
23
+ try:
24
+ return func(*args, **kwargs)
25
+ except catch_e:
26
+ pass
27
+ return fail_return
28
+
29
+ return wrapper
30
+
31
+ return decorator
ctools/excelOpt.py ADDED
@@ -0,0 +1,36 @@
1
+ from openpyxl import load_workbook
2
+ from openpyxl.worksheet.datavalidation import DataValidation
3
+
4
+
5
+ class excelUtil:
6
+ wb = None
7
+ sourcePath = None
8
+ savePath = None
9
+
10
+ def __init__(self, path, save_path):
11
+ # 创建一个 Workbook 对象
12
+ self.wb = load_workbook(path)
13
+ # 在 Workbook 中创建一个 Worksheet 对象
14
+ self.ws = self.wb.active
15
+ self.sourcePath = path
16
+ self.savePath = save_path
17
+
18
+ def makeDropData(self, col, drop_data):
19
+ # 定义下拉框的数据
20
+ dropdown_items = drop_data
21
+ # 将下拉框数据转换成字符串
22
+ dropdown_items_str = ','.join(dropdown_items)
23
+ # 在第一列中添加下拉框
24
+ dropdown_col = col
25
+ dropdown_start_row = 2
26
+ dropdown_end_row = 200
27
+ # 配置下拉框参数
28
+ dropdown = DataValidation(type="list", formula1=f'"{dropdown_items_str}"', allow_blank=True)
29
+ # 添加下拉框到指定的单元格区域
30
+ dropdown_range = f"{dropdown_col}{dropdown_start_row}:{dropdown_col}{dropdown_end_row}"
31
+ self.ws.add_data_validation(dropdown)
32
+ dropdown.add(dropdown_range)
33
+
34
+ def save(self):
35
+ # 保存工作簿
36
+ self.wb.save(self.savePath)
ctools/html_soup.py ADDED
@@ -0,0 +1,35 @@
1
+ from bs4 import BeautifulSoup
2
+
3
+ from ctools.ex import exception_handler
4
+
5
+
6
+ @exception_handler(fail_return=['解析错误'], print_exc=True)
7
+ def table2list(html, include_header=True, recursive_find=True,
8
+ table_tag='table', table_class=None, table_attrs: dict = {},
9
+ row_tag='tr', row_class=None, row_attrs: dict = {},
10
+ header_cell_tag='th', header_cell_class=None, header_cell_attrs: dict = {},
11
+ cell_tag='td', cell_class=None, cell_attrs: dict = {}):
12
+ soup = BeautifulSoup(markup=html, features='html.parser')
13
+ if table_class:
14
+ table = soup.find(table_tag, class_=table_class, **table_attrs)
15
+ else:
16
+ table = soup.find(table_tag, **table_attrs)
17
+ if row_class:
18
+ all_row = table.find_all(row_tag, class_=row_class, recursive=recursive_find, **row_attrs)
19
+ else:
20
+ all_row = table.find_all(row_tag, recursive=recursive_find, **row_attrs)
21
+ rows = []
22
+ if include_header:
23
+ if header_cell_class:
24
+ header = [i.text for i in all_row[0].find_all(header_cell_tag, class_=header_cell_class, recursive=recursive_find, **header_cell_attrs)]
25
+ else:
26
+ header = [i.text for i in all_row[0].find_all(header_cell_tag, recursive=recursive_find, **header_cell_attrs)]
27
+ rows.append(header)
28
+ for tr in all_row[1 if include_header else 0:]:
29
+ if cell_class:
30
+ td = tr.find_all(cell_tag, class_=cell_class, recursive=recursive_find, **cell_attrs)
31
+ else:
32
+ td = tr.find_all(cell_tag, recursive=recursive_find, **cell_attrs)
33
+ row = [i.text for i in td]
34
+ rows.append(row)
35
+ return rows
ctools/http_utils.py ADDED
@@ -0,0 +1,24 @@
1
+ import requests
2
+
3
+
4
+ def get(url, params=None, headers=None):
5
+ result = ""
6
+ try:
7
+ response = requests.get(url, params=params, headers=headers, timeout=60, verify=False)
8
+ response.raise_for_status()
9
+ if response.status_code == 200:
10
+ result = response.content
11
+ except Exception as e:
12
+ print("GET请求异常:", e)
13
+ if isinstance(result, bytes): return result.decode('utf-8')
14
+ return result
15
+
16
+
17
+ def post(url, data=None, json=None, headers=None, files=None):
18
+ result = ""
19
+ response = requests.post(url, data=data, json=json, files=files, headers=headers, timeout=60, verify=False)
20
+ response.raise_for_status()
21
+ if response.status_code == 200:
22
+ result = response.content
23
+ if isinstance(result, bytes): return result.decode('utf-8')
24
+ return result
ctools/images_tools.py ADDED
@@ -0,0 +1,27 @@
1
+ from io import BytesIO
2
+
3
+ from PIL import Image
4
+
5
+
6
+ def get_size(image_path):
7
+ return Image.open(image_path).size
8
+
9
+ def change_color(image_path, area=None, rgb_color=None):
10
+ """
11
+ 修改图片指定区域颜色
12
+ :param image_path: 图片路径
13
+ :param area: 修改区域: (x1, y1, x2, y2)
14
+ :param rgb_color: 入盘颜色 (255, 0, 0)
15
+ :return:
16
+ """
17
+ with Image.open(image_path) as img:
18
+ if area:
19
+ pixels = img.load()
20
+ for x in range(area[0], area[2]):
21
+ for y in range(area[1], area[3]):
22
+ pixels[x, y] = rgb_color
23
+ img_bytes = BytesIO()
24
+ img.save(img_bytes, format='JPEG')
25
+ img_binary = img_bytes.getvalue()
26
+ return img_binary
27
+
ctools/imgDialog.py ADDED
@@ -0,0 +1,44 @@
1
+ import tkinter
2
+ import tkinter as tk
3
+ from io import BytesIO
4
+ from tkinter import ttk
5
+
6
+ import requests
7
+ from PIL import Image, ImageTk
8
+
9
+ def showImageTip(root, title, imagePath, tips):
10
+ # 创建一个Tk对象
11
+ if root:
12
+ window = root
13
+ else:
14
+ window = tk.Tk()
15
+ # 设置窗口大小和位置
16
+ win_width = 400
17
+ win_height = 480
18
+ screen_width = window.winfo_screenwidth()
19
+ screen_height = window.winfo_screenheight()
20
+ x = int((screen_width - win_width) / 2)
21
+ y = int((screen_height - win_height) / 2)
22
+ window.geometry("{}x{}+{}+{}".format(win_width, win_height, x, y))
23
+
24
+ # 设置窗口大小和标题
25
+ window.title(title)
26
+
27
+ # 创建一个Label控件用于显示图片
28
+ resp = requests.get(imagePath)
29
+ image = Image.open(BytesIO(resp.content)) # 替换你自己的图片路径
30
+ image = image.resize((400, 400))
31
+ photo = ImageTk.PhotoImage(image)
32
+ label1 = ttk.Label(window, image=photo)
33
+ label1.pack(side=tkinter.TOP)
34
+
35
+ # 创建一个Label控件用于显示提示文字
36
+ label2 = ttk.Label(window, text=tips, font=("Arial Bold", 16))
37
+ label2.config(anchor='center', justify='center')
38
+ label2.pack(side=tkinter.BOTTOM)
39
+ # 显示窗口
40
+ window.mainloop()
41
+
42
+
43
+ if __name__ == '__main__':
44
+ showImageTip(root=None, title='在线授权', imagePath='https://blog.gomyck.com/img/pay-img/wechatPay2Me.jpg', tips='{}\n授权已失效,请联系微信:\n{}'.format(123, 123))
ctools/metrics.py ADDED
@@ -0,0 +1,131 @@
1
+ import os
2
+ import threading
3
+ from enum import Enum
4
+
5
+ from prometheus_client import Counter, Gauge, Summary, Histogram, start_http_server
6
+ from ctools import call, cjson, sys_log, work_path
7
+
8
+ log = sys_log.flog
9
+
10
+ _metrics_port = 8889
11
+ _persistent_json = {}
12
+ _metrics_initial = {}
13
+ _lock = threading.Lock()
14
+ _metrics_persistent_path = os.path.join(work_path.get_user_work_path('metrics', True), 'persistent.json')
15
+
16
+ # 认证中间件
17
+ # @app.before_request
18
+ # def check_authentication():
19
+ # auth = request.authorization
20
+ # if not auth or auth.username != USERNAME or auth.password != PASSWORD:
21
+ # return Response(
22
+ # "Unauthorized", 401, {"WWW-Authenticate": 'Basic realm="Login Required"'}
23
+ # )
24
+
25
+ class MetricType(Enum):
26
+ COUNTER = 'counter'
27
+ GAUGE = 'gauge'
28
+ SUMMARY = 'summary'
29
+ HISTOGRAM = 'histogram'
30
+
31
+ class Metric:
32
+ def __init__(self, metric_type: MetricType, metric_key: str, metric_labels: [], persistent: bool = False, buckets: [] = None, reset: bool = False, desc: str = ""):
33
+ self.metric_type = metric_type
34
+ self.metric_key = metric_key
35
+ self.metric_labels = metric_labels
36
+ self.buckets = buckets
37
+ self.metric = None
38
+ self.persistent = persistent
39
+ self.reset = reset
40
+ if metric_type == MetricType.COUNTER:
41
+ self.metric = Counter(metric_key, desc, metric_labels)
42
+ elif metric_type == MetricType.GAUGE:
43
+ self.metric = Gauge(metric_key, desc, metric_labels)
44
+ elif metric_type == MetricType.SUMMARY:
45
+ self.metric = Summary(metric_key, desc, metric_labels)
46
+ elif metric_type == MetricType.HISTOGRAM:
47
+ if buckets is None: raise Exception('histogram buckets can not empty')
48
+ self.metric = Histogram(metric_key, desc, metric_labels, buckets=self.buckets)
49
+ else:
50
+ raise Exception('metric type not found')
51
+ _metrics_initial[metric_key] = self
52
+
53
+ @call.once
54
+ def init(reset_persistent: bool = False):
55
+ if os.path.exists(_metrics_persistent_path) and not reset_persistent:
56
+ with open(_metrics_persistent_path, 'r') as persistent_file:
57
+ global _persistent_json
58
+ try:
59
+ content = persistent_file.readline()
60
+ log.info("persistent初始化: %s" % content)
61
+ _persistent_json = cjson.loads(content)
62
+ except Exception:
63
+ log.error('persistent.json is not valid json!!!!!')
64
+ _persistent_json = {}
65
+ _init_all_metrics()
66
+ _persistent_json = _persistent_json or {}
67
+ for key, item in _persistent_json.items():
68
+ metrics_key = key.split("-")[0]
69
+ if '_labels' in key or metrics_key not in _metrics_initial: continue
70
+ opt(metrics_key, _persistent_json[key + '_labels'], _persistent_json[key])
71
+ persistent_metrics()
72
+ start_http_server(port=_metrics_port)
73
+
74
+ @call.schd(5, start_by_call=True)
75
+ def persistent_metrics():
76
+ if _persistent_json and not _lock.locked():
77
+ log.info('begin persistent metrics to file...')
78
+ with open(_metrics_persistent_path, 'w') as persistent_file:
79
+ persistent_file.write(cjson.dumps(_persistent_json))
80
+ persistent_file.flush()
81
+
82
+ def opt(metric_key: str, label_values: [], metric_value: int):
83
+ _lock.acquire(timeout=5)
84
+ try:
85
+ persistent_key = "%s-%s" % (metric_key, "_".join(map(str, label_values)))
86
+ metric_entity: Metric = _metrics_initial[metric_key]
87
+ if metric_entity.persistent:
88
+ if not metric_entity.reset and persistent_key in _persistent_json:
89
+ _persistent_json[persistent_key] += metric_value
90
+ else:
91
+ _persistent_json[persistent_key] = metric_value
92
+ _persistent_json[persistent_key + '_labels'] = label_values
93
+
94
+ if _persistent_json[persistent_key] < 0:
95
+ _persistent_json[persistent_key] = 0
96
+ metric_value = 0
97
+
98
+ if metric_entity.metric_type == MetricType.COUNTER or metric_entity.metric_type == MetricType.GAUGE:
99
+ if label_values is None or len(label_values) == 0:
100
+ if metric_entity.metric_type == MetricType.COUNTER and metric_entity.reset:
101
+ metric_entity.metric.labels('').reset()
102
+ if metric_entity.metric_type == MetricType.GAUGE and metric_entity.reset:
103
+ metric_entity.metric.labels('').set(0)
104
+ metric_entity.metric.labels('').inc(metric_value)
105
+ else:
106
+ if metric_entity.reset:
107
+ if metric_entity.metric_type == MetricType.COUNTER and metric_entity.reset:
108
+ metric_entity.metric.labels(*label_values).reset()
109
+ if metric_entity.metric_type == MetricType.GAUGE and metric_entity.reset:
110
+ metric_entity.metric.labels(*label_values).set(0)
111
+ metric_entity.metric.labels(*label_values).inc(metric_value)
112
+ else:
113
+ if label_values is None or len(label_values) == 0:
114
+ metric_entity.metric.observe(metric_value)
115
+ else:
116
+ metric_entity.metric.labels(*label_values).observe(metric_value)
117
+ except Exception as e:
118
+ log.error("添加指标信息异常: %s" % e)
119
+ _lock.release()
120
+
121
+ def _init_all_metrics():
122
+ Metric(MetricType.GAUGE, 'gomyck', ['g_label1', 'g_label2'], persistent=True, reset=True)
123
+
124
+ # if __name__ == '__main__':
125
+ # init()
126
+ # import random
127
+ # while True:
128
+ # opt('data_reported_time', ['123', '123'], random.randint(1, 10))
129
+ # opt('data_received_time', ['123', '123'], random.randint(1, 10))
130
+ # opt('data_insert_time', ['123', '123'], random.randint(1, 10))
131
+ # time.sleep(1)