1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 /*  Fluent Bit
4  *  ==========
5  *  Copyright (C) 2019-2021 The Fluent Bit Authors
6  *  Copyright (C) 2015-2018 Treasure Data Inc.
7  *
8  *  Licensed under the Apache License, Version 2.0 (the "License");
9  *  you may not use this file except in compliance with the License.
10  *  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *  Unless required by applicable law or agreed to in writing, software
15  *  distributed under the License is distributed on an "AS IS" BASIS,
16  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *  See the License for the specific language governing permissions and
18  *  limitations under the License.
19  */
20 
21 /*
22  * Metrics interface is a helper to gather general metrics from the core or
23  * plugins at runtime.
24  */
25 
26 #include <fluent-bit/flb_info.h>
27 #include <fluent-bit/flb_mem.h>
28 #include <fluent-bit/flb_version.h>
29 #include <fluent-bit/flb_utils.h>
30 #include <fluent-bit/flb_metrics.h>
31 #include <msgpack.h>
32 
id_exists(int id,struct flb_metrics * metrics)33 static int id_exists(int id, struct flb_metrics *metrics)
34 {
35     struct mk_list *head;
36     struct flb_metric *metric;
37 
38     mk_list_foreach(head, &metrics->list) {
39         metric = mk_list_entry(head, struct flb_metric, _head);
40         if (metric->id == id) {
41             return FLB_TRUE;
42         }
43     }
44 
45     return FLB_FALSE;
46 }
47 
id_get(struct flb_metrics * metrics)48 static int id_get(struct flb_metrics *metrics)
49 {
50     int id;
51     int ret = FLB_FALSE;
52 
53     /* Try to use 'count' as an id */
54     id = metrics->count;
55 
56     while ((ret = id_exists(id, metrics)) == FLB_TRUE) {
57         id++;
58     }
59 
60     return id;
61 }
62 
flb_metrics_get_id(int id,struct flb_metrics * metrics)63 struct flb_metric *flb_metrics_get_id(int id, struct flb_metrics *metrics)
64 {
65     struct mk_list *head;
66     struct flb_metric *m;
67 
68     mk_list_foreach(head, &metrics->list) {
69         m = mk_list_entry(head, struct flb_metric, _head);
70         if (m->id == id) {
71             return m;
72         }
73     }
74 
75     return NULL;
76 }
77 
flb_metrics_create(const char * title)78 struct flb_metrics *flb_metrics_create(const char *title)
79 {
80     int ret;
81     struct flb_metrics *metrics;
82 
83     /* Create a metrics parent context */
84     metrics = flb_malloc(sizeof(struct flb_metrics));
85     if (!metrics) {
86         flb_errno();
87         return NULL;
88     }
89     metrics->count = 0;
90 
91     /* Set metrics title */
92     ret = flb_metrics_title(title, metrics);
93     if (ret == -1) {
94         flb_free(metrics);
95         return NULL;
96     }
97 
98     /* List head for specific metrics under the context */
99     mk_list_init(&metrics->list);
100     return metrics;
101 }
102 
flb_metrics_title(const char * title,struct flb_metrics * metrics)103 int flb_metrics_title(const char *title, struct flb_metrics *metrics)
104 {
105     int ret;
106 
107     ret = snprintf(metrics->title, sizeof(metrics->title) - 1, "%s", title);
108     if (ret == -1) {
109         flb_errno();
110         return -1;
111     }
112     metrics->title_len = strlen(metrics->title);
113     return 0;
114 }
115 
flb_metrics_add(int id,const char * title,struct flb_metrics * metrics)116 int flb_metrics_add(int id, const char *title, struct flb_metrics *metrics)
117 {
118     int ret;
119     struct flb_metric *m;
120 
121     /* Create context */
122     m = flb_malloc(sizeof(struct flb_metric));
123     if (!m) {
124         flb_errno();
125         return -1;
126     }
127     m->val = 0;
128 
129     /* Write title */
130     ret = snprintf(m->title, sizeof(m->title) - 1, "%s", title);
131     if (ret == -1) {
132         flb_errno();
133         flb_free(m);
134         return -1;
135     }
136     m->title_len = strlen(m->title);
137 
138     /* Assign an ID */
139     if (id >= 0) {
140         /* Check this new ID is available */
141         if (id_exists(id, metrics) == FLB_TRUE) {
142             flb_error("[metrics] id=%i already exists for metric '%s'",
143                       id, metrics->title);
144             flb_free(m);
145             return -1;
146         }
147     }
148     else {
149         id = id_get(metrics);
150     }
151 
152     /* Link to parent list */
153     mk_list_add(&m->_head, &metrics->list);
154     m->id = id;
155     metrics->count++;
156 
157     return id;
158 }
159 
flb_metrics_sum(int id,size_t val,struct flb_metrics * metrics)160 int flb_metrics_sum(int id, size_t val, struct flb_metrics *metrics)
161 {
162     struct flb_metric *m;
163 
164     m = flb_metrics_get_id(id, metrics);
165     if (!m) {
166         return -1;
167     }
168 
169     m->val += val;
170     return 0;
171 }
172 
flb_metrics_destroy(struct flb_metrics * metrics)173 int flb_metrics_destroy(struct flb_metrics *metrics)
174 {
175     int count = 0;
176     struct mk_list *tmp;
177     struct mk_list *head;
178     struct flb_metric *m;
179 
180     mk_list_foreach_safe(head, tmp, &metrics->list) {
181         m = mk_list_entry(head, struct flb_metric, _head);
182         mk_list_del(&m->_head);
183         flb_free(m);
184         count++;
185     }
186 
187     flb_free(metrics);
188     return count;
189 }
190 
flb_metrics_print(struct flb_metrics * metrics)191 int flb_metrics_print(struct flb_metrics *metrics)
192 {
193     struct mk_list *head;
194     struct flb_metric *m;
195 
196     printf("[metric dump] title => '%s'", metrics->title);
197 
198     mk_list_foreach(head, &metrics->list) {
199         m = mk_list_entry(head, struct flb_metric, _head);
200         printf(", '%s' => %lu", m->title, m->val);
201     }
202     printf("\n");
203 
204     return 0;
205 }
206 
207 /* Write metrics in messagepack format */
flb_metrics_dump_values(char ** out_buf,size_t * out_size,struct flb_metrics * me)208 int flb_metrics_dump_values(char **out_buf, size_t *out_size,
209                             struct flb_metrics *me)
210 {
211     struct mk_list *head;
212     struct flb_metric *m;
213     msgpack_sbuffer mp_sbuf;
214     msgpack_packer mp_pck;
215 
216     /* Prepare new outgoing buffer */
217     msgpack_sbuffer_init(&mp_sbuf);
218     msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write);
219 
220     msgpack_pack_map(&mp_pck, me->count);
221 
222     mk_list_foreach(head, &me->list) {
223         m = mk_list_entry(head, struct flb_metric, _head);
224         msgpack_pack_str(&mp_pck, m->title_len);
225         msgpack_pack_str_body(&mp_pck, m->title, m->title_len);
226         msgpack_pack_uint64(&mp_pck, m->val);
227     }
228 
229     *out_buf  = mp_sbuf.data;
230     *out_size = mp_sbuf.size;
231 
232     return 0;
233 }
234 
attach_uptime(struct flb_config * ctx,struct cmt * cmt,uint64_t ts,char * hostname)235 static int attach_uptime(struct flb_config *ctx, struct cmt *cmt,
236                          uint64_t ts, char *hostname)
237 {
238     double uptime;
239     struct cmt_counter *c;
240 
241     /* uptime */
242     c = cmt_counter_create(cmt, "fluentbit", "", "uptime",
243                            "Number of seconds that Fluent Bit has been running.",
244                            1, (char *[]) {"hostname"});
245     if (!c) {
246         return -1;
247     }
248 
249     uptime = time(NULL) - ctx->init_time;
250 
251     cmt_counter_set(c, ts, uptime, 1, (char *[]) {hostname});
252     return 0;
253 }
254 
attach_process_start_time_seconds(struct flb_config * ctx,struct cmt * cmt,uint64_t ts,char * hostname)255 static int attach_process_start_time_seconds(struct flb_config *ctx,
256                                              struct cmt *cmt,
257                                              uint64_t ts, char *hostname)
258 {
259     double val;
260     struct cmt_gauge *g;
261 
262     g = cmt_gauge_create(cmt, "fluentbit", "", "process_start_time_seconds",
263                          "Start time of the process since unix epoch in seconds.",
264                          1, (char *[]) {"hostname"});
265     if (!g) {
266         return -1;
267     }
268 
269     val = (double) ctx->init_time;
270     cmt_gauge_set(g, ts, val, 1, (char *[]) {hostname});
271     return 0;
272 }
273 
get_os_name()274 static char *get_os_name()
275 {
276 #ifdef _WIN64
277     return "win64";
278 #elif _WIN32
279     return "win32";
280 #elif __APPLE__ || __MACH__
281     return "macos";
282 #elif __linux__
283     return "linux";
284 #elif __FreeBSD__
285     return "freebsd";
286 #elif __unix || __unix__
287     return "unix";
288 #else
289     return "other";
290 #endif
291 }
292 
attach_build_info(struct flb_config * ctx,struct cmt * cmt,uint64_t ts,char * hostname)293 static int attach_build_info(struct flb_config *ctx, struct cmt *cmt, uint64_t ts,
294                              char *hostname)
295 {
296     double val;
297     char *os;
298     struct cmt_gauge *g;
299 
300     g = cmt_gauge_create(cmt, "fluentbit", "build", "info",
301                          "Build version information.",
302                          3, (char *[]) {"hostname", "version", "os"});
303     if (!g) {
304         return -1;
305     }
306 
307     val = (double) ctx->init_time;
308     os = get_os_name();
309 
310     cmt_gauge_set(g, ts, val, 3, (char *[]) {hostname, FLB_VERSION_STR, os});
311     return 0;
312 }
313 
314 /* Append internal Fluent Bit metrics to context */
flb_metrics_fluentbit_add(struct flb_config * ctx,struct cmt * cmt)315 int flb_metrics_fluentbit_add(struct flb_config *ctx, struct cmt *cmt)
316 {
317     int ret;
318     size_t ts;
319     char hostname[128];
320 
321     /* current timestamp */
322     ts = cmt_time_now();
323 
324     /* get hostname */
325     ret = gethostname(hostname, sizeof(hostname) - 1);
326     if (ret == -1) {
327         strncpy(hostname, "unknown", 7);
328         hostname[7] = '\0';
329     }
330 
331     /* Attach metrics to cmetrics context */
332     attach_uptime(ctx, cmt, ts, hostname);
333     attach_process_start_time_seconds(ctx, cmt, ts, hostname);
334     attach_build_info(ctx, cmt, ts, hostname);
335 
336     return 0;
337 }
338