prometheus.c 32 KB

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