prometheus.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "prometheus.h"
  3. // ----------------------------------------------------------------------------
  4. // PROMETHEUS
  5. // /api/v1/allmetrics?format=prometheus and /api/v1/allmetrics?format=prometheus_all_hosts
  6. static int is_matches_rrdset(struct instance *instance, RRDSET *st, SIMPLE_PATTERN *filter) {
  7. if (instance->config.options & EXPORTING_OPTION_SEND_NAMES) {
  8. return simple_pattern_matches_string(filter, st->name);
  9. }
  10. return simple_pattern_matches_string(filter, st->id);
  11. }
  12. /**
  13. * Check if a chart can be sent to Prometheus
  14. *
  15. * @param instance an instance data structure.
  16. * @param st a chart.
  17. * @param filter a simple pattern to match against.
  18. * @return Returns 1 if the chart can be sent, 0 otherwise.
  19. */
  20. inline int can_send_rrdset(struct instance *instance, RRDSET *st, SIMPLE_PATTERN *filter)
  21. {
  22. #ifdef NETDATA_INTERNAL_CHECKS
  23. RRDHOST *host = st->rrdhost;
  24. #endif
  25. if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_EXPORTING_IGNORE)))
  26. return 0;
  27. if (filter) {
  28. if (!is_matches_rrdset(instance, st, filter)) {
  29. return 0;
  30. }
  31. } else if (unlikely(!rrdset_flag_check(st, RRDSET_FLAG_EXPORTING_SEND))) {
  32. // we have not checked this chart
  33. if (is_matches_rrdset(instance, st, instance->config.charts_pattern)) {
  34. rrdset_flag_set(st, RRDSET_FLAG_EXPORTING_SEND);
  35. } else {
  36. rrdset_flag_set(st, RRDSET_FLAG_EXPORTING_IGNORE);
  37. netdata_log_debug(
  38. D_EXPORTING,
  39. "EXPORTING: not sending chart '%s' of host '%s', because it is disabled for exporting.",
  40. rrdset_id(st),
  41. rrdhost_hostname(host));
  42. return 0;
  43. }
  44. }
  45. if (unlikely(!rrdset_is_available_for_exporting_and_alarms(st))) {
  46. netdata_log_debug(
  47. D_EXPORTING,
  48. "EXPORTING: not sending chart '%s' of host '%s', because it is not available for exporting.",
  49. rrdset_id(st),
  50. rrdhost_hostname(host));
  51. return 0;
  52. }
  53. if (unlikely(
  54. st->rrd_memory_mode == RRD_MEMORY_MODE_NONE &&
  55. !(EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED))) {
  56. netdata_log_debug(
  57. D_EXPORTING,
  58. "EXPORTING: not sending chart '%s' of host '%s' because its memory mode is '%s' and the exporting connector requires database access.",
  59. rrdset_id(st),
  60. rrdhost_hostname(host),
  61. rrd_memory_mode_name(host->rrd_memory_mode));
  62. return 0;
  63. }
  64. return 1;
  65. }
  66. static struct prometheus_server {
  67. const char *server;
  68. uint32_t hash;
  69. RRDHOST *host;
  70. time_t last_access;
  71. struct prometheus_server *next;
  72. } *prometheus_server_root = NULL;
  73. static netdata_mutex_t prometheus_server_root_mutex = NETDATA_MUTEX_INITIALIZER;
  74. /**
  75. * Clean server root local structure
  76. */
  77. void prometheus_clean_server_root()
  78. {
  79. if (prometheus_server_root) {
  80. netdata_mutex_lock(&prometheus_server_root_mutex);
  81. struct prometheus_server *ps;
  82. for (ps = prometheus_server_root; ps; ) {
  83. struct prometheus_server *current = ps;
  84. ps = ps->next;
  85. if(current->server)
  86. freez((void *)current->server);
  87. freez(current);
  88. }
  89. prometheus_server_root = NULL;
  90. netdata_mutex_unlock(&prometheus_server_root_mutex);
  91. }
  92. }
  93. /**
  94. * Get the last time when a Prometheus server scraped the Netdata Prometheus exporter.
  95. *
  96. * @param server the name of the Prometheus server.
  97. * @param host a data collecting host.
  98. * @param now actual time.
  99. * @return Returns the last time when the server accessed Netdata, or 0 if it is the first occurrence.
  100. */
  101. static inline time_t prometheus_server_last_access(const char *server, RRDHOST *host, time_t now)
  102. {
  103. #ifdef UNIT_TESTING
  104. return 0;
  105. #endif
  106. uint32_t hash = simple_hash(server);
  107. netdata_mutex_lock(&prometheus_server_root_mutex);
  108. struct prometheus_server *ps;
  109. for (ps = prometheus_server_root; ps; ps = ps->next) {
  110. if (host == ps->host && hash == ps->hash && !strcmp(server, ps->server)) {
  111. time_t last = ps->last_access;
  112. ps->last_access = now;
  113. netdata_mutex_unlock(&prometheus_server_root_mutex);
  114. return last;
  115. }
  116. }
  117. ps = callocz(1, sizeof(struct prometheus_server));
  118. ps->server = strdupz(server);
  119. ps->hash = hash;
  120. ps->host = host;
  121. ps->last_access = now;
  122. ps->next = prometheus_server_root;
  123. prometheus_server_root = ps;
  124. netdata_mutex_unlock(&prometheus_server_root_mutex);
  125. return 0;
  126. }
  127. /**
  128. * Copy and sanitize name.
  129. *
  130. * @param d a destination string.
  131. * @param s a source string.
  132. * @param usable the number of characters to copy.
  133. * @return Returns the length of the copied string.
  134. */
  135. inline size_t prometheus_name_copy(char *d, const char *s, size_t usable)
  136. {
  137. size_t n;
  138. for (n = 0; *s && n < usable; d++, s++, n++) {
  139. register char c = *s;
  140. if (!isalnum(c))
  141. *d = '_';
  142. else
  143. *d = c;
  144. }
  145. *d = '\0';
  146. return n;
  147. }
  148. /**
  149. * Copy and sanitize label.
  150. *
  151. * @param d a destination string.
  152. * @param s a source string.
  153. * @param usable the number of characters to copy.
  154. * @return Returns the length of the copied string.
  155. */
  156. inline size_t prometheus_label_copy(char *d, const char *s, size_t usable)
  157. {
  158. size_t n;
  159. // make sure we can escape one character without overflowing the buffer
  160. usable--;
  161. for (n = 0; *s && n < usable; d++, s++, n++) {
  162. register char c = *s;
  163. if (unlikely(c == '"' || c == '\\' || c == '\n')) {
  164. *d++ = '\\';
  165. n++;
  166. }
  167. *d = c;
  168. }
  169. *d = '\0';
  170. return n;
  171. }
  172. /**
  173. * Copy and sanitize units.
  174. *
  175. * @param d a destination string.
  176. * @param s a source string.
  177. * @param usable the number of characters to copy.
  178. * @param showoldunits set this flag to 1 to show old (before v1.12) units.
  179. * @return Returns the destination string.
  180. */
  181. inline char *prometheus_units_copy(char *d, const char *s, size_t usable, int showoldunits)
  182. {
  183. const char *sorig = s;
  184. char *ret = d;
  185. size_t n;
  186. // Fix for issue 5227
  187. if (unlikely(showoldunits)) {
  188. static struct {
  189. const char *newunit;
  190. uint32_t hash;
  191. const char *oldunit;
  192. } units[] = { { "KiB/s", 0, "kilobytes/s" },
  193. { "MiB/s", 0, "MB/s" },
  194. { "GiB/s", 0, "GB/s" },
  195. { "KiB", 0, "KB" },
  196. { "MiB", 0, "MB" },
  197. { "GiB", 0, "GB" },
  198. { "inodes", 0, "Inodes" },
  199. { "percentage", 0, "percent" },
  200. { "faults/s", 0, "page faults/s" },
  201. { "KiB/operation", 0, "kilobytes per operation" },
  202. { "milliseconds/operation", 0, "ms per operation" },
  203. { NULL, 0, NULL } };
  204. static int initialized = 0;
  205. int i;
  206. if (unlikely(!initialized)) {
  207. for (i = 0; units[i].newunit; i++)
  208. units[i].hash = simple_hash(units[i].newunit);
  209. initialized = 1;
  210. }
  211. uint32_t hash = simple_hash(s);
  212. for (i = 0; units[i].newunit; i++) {
  213. if (unlikely(hash == units[i].hash && !strcmp(s, units[i].newunit))) {
  214. // netdata_log_info("matched extension for filename '%s': '%s'", filename, last_dot);
  215. s = units[i].oldunit;
  216. sorig = s;
  217. break;
  218. }
  219. }
  220. }
  221. *d++ = '_';
  222. for (n = 1; *s && n < usable; d++, s++, n++) {
  223. register char c = *s;
  224. if (!isalnum(c))
  225. *d = '_';
  226. else
  227. *d = c;
  228. }
  229. if (n == 2 && sorig[0] == '%') {
  230. n = 0;
  231. d = ret;
  232. s = "_percent";
  233. for (; *s && n < usable; n++)
  234. *d++ = *s++;
  235. } else if (n > 3 && sorig[n - 3] == '/' && sorig[n - 2] == 's') {
  236. n = n - 2;
  237. d -= 2;
  238. s = "_persec";
  239. for (; *s && n < usable; n++)
  240. *d++ = *s++;
  241. }
  242. *d = '\0';
  243. return ret;
  244. }
  245. /**
  246. * Format host labels for the Prometheus exporter
  247. *
  248. * @param instance an instance data structure.
  249. * @param host a data collecting host.
  250. */
  251. struct format_prometheus_label_callback {
  252. struct instance *instance;
  253. size_t count;
  254. };
  255. static int format_prometheus_label_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) {
  256. struct format_prometheus_label_callback *d = (struct format_prometheus_label_callback *)data;
  257. if (!should_send_label(d->instance, ls)) return 0;
  258. char k[PROMETHEUS_ELEMENT_MAX + 1];
  259. char v[PROMETHEUS_ELEMENT_MAX + 1];
  260. prometheus_name_copy(k, name, PROMETHEUS_ELEMENT_MAX);
  261. prometheus_label_copy(v, value, PROMETHEUS_ELEMENT_MAX);
  262. if (*k && *v) {
  263. if (d->count > 0) buffer_strcat(d->instance->labels_buffer, ",");
  264. buffer_sprintf(d->instance->labels_buffer, "%s=\"%s\"", k, v);
  265. d->count++;
  266. }
  267. return 1;
  268. }
  269. void format_host_labels_prometheus(struct instance *instance, RRDHOST *host)
  270. {
  271. if (unlikely(!sending_labels_configured(instance)))
  272. return;
  273. if (!instance->labels_buffer)
  274. instance->labels_buffer = buffer_create(1024, &netdata_buffers_statistics.buffers_exporters);
  275. struct format_prometheus_label_callback tmp = {
  276. .instance = instance,
  277. .count = 0
  278. };
  279. rrdlabels_walkthrough_read(host->rrdlabels, format_prometheus_label_callback, &tmp);
  280. }
  281. /**
  282. * Format host labels for the Prometheus exporter
  283. * We are using a structure instead a direct buffer to expand options quickly.
  284. *
  285. * @param data is the buffer used to add labels.
  286. */
  287. static int format_prometheus_chart_label_callback(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) {
  288. BUFFER *wb = data;
  289. if (name[0] == '_' )
  290. return 1;
  291. char k[PROMETHEUS_ELEMENT_MAX + 1];
  292. char v[PROMETHEUS_ELEMENT_MAX + 1];
  293. prometheus_name_copy(k, name, PROMETHEUS_ELEMENT_MAX);
  294. prometheus_label_copy(v, value, PROMETHEUS_ELEMENT_MAX);
  295. if (*k && *v)
  296. buffer_sprintf(wb, ",%s=\"%s\"", k, v);
  297. return 1;
  298. }
  299. struct host_variables_callback_options {
  300. RRDHOST *host;
  301. BUFFER *wb;
  302. EXPORTING_OPTIONS exporting_options;
  303. PROMETHEUS_OUTPUT_OPTIONS output_options;
  304. const char *prefix;
  305. const char *labels;
  306. time_t now;
  307. int host_header_printed;
  308. char name[PROMETHEUS_VARIABLE_MAX + 1];
  309. };
  310. /**
  311. * Print host variables.
  312. *
  313. * @param rv a variable.
  314. * @param data callback options.
  315. * @return Returns 1 if the chart can be sent, 0 otherwise.
  316. */
  317. static int print_host_variables_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rv_ptr __maybe_unused, void *data) {
  318. const RRDVAR_ACQUIRED *rv = (const RRDVAR_ACQUIRED *)item;
  319. struct host_variables_callback_options *opts = data;
  320. if (rrdvar_flags(rv) & (RRDVAR_FLAG_CUSTOM_HOST_VAR | RRDVAR_FLAG_CUSTOM_CHART_VAR)) {
  321. if (!opts->host_header_printed) {
  322. opts->host_header_printed = 1;
  323. if (opts->output_options & PROMETHEUS_OUTPUT_HELP) {
  324. buffer_sprintf(opts->wb, "\n# COMMENT global host and chart variables\n");
  325. }
  326. }
  327. NETDATA_DOUBLE value = rrdvar2number(rv);
  328. if (isnan(value) || isinf(value)) {
  329. if (opts->output_options & PROMETHEUS_OUTPUT_HELP)
  330. buffer_sprintf(
  331. opts->wb, "# COMMENT variable \"%s\" is %s. Skipped.\n", rrdvar_name(rv), (isnan(value)) ? "NAN" : "INF");
  332. return 0;
  333. }
  334. char *label_pre = "";
  335. char *label_post = "";
  336. if (opts->labels && *opts->labels) {
  337. label_pre = "{";
  338. label_post = "}";
  339. }
  340. prometheus_name_copy(opts->name, rrdvar_name(rv), sizeof(opts->name));
  341. if (opts->output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  342. buffer_sprintf(
  343. opts->wb,
  344. "%s_%s%s%s%s " NETDATA_DOUBLE_FORMAT " %llu\n",
  345. opts->prefix,
  346. opts->name,
  347. label_pre,
  348. opts->labels,
  349. label_post,
  350. value,
  351. opts->now * 1000ULL);
  352. else
  353. buffer_sprintf(
  354. opts->wb,
  355. "%s_%s%s%s%s " NETDATA_DOUBLE_FORMAT "\n",
  356. opts->prefix,
  357. opts->name,
  358. label_pre,
  359. opts->labels,
  360. label_post,
  361. value);
  362. return 1;
  363. }
  364. return 0;
  365. }
  366. struct gen_parameters {
  367. const char *prefix;
  368. const char *labels_prefix;
  369. char *context;
  370. char *suffix;
  371. char *chart;
  372. char *dimension;
  373. char *family;
  374. char *labels;
  375. PROMETHEUS_OUTPUT_OPTIONS output_options;
  376. RRDSET *st;
  377. RRDDIM *rd;
  378. const char *relation;
  379. const char *type;
  380. };
  381. /**
  382. * Write an as-collected help comment to a buffer.
  383. *
  384. * @param wb the buffer to write the comment to.
  385. * @param p parameters for generating the comment string.
  386. * @param homogeneous a flag for homogeneous charts.
  387. * @param prometheus_collector a flag for metrics from prometheus collector.
  388. */
  389. static void generate_as_collected_prom_help(BUFFER *wb, struct gen_parameters *p, int homogeneous, int prometheus_collector)
  390. {
  391. buffer_sprintf(wb, "# COMMENT %s_%s", p->prefix, p->context);
  392. if (!homogeneous)
  393. buffer_sprintf(wb, "_%s", p->dimension);
  394. buffer_sprintf(
  395. wb,
  396. "%s: chart \"%s\", context \"%s\", family \"%s\", dimension \"%s\", value * ",
  397. p->suffix,
  398. (p->output_options & PROMETHEUS_OUTPUT_NAMES && p->st->name) ? rrdset_name(p->st) : rrdset_id(p->st),
  399. rrdset_context(p->st),
  400. rrdset_family(p->st),
  401. (p->output_options & PROMETHEUS_OUTPUT_NAMES && p->rd->name) ? rrddim_name(p->rd) : rrddim_id(p->rd));
  402. if (prometheus_collector)
  403. buffer_sprintf(wb, "1 / 1");
  404. else
  405. buffer_sprintf(wb, "%d / %d", p->rd->multiplier, p->rd->divisor);
  406. buffer_sprintf(wb, " %s %s (%s)\n", p->relation, rrdset_units(p->st), p->type);
  407. }
  408. /**
  409. * Write an as-collected metric to a buffer.
  410. *
  411. * @param wb the buffer to write the metric to.
  412. * @param p parameters for generating the metric string.
  413. * @param homogeneous a flag for homogeneous charts.
  414. * @param prometheus_collector a flag for metrics from prometheus collector.
  415. * @param chart_labels the dictionary with chart labels
  416. */
  417. static void generate_as_collected_prom_metric(BUFFER *wb,
  418. struct gen_parameters *p,
  419. int homogeneous,
  420. int prometheus_collector,
  421. RRDLABELS *chart_labels)
  422. {
  423. buffer_sprintf(wb, "%s_%s", p->prefix, p->context);
  424. if (!homogeneous)
  425. buffer_sprintf(wb, "_%s", p->dimension);
  426. buffer_sprintf(wb, "%s{%schart=\"%s\"", p->suffix, p->labels_prefix, p->chart);
  427. if (homogeneous)
  428. buffer_sprintf(wb, ",%sdimension=\"%s\"", p->labels_prefix, p->dimension);
  429. buffer_sprintf(wb, ",%sfamily=\"%s\"", p->labels_prefix, p->family);
  430. rrdlabels_walkthrough_read(chart_labels, format_prometheus_chart_label_callback, wb);
  431. buffer_sprintf(wb, "%s} ", p->labels);
  432. if (prometheus_collector)
  433. buffer_sprintf(
  434. wb,
  435. NETDATA_DOUBLE_FORMAT,
  436. (NETDATA_DOUBLE)p->rd->collector.last_collected_value * (NETDATA_DOUBLE)p->rd->multiplier /
  437. (NETDATA_DOUBLE)p->rd->divisor);
  438. else
  439. buffer_sprintf(wb, COLLECTED_NUMBER_FORMAT, p->rd->collector.last_collected_value);
  440. if (p->output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  441. buffer_sprintf(wb, " %"PRIu64"\n", timeval_msec(&p->rd->collector.last_collected_time));
  442. else
  443. buffer_sprintf(wb, "\n");
  444. }
  445. /**
  446. * Write metrics in Prometheus format to a buffer.
  447. *
  448. * @param instance an instance data structure.
  449. * @param host a data collecting host.
  450. * @param filter_string a simple pattern filter.
  451. * @param wb the buffer to fill with metrics.
  452. * @param prefix a prefix for every metric.
  453. * @param exporting_options options to configure what data is exported.
  454. * @param allhosts set to 1 if host instance should be in the output for tags.
  455. * @param output_options options to configure the format of the output.
  456. */
  457. static void rrd_stats_api_v1_charts_allmetrics_prometheus(
  458. struct instance *instance,
  459. RRDHOST *host,
  460. const char *filter_string,
  461. BUFFER *wb,
  462. const char *prefix,
  463. EXPORTING_OPTIONS exporting_options,
  464. int allhosts,
  465. PROMETHEUS_OUTPUT_OPTIONS output_options)
  466. {
  467. SIMPLE_PATTERN *filter = simple_pattern_create(filter_string, NULL, SIMPLE_PATTERN_EXACT, true);
  468. char hostname[PROMETHEUS_ELEMENT_MAX + 1];
  469. prometheus_label_copy(hostname, rrdhost_hostname(host), PROMETHEUS_ELEMENT_MAX);
  470. format_host_labels_prometheus(instance, host);
  471. buffer_sprintf(
  472. wb,
  473. "netdata_info{instance=\"%s\",application=\"%s\",version=\"%s\"",
  474. hostname,
  475. rrdhost_program_name(host),
  476. rrdhost_program_version(host));
  477. if (instance->labels_buffer && *buffer_tostring(instance->labels_buffer)) {
  478. buffer_sprintf(wb, ",%s", buffer_tostring(instance->labels_buffer));
  479. }
  480. if (output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  481. buffer_sprintf(wb, "} 1 %llu\n", now_realtime_usec() / USEC_PER_MS);
  482. else
  483. buffer_sprintf(wb, "} 1\n");
  484. char labels[PROMETHEUS_LABELS_MAX + 1] = "";
  485. if (allhosts) {
  486. snprintfz(labels, PROMETHEUS_LABELS_MAX, ",%sinstance=\"%s\"", instance->config.label_prefix, hostname);
  487. }
  488. if (instance->labels_buffer)
  489. buffer_flush(instance->labels_buffer);
  490. // send custom variables set for the host
  491. if (output_options & PROMETHEUS_OUTPUT_VARIABLES) {
  492. struct host_variables_callback_options opts = {
  493. .host = host,
  494. .wb = wb,
  495. .labels = (labels[0] == ',') ? &labels[1] : labels,
  496. .exporting_options = exporting_options,
  497. .output_options = output_options,
  498. .prefix = prefix,
  499. .now = now_realtime_sec(),
  500. .host_header_printed = 0
  501. };
  502. rrdvar_walkthrough_read(host->rrdvars, print_host_variables_callback, &opts);
  503. }
  504. // for each chart
  505. RRDSET *st;
  506. BUFFER *plabels_buffer = buffer_create(0, NULL);
  507. const char *plabels_prefix = instance->config.label_prefix;
  508. STRING *prometheus = string_strdupz("prometheus");
  509. rrdset_foreach_read(st, host) {
  510. if (likely(can_send_rrdset(instance, st, filter))) {
  511. char chart[PROMETHEUS_ELEMENT_MAX + 1];
  512. char context[PROMETHEUS_ELEMENT_MAX + 1];
  513. char family[PROMETHEUS_ELEMENT_MAX + 1];
  514. char units[PROMETHEUS_ELEMENT_MAX + 1] = "";
  515. prometheus_label_copy(chart, (output_options & PROMETHEUS_OUTPUT_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st), PROMETHEUS_ELEMENT_MAX);
  516. prometheus_label_copy(family, rrdset_family(st), PROMETHEUS_ELEMENT_MAX);
  517. prometheus_name_copy(context, rrdset_context(st), PROMETHEUS_ELEMENT_MAX);
  518. int as_collected = (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_AS_COLLECTED);
  519. int homogeneous = 1;
  520. int prometheus_collector = 0;
  521. RRDSET_FLAGS flags = rrdset_flag_get(st);
  522. if (as_collected) {
  523. if (flags & RRDSET_FLAG_HOMOGENEOUS_CHECK)
  524. rrdset_update_heterogeneous_flag(st);
  525. if (flags & RRDSET_FLAG_HETEROGENEOUS)
  526. homogeneous = 0;
  527. if (st->module_name == prometheus)
  528. prometheus_collector = 1;
  529. }
  530. else {
  531. if (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_AVERAGE &&
  532. !(output_options & PROMETHEUS_OUTPUT_HIDEUNITS))
  533. prometheus_units_copy(
  534. units, rrdset_units(st), PROMETHEUS_ELEMENT_MAX, output_options & PROMETHEUS_OUTPUT_OLDUNITS);
  535. }
  536. if (unlikely(output_options & PROMETHEUS_OUTPUT_HELP))
  537. buffer_sprintf(
  538. wb,
  539. "\n# COMMENT %s chart \"%s\", context \"%s\", family \"%s\", units \"%s\"\n",
  540. (homogeneous) ? "homogeneous" : "heterogeneous",
  541. (output_options & PROMETHEUS_OUTPUT_NAMES && st->name) ? rrdset_name(st) : rrdset_id(st),
  542. rrdset_context(st),
  543. rrdset_family(st),
  544. rrdset_units(st));
  545. // for each dimension
  546. RRDDIM *rd;
  547. rrddim_foreach_read(rd, st) {
  548. if (rd->collector.counter && !rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE)) {
  549. char dimension[PROMETHEUS_ELEMENT_MAX + 1];
  550. char *suffix = "";
  551. if (as_collected) {
  552. // we need as-collected / raw data
  553. struct gen_parameters p;
  554. p.prefix = prefix;
  555. p.labels_prefix = instance->config.label_prefix;
  556. p.context = context;
  557. p.suffix = suffix;
  558. p.chart = chart;
  559. p.dimension = dimension;
  560. p.family = family;
  561. p.labels = labels;
  562. p.output_options = output_options;
  563. p.st = st;
  564. p.rd = rd;
  565. if (unlikely(rd->collector.last_collected_time.tv_sec < instance->after))
  566. continue;
  567. p.type = "gauge";
  568. p.relation = "gives";
  569. if (rd->algorithm == RRD_ALGORITHM_INCREMENTAL ||
  570. rd->algorithm == RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL) {
  571. p.type = "counter";
  572. p.relation = "delta gives";
  573. if (!prometheus_collector)
  574. p.suffix = "_total";
  575. }
  576. if (homogeneous) {
  577. // all the dimensions of the chart, has the same algorithm, multiplier and divisor
  578. // we add all dimensions as labels
  579. prometheus_label_copy(
  580. dimension,
  581. (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd),
  582. PROMETHEUS_ELEMENT_MAX);
  583. if (unlikely(output_options & PROMETHEUS_OUTPUT_HELP))
  584. generate_as_collected_prom_help(wb, &p, homogeneous, prometheus_collector);
  585. if (unlikely(output_options & PROMETHEUS_OUTPUT_TYPES))
  586. buffer_sprintf(wb, "# TYPE %s_%s%s %s\n", prefix, context, suffix, p.type);
  587. generate_as_collected_prom_metric(wb, &p, homogeneous, prometheus_collector, st->rrdlabels);
  588. }
  589. else {
  590. // the dimensions of the chart, do not have the same algorithm, multiplier or divisor
  591. // we create a metric per dimension
  592. prometheus_name_copy(
  593. dimension,
  594. (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd),
  595. PROMETHEUS_ELEMENT_MAX);
  596. if (unlikely(output_options & PROMETHEUS_OUTPUT_HELP))
  597. generate_as_collected_prom_help(wb, &p, homogeneous, prometheus_collector);
  598. if (unlikely(output_options & PROMETHEUS_OUTPUT_TYPES))
  599. buffer_sprintf(
  600. wb, "# TYPE %s_%s_%s%s %s\n", prefix, context, dimension, suffix, p.type);
  601. generate_as_collected_prom_metric(wb, &p, homogeneous, prometheus_collector, st->rrdlabels);
  602. }
  603. }
  604. else {
  605. // we need average or sum of the data
  606. time_t first_time = instance->after;
  607. time_t last_time = instance->before;
  608. NETDATA_DOUBLE value = exporting_calculate_value_from_stored_data(instance, rd, &last_time);
  609. if (!isnan(value) && !isinf(value)) {
  610. if (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_AVERAGE)
  611. suffix = "_average";
  612. else if (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_SUM)
  613. suffix = "_sum";
  614. prometheus_label_copy(
  615. dimension,
  616. (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd),
  617. PROMETHEUS_ELEMENT_MAX);
  618. buffer_flush(plabels_buffer);
  619. buffer_sprintf(plabels_buffer, "%1$schart=\"%2$s\",%1$sdimension=\"%3$s\",%1$sfamily=\"%4$s\"", plabels_prefix, chart, dimension, family);
  620. rrdlabels_walkthrough_read(st->rrdlabels, format_prometheus_chart_label_callback, plabels_buffer);
  621. if (unlikely(output_options & PROMETHEUS_OUTPUT_HELP))
  622. buffer_sprintf(
  623. wb,
  624. "# COMMENT %s_%s%s%s: dimension \"%s\", value is %s, gauge, dt %llu to %llu inclusive\n",
  625. prefix,
  626. context,
  627. units,
  628. suffix,
  629. (output_options & PROMETHEUS_OUTPUT_NAMES && rd->name) ? rrddim_name(rd) : rrddim_id(rd),
  630. rrdset_units(st),
  631. (unsigned long long)first_time,
  632. (unsigned long long)last_time);
  633. if (unlikely(output_options & PROMETHEUS_OUTPUT_TYPES))
  634. buffer_sprintf(wb, "# TYPE %s_%s%s%s gauge\n", prefix, context, units, suffix);
  635. if (output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
  636. buffer_sprintf(
  637. wb,
  638. "%s_%s%s%s{%s%s} " NETDATA_DOUBLE_FORMAT
  639. " %llu\n",
  640. prefix,
  641. context,
  642. units,
  643. suffix,
  644. buffer_tostring(plabels_buffer),
  645. labels,
  646. value,
  647. last_time * MSEC_PER_SEC);
  648. else
  649. buffer_sprintf(
  650. wb,
  651. "%s_%s%s%s{%s%s} " NETDATA_DOUBLE_FORMAT
  652. "\n",
  653. prefix,
  654. context,
  655. units,
  656. suffix,
  657. buffer_tostring(plabels_buffer),
  658. labels,
  659. value);
  660. }
  661. }
  662. }
  663. }
  664. rrddim_foreach_done(rd);
  665. }
  666. }
  667. rrdset_foreach_done(st);
  668. buffer_free(plabels_buffer);
  669. simple_pattern_free(filter);
  670. }
  671. /**
  672. * Get the last time time when a server accessed Netdata. Write information about an API request to a buffer.
  673. *
  674. * @param instance an instance data structure.
  675. * @param host a data collecting host.
  676. * @param wb the buffer to write to.
  677. * @param exporting_options options to configure what data is exported.
  678. * @param server the name of a Prometheus server..
  679. * @param now actual time.
  680. * @param output_options options to configure the format of the output.
  681. * @return Returns the last time when the server accessed Netdata.
  682. */
  683. static inline time_t prometheus_preparation(
  684. struct instance *instance,
  685. RRDHOST *host,
  686. BUFFER *wb,
  687. EXPORTING_OPTIONS exporting_options,
  688. const char *server,
  689. time_t now,
  690. PROMETHEUS_OUTPUT_OPTIONS output_options)
  691. {
  692. #ifndef UNIT_TESTING
  693. analytics_log_prometheus();
  694. #endif
  695. if (!server || !*server)
  696. server = "default";
  697. time_t after = prometheus_server_last_access(server, host, now);
  698. int first_seen = 0;
  699. if (!after) {
  700. after = now - instance->config.update_every;
  701. first_seen = 1;
  702. }
  703. if (after > now) {
  704. // oops! this should never happen
  705. after = now - instance->config.update_every;
  706. }
  707. if (output_options & PROMETHEUS_OUTPUT_HELP) {
  708. char *mode;
  709. if (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_AS_COLLECTED)
  710. mode = "as collected";
  711. else if (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_AVERAGE)
  712. mode = "average";
  713. else if (EXPORTING_OPTIONS_DATA_SOURCE(exporting_options) == EXPORTING_SOURCE_DATA_SUM)
  714. mode = "sum";
  715. else
  716. mode = "unknown";
  717. buffer_sprintf(
  718. wb,
  719. "# COMMENT netdata \"%s\" to %sprometheus \"%s\", source \"%s\", last seen %lu %s, time range %lu to %lu\n\n",
  720. rrdhost_hostname(host),
  721. (first_seen) ? "FIRST SEEN " : "",
  722. server,
  723. mode,
  724. (unsigned long)((first_seen) ? 0 : (now - after)),
  725. (first_seen) ? "never" : "seconds ago",
  726. (unsigned long)after,
  727. (unsigned long)now);
  728. }
  729. return after;
  730. }
  731. /**
  732. * Write metrics and auxiliary information for one host to a buffer.
  733. *
  734. * @param host a data collecting host.
  735. * @param filter_string a simple pattern filter.
  736. * @param wb the buffer to write to.
  737. * @param server the name of a Prometheus server.
  738. * @param prefix a prefix for every metric.
  739. * @param exporting_options options to configure what data is exported.
  740. * @param output_options options to configure the format of the output.
  741. */
  742. void rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(
  743. RRDHOST *host,
  744. const char *filter_string,
  745. BUFFER *wb,
  746. const char *server,
  747. const char *prefix,
  748. EXPORTING_OPTIONS exporting_options,
  749. PROMETHEUS_OUTPUT_OPTIONS output_options)
  750. {
  751. if (unlikely(!prometheus_exporter_instance || !prometheus_exporter_instance->config.initialized))
  752. return;
  753. prometheus_exporter_instance->before = now_realtime_sec();
  754. // we start at the point we had stopped before
  755. prometheus_exporter_instance->after = prometheus_preparation(
  756. prometheus_exporter_instance,
  757. host,
  758. wb,
  759. exporting_options,
  760. server,
  761. prometheus_exporter_instance->before,
  762. output_options);
  763. rrd_stats_api_v1_charts_allmetrics_prometheus(
  764. prometheus_exporter_instance, host, filter_string, wb, prefix, exporting_options, 0, output_options);
  765. }
  766. /**
  767. * Write metrics and auxiliary information for all hosts to a buffer.
  768. *
  769. * @param host a data collecting host.
  770. * @param filter_string a simple pattern filter.
  771. * @param wb the buffer to write to.
  772. * @param server the name of a Prometheus server.
  773. * @param prefix a prefix for every metric.
  774. * @param exporting_options options to configure what data is exported.
  775. * @param output_options options to configure the format of the output.
  776. */
  777. void rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(
  778. RRDHOST *host,
  779. const char *filter_string,
  780. BUFFER *wb,
  781. const char *server,
  782. const char *prefix,
  783. EXPORTING_OPTIONS exporting_options,
  784. PROMETHEUS_OUTPUT_OPTIONS output_options)
  785. {
  786. if (unlikely(!prometheus_exporter_instance || !prometheus_exporter_instance->config.initialized))
  787. return;
  788. prometheus_exporter_instance->before = now_realtime_sec();
  789. // we start at the point we had stopped before
  790. prometheus_exporter_instance->after = prometheus_preparation(
  791. prometheus_exporter_instance,
  792. host,
  793. wb,
  794. exporting_options,
  795. server,
  796. prometheus_exporter_instance->before,
  797. output_options);
  798. dfe_start_reentrant(rrdhost_root_index, host)
  799. {
  800. rrd_stats_api_v1_charts_allmetrics_prometheus(
  801. prometheus_exporter_instance, host, filter_string, wb, prefix, exporting_options, 1, output_options);
  802. }
  803. dfe_done(host);
  804. }