backend_prometheus.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #define BACKENDS_INTERNALS
  3. #include "backend_prometheus.h"
  4. // ----------------------------------------------------------------------------
  5. // PROMETHEUS
  6. // /api/v1/allmetrics?format=prometheus and /api/v1/allmetrics?format=prometheus_all_hosts
  7. static struct prometheus_server {
  8. const char *server;
  9. uint32_t hash;
  10. RRDHOST *host;
  11. time_t last_access;
  12. struct prometheus_server *next;
  13. } *prometheus_server_root = NULL;
  14. static inline time_t prometheus_server_last_access(const char *server, RRDHOST *host, time_t now) {
  15. static netdata_mutex_t prometheus_server_root_mutex = NETDATA_MUTEX_INITIALIZER;
  16. uint32_t hash = simple_hash(server);
  17. netdata_mutex_lock(&prometheus_server_root_mutex);
  18. struct prometheus_server *ps;
  19. for(ps = prometheus_server_root; ps ;ps = ps->next) {
  20. if (host == ps->host && hash == ps->hash && !strcmp(server, ps->server)) {
  21. time_t last = ps->last_access;
  22. ps->last_access = now;
  23. netdata_mutex_unlock(&prometheus_server_root_mutex);
  24. return last;
  25. }
  26. }
  27. ps = callocz(1, sizeof(struct prometheus_server));
  28. ps->server = strdupz(server);
  29. ps->hash = hash;
  30. ps->host = host;
  31. ps->last_access = now;
  32. ps->next = prometheus_server_root;
  33. prometheus_server_root = ps;
  34. netdata_mutex_unlock(&prometheus_server_root_mutex);
  35. return 0;
  36. }
  37. static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable) {
  38. size_t n;
  39. for(n = 0; *s && n < usable ; d++, s++, n++) {
  40. register char c = *s;
  41. if(!isalnum(c)) *d = '_';
  42. else *d = c;
  43. }
  44. *d = '\0';
  45. return n;
  46. }
  47. static inline size_t prometheus_label_copy(char *d, const char *s, size_t usable) {
  48. size_t n;
  49. // make sure we can escape one character without overflowing the buffer
  50. usable--;
  51. for(n = 0; *s && n < usable ; d++, s++, n++) {
  52. register char c = *s;
  53. if(unlikely(c == '"' || c == '\\' || c == '\n')) {
  54. *d++ = '\\';
  55. n++;
  56. }
  57. *d = c;
  58. }
  59. *d = '\0';
  60. return n;
  61. }
  62. static inline char *prometheus_units_copy(char *d, const char *s, size_t usable, int showoldunits) {
  63. const char *sorig = s;
  64. char *ret = d;
  65. size_t n;
  66. // Fix for issue 5227
  67. if (unlikely(showoldunits)) {
  68. static struct {
  69. const char *newunit;
  70. uint32_t hash;
  71. const char *oldunit;
  72. } units[] = {
  73. {"KiB/s", 0, "kilobytes/s"}
  74. , {"MiB/s", 0, "MB/s"}
  75. , {"GiB/s", 0, "GB/s"}
  76. , {"KiB" , 0, "KB"}
  77. , {"MiB" , 0, "MB"}
  78. , {"GiB" , 0, "GB"}
  79. , {"inodes" , 0, "Inodes"}
  80. , {"percentage" , 0, "percent"}
  81. , {"faults/s" , 0, "page faults/s"}
  82. , {"KiB/operation", 0, "kilobytes per operation"}
  83. , {"milliseconds/operation", 0, "ms per operation"}
  84. , {NULL, 0, NULL}
  85. };
  86. static int initialized = 0;
  87. int i;
  88. if(unlikely(!initialized)) {
  89. for (i = 0; units[i].newunit; i++)
  90. units[i].hash = simple_hash(units[i].newunit);
  91. initialized = 1;
  92. }
  93. uint32_t hash = simple_hash(s);
  94. for(i = 0; units[i].newunit ; i++) {
  95. if(unlikely(hash == units[i].hash && !strcmp(s, units[i].newunit))) {
  96. // info("matched extension for filename '%s': '%s'", filename, last_dot);
  97. s=units[i].oldunit;
  98. sorig = s;
  99. break;
  100. }
  101. }
  102. }
  103. *d++ = '_';
  104. for(n = 1; *s && n < usable ; d++, s++, n++) {
  105. register char c = *s;
  106. if(!isalnum(c)) *d = '_';
  107. else *d = c;
  108. }
  109. if(n == 2 && sorig[0] == '%') {
  110. n = 0;
  111. d = ret;
  112. s = "_percent";
  113. for( ; *s && n < usable ; n++) *d++ = *s++;
  114. }
  115. else if(n > 3 && sorig[n-3] == '/' && sorig[n-2] == 's') {
  116. n = n - 2;
  117. d -= 2;
  118. s = "_persec";
  119. for( ; *s && n < usable ; n++) *d++ = *s++;
  120. }
  121. *d = '\0';
  122. return ret;
  123. }
  124. #define PROMETHEUS_ELEMENT_MAX 256
  125. #define PROMETHEUS_LABELS_MAX 1024
  126. #define PROMETHEUS_VARIABLE_MAX 256
  127. struct host_variables_callback_options {
  128. RRDHOST *host;
  129. BUFFER *wb;
  130. BACKEND_OPTIONS backend_options;
  131. PROMETHEUS_OUTPUT_OPTIONS output_options;
  132. const char *prefix;
  133. const char *labels;
  134. time_t now;
  135. int host_header_printed;
  136. char name[PROMETHEUS_VARIABLE_MAX+1];
  137. };
  138. static int print_host_variables(RRDVAR *rv, void *data) {
  139. struct host_variables_callback_options *opts = data;
  140. if(rv->options & (RRDVAR_OPTION_CUSTOM_HOST_VAR|RRDVAR_OPTION_CUSTOM_CHART_VAR)) {
  141. if(!opts->host_header_printed) {
  142. opts->host_header_printed = 1;
  143. if(opts->output_options & PROMETHEUS_OUTPUT_HELP) {
  144. buffer_sprintf(opts->wb, "\n# COMMENT global host and chart variables\n");
  145. }
  146. }
  147. calculated_number value = rrdvar2number(rv);
  148. if(isnan(value) || isinf(value)) {
  149. if(opts->output_options & PROMETHEUS_OUTPUT_HELP)
  150. buffer_sprintf(opts->wb, "# COMMENT variable \"%s\" is %s. Skipped.\n", rv->name, (isnan(value))?"NAN":"INF");
  151. return 0;
  152. }
  153. char *label_pre = "";
  154. char *label_post = "";
  155. if(opts->labels && *opts->labels) {
  156. label_pre = "{";
  157. label_post = "}";
  158. }
  159. prometheus_name_copy(opts->name, rv->name, sizeof(opts->name));
  160. if(opts->output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  161. buffer_sprintf(opts->wb
  162. , "%s_%s%s%s%s " CALCULATED_NUMBER_FORMAT " %llu\n"
  163. , opts->prefix
  164. , opts->name
  165. , label_pre
  166. , opts->labels
  167. , label_post
  168. , value
  169. , ((rv->last_updated) ? rv->last_updated : opts->now) * 1000ULL
  170. );
  171. else
  172. buffer_sprintf(opts->wb, "%s_%s%s%s%s " CALCULATED_NUMBER_FORMAT "\n"
  173. , opts->prefix
  174. , opts->name
  175. , label_pre
  176. , opts->labels
  177. , label_post
  178. , value
  179. );
  180. return 1;
  181. }
  182. return 0;
  183. }
  184. static void rrd_stats_api_v1_charts_allmetrics_prometheus(RRDHOST *host, BUFFER *wb, const char *prefix, BACKEND_OPTIONS backend_options, time_t after, time_t before, int allhosts, PROMETHEUS_OUTPUT_OPTIONS output_options) {
  185. rrdhost_rdlock(host);
  186. char hostname[PROMETHEUS_ELEMENT_MAX + 1];
  187. prometheus_label_copy(hostname, host->hostname, PROMETHEUS_ELEMENT_MAX);
  188. char labels[PROMETHEUS_LABELS_MAX + 1] = "";
  189. if(allhosts) {
  190. if(output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  191. buffer_sprintf(wb, "netdata_info{instance=\"%s\",application=\"%s\",version=\"%s\"} 1 %llu\n", hostname, host->program_name, host->program_version, now_realtime_usec() / USEC_PER_MS);
  192. else
  193. buffer_sprintf(wb, "netdata_info{instance=\"%s\",application=\"%s\",version=\"%s\"} 1\n", hostname, host->program_name, host->program_version);
  194. if(host->tags && *(host->tags)) {
  195. if(output_options & PROMETHEUS_OUTPUT_TIMESTAMPS) {
  196. buffer_sprintf(wb, "netdata_host_tags_info{instance=\"%s\",%s} 1 %llu\n", hostname, host->tags, now_realtime_usec() / USEC_PER_MS);
  197. // deprecated, exists only for compatibility with older queries
  198. buffer_sprintf(wb, "netdata_host_tags{instance=\"%s\",%s} 1 %llu\n", hostname, host->tags, now_realtime_usec() / USEC_PER_MS);
  199. }
  200. else {
  201. buffer_sprintf(wb, "netdata_host_tags_info{instance=\"%s\",%s} 1\n", hostname, host->tags);
  202. // deprecated, exists only for compatibility with older queries
  203. buffer_sprintf(wb, "netdata_host_tags{instance=\"%s\",%s} 1\n", hostname, host->tags);
  204. }
  205. }
  206. snprintfz(labels, PROMETHEUS_LABELS_MAX, ",instance=\"%s\"", hostname);
  207. }
  208. else {
  209. if(output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  210. buffer_sprintf(wb, "netdata_info{instance=\"%s\",application=\"%s\",version=\"%s\"} 1 %llu\n", hostname, host->program_name, host->program_version, now_realtime_usec() / USEC_PER_MS);
  211. else
  212. buffer_sprintf(wb, "netdata_info{instance=\"%s\",application=\"%s\",version=\"%s\"} 1\n", hostname, host->program_name, host->program_version);
  213. if(host->tags && *(host->tags)) {
  214. if(output_options & PROMETHEUS_OUTPUT_TIMESTAMPS) {
  215. buffer_sprintf(wb, "netdata_host_tags_info{%s} 1 %llu\n", host->tags, now_realtime_usec() / USEC_PER_MS);
  216. // deprecated, exists only for compatibility with older queries
  217. buffer_sprintf(wb, "netdata_host_tags{%s} 1 %llu\n", host->tags, now_realtime_usec() / USEC_PER_MS);
  218. }
  219. else {
  220. buffer_sprintf(wb, "netdata_host_tags_info{%s} 1\n", host->tags);
  221. // deprecated, exists only for compatibility with older queries
  222. buffer_sprintf(wb, "netdata_host_tags{%s} 1\n", host->tags);
  223. }
  224. }
  225. }
  226. // send custom variables set for the host
  227. if(output_options & PROMETHEUS_OUTPUT_VARIABLES){
  228. struct host_variables_callback_options opts = {
  229. .host = host,
  230. .wb = wb,
  231. .labels = (labels[0] == ',')?&labels[1]:labels,
  232. .backend_options = backend_options,
  233. .output_options = output_options,
  234. .prefix = prefix,
  235. .now = now_realtime_sec(),
  236. .host_header_printed = 0
  237. };
  238. foreach_host_variable_callback(host, print_host_variables, &opts);
  239. }
  240. // for each chart
  241. RRDSET *st;
  242. rrdset_foreach_read(st, host) {
  243. char chart[PROMETHEUS_ELEMENT_MAX + 1];
  244. char context[PROMETHEUS_ELEMENT_MAX + 1];
  245. char family[PROMETHEUS_ELEMENT_MAX + 1];
  246. char units[PROMETHEUS_ELEMENT_MAX + 1] = "";
  247. prometheus_label_copy(chart, (output_options & PROMETHEUS_OUTPUT_NAMES && st->name)?st->name:st->id, PROMETHEUS_ELEMENT_MAX);
  248. prometheus_label_copy(family, st->family, PROMETHEUS_ELEMENT_MAX);
  249. prometheus_name_copy(context, st->context, PROMETHEUS_ELEMENT_MAX);
  250. if(likely(backends_can_send_rrdset(backend_options, st))) {
  251. rrdset_rdlock(st);
  252. int as_collected = (BACKEND_OPTIONS_DATA_SOURCE(backend_options) == BACKEND_SOURCE_DATA_AS_COLLECTED);
  253. int homogeneous = 1;
  254. if(as_collected) {
  255. if(rrdset_flag_check(st, RRDSET_FLAG_HOMEGENEOUS_CHECK))
  256. rrdset_update_heterogeneous_flag(st);
  257. if(rrdset_flag_check(st, RRDSET_FLAG_HETEROGENEOUS))
  258. homogeneous = 0;
  259. }
  260. else {
  261. if(BACKEND_OPTIONS_DATA_SOURCE(backend_options) == BACKEND_SOURCE_DATA_AVERAGE && !(output_options & PROMETHEUS_OUTPUT_HIDEUNITS))
  262. prometheus_units_copy(units, st->units, PROMETHEUS_ELEMENT_MAX, output_options & PROMETHEUS_OUTPUT_OLDUNITS);
  263. }
  264. if(unlikely(output_options & PROMETHEUS_OUTPUT_HELP))
  265. buffer_sprintf(wb, "\n# COMMENT %s chart \"%s\", context \"%s\", family \"%s\", units \"%s\"\n"
  266. , (homogeneous)?"homogeneous":"heterogeneous"
  267. , (output_options & PROMETHEUS_OUTPUT_NAMES && st->name) ? st->name : st->id
  268. , st->context
  269. , st->family
  270. , st->units
  271. );
  272. // for each dimension
  273. RRDDIM *rd;
  274. rrddim_foreach_read(rd, st) {
  275. if(rd->collections_counter && !rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) {
  276. char dimension[PROMETHEUS_ELEMENT_MAX + 1];
  277. char *suffix = "";
  278. if (as_collected) {
  279. // we need as-collected / raw data
  280. if(unlikely(rd->last_collected_time.tv_sec < after))
  281. continue;
  282. const char *t = "gauge", *h = "gives";
  283. if(rd->algorithm == RRD_ALGORITHM_INCREMENTAL ||
  284. rd->algorithm == RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL) {
  285. t = "counter";
  286. h = "delta gives";
  287. suffix = "_total";
  288. }
  289. if(homogeneous) {
  290. // all the dimensions of the chart, has the same algorithm, multiplier and divisor
  291. // we add all dimensions as labels
  292. prometheus_label_copy(dimension, (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id, PROMETHEUS_ELEMENT_MAX);
  293. if(unlikely(output_options & PROMETHEUS_OUTPUT_HELP))
  294. buffer_sprintf(wb
  295. , "# COMMENT %s_%s%s: chart \"%s\", context \"%s\", family \"%s\", dimension \"%s\", value * " COLLECTED_NUMBER_FORMAT " / " COLLECTED_NUMBER_FORMAT " %s %s (%s)\n"
  296. , prefix
  297. , context
  298. , suffix
  299. , (output_options & PROMETHEUS_OUTPUT_NAMES && st->name) ? st->name : st->id
  300. , st->context
  301. , st->family
  302. , (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id
  303. , rd->multiplier
  304. , rd->divisor
  305. , h
  306. , st->units
  307. , t
  308. );
  309. if(unlikely(output_options & PROMETHEUS_OUTPUT_TYPES))
  310. buffer_sprintf(wb, "# COMMENT TYPE %s_%s%s %s\n"
  311. , prefix
  312. , context
  313. , suffix
  314. , t
  315. );
  316. if(output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  317. buffer_sprintf(wb
  318. , "%s_%s%s{chart=\"%s\",family=\"%s\",dimension=\"%s\"%s} " COLLECTED_NUMBER_FORMAT " %llu\n"
  319. , prefix
  320. , context
  321. , suffix
  322. , chart
  323. , family
  324. , dimension
  325. , labels
  326. , rd->last_collected_value
  327. , timeval_msec(&rd->last_collected_time)
  328. );
  329. else
  330. buffer_sprintf(wb
  331. , "%s_%s%s{chart=\"%s\",family=\"%s\",dimension=\"%s\"%s} " COLLECTED_NUMBER_FORMAT "\n"
  332. , prefix
  333. , context
  334. , suffix
  335. , chart
  336. , family
  337. , dimension
  338. , labels
  339. , rd->last_collected_value
  340. );
  341. }
  342. else {
  343. // the dimensions of the chart, do not have the same algorithm, multiplier or divisor
  344. // we create a metric per dimension
  345. prometheus_name_copy(dimension, (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id, PROMETHEUS_ELEMENT_MAX);
  346. if(unlikely(output_options & PROMETHEUS_OUTPUT_HELP))
  347. buffer_sprintf(wb
  348. , "# COMMENT %s_%s_%s%s: chart \"%s\", context \"%s\", family \"%s\", dimension \"%s\", value * " COLLECTED_NUMBER_FORMAT " / " COLLECTED_NUMBER_FORMAT " %s %s (%s)\n"
  349. , prefix
  350. , context
  351. , dimension
  352. , suffix
  353. , (output_options & PROMETHEUS_OUTPUT_NAMES && st->name) ? st->name : st->id
  354. , st->context
  355. , st->family
  356. , (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id
  357. , rd->multiplier
  358. , rd->divisor
  359. , h
  360. , st->units
  361. , t
  362. );
  363. if(unlikely(output_options & PROMETHEUS_OUTPUT_TYPES))
  364. buffer_sprintf(wb, "# COMMENT TYPE %s_%s_%s%s %s\n"
  365. , prefix
  366. , context
  367. , dimension
  368. , suffix
  369. , t
  370. );
  371. if(output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  372. buffer_sprintf(wb
  373. , "%s_%s_%s%s{chart=\"%s\",family=\"%s\"%s} " COLLECTED_NUMBER_FORMAT " %llu\n"
  374. , prefix
  375. , context
  376. , dimension
  377. , suffix
  378. , chart
  379. , family
  380. , labels
  381. , rd->last_collected_value
  382. , timeval_msec(&rd->last_collected_time)
  383. );
  384. else
  385. buffer_sprintf(wb
  386. , "%s_%s_%s%s{chart=\"%s\",family=\"%s\"%s} " COLLECTED_NUMBER_FORMAT "\n"
  387. , prefix
  388. , context
  389. , dimension
  390. , suffix
  391. , chart
  392. , family
  393. , labels
  394. , rd->last_collected_value
  395. );
  396. }
  397. }
  398. else {
  399. // we need average or sum of the data
  400. time_t first_t = after, last_t = before;
  401. calculated_number value = backend_calculate_value_from_stored_data(st, rd, after, before, backend_options, &first_t, &last_t);
  402. if(!isnan(value) && !isinf(value)) {
  403. if(BACKEND_OPTIONS_DATA_SOURCE(backend_options) == BACKEND_SOURCE_DATA_AVERAGE)
  404. suffix = "_average";
  405. else if(BACKEND_OPTIONS_DATA_SOURCE(backend_options) == BACKEND_SOURCE_DATA_SUM)
  406. suffix = "_sum";
  407. prometheus_label_copy(dimension, (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id, PROMETHEUS_ELEMENT_MAX);
  408. if (unlikely(output_options & PROMETHEUS_OUTPUT_HELP))
  409. buffer_sprintf(wb, "# COMMENT %s_%s%s%s: dimension \"%s\", value is %s, gauge, dt %llu to %llu inclusive\n"
  410. , prefix
  411. , context
  412. , units
  413. , suffix
  414. , (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rd->name : rd->id
  415. , st->units
  416. , (unsigned long long)first_t
  417. , (unsigned long long)last_t
  418. );
  419. if (unlikely(output_options & PROMETHEUS_OUTPUT_TYPES))
  420. buffer_sprintf(wb, "# COMMENT TYPE %s_%s%s%s gauge\n"
  421. , prefix
  422. , context
  423. , units
  424. , suffix
  425. );
  426. if(output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  427. buffer_sprintf(wb, "%s_%s%s%s{chart=\"%s\",family=\"%s\",dimension=\"%s\"%s} " CALCULATED_NUMBER_FORMAT " %llu\n"
  428. , prefix
  429. , context
  430. , units
  431. , suffix
  432. , chart
  433. , family
  434. , dimension
  435. , labels
  436. , value
  437. , last_t * MSEC_PER_SEC
  438. );
  439. else
  440. buffer_sprintf(wb, "%s_%s%s%s{chart=\"%s\",family=\"%s\",dimension=\"%s\"%s} " CALCULATED_NUMBER_FORMAT "\n"
  441. , prefix
  442. , context
  443. , units
  444. , suffix
  445. , chart
  446. , family
  447. , dimension
  448. , labels
  449. , value
  450. );
  451. }
  452. }
  453. }
  454. }
  455. rrdset_unlock(st);
  456. }
  457. }
  458. rrdhost_unlock(host);
  459. }
  460. static inline time_t prometheus_preparation(RRDHOST *host, BUFFER *wb, BACKEND_OPTIONS backend_options, const char *server, time_t now, PROMETHEUS_OUTPUT_OPTIONS output_options) {
  461. if(!server || !*server) server = "default";
  462. time_t after = prometheus_server_last_access(server, host, now);
  463. int first_seen = 0;
  464. if(!after) {
  465. after = now - global_backend_update_every;
  466. first_seen = 1;
  467. }
  468. if(after > now) {
  469. // oops! this should never happen
  470. after = now - global_backend_update_every;
  471. }
  472. if(output_options & PROMETHEUS_OUTPUT_HELP) {
  473. char *mode;
  474. if(BACKEND_OPTIONS_DATA_SOURCE(backend_options) == BACKEND_SOURCE_DATA_AS_COLLECTED)
  475. mode = "as collected";
  476. else if(BACKEND_OPTIONS_DATA_SOURCE(backend_options) == BACKEND_SOURCE_DATA_AVERAGE)
  477. mode = "average";
  478. else if(BACKEND_OPTIONS_DATA_SOURCE(backend_options) == BACKEND_SOURCE_DATA_SUM)
  479. mode = "sum";
  480. else
  481. mode = "unknown";
  482. buffer_sprintf(wb, "# COMMENT netdata \"%s\" to %sprometheus \"%s\", source \"%s\", last seen %lu %s, time range %lu to %lu\n\n"
  483. , host->hostname
  484. , (first_seen)?"FIRST SEEN ":""
  485. , server
  486. , mode
  487. , (unsigned long)((first_seen)?0:(now - after))
  488. , (first_seen)?"never":"seconds ago"
  489. , (unsigned long)after, (unsigned long)now
  490. );
  491. }
  492. return after;
  493. }
  494. void rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(RRDHOST *host, BUFFER *wb, const char *server, const char *prefix, BACKEND_OPTIONS backend_options, PROMETHEUS_OUTPUT_OPTIONS output_options) {
  495. time_t before = now_realtime_sec();
  496. // we start at the point we had stopped before
  497. time_t after = prometheus_preparation(host, wb, backend_options, server, before, output_options);
  498. rrd_stats_api_v1_charts_allmetrics_prometheus(host, wb, prefix, backend_options, after, before, 0, output_options);
  499. }
  500. void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(RRDHOST *host, BUFFER *wb, const char *server, const char *prefix, BACKEND_OPTIONS backend_options, PROMETHEUS_OUTPUT_OPTIONS output_options) {
  501. time_t before = now_realtime_sec();
  502. // we start at the point we had stopped before
  503. time_t after = prometheus_preparation(host, wb, backend_options, server, before, output_options);
  504. rrd_rdlock();
  505. rrdhost_foreach_read(host) {
  506. rrd_stats_api_v1_charts_allmetrics_prometheus(host, wb, prefix, backend_options, after, before, 1, output_options);
  507. }
  508. rrd_unlock();
  509. }