prometheus.c 33 KB

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