blob: f979bdc8d68ce427e76ef803b5483d6b8261bc03 [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
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010043#define STATS_DEFAULT_INTERVAL 5 /* secs */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010044#define STATS_DEFAULT_STATSD_BUFLEN 256
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020045
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010046static LLIST_HEAD(osmo_stats_reporter_list);
47static void *osmo_stats_ctx = NULL;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010048static int is_initialised = 0;
49static int32_t current_stat_item_index = 0;
50
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010051static struct osmo_stats_config s_stats_config = {
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010052 .interval = STATS_DEFAULT_INTERVAL,
53};
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010054struct osmo_stats_config *osmo_stats_config = &s_stats_config;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010055
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010056static struct osmo_timer_list osmo_stats_timer;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020057
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010058static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep);
59static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep);
60static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010061 const struct rate_ctr_group *ctrg,
62 const struct rate_ctr_desc *desc,
63 int64_t value, int64_t delta);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010064static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
65 const struct osmo_stat_item_group *statg,
66 const struct osmo_stat_item_desc *desc, int value);
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010067
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010068static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +010069 const struct rate_ctr_group *ctrg,
70 const struct rate_ctr_desc *desc,
71 int64_t value, int64_t delta);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010072static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
73 const struct osmo_stat_item_group *statg,
74 const struct osmo_stat_item_desc *desc, int value);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +010075
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010076static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020077 int data_len);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010078static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020079
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010080static int update_srep_config(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020081{
82 int rc = 0;
83
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020084 if (srep->running) {
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010085 if (srep->close)
86 rc = srep->close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020087 srep->running = 0;
88 }
89
90 if (!srep->enabled)
91 return rc;
92
Jacob Erlbeck490b38f2015-10-27 15:10:28 +010093 if (srep->open)
94 rc = srep->open(srep);
95 else
96 rc = 0;
97
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020098 if (rc < 0)
99 srep->enabled = 0;
100 else
101 srep->running = 1;
102
103 return rc;
104}
105
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100106static void osmo_stats_timer_cb(void *data)
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100107{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100108 int interval = osmo_stats_config->interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100109
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100110 if (!llist_empty(&osmo_stats_reporter_list))
111 osmo_stats_report();
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100112
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100113 osmo_timer_schedule(&osmo_stats_timer, interval, 0);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100114}
115
116static int start_timer()
117{
118 if (!is_initialised)
119 return -ESRCH;
120
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100121 osmo_stats_timer.cb = osmo_stats_timer_cb;
122 osmo_timer_schedule(&osmo_stats_timer, 0, 1);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100123
124 return 0;
125}
126
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100127struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200128 const char *name)
129{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100130 struct osmo_stats_reporter *srep;
131 srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200132 OSMO_ASSERT(srep);
133 srep->type = type;
134 if (name)
135 srep->name = talloc_strdup(srep, name);
136 srep->fd = -1;
137
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100138 llist_add(&srep->list, &osmo_stats_reporter_list);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200139
140 return srep;
141}
142
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100143void osmo_stats_reporter_free(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200144{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100145 osmo_stats_reporter_disable(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200146 llist_del(&srep->list);
147 talloc_free(srep);
148}
149
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100150void osmo_stats_init(void *ctx)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200151{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100152 osmo_stats_ctx = ctx;
153 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100154
155 is_initialised = 1;
156 start_timer();
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200157}
158
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100159struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200160 const char *name)
161{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100162 struct osmo_stats_reporter *srep;
163 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200164 if (srep->type != type)
165 continue;
166 if (srep->name != name) {
167 if (name == NULL || srep->name == NULL ||
168 strcmp(name, srep->name) != 0)
169 continue;
170 }
171 return srep;
172 }
173 return NULL;
174}
175
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100176int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200177{
178 int rc;
179 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
180 struct in_addr inaddr;
181
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100182 if (!srep->have_net_config)
183 return -ENOTSUP;
184
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200185 OSMO_ASSERT(addr != NULL);
186
187 rc = inet_pton(AF_INET, addr, &inaddr);
188 if (rc <= 0)
189 return -EINVAL;
190
191 sock_addr->sin_addr = inaddr;
192 sock_addr->sin_family = AF_INET;
193 srep->dest_addr_len = sizeof(*sock_addr);
194
195 talloc_free(srep->dest_addr_str);
196 srep->dest_addr_str = talloc_strdup(srep, addr);
197
198 return update_srep_config(srep);
199}
200
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100201int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200202{
203 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
204
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100205 if (!srep->have_net_config)
206 return -ENOTSUP;
207
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200208 srep->dest_port = port;
209 sock_addr->sin_port = htons(port);
210
211 return update_srep_config(srep);
212}
213
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100214int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200215{
216 int rc;
217 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
218 struct in_addr inaddr;
219
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100220 if (!srep->have_net_config)
221 return -ENOTSUP;
222
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200223 if (addr) {
224 rc = inet_pton(AF_INET, addr, &inaddr);
225 if (rc <= 0)
226 return -EINVAL;
227 } else {
Holger Hans Peter Freyther79219752015-11-02 15:50:32 +0100228 inaddr.s_addr = INADDR_ANY;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200229 }
230
231 sock_addr->sin_addr = inaddr;
232 sock_addr->sin_family = AF_INET;
233 srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
234
235 talloc_free(srep->bind_addr_str);
236 srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
237
238 return update_srep_config(srep);
239}
240
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100241int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100242{
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100243 if (!srep->have_net_config)
244 return -ENOTSUP;
245
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100246 if (mtu < 0)
247 return -EINVAL;
248
249 srep->mtu = mtu;
250
251 return update_srep_config(srep);
252}
253
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100254int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
255 enum osmo_stats_class class_id)
256{
257 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
258 return -EINVAL;
259
260 srep->max_class = class_id;
261
262 return 0;
263}
264
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100265int osmo_stats_set_interval(int interval)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200266{
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100267 if (interval <= 0)
268 return -EINVAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200269
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100270 osmo_stats_config->interval = interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100271 if (is_initialised)
272 start_timer();
273
274 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200275}
276
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100277int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200278{
279 talloc_free(srep->name_prefix);
280 srep->name_prefix = prefix ? talloc_strdup(srep, prefix) : NULL;
281
282 return update_srep_config(srep);
283}
284
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100285int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200286{
287 srep->enabled = 1;
288
289 return update_srep_config(srep);
290}
291
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100292int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200293{
294 srep->enabled = 0;
295
296 return update_srep_config(srep);
297}
298
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100299static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200300 int data_len)
301{
302 int rc;
303
304 rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT,
305 &srep->dest_addr, srep->dest_addr_len);
306
307 if (rc == -1)
308 rc = -errno;
309
310 return rc;
311}
312
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100313static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100314{
315 int rc;
316
317 if (!srep->buffer || msgb_length(srep->buffer) == 0)
318 return 0;
319
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100320 rc = osmo_stats_reporter_send(srep,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100321 (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
322
323 msgb_trim(srep->buffer, 0);
324
325 return rc;
326}
327
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100328static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep,
329 unsigned int index, int class_id)
330{
331 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
332 class_id = index != 0 ?
333 OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL;
334
335 return class_id <= srep->max_class;
336}
337
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100338/*** log reporter ***/
339
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100340struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100341{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100342 struct osmo_stats_reporter *srep;
343 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100344
345 srep->have_net_config = 0;
346
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100347 srep->send_counter = osmo_stats_reporter_log_send_counter;
348 srep->send_item = osmo_stats_reporter_log_send_item;
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100349
350 return srep;
351}
352
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100353static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100354 const char *type,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100355 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100356 const char *unit)
357{
Jacob Erlbeck79125ec2015-11-02 15:17:50 +0100358 LOGP(DLSTATS, LOGL_INFO,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100359 "stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100360 type, srep->name_prefix ? srep->name_prefix : "",
361 name1 ? name1 : "", index1,
362 name2, value, unit ? unit : "");
363
364 return 0;
365}
366
367
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100368static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100369 const struct rate_ctr_group *ctrg,
370 const struct rate_ctr_desc *desc,
371 int64_t value, int64_t delta)
372{
373 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100374 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100375 ctrg->desc->group_name_prefix,
376 ctrg->idx,
377 desc->name, value, NULL);
378 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100379 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100380 NULL, 0,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100381 desc->name, value, NULL);
382}
383
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100384static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
385 const struct osmo_stat_item_group *statg,
386 const struct osmo_stat_item_desc *desc, int value)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100387{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100388 return osmo_stats_reporter_log_send(srep, "i",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100389 statg->desc->group_name_prefix, statg->idx,
390 desc->name, value, desc->unit);
391}
392
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200393/*** statsd reporter ***/
394
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100395struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200396{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100397 struct osmo_stats_reporter *srep;
398 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_STATSD, name);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200399
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100400 srep->have_net_config = 1;
401
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100402 srep->open = osmo_stats_reporter_statsd_open;
403 srep->close = osmo_stats_reporter_statsd_close;
404 srep->send_counter = osmo_stats_reporter_statsd_send_counter;
405 srep->send_item = osmo_stats_reporter_statsd_send_item;
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100406
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200407 return srep;
408}
409
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100410static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200411{
412 int sock;
413 int rc;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100414 int buffer_size = STATS_DEFAULT_STATSD_BUFLEN;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200415
416 if (srep->fd != -1)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100417 osmo_stats_reporter_statsd_close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200418
419 sock = socket(AF_INET, SOCK_DGRAM, 0);
420 if (sock == -1)
421 return -errno;
422
423 if (srep->bind_addr_len > 0) {
424 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
425 if (rc == -1)
426 goto failed;
427 }
428
429 srep->fd = sock;
430
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100431 if (srep->mtu > 0) {
432 buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
433 srep->agg_enabled = 1;
434 }
435
436 srep->buffer = msgb_alloc(buffer_size, "stats buffer");
437
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200438 return 0;
439
440failed:
441 rc = -errno;
442 close(sock);
443
444 return rc;
445}
446
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100447static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200448{
449 int rc;
450 if (srep->fd == -1)
451 return -EBADF;
452
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100453 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100454
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200455 rc = close(srep->fd);
456 srep->fd = -1;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100457 msgb_free(srep->buffer);
458 srep->buffer = NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200459 return rc == -1 ? -errno : 0;
460}
461
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100462static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100463 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200464 const char *unit)
465{
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100466 char *buf;
467 int buf_size;
468 int nchars, rc = 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200469 char *fmt = NULL;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100470 int old_len = msgb_length(srep->buffer);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200471
472 if (name1) {
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100473 if (index1 != 0)
474 fmt = "%1$s.%2$s.%6$u.%3$s:%4$d|%5$s";
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200475 else
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100476 fmt = "%1$s.%2$s.%3$s:%4$d|%5$s";
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200477 } else {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100478 fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s";
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200479 }
480 if (!srep->name_prefix)
481 fmt += 5; /* skip prefix part */
482
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100483 if (srep->agg_enabled) {
484 if (msgb_length(srep->buffer) > 0 &&
485 msgb_tailroom(srep->buffer) > 0)
486 {
487 msgb_put_u8(srep->buffer, '\n');
488 }
489 }
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200490
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100491 buf = (char *)msgb_put(srep->buffer, 0);
492 buf_size = msgb_tailroom(srep->buffer);
493
494 nchars = snprintf(buf, buf_size, fmt,
495 srep->name_prefix, name1, name2,
496 value, unit, index1);
497
498 if (nchars >= buf_size) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200499 /* Truncated */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100500 /* Restore original buffer (without trailing LF) */
501 msgb_trim(srep->buffer, old_len);
502 /* Send it */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100503 rc = osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200504
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100505 /* Try again */
506 buf = (char *)msgb_put(srep->buffer, 0);
507 buf_size = msgb_tailroom(srep->buffer);
508
509 nchars = snprintf(buf, buf_size, fmt,
510 srep->name_prefix, name1, name2,
511 value, unit, index1);
512
513 if (nchars >= buf_size)
514 return -EMSGSIZE;
515 }
516
517 if (nchars > 0)
518 msgb_trim(srep->buffer, msgb_length(srep->buffer) + nchars);
519
520 if (!srep->agg_enabled)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100521 rc = osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200522
523 return rc;
524}
525
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100526static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200527 const struct rate_ctr_group *ctrg,
528 const struct rate_ctr_desc *desc,
529 int64_t value, int64_t delta)
530{
531 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100532 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200533 ctrg->desc->group_name_prefix,
534 ctrg->idx,
535 desc->name, delta, "c");
536 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100537 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100538 NULL, 0,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200539 desc->name, delta, "c");
540}
541
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100542static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
543 const struct osmo_stat_item_group *statg,
544 const struct osmo_stat_item_desc *desc, int value)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200545{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100546 return osmo_stats_reporter_statsd_send(srep,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100547 statg->desc->group_name_prefix,
548 statg->idx,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200549 desc->name, value, desc->unit);
550}
551
552/*** generic rate counter support ***/
553
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100554static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200555 const struct rate_ctr_group *ctrg,
556 const struct rate_ctr_desc *desc,
557 int64_t value, int64_t delta)
558{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100559 if (!srep->send_counter)
560 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200561
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100562 return srep->send_counter(srep, ctrg, desc, value, delta);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200563}
564
565static int rate_ctr_handler(
566 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
567 const struct rate_ctr_desc *desc, void *sctx_)
568{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100569 struct osmo_stats_reporter *srep;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200570 int64_t delta = rate_ctr_difference(ctr);
571
572 if (delta == 0)
573 return 0;
574
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100575 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200576 if (!srep->running)
577 continue;
578
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100579 if (!osmo_stats_reporter_check_config(srep,
580 ctrg->idx, ctrg->desc->class_id))
581 return 0;
582
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100583 osmo_stats_reporter_send_counter(srep, ctrg, desc,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200584 ctr->current, delta);
585
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100586 /* TODO: handle result (log?, inc counter(!)?) or remove it */
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200587 }
588
589 return 0;
590}
591
592static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
593{
594 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
595
596 return 0;
597}
598
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100599/*** stat item support ***/
600
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100601static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep,
602 const struct osmo_stat_item_group *statg,
603 const struct osmo_stat_item_desc *desc,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100604 int32_t value)
605{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100606 if (!srep->send_item)
607 return 0;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100608
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100609 return srep->send_item(srep, statg, desc, value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100610}
611
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100612static int osmo_stat_item_handler(
613 struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100614{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100615 struct osmo_stats_reporter *srep;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100616 int32_t idx = current_stat_item_index;
617 int32_t value;
618
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100619 while (osmo_stat_item_get_next(item, &idx, &value) > 0) {
620 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100621 if (!srep->running)
622 continue;
623
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100624 if (!osmo_stats_reporter_check_config(srep,
625 statg->idx, statg->desc->class_id))
626 return 0;
627
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100628 osmo_stats_reporter_send_item(srep, statg,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100629 item->desc, value);
630 }
631 }
632
633 return 0;
634}
635
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100636static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100637{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100638 osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_);
639 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100640
641 return 0;
642}
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200643
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100644/*** osmo counter support ***/
645
646static int handle_counter(struct osmo_counter *counter, void *sctx_)
647{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100648 struct osmo_stats_reporter *srep;
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100649 struct rate_ctr_desc desc = {0};
650 /* Fake a rate counter description */
651 desc.name = counter->name;
652 desc.description = counter->description;
653
654 int delta = osmo_counter_difference(counter);
655
656 if (delta == 0)
657 return 0;
658
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100659 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100660 if (!srep->running)
661 continue;
662
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100663 osmo_stats_reporter_send_counter(srep, NULL, &desc,
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100664 counter->value, delta);
665
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100666 /* TODO: handle result (log?, inc counter(!)?) */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100667 }
668
669 return 0;
670}
671
672
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200673/*** main reporting function ***/
674
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100675static void flush_all_reporters()
676{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100677 struct osmo_stats_reporter *srep;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100678
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100679 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100680 if (!srep->running)
681 continue;
682
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100683 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100684 }
685}
686
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100687int osmo_stats_report()
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200688{
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100689 osmo_counters_for_each(handle_counter, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200690 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100691 osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200692
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100693 flush_all_reporters();
694
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200695 return 0;
696}