1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3 /* CMetrics
4 * ========
5 * Copyright 2021 Eduardo Silva <eduardo@calyptia.com>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 #include <cmetrics/cmetrics.h>
21 #include <cmetrics/cmt_metric.h>
22 #include <cmetrics/cmt_map.h>
23 #include <cmetrics/cmt_sds.h>
24 #include <cmetrics/cmt_counter.h>
25 #include <cmetrics/cmt_gauge.h>
26 #include <cmetrics/cmt_untyped.h>
27 #include <cmetrics/cmt_time.h>
28 #include <cmetrics/cmt_compat.h>
29
append_metric_value(cmt_sds_t * buf,struct cmt_metric * metric)30 static void append_metric_value(cmt_sds_t *buf, struct cmt_metric *metric)
31 {
32 int len;
33 double val;
34 char tmp[128];
35
36 /* Retrieve metric value */
37 val = cmt_metric_get_value(metric);
38
39 len = snprintf(tmp, sizeof(tmp) - 1, " = %.17g\n", val);
40 cmt_sds_cat_safe(buf, tmp, len);
41 }
42
format_metric(struct cmt * cmt,cmt_sds_t * buf,struct cmt_map * map,struct cmt_metric * metric)43 static void format_metric(struct cmt *cmt, cmt_sds_t *buf, struct cmt_map *map,
44 struct cmt_metric *metric)
45 {
46 int i;
47 int n;
48 int len;
49 int count = 0;
50 int static_labels = 0;
51 char tmp[128];
52 uint64_t ts;
53 struct tm tm;
54 struct timespec tms;
55 struct cmt_map_label *label_k;
56 struct cmt_map_label *label_v;
57 struct mk_list *head;
58 struct cmt_opts *opts;
59 struct cmt_label *slabel;
60
61 opts = map->opts;
62
63 /* timestamp (RFC3339Nano) */
64 ts = cmt_metric_get_timestamp(metric);
65
66 cmt_time_from_ns(&tms, ts);
67
68 cmt_platform_gmtime_r(&tms.tv_sec, &tm);
69 len = strftime(tmp, sizeof(tmp) - 1, "%Y-%m-%dT%H:%M:%S.", &tm);
70 cmt_sds_cat_safe(buf, tmp, len);
71
72 len = snprintf(tmp, sizeof(tmp) - 1, "%09luZ ", tms.tv_nsec);
73 cmt_sds_cat_safe(buf, tmp, len);
74
75 /* Metric info */
76 cmt_sds_cat_safe(buf, opts->fqname, cmt_sds_len(opts->fqname));
77
78 /* Static labels */
79 static_labels = cmt_labels_count(cmt->static_labels);
80 if (static_labels > 0) {
81 cmt_sds_cat_safe(buf, "{", 1);
82 mk_list_foreach(head, &cmt->static_labels->list) {
83 count++;
84 slabel = mk_list_entry(head, struct cmt_label, _head);
85 cmt_sds_cat_safe(buf, slabel->key, cmt_sds_len(slabel->key));
86 cmt_sds_cat_safe(buf, "=\"", 2);
87 cmt_sds_cat_safe(buf, slabel->val, cmt_sds_len(slabel->val));
88 cmt_sds_cat_safe(buf, "\"", 1);
89
90 if (count < static_labels) {
91 cmt_sds_cat_safe(buf, ",", 1);
92 }
93 }
94 }
95
96 n = mk_list_size(&metric->labels);
97 if (n > 0) {
98 if (static_labels > 0) {
99 cmt_sds_cat_safe(buf, ",", 1);
100 }
101 else {
102 cmt_sds_cat_safe(buf, "{", 1);
103 }
104
105 label_k = mk_list_entry_first(&map->label_keys, struct cmt_map_label, _head);
106
107 i = 1;
108 mk_list_foreach(head, &metric->labels) {
109 label_v = mk_list_entry(head, struct cmt_map_label, _head);
110
111 cmt_sds_cat_safe(buf, label_k->name, cmt_sds_len(label_k->name));
112 cmt_sds_cat_safe(buf, "=\"", 2);
113 cmt_sds_cat_safe(buf, label_v->name, cmt_sds_len(label_v->name));
114
115 if (i < n) {
116 cmt_sds_cat_safe(buf, "\",", 2);
117 }
118 else {
119 cmt_sds_cat_safe(buf, "\"", 1);
120 }
121 i++;
122
123 label_k = mk_list_entry_next(&label_k->_head, struct cmt_map_label,
124 _head, &map->label_keys);
125 }
126 cmt_sds_cat_safe(buf, "}", 1);
127
128 append_metric_value(buf, metric);
129 }
130 else {
131 if (static_labels > 0) {
132 cmt_sds_cat_safe(buf, "}", 1);
133 }
134 append_metric_value(buf, metric);
135 }
136 }
137
format_metrics(struct cmt * cmt,cmt_sds_t * buf,struct cmt_map * map)138 static void format_metrics(struct cmt *cmt, cmt_sds_t *buf, struct cmt_map *map)
139 {
140 struct mk_list *head;
141 struct cmt_metric *metric;
142
143 /* Simple metric, no labels */
144 if (map->metric_static_set == 1) {
145 format_metric(cmt, buf, map, &map->metric);
146 }
147
148 mk_list_foreach(head, &map->metrics) {
149 metric = mk_list_entry(head, struct cmt_metric, _head);
150 format_metric(cmt, buf, map, metric);
151 }
152 }
153
154 /* Format all the registered metrics in Prometheus Text format */
cmt_encode_text_create(struct cmt * cmt)155 cmt_sds_t cmt_encode_text_create(struct cmt *cmt)
156 {
157 cmt_sds_t buf;
158 struct mk_list *head;
159 struct cmt_counter *counter;
160 struct cmt_gauge *gauge;
161 struct cmt_untyped *untyped;
162
163 /* Allocate a 1KB of buffer */
164 buf = cmt_sds_create_size(1024);
165 if (!buf) {
166 return NULL;
167 }
168
169 /* Counters */
170 mk_list_foreach(head, &cmt->counters) {
171 counter = mk_list_entry(head, struct cmt_counter, _head);
172 format_metrics(cmt, &buf, counter->map);
173 }
174
175 /* Gauges */
176 mk_list_foreach(head, &cmt->gauges) {
177 gauge = mk_list_entry(head, struct cmt_gauge, _head);
178 format_metrics(cmt, &buf, gauge->map);
179 }
180
181 /* Untyped */
182 mk_list_foreach(head, &cmt->untypeds) {
183 untyped = mk_list_entry(head, struct cmt_untyped, _head);
184 format_metrics(cmt, &buf, untyped->map);
185 }
186
187 return buf;
188 }
189
cmt_encode_text_destroy(cmt_sds_t text)190 void cmt_encode_text_destroy(cmt_sds_t text)
191 {
192 cmt_sds_destroy(text);
193 }
194