# import json # from pyecharts.globals import CurrentConfig # # CurrentConfig.ONLINE_HOST = "http://127.0.0.1:8000/assets/" import os, time, re, sys import requests import yaml import pyecharts.options as opts from pyecharts.charts import Line, Bar, Page from pyecharts.commons.utils import JsCode from pyecharts.globals import ThemeType def f_PrintfLog(logtext='', logflag='info'): '''打印日志,并写入日志文件''' print('%s [%s]: %s ' % ( time.strftime("%Y-%m-%d-%H.%M.%S", time.localtime()), logflag.upper()[0:4], logtext)) with open(LOGFILE, mode='a') as f: f.write('%s [%s]: %s \n' % ( time.strftime("%Y-%m-%d-%H.%M.%S", time.localtime()), logflag.upper()[0:4], logtext)) def f_syntax(): print('''Syntax: Pls run %s with 5 parameters 1. required 2. optional default:check_pqls.yaml 3. optional default:current time - 1 hour 4. optional default:current time 5. optional default:7 ''' % (sys.argv[0])) print('''Sample: python3 %s 172.1.1.1:9090 check_pqls.yaml 2022-10-31-10.00.00 2022-10-31-11.00.00 7 ''' % (sys.argv[0])) sys.exit(0) def to_unix_time(dt): '''本地时间转unix时间''' if ":" in dt: timeArray = time.strptime(dt, "%Y-%m-%d %H:%M:%S") else: timeArray = time.strptime(dt, "%Y-%m-%d-%H.%M.%S") unixtimestamp = time.mktime(timeArray) return unixtimestamp def to_local_time(unixtimestamp): '''unix时间转本地时间''' time_local = time.localtime(unixtimestamp) dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local) return dt # def local_time(unixtime): # # UnixTime转换成localtime # time_local = time.localtime(unixtime) # dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local) # return dt def bar_chart(title_str="", subtitle_str="", unit="", charttype="", pql_str="", data=[]): '''生成柱状图''' data_t = [] # lable处理 if charttype == "Histogram": chart_group_keys = re.findall('by\((.+?)\)', pql_str.replace(' ', '')) chart_key_lst = chart_group_keys[0].split(',') chart_key_lst.remove('le') if len(chart_key_lst) > 0: chart_key = chart_key_lst[0] else: chart_key = 'total' elif 'by_' in title_str: chart_key = title_str.split('by_')[1] else: chart_key = 'total' # print(pql_str) # print(chart_key) # print(len(data), data) bar = Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT)) i = 0 for d in data: # print(type(d), d) data_t.append(d['metric'].get(chart_key, 'Total')) data_x = [] data_y = [] data_y0 = [] for y in d['values']: # x时间轴 tm = to_local_time(y[0]) data_x.append(tm) if i <= 1: bar.add_xaxis(xaxis_data=data_x) # print('data_x:', data_x) # # y轴类型 # bar.add_yaxis(d['metric'].get(chart_key, 'Total'), data_y0, stack="stack1", category_gap="20%") # print('data_y0:', len(data_y0), data_y0) i += 1 # y轴数值 if y[1] == "NaN": v0 = 0 elif unit == "1": v0 = int(float(y[1])) elif unit == "ms": v0 = round(float(y[1])*10000/10.0, 1) if '(ms)' not in title_str: title_str = title_str + '(ms)' elif unit == "µs": v0 = round(float(y[1])*1000*10000/10.0, 1) if '(µs)' not in title_str: title_str = title_str + '(µs)' elif unit == "s": v0 = round(float(y[1]), 1) if '(s)' not in title_str: title_str = title_str + '(s)' elif unit == "%": v0 = round(float(y[1])*10000/100.0, 2) elif unit == "Bytes": v0 = round(float(y[1])/1024/1024, 3) if '(MB)' not in title_str: title_str = title_str + '(MB)' else: v0 = int(y[1]) data_y0.append(int(float(v0))) # print("data_t:", data_t) # print("data_x:", data_x) # print("data_y0", data_y0) # y轴类型 bar.add_yaxis(d['metric'].get(chart_key, 'Total'), data_y0, stack="stack1", category_gap="20%") # print('data_y0:', len(data_y0), data_y0) bar.set_global_opts(xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-25)), title_opts=opts.TitleOpts(title=title_str, subtitle=subtitle_str, pos_left="left", pos_top="top"), legend_opts=opts.LegendOpts(type_="scroll", orient="vertical", pos_top=55, pos_right=5), tooltip_opts=opts.TooltipOpts(is_show=True), datazoom_opts=[opts.DataZoomOpts(range_start=50, range_end=100)]) bar.set_series_opts( label_opts=opts.LabelOpts( is_show=False, position="inside", # formatter=JsCode( # "function(x){return Number(x.data.percent * 100).toFixed() + '%';}" # ), ) ) # bar.render("%s.html" % (title_str)) return bar # f_PrintfLog("generate chart %s finish" % (title_str)) def line_chart(title_str="", subtitle_str="", unit="", charttype="", pql_str="", data=[]): '''生成折线图''' data_t = [] # lable处理 if charttype == "Histogram": chart_group_keys = re.findall('by\((.+?)\)', pql_str.replace(' ', '')) chart_key_lst = chart_group_keys[0].split(',') chart_key_lst.remove('le') if len(chart_key_lst) > 0: chart_key = chart_key_lst[0] else: chart_key = 'total' elif 'by_' in title_str: chart_key = title_str.split('by_')[1] else: chart_key = 'total' # print(pql_str) # print(chart_key) # print(len(data), data) line = Line() i = 0 for d in data: # print(type(d), d) # 多个label值处理,组合 if '&' in chart_key: # print(chart_key.split('&')) for i in range(len(chart_key.split('&'))): if i == 0: y_legend = d['metric'].get(chart_key.split('&')[i], 'Total') else: y_legend = y_legend + '-' + d['metric'].get(chart_key.split('&')[i], 'Total') else: y_legend = d['metric'].get(chart_key, 'Total') data_t.append(y_legend) data_x = [] data_y = [] data_y0 = [] for y in d['values']: # x时间轴 tm = to_local_time(y[0]) data_x.append(tm) if i <= 1: line.add_xaxis(xaxis_data=data_x) # # y轴类型 # line.add_yaxis(y_legend, data_y0, is_smooth=False) i += 1 # y轴数值 if y[1] == "NaN": v0 = 0 elif unit == "1": v0 = int(float(y[1])) elif unit == "ms": v0 = round(float(y[1])*10000/10.0, 1) if '(ms)' not in title_str: title_str = title_str + '(ms)' elif unit == "µs": v0 = round(float(y[1])*1000*10000/10.0, 1) if '(µs)' not in title_str: title_str = title_str + '(µs)' elif unit == "s": v0 = round(float(y[1]), 1) if '(s)' not in title_str: title_str = title_str + '(s)' elif unit == "%": v0 = round(float(y[1])*10000/100.0, 2) elif unit == "Bytes": v0 = round(float(y[1])/1024.0/1024, 3) if '(MB)' not in title_str: title_str = title_str + '(MB)' else: v0 = int(y[1]) data_y0.append(int(float(v0))) # print("data_t:", data_t) # print("data_x:", data_x) # print("data_y0", data_y0) # y轴类型 line.add_yaxis(y_legend, data_y0, is_smooth=True) line.set_global_opts(xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-25)), title_opts=opts.TitleOpts(title=title_str, subtitle=subtitle_str, pos_left="left", pos_top="top"), legend_opts=opts.LegendOpts(type_="scroll", pos_right=25, orient="vertical", padding=[95, 5, 5, 5]), tooltip_opts=opts.TooltipOpts(is_show=True), datazoom_opts=[opts.DataZoomOpts(range_start=50, range_end=100)] ) line.set_series_opts(label_opts=opts.LabelOpts(is_show=False) ) # line.render("%s.html" % (title_str)) return line # f_PrintfLog("generate chart %s finish" % (title_str)) def get_chartid(htmlfile=""): """获取html文件的图形id""" f_PrintfLog("start to get chartid from %s." % (htmlfile)) with open(htmlfile, mode='r') as fd: chart_id_dict = {} chart_title_dict = {} for line in fd: if re.search('
', line): chart_id = re.findall('
': heading_text = '

性能图表目录

\n%s\n
    ' % (line) fw.write(heading_text) for chart_id in chart_id_name_dict: heading_lst = chart_id_name_dict[chart_id].split(':') if len(heading_lst) > 3: heading_text = '''
\n
  • %s
    • %s
    • ''' % (heading_lst[-1].upper(), heading_lst[0], heading_lst[-1].lower(), heading_lst[1]) else: heading_text = '''
    • %s
    • \n''' % ( heading_lst[-1].lower(), heading_lst[0]) fw.write(heading_text) fw.write('
    \n') # 判断id处是否添加标题 elif re.search('
    ', line): chart_id = re.findall('
    3: heading_text = '''


    %s

    %s

    TOP
    %s''' % (heading_lst[-1].upper(), heading_lst[0], heading_lst[-1].lower(), heading_lst[1], line) else: heading_text = '''

    %s

    TOP
    %s''' % (heading_lst[-1].lower(), heading_lst[0], line) fw.write(heading_text) else: fw.write(line) else: fw.write(line) fr.close() fw.close() def get_all_metrics(configfile="", reportfile="", pqlstart="", pqlend=""): """获取所有的监控数据""" # 读取YAML配置文件中的PQLS,转换为DICT with open(configfile, mode='r') as f: chart_lst = yaml.safe_load(f) # print(type(chart_lst), len(chart_lst)) # 生成页面Page page = Page(layout=Page.SimplePageLayout) # 依次处理yaml文件的各个指标 for chart_item in chart_lst: if chart_item.get('stats', "enable") == "disable": # f_PrintfLog("skip %s" %(chart_item["chartTitle"])) continue f_PrintfLog("start to create chart %s." % (chart_item["name"])) chart_title = chart_item["chartTitle"] chart_subtitle = chart_item["dashboard"][0]['title'] chart_type = chart_item.get('chartType', "line").lower() chart_unit = chart_item.get('unit', "1") chart_metricstype = chart_item.get('metricsType', "Counter") pql = chart_item["expr"] f_PrintfLog("get data from prometheus:%s" %(pql), "debug") # try: response = requests.get('http://%s/api/v1/query_range' % prometheus_address, params={'query': pql, 'start': pqlstart, 'end': pqlend, 'step': '1m'}) # print(type(response), type(response.json()), response.json()) if response.json()['status'] == 'success': if chart_type == "line": chart = line_chart(chart_title, chart_subtitle, chart_unit, chart_metricstype, pql, response.json()['data']['result']) elif chart_type == "bar": # continue chart = bar_chart(chart_title, chart_subtitle, chart_unit, chart_metricstype, pql, response.json()['data']['result']) page.add(chart) else: f_PrintfLog("run qpls error, %s" % (response)) f_PrintfLog(response.json()) f_PrintfLog(pql) # 生成图形 page.render('tidb_temp.html') # 格式化html,添加需要注入的标题 chart_title_dict = get_chartid('tidb_temp.html') global chart_id_name_dict chart_id_name_dict = {} i = 0 for chart_item in chart_lst: chart_name = chart_item['name'] chart_title = chart_item["chartTitle"] if len(chart_name.split(':')) > 2: i += 1 title_h1 = chart_name.split(':')[0] title_h2 = chart_name.split(':')[1] # print('HHHH1:', chart_name, title_h1, title_h2, chart_title_dict[chart_title]) chart_id_name_dict[chart_title_dict[chart_title]] = "%s:A%s" % (chart_name, i) else: if title_h2 != chart_name.split(':')[0]: i += 1 title_h2 = chart_name.split(':')[0] # print('HH2:', chart_name, title_h2, chart_title_dict[chart_title]) chart_id_name_dict[chart_title_dict[chart_title]] = "%s:a%s" % (chart_name, i) else: continue # print('HH3:', chart_name, title_h2, chart_title_dict[chart_title]) # print(chart_id_name_dict) format_html('tidb_temp.html', reportfile) if __name__ == "__main__": # 环境变量初始化 SCRIPTNAME = os.path.basename(__file__) WORKDIR = os.getcwd() LOGDIR = "%s/logs" % (WORKDIR) RUNTS = time.strftime("%Y%m%d.%H%M%S", time.localtime()) LOGFILE = "%s/logs/%s_%s.log" % (WORKDIR, SCRIPTNAME, RUNTS) if not os.path.exists(LOGDIR): os.mkdir(LOGDIR) # 脚本入参处理 script_input = ' '.join(sys.argv) if 'help' in script_input.lower(): f_syntax() # 判断输入参数,确定时间范围 if len(sys.argv) >= 6: prometheus_address = sys.argv[1] chart_config_yaml = sys.argv[2] check_starttime = sys.argv[3] check_endtime = sys.argv[4] check_subtract_days = sys.argv[5] elif len(sys.argv) >= 2: prometheus_address = sys.argv[1] # prometheus_address = "172.1.1.1:9090" cur_time = time.time() chart_config_yaml = "check_pqls.yaml" check_starttime = time.strftime("%Y-%m-%d-%H.%M.%S", time.localtime(cur_time - 3600)) check_endtime = time.strftime("%Y-%m-%d-%H.%M.%S", time.localtime(cur_time)) check_subtract_days = 7 else: f_syntax() # 格式化参数 # 第一份报表为当前时间范围 cur_starttime = to_unix_time(check_starttime) cur_endtime = to_unix_time(check_endtime) cur_endtime_local = time.strftime("%Y%m%d_%H%M%S", time.localtime(cur_endtime)) cur_reportfile = "TiDB_metrics_page_tidb_%s.html" % (cur_endtime_local) f_PrintfLog("PQL query time range is:%s - %s" % (check_starttime, check_endtime)) f_PrintfLog("Html report is:%s" % (cur_reportfile)) get_all_metrics(chart_config_yaml, cur_reportfile, cur_starttime, cur_endtime) # 第二份报表为历史时间范围 his_starttime = to_unix_time(check_starttime) - 3600*24*int(check_subtract_days) his_endtime = to_unix_time(check_endtime) - 3600*24*int(check_subtract_days) his_starttime_local = time.strftime("%Y%m%d_%H%M%S", time.localtime(his_starttime)) his_endtime_local = time.strftime("%Y%m%d_%H%M%S", time.localtime(his_endtime)) his_reportfile = "TiDB_metrics_page_tidb_%s.html" % (his_endtime_local) f_PrintfLog("PQL query time range is:%s - %s" % (his_starttime_local, his_endtime_local)) f_PrintfLog("Html report is:%s" % (his_reportfile)) # get_all_metrics(chart_config_yaml, his_reportfile, his_starttime, his_endtime)