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