blob: 16e2971d3f7782f9740c5f1f689dae308a8cd29d [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>
Holger Hans Peter Freytherc3376932015-08-21 19:56:54 +000031#include <sys/types.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020032#include <sys/socket.h>
Holger Hans Peter Freytherc3376932015-08-21 19:56:54 +000033#include <netinet/in.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020034#include <arpa/inet.h>
35
36#include <osmocom/core/utils.h>
37#include <osmocom/core/logging.h>
38#include <osmocom/core/rate_ctr.h>
39#include <osmocom/core/stat_item.h>
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010040#include <osmocom/core/timer.h>
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +010041#include <osmocom/core/statistics.h>
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010042#include <osmocom/core/msgb.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020043
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010044#define STATS_DEFAULT_INTERVAL 5 /* secs */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010045#define STATS_DEFAULT_STATSD_BUFLEN 256
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020046
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010047static LLIST_HEAD(osmo_stats_reporter_list);
48static void *osmo_stats_ctx = NULL;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010049static int is_initialised = 0;
50static int32_t current_stat_item_index = 0;
51
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010052static struct osmo_stats_config s_stats_config = {
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010053 .interval = STATS_DEFAULT_INTERVAL,
54};
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010055struct osmo_stats_config *osmo_stats_config = &s_stats_config;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010056
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010057static struct osmo_timer_list osmo_stats_timer;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020058
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010059static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep);
60static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep);
61static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010062 const struct rate_ctr_group *ctrg,
63 const struct rate_ctr_desc *desc,
64 int64_t value, int64_t delta);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010065static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
66 const struct osmo_stat_item_group *statg,
67 const struct osmo_stat_item_desc *desc, int value);
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010068
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010069static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +010070 const struct rate_ctr_group *ctrg,
71 const struct rate_ctr_desc *desc,
72 int64_t value, int64_t delta);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010073static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
74 const struct osmo_stat_item_group *statg,
75 const struct osmo_stat_item_desc *desc, int value);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +010076
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010077static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020078 int data_len);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010079static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020080
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010081static int update_srep_config(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020082{
83 int rc = 0;
84
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020085 if (srep->running) {
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010086 if (srep->close)
87 rc = srep->close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020088 srep->running = 0;
89 }
90
91 if (!srep->enabled)
92 return rc;
93
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010094 if (srep->open)
95 rc = srep->open(srep);
96 else
97 rc = 0;
98
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020099 if (rc < 0)
100 srep->enabled = 0;
101 else
102 srep->running = 1;
103
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100104 srep->force_single_flush = 1;
105
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200106 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 {
Holger Hans Peter Freyther79219752015-11-02 15:50:32 +0100231 inaddr.s_addr = INADDR_ANY;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200232 }
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);
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100283 srep->name_prefix = prefix && strlen(prefix) > 0 ?
284 talloc_strdup(srep, prefix) : NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200285
286 return update_srep_config(srep);
287}
288
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100289int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200290{
291 srep->enabled = 1;
292
293 return update_srep_config(srep);
294}
295
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100296int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200297{
298 srep->enabled = 0;
299
300 return update_srep_config(srep);
301}
302
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100303static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200304 int data_len)
305{
306 int rc;
307
308 rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT,
309 &srep->dest_addr, srep->dest_addr_len);
310
311 if (rc == -1)
312 rc = -errno;
313
314 return rc;
315}
316
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100317static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100318{
319 int rc;
320
321 if (!srep->buffer || msgb_length(srep->buffer) == 0)
322 return 0;
323
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100324 rc = osmo_stats_reporter_send(srep,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100325 (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
326
327 msgb_trim(srep->buffer, 0);
328
329 return rc;
330}
331
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100332static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep,
333 unsigned int index, int class_id)
334{
335 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
336 class_id = index != 0 ?
337 OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL;
338
339 return class_id <= srep->max_class;
340}
341
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100342/*** log reporter ***/
343
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100344struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100345{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100346 struct osmo_stats_reporter *srep;
347 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100348
349 srep->have_net_config = 0;
350
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100351 srep->send_counter = osmo_stats_reporter_log_send_counter;
352 srep->send_item = osmo_stats_reporter_log_send_item;
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100353
354 return srep;
355}
356
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100357static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100358 const char *type,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100359 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100360 const char *unit)
361{
Jacob Erlbeck79125ec2015-11-02 15:17:50 +0100362 LOGP(DLSTATS, LOGL_INFO,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100363 "stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100364 type, srep->name_prefix ? srep->name_prefix : "",
365 name1 ? name1 : "", index1,
366 name2, value, unit ? unit : "");
367
368 return 0;
369}
370
371
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100372static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100373 const struct rate_ctr_group *ctrg,
374 const struct rate_ctr_desc *desc,
375 int64_t value, int64_t delta)
376{
377 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100378 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100379 ctrg->desc->group_name_prefix,
380 ctrg->idx,
381 desc->name, value, NULL);
382 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100383 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100384 NULL, 0,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100385 desc->name, value, NULL);
386}
387
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100388static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
389 const struct osmo_stat_item_group *statg,
390 const struct osmo_stat_item_desc *desc, int value)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100391{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100392 return osmo_stats_reporter_log_send(srep, "i",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100393 statg->desc->group_name_prefix, statg->idx,
394 desc->name, value, desc->unit);
395}
396
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200397/*** statsd reporter ***/
398
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100399struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200400{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100401 struct osmo_stats_reporter *srep;
402 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_STATSD, name);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200403
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100404 srep->have_net_config = 1;
405
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100406 srep->open = osmo_stats_reporter_statsd_open;
407 srep->close = osmo_stats_reporter_statsd_close;
408 srep->send_counter = osmo_stats_reporter_statsd_send_counter;
409 srep->send_item = osmo_stats_reporter_statsd_send_item;
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100410
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200411 return srep;
412}
413
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100414static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200415{
416 int sock;
417 int rc;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100418 int buffer_size = STATS_DEFAULT_STATSD_BUFLEN;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200419
420 if (srep->fd != -1)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100421 osmo_stats_reporter_statsd_close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200422
423 sock = socket(AF_INET, SOCK_DGRAM, 0);
424 if (sock == -1)
425 return -errno;
426
427 if (srep->bind_addr_len > 0) {
428 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
429 if (rc == -1)
430 goto failed;
431 }
432
433 srep->fd = sock;
434
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100435 if (srep->mtu > 0) {
436 buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
437 srep->agg_enabled = 1;
438 }
439
440 srep->buffer = msgb_alloc(buffer_size, "stats buffer");
441
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200442 return 0;
443
444failed:
445 rc = -errno;
446 close(sock);
447
448 return rc;
449}
450
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100451static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200452{
453 int rc;
454 if (srep->fd == -1)
455 return -EBADF;
456
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100457 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100458
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200459 rc = close(srep->fd);
460 srep->fd = -1;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100461 msgb_free(srep->buffer);
462 srep->buffer = NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200463 return rc == -1 ? -errno : 0;
464}
465
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100466static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100467 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200468 const char *unit)
469{
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100470 char *buf;
471 int buf_size;
472 int nchars, rc = 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200473 char *fmt = NULL;
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100474 char *prefix = srep->name_prefix;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100475 int old_len = msgb_length(srep->buffer);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200476
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100477 if (prefix) {
478 if (name1) {
479 if (index1 != 0)
480 fmt = "%1$s.%2$s.%6$u.%3$s:%4$d|%5$s";
481 else
482 fmt = "%1$s.%2$s.%3$s:%4$d|%5$s";
483 } else {
484 fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s";
485 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200486 } else {
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100487 prefix = "";
488 if (name1) {
489 if (index1 != 0)
490 fmt = "%1$s%2$s.%6$u.%3$s:%4$d|%5$s";
491 else
492 fmt = "%1$s%2$s.%3$s:%4$d|%5$s";
493 } else {
494 fmt = "%1$s%2$0.0s%3$s:%4$d|%5$s";
495 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200496 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200497
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100498 if (srep->agg_enabled) {
499 if (msgb_length(srep->buffer) > 0 &&
500 msgb_tailroom(srep->buffer) > 0)
501 {
502 msgb_put_u8(srep->buffer, '\n');
503 }
504 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200505
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100506 buf = (char *)msgb_put(srep->buffer, 0);
507 buf_size = msgb_tailroom(srep->buffer);
508
509 nchars = snprintf(buf, buf_size, fmt,
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100510 prefix, name1, name2,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100511 value, unit, index1);
512
513 if (nchars >= buf_size) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200514 /* Truncated */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100515 /* Restore original buffer (without trailing LF) */
516 msgb_trim(srep->buffer, old_len);
517 /* Send it */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100518 rc = osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200519
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100520 /* Try again */
521 buf = (char *)msgb_put(srep->buffer, 0);
522 buf_size = msgb_tailroom(srep->buffer);
523
524 nchars = snprintf(buf, buf_size, fmt,
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100525 prefix, name1, name2,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100526 value, unit, index1);
527
528 if (nchars >= buf_size)
529 return -EMSGSIZE;
530 }
531
532 if (nchars > 0)
533 msgb_trim(srep->buffer, msgb_length(srep->buffer) + nchars);
534
535 if (!srep->agg_enabled)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100536 rc = osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200537
538 return rc;
539}
540
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100541static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200542 const struct rate_ctr_group *ctrg,
543 const struct rate_ctr_desc *desc,
544 int64_t value, int64_t delta)
545{
546 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100547 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200548 ctrg->desc->group_name_prefix,
549 ctrg->idx,
550 desc->name, delta, "c");
551 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100552 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100553 NULL, 0,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200554 desc->name, delta, "c");
555}
556
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100557static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
558 const struct osmo_stat_item_group *statg,
559 const struct osmo_stat_item_desc *desc, int value)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200560{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100561 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100562 statg->desc->group_name_prefix,
563 statg->idx,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200564 desc->name, value, desc->unit);
565}
566
567/*** generic rate counter support ***/
568
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100569static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200570 const struct rate_ctr_group *ctrg,
571 const struct rate_ctr_desc *desc,
572 int64_t value, int64_t delta)
573{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100574 if (!srep->send_counter)
575 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200576
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100577 return srep->send_counter(srep, ctrg, desc, value, delta);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200578}
579
580static int rate_ctr_handler(
581 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
582 const struct rate_ctr_desc *desc, void *sctx_)
583{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100584 struct osmo_stats_reporter *srep;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200585 int64_t delta = rate_ctr_difference(ctr);
586
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100587 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200588 if (!srep->running)
589 continue;
590
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100591 if (delta == 0 && !srep->force_single_flush)
592 continue;
593
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100594 if (!osmo_stats_reporter_check_config(srep,
595 ctrg->idx, ctrg->desc->class_id))
Jacob Erlbeck8a97cb92015-11-09 11:39:42 +0100596 continue;
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100597
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100598 osmo_stats_reporter_send_counter(srep, ctrg, desc,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200599 ctr->current, delta);
600
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100601 /* TODO: handle result (log?, inc counter(!)?) or remove it */
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200602 }
603
604 return 0;
605}
606
607static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
608{
609 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
610
611 return 0;
612}
613
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100614/*** stat item support ***/
615
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100616static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep,
617 const struct osmo_stat_item_group *statg,
618 const struct osmo_stat_item_desc *desc,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100619 int32_t value)
620{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100621 if (!srep->send_item)
622 return 0;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100623
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100624 return srep->send_item(srep, statg, desc, value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100625}
626
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100627static int osmo_stat_item_handler(
628 struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100629{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100630 struct osmo_stats_reporter *srep;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100631 int32_t idx = current_stat_item_index;
632 int32_t value;
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100633 int have_value;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100634
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100635 have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
636 if (!have_value)
637 /* Send the last value in case a flush is requested */
638 value = osmo_stat_item_get_last(item);
639
640 do {
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100641 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100642 if (!srep->running)
643 continue;
644
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100645 if (!have_value && !srep->force_single_flush)
646 continue;
647
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100648 if (!osmo_stats_reporter_check_config(srep,
649 statg->idx, statg->desc->class_id))
Jacob Erlbeck8a97cb92015-11-09 11:39:42 +0100650 continue;
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100651
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100652 osmo_stats_reporter_send_item(srep, statg,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100653 item->desc, value);
654 }
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100655
656 if (!have_value)
657 break;
658
659 have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
660 } while (have_value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100661
662 return 0;
663}
664
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100665static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100666{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100667 osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100668
669 return 0;
670}
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200671
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100672/*** osmo counter support ***/
673
674static int handle_counter(struct osmo_counter *counter, void *sctx_)
675{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100676 struct osmo_stats_reporter *srep;
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100677 struct rate_ctr_desc desc = {0};
678 /* Fake a rate counter description */
679 desc.name = counter->name;
680 desc.description = counter->description;
681
682 int delta = osmo_counter_difference(counter);
683
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100684 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100685 if (!srep->running)
686 continue;
687
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100688 if (delta == 0 && !srep->force_single_flush)
689 continue;
690
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100691 osmo_stats_reporter_send_counter(srep, NULL, &desc,
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100692 counter->value, delta);
693
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100694 /* TODO: handle result (log?, inc counter(!)?) */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100695 }
696
697 return 0;
698}
699
700
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200701/*** main reporting function ***/
702
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100703static void flush_all_reporters()
704{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100705 struct osmo_stats_reporter *srep;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100706
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100707 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100708 if (!srep->running)
709 continue;
710
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100711 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100712 srep->force_single_flush = 0;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100713 }
714}
715
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100716int osmo_stats_report()
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200717{
Jacob Erlbeck01e8c912015-11-09 14:13:23 +0100718 /* per group actions */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100719 osmo_counters_for_each(handle_counter, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200720 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100721 osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200722
Jacob Erlbeck01e8c912015-11-09 14:13:23 +0100723 /* global actions */
724 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100725 flush_all_reporters();
726
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200727 return 0;
728}