blob: fa56f50aa9d46b899ef90f79184182ce4dc2dbda [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
104 return rc;
105}
106
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100107static void osmo_stats_timer_cb(void *data)
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100108{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100109 int interval = osmo_stats_config->interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100110
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100111 if (!llist_empty(&osmo_stats_reporter_list))
112 osmo_stats_report();
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100113
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100114 osmo_timer_schedule(&osmo_stats_timer, interval, 0);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100115}
116
117static int start_timer()
118{
119 if (!is_initialised)
120 return -ESRCH;
121
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100122 osmo_stats_timer.cb = osmo_stats_timer_cb;
123 osmo_timer_schedule(&osmo_stats_timer, 0, 1);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100124
125 return 0;
126}
127
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100128struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200129 const char *name)
130{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100131 struct osmo_stats_reporter *srep;
132 srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200133 OSMO_ASSERT(srep);
134 srep->type = type;
135 if (name)
136 srep->name = talloc_strdup(srep, name);
137 srep->fd = -1;
138
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100139 llist_add(&srep->list, &osmo_stats_reporter_list);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200140
141 return srep;
142}
143
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100144void osmo_stats_reporter_free(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200145{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100146 osmo_stats_reporter_disable(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200147 llist_del(&srep->list);
148 talloc_free(srep);
149}
150
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100151void osmo_stats_init(void *ctx)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200152{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100153 osmo_stats_ctx = ctx;
154 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100155
156 is_initialised = 1;
157 start_timer();
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200158}
159
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100160struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200161 const char *name)
162{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100163 struct osmo_stats_reporter *srep;
164 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200165 if (srep->type != type)
166 continue;
167 if (srep->name != name) {
168 if (name == NULL || srep->name == NULL ||
169 strcmp(name, srep->name) != 0)
170 continue;
171 }
172 return srep;
173 }
174 return NULL;
175}
176
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100177int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200178{
179 int rc;
180 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
181 struct in_addr inaddr;
182
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100183 if (!srep->have_net_config)
184 return -ENOTSUP;
185
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200186 OSMO_ASSERT(addr != NULL);
187
188 rc = inet_pton(AF_INET, addr, &inaddr);
189 if (rc <= 0)
190 return -EINVAL;
191
192 sock_addr->sin_addr = inaddr;
193 sock_addr->sin_family = AF_INET;
194 srep->dest_addr_len = sizeof(*sock_addr);
195
196 talloc_free(srep->dest_addr_str);
197 srep->dest_addr_str = talloc_strdup(srep, addr);
198
199 return update_srep_config(srep);
200}
201
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100202int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200203{
204 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
205
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100206 if (!srep->have_net_config)
207 return -ENOTSUP;
208
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200209 srep->dest_port = port;
210 sock_addr->sin_port = htons(port);
211
212 return update_srep_config(srep);
213}
214
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100215int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200216{
217 int rc;
218 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
219 struct in_addr inaddr;
220
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100221 if (!srep->have_net_config)
222 return -ENOTSUP;
223
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200224 if (addr) {
225 rc = inet_pton(AF_INET, addr, &inaddr);
226 if (rc <= 0)
227 return -EINVAL;
228 } else {
Holger Hans Peter Freyther79219752015-11-02 15:50:32 +0100229 inaddr.s_addr = INADDR_ANY;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200230 }
231
232 sock_addr->sin_addr = inaddr;
233 sock_addr->sin_family = AF_INET;
234 srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
235
236 talloc_free(srep->bind_addr_str);
237 srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
238
239 return update_srep_config(srep);
240}
241
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100242int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100243{
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100244 if (!srep->have_net_config)
245 return -ENOTSUP;
246
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100247 if (mtu < 0)
248 return -EINVAL;
249
250 srep->mtu = mtu;
251
252 return update_srep_config(srep);
253}
254
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100255int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
256 enum osmo_stats_class class_id)
257{
258 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
259 return -EINVAL;
260
261 srep->max_class = class_id;
262
263 return 0;
264}
265
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100266int osmo_stats_set_interval(int interval)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200267{
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100268 if (interval <= 0)
269 return -EINVAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200270
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100271 osmo_stats_config->interval = interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100272 if (is_initialised)
273 start_timer();
274
275 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200276}
277
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100278int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200279{
280 talloc_free(srep->name_prefix);
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100281 srep->name_prefix = prefix && strlen(prefix) > 0 ?
282 talloc_strdup(srep, prefix) : NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200283
284 return update_srep_config(srep);
285}
286
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100287int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200288{
289 srep->enabled = 1;
290
291 return update_srep_config(srep);
292}
293
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100294int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200295{
296 srep->enabled = 0;
297
298 return update_srep_config(srep);
299}
300
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100301static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200302 int data_len)
303{
304 int rc;
305
306 rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT,
307 &srep->dest_addr, srep->dest_addr_len);
308
309 if (rc == -1)
310 rc = -errno;
311
312 return rc;
313}
314
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100315static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100316{
317 int rc;
318
319 if (!srep->buffer || msgb_length(srep->buffer) == 0)
320 return 0;
321
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100322 rc = osmo_stats_reporter_send(srep,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100323 (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
324
325 msgb_trim(srep->buffer, 0);
326
327 return rc;
328}
329
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100330static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep,
331 unsigned int index, int class_id)
332{
333 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
334 class_id = index != 0 ?
335 OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL;
336
337 return class_id <= srep->max_class;
338}
339
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100340/*** log reporter ***/
341
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100342struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100343{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100344 struct osmo_stats_reporter *srep;
345 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100346
347 srep->have_net_config = 0;
348
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100349 srep->send_counter = osmo_stats_reporter_log_send_counter;
350 srep->send_item = osmo_stats_reporter_log_send_item;
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100351
352 return srep;
353}
354
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100355static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100356 const char *type,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100357 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100358 const char *unit)
359{
Jacob Erlbeck79125ec2015-11-02 15:17:50 +0100360 LOGP(DLSTATS, LOGL_INFO,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100361 "stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100362 type, srep->name_prefix ? srep->name_prefix : "",
363 name1 ? name1 : "", index1,
364 name2, value, unit ? unit : "");
365
366 return 0;
367}
368
369
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100370static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100371 const struct rate_ctr_group *ctrg,
372 const struct rate_ctr_desc *desc,
373 int64_t value, int64_t delta)
374{
375 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100376 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100377 ctrg->desc->group_name_prefix,
378 ctrg->idx,
379 desc->name, value, NULL);
380 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100381 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100382 NULL, 0,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100383 desc->name, value, NULL);
384}
385
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100386static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
387 const struct osmo_stat_item_group *statg,
388 const struct osmo_stat_item_desc *desc, int value)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100389{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100390 return osmo_stats_reporter_log_send(srep, "i",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100391 statg->desc->group_name_prefix, statg->idx,
392 desc->name, value, desc->unit);
393}
394
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200395/*** statsd reporter ***/
396
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100397struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200398{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100399 struct osmo_stats_reporter *srep;
400 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_STATSD, name);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200401
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100402 srep->have_net_config = 1;
403
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100404 srep->open = osmo_stats_reporter_statsd_open;
405 srep->close = osmo_stats_reporter_statsd_close;
406 srep->send_counter = osmo_stats_reporter_statsd_send_counter;
407 srep->send_item = osmo_stats_reporter_statsd_send_item;
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100408
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200409 return srep;
410}
411
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100412static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200413{
414 int sock;
415 int rc;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100416 int buffer_size = STATS_DEFAULT_STATSD_BUFLEN;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200417
418 if (srep->fd != -1)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100419 osmo_stats_reporter_statsd_close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200420
421 sock = socket(AF_INET, SOCK_DGRAM, 0);
422 if (sock == -1)
423 return -errno;
424
425 if (srep->bind_addr_len > 0) {
426 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
427 if (rc == -1)
428 goto failed;
429 }
430
431 srep->fd = sock;
432
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100433 if (srep->mtu > 0) {
434 buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
435 srep->agg_enabled = 1;
436 }
437
438 srep->buffer = msgb_alloc(buffer_size, "stats buffer");
439
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200440 return 0;
441
442failed:
443 rc = -errno;
444 close(sock);
445
446 return rc;
447}
448
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100449static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200450{
451 int rc;
452 if (srep->fd == -1)
453 return -EBADF;
454
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100455 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100456
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200457 rc = close(srep->fd);
458 srep->fd = -1;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100459 msgb_free(srep->buffer);
460 srep->buffer = NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200461 return rc == -1 ? -errno : 0;
462}
463
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100464static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100465 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200466 const char *unit)
467{
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100468 char *buf;
469 int buf_size;
470 int nchars, rc = 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200471 char *fmt = NULL;
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100472 char *prefix = srep->name_prefix;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100473 int old_len = msgb_length(srep->buffer);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200474
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100475 if (prefix) {
476 if (name1) {
477 if (index1 != 0)
478 fmt = "%1$s.%2$s.%6$u.%3$s:%4$d|%5$s";
479 else
480 fmt = "%1$s.%2$s.%3$s:%4$d|%5$s";
481 } else {
482 fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s";
483 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200484 } else {
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100485 prefix = "";
486 if (name1) {
487 if (index1 != 0)
488 fmt = "%1$s%2$s.%6$u.%3$s:%4$d|%5$s";
489 else
490 fmt = "%1$s%2$s.%3$s:%4$d|%5$s";
491 } else {
492 fmt = "%1$s%2$0.0s%3$s:%4$d|%5$s";
493 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200494 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200495
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100496 if (srep->agg_enabled) {
497 if (msgb_length(srep->buffer) > 0 &&
498 msgb_tailroom(srep->buffer) > 0)
499 {
500 msgb_put_u8(srep->buffer, '\n');
501 }
502 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200503
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100504 buf = (char *)msgb_put(srep->buffer, 0);
505 buf_size = msgb_tailroom(srep->buffer);
506
507 nchars = snprintf(buf, buf_size, fmt,
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100508 prefix, name1, name2,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100509 value, unit, index1);
510
511 if (nchars >= buf_size) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200512 /* Truncated */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100513 /* Restore original buffer (without trailing LF) */
514 msgb_trim(srep->buffer, old_len);
515 /* Send it */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100516 rc = osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200517
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100518 /* Try again */
519 buf = (char *)msgb_put(srep->buffer, 0);
520 buf_size = msgb_tailroom(srep->buffer);
521
522 nchars = snprintf(buf, buf_size, fmt,
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100523 prefix, name1, name2,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100524 value, unit, index1);
525
526 if (nchars >= buf_size)
527 return -EMSGSIZE;
528 }
529
530 if (nchars > 0)
531 msgb_trim(srep->buffer, msgb_length(srep->buffer) + nchars);
532
533 if (!srep->agg_enabled)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100534 rc = osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200535
536 return rc;
537}
538
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100539static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200540 const struct rate_ctr_group *ctrg,
541 const struct rate_ctr_desc *desc,
542 int64_t value, int64_t delta)
543{
544 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100545 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200546 ctrg->desc->group_name_prefix,
547 ctrg->idx,
548 desc->name, delta, "c");
549 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100550 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100551 NULL, 0,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200552 desc->name, delta, "c");
553}
554
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100555static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
556 const struct osmo_stat_item_group *statg,
557 const struct osmo_stat_item_desc *desc, int value)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200558{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100559 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100560 statg->desc->group_name_prefix,
561 statg->idx,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200562 desc->name, value, desc->unit);
563}
564
565/*** generic rate counter support ***/
566
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100567static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200568 const struct rate_ctr_group *ctrg,
569 const struct rate_ctr_desc *desc,
570 int64_t value, int64_t delta)
571{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100572 if (!srep->send_counter)
573 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200574
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100575 return srep->send_counter(srep, ctrg, desc, value, delta);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200576}
577
578static int rate_ctr_handler(
579 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
580 const struct rate_ctr_desc *desc, void *sctx_)
581{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100582 struct osmo_stats_reporter *srep;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200583 int64_t delta = rate_ctr_difference(ctr);
584
585 if (delta == 0)
586 return 0;
587
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100588 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200589 if (!srep->running)
590 continue;
591
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100592 if (!osmo_stats_reporter_check_config(srep,
593 ctrg->idx, ctrg->desc->class_id))
594 return 0;
595
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100596 osmo_stats_reporter_send_counter(srep, ctrg, desc,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200597 ctr->current, delta);
598
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100599 /* TODO: handle result (log?, inc counter(!)?) or remove it */
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200600 }
601
602 return 0;
603}
604
605static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
606{
607 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
608
609 return 0;
610}
611
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100612/*** stat item support ***/
613
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100614static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep,
615 const struct osmo_stat_item_group *statg,
616 const struct osmo_stat_item_desc *desc,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100617 int32_t value)
618{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100619 if (!srep->send_item)
620 return 0;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100621
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100622 return srep->send_item(srep, statg, desc, value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100623}
624
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100625static int osmo_stat_item_handler(
626 struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100627{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100628 struct osmo_stats_reporter *srep;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100629 int32_t idx = current_stat_item_index;
630 int32_t value;
631
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100632 while (osmo_stat_item_get_next(item, &idx, &value) > 0) {
633 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100634 if (!srep->running)
635 continue;
636
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100637 if (!osmo_stats_reporter_check_config(srep,
638 statg->idx, statg->desc->class_id))
639 return 0;
640
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100641 osmo_stats_reporter_send_item(srep, statg,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100642 item->desc, value);
643 }
644 }
645
646 return 0;
647}
648
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100649static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100650{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100651 osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_);
652 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100653
654 return 0;
655}
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200656
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100657/*** osmo counter support ***/
658
659static int handle_counter(struct osmo_counter *counter, void *sctx_)
660{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100661 struct osmo_stats_reporter *srep;
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100662 struct rate_ctr_desc desc = {0};
663 /* Fake a rate counter description */
664 desc.name = counter->name;
665 desc.description = counter->description;
666
667 int delta = osmo_counter_difference(counter);
668
669 if (delta == 0)
670 return 0;
671
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100672 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100673 if (!srep->running)
674 continue;
675
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100676 osmo_stats_reporter_send_counter(srep, NULL, &desc,
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100677 counter->value, delta);
678
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100679 /* TODO: handle result (log?, inc counter(!)?) */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100680 }
681
682 return 0;
683}
684
685
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200686/*** main reporting function ***/
687
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100688static void flush_all_reporters()
689{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100690 struct osmo_stats_reporter *srep;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100691
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100692 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100693 if (!srep->running)
694 continue;
695
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100696 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100697 }
698}
699
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100700int osmo_stats_report()
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200701{
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100702 osmo_counters_for_each(handle_counter, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200703 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100704 osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200705
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100706 flush_all_reporters();
707
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200708 return 0;
709}