Skip to content

Commit 8e631b3

Browse files
Import dumped Prometheus metric data to an influxdb instance (#32)
This feature can re-import dumped Prometheus metric data into an influxdb instance, thus we can then use that influxdb as a remote_read storage backend to analysis data offline.
1 parent e31b848 commit 8e631b3

File tree

5 files changed

+137
-5
lines changed

5 files changed

+137
-5
lines changed

insight.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from file import configfiles
2727
from file import logfiles
2828
from metric import prometheus
29+
from metric.importer import prometheus as import_prom
2930
from runtime import perf
3031
from tidb import pdctl
3132
from utils import fileopt
@@ -291,6 +292,12 @@ def dump_metrics(self, args):
291292
you find certain data missing or empty in result, please try
292293
to run this script again with root.""")
293294

295+
# re-import dumped data
296+
if args.subcmd == 'metric' and args.subcmd_metric == "load":
297+
insight_importer = import_prom.PromDump(args)
298+
insight_importer.run_importing()
299+
exit(0)
300+
294301
insight = Insight(args)
295302

296303
try:

metric/importer/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# -*- coding: utf-8 -*-

metric/importer/prometheus.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# -*- coding: utf-8 -*-
2+
# Re-import dumped Prometheus metric data (in plain or compressed
3+
# JSON format) to a local influxdb instance.
4+
# Imported data will be put into a dedicated (or specified) database
5+
# for futher analysis.
6+
7+
import datetime
8+
import influxdb
9+
import json
10+
import logging
11+
import os
12+
import random
13+
import string
14+
import zlib
15+
16+
from utils import fileopt
17+
from utils import util
18+
19+
20+
class PromDump():
21+
def __init__(self, args):
22+
# if db_name, else db_name = prom_dump_`date`
23+
self.host = args.host if args.host else 'localhost'
24+
self.port = args.port if args.port else 8086
25+
self.datadir = args.dir if args.dir else 'data'
26+
self.db_name = args.db if args.db else self.unique_dbname()
27+
self.user = args.user
28+
self.passwd = args.passwd
29+
30+
# unique_dbname() generates a unique database name for importing, to prevents
31+
# overwritting of previous imported data
32+
def unique_dbname(self):
33+
dbname = []
34+
# universal prefix
35+
dbname.append('tidb_insight_prom')
36+
# current time
37+
dbname.append(datetime.datetime.now().strftime("%Y%m%d%H%M"))
38+
# a 4 digits random string
39+
dbname.append(''.join(random.choice(
40+
string.ascii_lowercase + string.digits) for _ in range(4)))
41+
42+
return '_'.join(dbname)
43+
44+
def load_dump(self):
45+
def file_list(dir=None):
46+
f_list = []
47+
for file in fileopt.list_dir(dir):
48+
if os.path.isdir(file):
49+
f_list += file_list(file)
50+
else:
51+
f_list.append(file)
52+
return f_list
53+
54+
for file in file_list(self.datadir):
55+
if file.endswith('.json'):
56+
raw = fileopt.read_file(file)
57+
elif file.endswith('.dat'):
58+
raw = zlib.decompress(fileopt.read_file(file, 'rb'))
59+
else:
60+
logging.debug("Skipped unrecorgnized file '%s'" % file)
61+
continue
62+
yield json.loads(raw)
63+
64+
def build_series(self):
65+
def format_prom_metric(key=None):
66+
points = []
67+
point = {'fields': {}}
68+
# build point header
69+
for metric in key:
70+
point['measurement'] = metric['metric']['__name__']
71+
point['tags'] = {
72+
'cluster': self.db_name,
73+
'monitor': 'prometheus',
74+
}
75+
for k, v in metric['metric'].items():
76+
point['tags'][k] = v
77+
# build point values
78+
for value in metric['values']:
79+
point['time'] = datetime.datetime.utcfromtimestamp(
80+
value[0]).strftime('%Y-%m-%dT%H:%M:%SZ')
81+
try:
82+
point['fields']['value'] = float(value[1])
83+
except ValueError:
84+
point['fields']['value'] = value[1]
85+
points.append(point.copy())
86+
return points
87+
88+
for key in self.load_dump():
89+
yield format_prom_metric(key)
90+
91+
def write2influxdb(self):
92+
client = influxdb.InfluxDBClient(
93+
host=self.host, port=self.port, username=self.user, password=self.passwd,
94+
database=self.db_name, timeout=30)
95+
# create_database has no effect if the database already exist
96+
client.create_database(self.db_name)
97+
logging.info("Metrics will be imported to database '%s'." %
98+
self.db_name)
99+
100+
for series in self.build_series():
101+
try:
102+
client.write_points(series, batch_size=2000)
103+
except influxdb.exceptions.InfluxDBClientError as e:
104+
logging.warn(
105+
"Write error for key '%s', data may be empty." % series[0]['measurement'])
106+
logging.debug(e)
107+
108+
def run_importing(self):
109+
self.write2influxdb()

utils/fileopt.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010
# read data from file
1111

1212

13-
def read_file(filename):
13+
def read_file(filename, mode='r'):
1414
data = None
15-
with open(filename, 'r') as f:
15+
with open(filename, mode) as f:
1616
data = f.read()
1717
f.close()
1818
return data
1919

2020

21-
# write data to file, in plain text
22-
def write_file(filename, data):
23-
with open(filename, 'w') as f:
21+
# write data to file
22+
def write_file(filename, data, mode='w'):
23+
with open(filename, mode) as f:
2424
try:
2525
f.write(str(data, 'utf-8'))
2626
except TypeError:

utils/util.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,21 @@ def parse_insight_opts():
197197
help="Query resolution step width of Prometheus in seconds, 15.0 by default.")
198198
parser_prom.add_argument("--compress", action="store_true", default=False,
199199
help="Compress dumped JSON file, disabled by default. If compressed, the dumped file won't be able to read directly.")
200+
201+
parser_load = subparser_metric.add_parser(
202+
"load", help="Load dumped metrics to local influxdb.")
203+
parser_load.add_argument("--host", action="store", default=None,
204+
help="The host of local influxdb, `localhost` by default.")
205+
parser_load.add_argument("--port", type=int, action="store",
206+
default=None, help="The port of local TSDB, `8086` by default.")
207+
parser_load.add_argument("--dir", action="store", default=None,
208+
help="The directory of dumped data, `data` by default.")
209+
parser_load.add_argument("--db", action="store", default=None,
210+
help="The database of imported metrics, if not set, a unique name will be auto generated by default.")
211+
parser_load.add_argument("--user", action="store", default=None,
212+
help="The user with priviledge to create database, empty (no authentication needed) by default.")
213+
parser_load.add_argument("--passwd", action="store", default=None,
214+
help="The password of user, empty (no authentication needed) by default.")
200215
####
201216

202217
return parser.parse_args()

0 commit comments

Comments
 (0)