prometheus.c 33 KB

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