prometheus.c 32 KB

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