blob: bdb0fbe6bf06fabb6b2571b3dad6f01585af489b [file] [log] [blame]
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02001/*
2 * (C) 2015 by Sysmocom s.f.m.c. GmbH
3 *
4 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
5 *
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24#include <osmocom/core/stats.h>
25
26#include <unistd.h>
27#include <string.h>
28#include <stdint.h>
29#include <errno.h>
30#include <stdio.h>
31#include <sys/socket.h>
32#include <netinet/ip.h>
33#include <arpa/inet.h>
34
35#include <osmocom/core/utils.h>
36#include <osmocom/core/logging.h>
37#include <osmocom/core/rate_ctr.h>
38#include <osmocom/core/stat_item.h>
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010039#include <osmocom/core/timer.h>
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +010040#include <osmocom/core/statistics.h>
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010041#include <osmocom/core/msgb.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020042
43/* TODO: register properly */
44#define DSTATS DLGLOBAL
45
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010046#define STATS_DEFAULT_INTERVAL 5 /* secs */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010047#define STATS_DEFAULT_STATSD_BUFLEN 256
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020048
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010049static LLIST_HEAD(osmo_stats_reporter_list);
50static void *osmo_stats_ctx = NULL;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010051static int is_initialised = 0;
52static int32_t current_stat_item_index = 0;
53
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010054static struct osmo_stats_config s_stats_config = {
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010055 .interval = STATS_DEFAULT_INTERVAL,
56};
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010057struct osmo_stats_config *osmo_stats_config = &s_stats_config;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010058
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010059static struct osmo_timer_list osmo_stats_timer;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020060
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010061static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep);
62static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep);
63static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010064 const struct rate_ctr_group *ctrg,
65 const struct rate_ctr_desc *desc,
66 int64_t value, int64_t delta);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010067static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
68 const struct osmo_stat_item_group *statg,
69 const struct osmo_stat_item_desc *desc, int value);
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010070
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010071static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +010072 const struct rate_ctr_group *ctrg,
73 const struct rate_ctr_desc *desc,
74 int64_t value, int64_t delta);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010075static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
76 const struct osmo_stat_item_group *statg,
77 const struct osmo_stat_item_desc *desc, int value);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +010078
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010079static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020080 int data_len);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010081static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020082
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010083static int update_srep_config(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020084{
85 int rc = 0;
86
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020087 if (srep->running) {
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010088 if (srep->close)
89 rc = srep->close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020090 srep->running = 0;
91 }
92
93 if (!srep->enabled)
94 return rc;
95
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010096 if (srep->open)
97 rc = srep->open(srep);
98 else
99 rc = 0;
100
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200101 if (rc < 0)
102 srep->enabled = 0;
103 else
104 srep->running = 1;
105
106 return rc;
107}
108
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100109static void osmo_stats_timer_cb(void *data)
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100110{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100111 int interval = osmo_stats_config->interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100112
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100113 if (!llist_empty(&osmo_stats_reporter_list))
114 osmo_stats_report();
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100115
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100116 osmo_timer_schedule(&osmo_stats_timer, interval, 0);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100117}
118
119static int start_timer()
120{
121 if (!is_initialised)
122 return -ESRCH;
123
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100124 osmo_stats_timer.cb = osmo_stats_timer_cb;
125 osmo_timer_schedule(&osmo_stats_timer, 0, 1);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100126
127 return 0;
128}
129
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100130struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200131 const char *name)
132{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100133 struct osmo_stats_reporter *srep;
134 srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200135 OSMO_ASSERT(srep);
136 srep->type = type;
137 if (name)
138 srep->name = talloc_strdup(srep, name);
139 srep->fd = -1;
140
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100141 llist_add(&srep->list, &osmo_stats_reporter_list);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200142
143 return srep;
144}
145
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100146void osmo_stats_reporter_free(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200147{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100148 osmo_stats_reporter_disable(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200149 llist_del(&srep->list);
150 talloc_free(srep);
151}
152
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100153void osmo_stats_init(void *ctx)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200154{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100155 osmo_stats_ctx = ctx;
156 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100157
158 is_initialised = 1;
159 start_timer();
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200160}
161
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100162struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200163 const char *name)
164{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100165 struct osmo_stats_reporter *srep;
166 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200167 if (srep->type != type)
168 continue;
169 if (srep->name != name) {
170 if (name == NULL || srep->name == NULL ||
171 strcmp(name, srep->name) != 0)
172 continue;
173 }
174 return srep;
175 }
176 return NULL;
177}
178
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100179int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200180{
181 int rc;
182 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
183 struct in_addr inaddr;
184
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100185 if (!srep->have_net_config)
186 return -ENOTSUP;
187
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200188 OSMO_ASSERT(addr != NULL);
189
190 rc = inet_pton(AF_INET, addr, &inaddr);
191 if (rc <= 0)
192 return -EINVAL;
193
194 sock_addr->sin_addr = inaddr;
195 sock_addr->sin_family = AF_INET;
196 srep->dest_addr_len = sizeof(*sock_addr);
197
198 talloc_free(srep->dest_addr_str);
199 srep->dest_addr_str = talloc_strdup(srep, addr);
200
201 return update_srep_config(srep);
202}
203
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100204int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200205{
206 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
207
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100208 if (!srep->have_net_config)
209 return -ENOTSUP;
210
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200211 srep->dest_port = port;
212 sock_addr->sin_port = htons(port);
213
214 return update_srep_config(srep);
215}
216
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100217int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200218{
219 int rc;
220 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
221 struct in_addr inaddr;
222
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100223 if (!srep->have_net_config)
224 return -ENOTSUP;
225
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200226 if (addr) {
227 rc = inet_pton(AF_INET, addr, &inaddr);
228 if (rc <= 0)
229 return -EINVAL;
230 } else {
231 addr = INADDR_ANY;
232 }
233
234 sock_addr->sin_addr = inaddr;
235 sock_addr->sin_family = AF_INET;
236 srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
237
238 talloc_free(srep->bind_addr_str);
239 srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
240
241 return update_srep_config(srep);
242}
243
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100244int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100245{
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100246 if (!srep->have_net_config)
247 return -ENOTSUP;
248
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100249 if (mtu < 0)
250 return -EINVAL;
251
252 srep->mtu = mtu;
253
254 return update_srep_config(srep);
255}
256
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100257int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
258 enum osmo_stats_class class_id)
259{
260 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
261 return -EINVAL;
262
263 srep->max_class = class_id;
264
265 return 0;
266}
267
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100268int osmo_stats_set_interval(int interval)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200269{
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100270 if (interval <= 0)
271 return -EINVAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200272
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100273 osmo_stats_config->interval = interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100274 if (is_initialised)
275 start_timer();
276
277 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200278}
279
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100280int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200281{
282 talloc_free(srep->name_prefix);
283 srep->name_prefix = prefix ? talloc_strdup(srep, prefix) : NULL;
284
285 return update_srep_config(srep);
286}
287
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100288int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200289{
290 srep->enabled = 1;
291
292 return update_srep_config(srep);
293}
294
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100295int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200296{
297 srep->enabled = 0;
298
299 return update_srep_config(srep);
300}
301
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100302static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200303 int data_len)
304{
305 int rc;
306
307 rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT,
308 &srep->dest_addr, srep->dest_addr_len);
309
310 if (rc == -1)
311 rc = -errno;
312
313 return rc;
314}
315
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100316static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100317{
318 int rc;
319
320 if (!srep->buffer || msgb_length(srep->buffer) == 0)
321 return 0;
322
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100323 rc = osmo_stats_reporter_send(srep,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100324 (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
325
326 msgb_trim(srep->buffer, 0);
327
328 return rc;
329}
330
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100331static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep,
332 unsigned int index, int class_id)
333{
334 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
335 class_id = index != 0 ?
336 OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL;
337
338 return class_id <= srep->max_class;
339}
340
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100341/*** log reporter ***/
342
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100343struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100344{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100345 struct osmo_stats_reporter *srep;
346 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100347
348 srep->have_net_config = 0;
349
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100350 srep->send_counter = osmo_stats_reporter_log_send_counter;
351 srep->send_item = osmo_stats_reporter_log_send_item;
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100352
353 return srep;
354}
355
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100356static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100357 const char *type,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100358 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100359 const char *unit)
360{
361 LOGP(DSTATS, LOGL_INFO,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100362 "stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100363 type, srep->name_prefix ? srep->name_prefix : "",
364 name1 ? name1 : "", index1,
365 name2, value, unit ? unit : "");
366
367 return 0;
368}
369
370
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100371static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100372 const struct rate_ctr_group *ctrg,
373 const struct rate_ctr_desc *desc,
374 int64_t value, int64_t delta)
375{
376 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100377 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100378 ctrg->desc->group_name_prefix,
379 ctrg->idx,
380 desc->name, value, NULL);
381 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100382 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100383 NULL, 0,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100384 desc->name, value, NULL);
385}
386
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100387static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
388 const struct osmo_stat_item_group *statg,
389 const struct osmo_stat_item_desc *desc, int value)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100390{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100391 return osmo_stats_reporter_log_send(srep, "i",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100392 statg->desc->group_name_prefix, statg->idx,
393 desc->name, value, desc->unit);
394}
395
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200396/*** statsd reporter ***/
397
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100398struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200399{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100400 struct osmo_stats_reporter *srep;
401 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_STATSD, name);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200402
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100403 srep->have_net_config = 1;
404
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100405 srep->open = osmo_stats_reporter_statsd_open;
406 srep->close = osmo_stats_reporter_statsd_close;
407 srep->send_counter = osmo_stats_reporter_statsd_send_counter;
408 srep->send_item = osmo_stats_reporter_statsd_send_item;
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100409
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200410 return srep;
411}
412
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100413static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200414{
415 int sock;
416 int rc;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100417 int buffer_size = STATS_DEFAULT_STATSD_BUFLEN;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200418
419 if (srep->fd != -1)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100420 osmo_stats_reporter_statsd_close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200421
422 sock = socket(AF_INET, SOCK_DGRAM, 0);
423 if (sock == -1)
424 return -errno;
425
426 if (srep->bind_addr_len > 0) {
427 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
428 if (rc == -1)
429 goto failed;
430 }
431
432 srep->fd = sock;
433
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100434 if (srep->mtu > 0) {
435 buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
436 srep->agg_enabled = 1;
437 }
438
439 srep->buffer = msgb_alloc(buffer_size, "stats buffer");
440
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200441 return 0;
442
443failed:
444 rc = -errno;
445 close(sock);
446
447 return rc;
448}
449
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100450static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200451{
452 int rc;
453 if (srep->fd == -1)
454 return -EBADF;
455
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100456 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100457
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200458 rc = close(srep->fd);
459 srep->fd = -1;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100460 msgb_free(srep->buffer);
461 srep->buffer = NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200462 return rc == -1 ? -errno : 0;
463}
464
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100465static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100466 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200467 const char *unit)
468{
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100469 char *buf;
470 int buf_size;
471 int nchars, rc = 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200472 char *fmt = NULL;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100473 int old_len = msgb_length(srep->buffer);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200474
475 if (name1) {
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100476 if (index1 != 0)
477 fmt = "%1$s.%2$s.%6$u.%3$s:%4$d|%5$s";
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200478 else
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100479 fmt = "%1$s.%2$s.%3$s:%4$d|%5$s";
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200480 } else {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100481 fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s";
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200482 }
483 if (!srep->name_prefix)
484 fmt += 5; /* skip prefix part */
485
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100486 if (srep->agg_enabled) {
487 if (msgb_length(srep->buffer) > 0 &&
488 msgb_tailroom(srep->buffer) > 0)
489 {
490 msgb_put_u8(srep->buffer, '\n');
491 }
492 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200493
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100494 buf = (char *)msgb_put(srep->buffer, 0);
495 buf_size = msgb_tailroom(srep->buffer);
496
497 nchars = snprintf(buf, buf_size, fmt,
498 srep->name_prefix, name1, name2,
499 value, unit, index1);
500
501 if (nchars >= buf_size) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200502 /* Truncated */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100503 /* Restore original buffer (without trailing LF) */
504 msgb_trim(srep->buffer, old_len);
505 /* Send it */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100506 rc = osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200507
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100508 /* Try again */
509 buf = (char *)msgb_put(srep->buffer, 0);
510 buf_size = msgb_tailroom(srep->buffer);
511
512 nchars = snprintf(buf, buf_size, fmt,
513 srep->name_prefix, name1, name2,
514 value, unit, index1);
515
516 if (nchars >= buf_size)
517 return -EMSGSIZE;
518 }
519
520 if (nchars > 0)
521 msgb_trim(srep->buffer, msgb_length(srep->buffer) + nchars);
522
523 if (!srep->agg_enabled)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100524 rc = osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200525
526 return rc;
527}
528
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100529static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200530 const struct rate_ctr_group *ctrg,
531 const struct rate_ctr_desc *desc,
532 int64_t value, int64_t delta)
533{
534 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100535 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200536 ctrg->desc->group_name_prefix,
537 ctrg->idx,
538 desc->name, delta, "c");
539 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100540 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100541 NULL, 0,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200542 desc->name, delta, "c");
543}
544
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100545static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
546 const struct osmo_stat_item_group *statg,
547 const struct osmo_stat_item_desc *desc, int value)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200548{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100549 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100550 statg->desc->group_name_prefix,
551 statg->idx,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200552 desc->name, value, desc->unit);
553}
554
555/*** generic rate counter support ***/
556
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100557static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200558 const struct rate_ctr_group *ctrg,
559 const struct rate_ctr_desc *desc,
560 int64_t value, int64_t delta)
561{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100562 if (!srep->send_counter)
563 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200564
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100565 return srep->send_counter(srep, ctrg, desc, value, delta);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200566}
567
568static int rate_ctr_handler(
569 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
570 const struct rate_ctr_desc *desc, void *sctx_)
571{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100572 struct osmo_stats_reporter *srep;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200573 int rc;
574 int64_t delta = rate_ctr_difference(ctr);
575
576 if (delta == 0)
577 return 0;
578
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100579 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200580 if (!srep->running)
581 continue;
582
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100583 if (!osmo_stats_reporter_check_config(srep,
584 ctrg->idx, ctrg->desc->class_id))
585 return 0;
586
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100587 rc = osmo_stats_reporter_send_counter(srep, ctrg, desc,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200588 ctr->current, delta);
589
590 /* TODO: handle rc (log?, inc counter(!)?) or remove it */
591 }
592
593 return 0;
594}
595
596static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
597{
598 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
599
600 return 0;
601}
602
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100603/*** stat item support ***/
604
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100605static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep,
606 const struct osmo_stat_item_group *statg,
607 const struct osmo_stat_item_desc *desc,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100608 int32_t value)
609{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100610 if (!srep->send_item)
611 return 0;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100612
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100613 return srep->send_item(srep, statg, desc, value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100614}
615
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100616static int osmo_stat_item_handler(
617 struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100618{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100619 struct osmo_stats_reporter *srep;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100620 int rc;
621 int32_t idx = current_stat_item_index;
622 int32_t value;
623
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100624 while (osmo_stat_item_get_next(item, &idx, &value) > 0) {
625 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100626 if (!srep->running)
627 continue;
628
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100629 if (!osmo_stats_reporter_check_config(srep,
630 statg->idx, statg->desc->class_id))
631 return 0;
632
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100633 rc = osmo_stats_reporter_send_item(srep, statg,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100634 item->desc, value);
635 }
636 }
637
638 return 0;
639}
640
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100641static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100642{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100643 osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_);
644 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100645
646 return 0;
647}
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200648
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100649/*** osmo counter support ***/
650
651static int handle_counter(struct osmo_counter *counter, void *sctx_)
652{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100653 struct osmo_stats_reporter *srep;
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100654 int rc;
655 struct rate_ctr_desc desc = {0};
656 /* Fake a rate counter description */
657 desc.name = counter->name;
658 desc.description = counter->description;
659
660 int delta = osmo_counter_difference(counter);
661
662 if (delta == 0)
663 return 0;
664
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100665 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100666 if (!srep->running)
667 continue;
668
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100669 rc = osmo_stats_reporter_send_counter(srep, NULL, &desc,
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100670 counter->value, delta);
671
672 /* TODO: handle rc (log?, inc counter(!)?) */
673 }
674
675 return 0;
676}
677
678
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200679/*** main reporting function ***/
680
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100681static void flush_all_reporters()
682{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100683 struct osmo_stats_reporter *srep;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100684
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100685 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100686 if (!srep->running)
687 continue;
688
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100689 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100690 }
691}
692
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100693int osmo_stats_report()
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200694{
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100695 osmo_counters_for_each(handle_counter, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200696 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100697 osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200698
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100699 flush_all_reporters();
700
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200701 return 0;
702}