blob: 61b7df87d04d2b52d7bf86db4ae55757110fbad2 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file stats.c */
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02002/*
Harald Weltee08da972017-11-13 01:00:26 +09003 * (C) 2015 by sysmocom - s.f.m.c. GmbH
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02004 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02005 * All Rights Reserved
6 *
Harald Weltee08da972017-11-13 01:00:26 +09007 * SPDX-License-Identifier: GPL-2.0+
8 *
Jacob Erlbeck95bf82802015-10-20 19:05:52 +02009 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 */
24
Harald Welteeb5b6ce2017-10-15 20:03:24 +020025/*! \addtogroup stats
26 * @{
27 *
28 * This module implements periodic reporting of statistics / counters.
29 * It supports the notion of multiple \ref osmo_stats_reporter objects
30 * which independently of each other can report statistics at different
31 * configurable intervals to different destinations.
32 *
33 * In order to use this facility, you have to call \ref
34 * osmo_stats_init() once at application start-up and then create one or
35 * more \ref osmo_stats_reporter, either using the direct API functions
36 * or by using the optional VTY bindings:
37 *
38 * - reporting to any of the libosmocore log targets
39 * \ref osmo_stats_reporter_create_log() creates a new stats_reporter
40 * which reports to the libosmcoore \ref logging subsystem.
41 *
42 * - reporting to statsd (a front-end proxy for the Graphite/Carbon
43 * metrics server
44 * \ref osmo_stats_reporter_create_statsd() creates a new stats_reporter
45 * which reports via UDP to statsd.
46 *
47 * You can either use the above API functions directly to create \ref
48 * osmo_stats_reporter instances, or you can use the VTY support
49 * contained in libosmovty. See the "stats" configuration node
50 * installed by osmo_stats_vty_Add_cmds().
51 *
52 * An \ref osmo_stats_reporter reports statistics on all of the following
53 * libosmocore internal counter/statistics objects:
54 * - \ref osmo_counter
55 * - \ref rate_ctr
56 * - \ref osmo_stat_item
57 *
58 * You do not need to do anything in particular to expose a given
59 * counter or stat_item, they are all exported automatically via any
60 * \ref osmo_stats_reporter. If you have multiple \ref
61 * osmo_stats_reporter, they will each report all counters/stat_items.
62 *
63 * \file stats.c */
64
Harald Welte67bdd802017-01-15 17:56:11 +010065#include "config.h"
66#if !defined(EMBEDDED)
67
Harald Welte95871da2017-05-15 12:11:36 +020068#include <osmocom/core/byteswap.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020069#include <osmocom/core/stats.h>
70
71#include <unistd.h>
72#include <string.h>
73#include <stdint.h>
74#include <errno.h>
75#include <stdio.h>
Holger Hans Peter Freytherc3376932015-08-21 19:56:54 +000076#include <sys/types.h>
Harald Welte67bdd802017-01-15 17:56:11 +010077
78#ifdef HAVE_SYS_SOCKET_H
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020079#include <sys/socket.h>
Holger Hans Peter Freytherc3376932015-08-21 19:56:54 +000080#include <netinet/in.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020081#include <arpa/inet.h>
Harald Welte67bdd802017-01-15 17:56:11 +010082#endif
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020083
84#include <osmocom/core/utils.h>
85#include <osmocom/core/logging.h>
86#include <osmocom/core/rate_ctr.h>
87#include <osmocom/core/stat_item.h>
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010088#include <osmocom/core/timer.h>
Harald Welte216338c2017-10-15 19:46:19 +020089#include <osmocom/core/counter.h>
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +010090#include <osmocom/core/msgb.h>
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020091
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010092#define STATS_DEFAULT_INTERVAL 5 /* secs */
Jacob Erlbeckb6e6bea2015-11-09 15:33:44 +010093#define STATS_DEFAULT_BUFLEN 256
Jacob Erlbeck95bf82802015-10-20 19:05:52 +020094
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +010095static LLIST_HEAD(osmo_stats_reporter_list);
96static void *osmo_stats_ctx = NULL;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +010097static int is_initialised = 0;
98static int32_t current_stat_item_index = 0;
99
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100100static struct osmo_stats_config s_stats_config = {
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100101 .interval = STATS_DEFAULT_INTERVAL,
102};
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100103struct osmo_stats_config *osmo_stats_config = &s_stats_config;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100104
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100105static struct osmo_timer_list osmo_stats_timer;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200106
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100107static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100108 const struct rate_ctr_group *ctrg,
109 const struct rate_ctr_desc *desc,
110 int64_t value, int64_t delta);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100111static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
112 const struct osmo_stat_item_group *statg,
Harald Welte1554f802016-11-11 15:06:06 +0100113 const struct osmo_stat_item_desc *desc, int64_t value);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100114
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100115static int update_srep_config(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200116{
117 int rc = 0;
118
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200119 if (srep->running) {
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100120 if (srep->close)
121 rc = srep->close(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200122 srep->running = 0;
123 }
124
125 if (!srep->enabled)
126 return rc;
127
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100128 if (srep->open)
129 rc = srep->open(srep);
130 else
131 rc = 0;
132
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200133 if (rc < 0)
134 srep->enabled = 0;
135 else
136 srep->running = 1;
137
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100138 srep->force_single_flush = 1;
139
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200140 return rc;
141}
142
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100143static void osmo_stats_timer_cb(void *data)
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100144{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100145 int interval = osmo_stats_config->interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100146
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100147 if (!llist_empty(&osmo_stats_reporter_list))
148 osmo_stats_report();
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100149
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100150 osmo_timer_schedule(&osmo_stats_timer, interval, 0);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100151}
152
153static int start_timer()
154{
155 if (!is_initialised)
156 return -ESRCH;
157
Pablo Neira Ayuso44f423f2017-05-08 18:00:28 +0200158 osmo_timer_setup(&osmo_stats_timer, osmo_stats_timer_cb, NULL);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100159 osmo_timer_schedule(&osmo_stats_timer, 0, 1);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100160
161 return 0;
162}
163
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100164struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200165 const char *name)
166{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100167 struct osmo_stats_reporter *srep;
168 srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200169 OSMO_ASSERT(srep);
170 srep->type = type;
171 if (name)
172 srep->name = talloc_strdup(srep, name);
173 srep->fd = -1;
174
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100175 llist_add(&srep->list, &osmo_stats_reporter_list);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200176
177 return srep;
178}
179
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200180/*! Destroy a given stats_reporter. Takes care of first disabling it.
181 * \param[in] srep stats_reporter that shall be disabled + destroyed */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100182void osmo_stats_reporter_free(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200183{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100184 osmo_stats_reporter_disable(srep);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200185 llist_del(&srep->list);
186 talloc_free(srep);
187}
188
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200189/*! Initilize the stats reporting module; call this once in your program
190 * \param[in] ctx Talloc context from which stats related memory is allocated */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100191void osmo_stats_init(void *ctx)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200192{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100193 osmo_stats_ctx = ctx;
194 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100195
196 is_initialised = 1;
197 start_timer();
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200198}
199
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200200/*! Find a stats_reporter of given \a type and \a name.
201 * \param[in] type Type of stats_reporter to find
202 * \param[in] name Name of stats_reporter to find
203 * \returns stats_reporter matching \a type and \a name; NULL otherwise */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100204struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200205 const char *name)
206{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100207 struct osmo_stats_reporter *srep;
208 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200209 if (srep->type != type)
210 continue;
211 if (srep->name != name) {
212 if (name == NULL || srep->name == NULL ||
213 strcmp(name, srep->name) != 0)
214 continue;
215 }
216 return srep;
217 }
218 return NULL;
219}
220
Harald Welte67bdd802017-01-15 17:56:11 +0100221#ifdef HAVE_SYS_SOCKET_H
222
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200223/*! Set the remote (IP) address of a given stats_reporter.
224 * \param[in] srep stats_reporter whose remote address is to be set
225 * \param[in] addr String representation of remote IPv4 address
226 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100227int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200228{
229 int rc;
230 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
231 struct in_addr inaddr;
232
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100233 if (!srep->have_net_config)
234 return -ENOTSUP;
235
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200236 OSMO_ASSERT(addr != NULL);
237
238 rc = inet_pton(AF_INET, addr, &inaddr);
239 if (rc <= 0)
240 return -EINVAL;
241
242 sock_addr->sin_addr = inaddr;
243 sock_addr->sin_family = AF_INET;
244 srep->dest_addr_len = sizeof(*sock_addr);
245
246 talloc_free(srep->dest_addr_str);
247 srep->dest_addr_str = talloc_strdup(srep, addr);
248
249 return update_srep_config(srep);
250}
251
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200252/*! Set the remote (UDP) port of a given stats_reporter
253 * \param[in] srep stats_reporter whose remote address is to be set
254 * \param[in] port UDP port of remote statsd to which we report
255 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100256int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200257{
258 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr;
259
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100260 if (!srep->have_net_config)
261 return -ENOTSUP;
262
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200263 srep->dest_port = port;
Harald Welte95871da2017-05-15 12:11:36 +0200264 sock_addr->sin_port = osmo_htons(port);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200265
266 return update_srep_config(srep);
267}
268
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200269/*! Set the local (IP) address of a given stats_reporter.
270 * \param[in] srep stats_reporter whose remote address is to be set
271 * \param[in] addr String representation of local IP address
272 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100273int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200274{
275 int rc;
276 struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr;
277 struct in_addr inaddr;
278
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100279 if (!srep->have_net_config)
280 return -ENOTSUP;
281
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200282 if (addr) {
283 rc = inet_pton(AF_INET, addr, &inaddr);
284 if (rc <= 0)
285 return -EINVAL;
286 } else {
Holger Hans Peter Freyther79219752015-11-02 15:50:32 +0100287 inaddr.s_addr = INADDR_ANY;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200288 }
289
290 sock_addr->sin_addr = inaddr;
291 sock_addr->sin_family = AF_INET;
292 srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0;
293
294 talloc_free(srep->bind_addr_str);
295 srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL;
296
297 return update_srep_config(srep);
298}
299
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200300/*! Set the maximum transmission unit of a given stats_reporter.
301 * \param[in] srep stats_reporter whose remote address is to be set
302 * \param[in] mtu Maximum Transmission Unit of \a srep
303 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100304int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100305{
Jacob Erlbecked197fd2015-10-27 14:43:24 +0100306 if (!srep->have_net_config)
307 return -ENOTSUP;
308
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100309 if (mtu < 0)
310 return -EINVAL;
311
312 srep->mtu = mtu;
313
314 return update_srep_config(srep);
315}
Harald Welte67bdd802017-01-15 17:56:11 +0100316#endif /* HAVE_SYS_SOCKETS_H */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100317
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100318int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
319 enum osmo_stats_class class_id)
320{
321 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
322 return -EINVAL;
323
324 srep->max_class = class_id;
325
326 return 0;
327}
328
Alexander Chemerisf203ed32020-05-08 19:09:22 +0300329/*! Set the reporting interval (common for all reporters)
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200330 * \param[in] interval Reporting interval in seconds
331 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100332int osmo_stats_set_interval(int interval)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200333{
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100334 if (interval <= 0)
335 return -EINVAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200336
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100337 osmo_stats_config->interval = interval;
Jacob Erlbeckb1dbfb42015-10-26 11:58:38 +0100338 if (is_initialised)
339 start_timer();
340
341 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200342}
343
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200344/*! Set the name prefix of a given stats_reporter.
345 * \param[in] srep stats_reporter whose name prefix is to be set
346 * \param[in] prefix NAme perfix to pre-pend for any reported value
347 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100348int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200349{
350 talloc_free(srep->name_prefix);
Jacob Erlbeck916423e2015-11-09 10:52:19 +0100351 srep->name_prefix = prefix && strlen(prefix) > 0 ?
352 talloc_strdup(srep, prefix) : NULL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200353
354 return update_srep_config(srep);
355}
356
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200357
358/*! Enable the given stats_reporter.
359 * \param[in] srep stats_reporter who is to be enabled
360 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100361int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200362{
363 srep->enabled = 1;
364
365 return update_srep_config(srep);
366}
367
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200368/*! Disable the given stats_reporter.
369 * \param[in] srep stats_reporter who is to be disabled
370 * \returns 0 on success; negative on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100371int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200372{
373 srep->enabled = 0;
374
375 return update_srep_config(srep);
376}
377
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100378/*** i/o helper functions ***/
379
Harald Welte67bdd802017-01-15 17:56:11 +0100380#ifdef HAVE_SYS_SOCKET_H
381
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200382/*! Open the UDP socket for given stats_reporter.
383 * \param[in] srep stats_reporter whose UDP socket is to be opened
384 * ]returns 0 on success; negative otherwise */
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100385int osmo_stats_reporter_udp_open(struct osmo_stats_reporter *srep)
386{
387 int sock;
388 int rc;
389 int buffer_size = STATS_DEFAULT_BUFLEN;
390
391 if (srep->fd != -1 && srep->close)
392 srep->close(srep);
393
394 sock = socket(AF_INET, SOCK_DGRAM, 0);
395 if (sock == -1)
396 return -errno;
397
Arran Cudbard-Bellcc3694b2016-05-18 16:02:19 -0400398#if defined(__APPLE__) && !defined(MSG_NOSIGNAL)
399 {
400 static int val = 1;
401
402 rc = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val));
403 goto failed;
404 }
405#endif
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100406 if (srep->bind_addr_len > 0) {
407 rc = bind(sock, &srep->bind_addr, srep->bind_addr_len);
408 if (rc == -1)
409 goto failed;
410 }
411
412 srep->fd = sock;
413
414 if (srep->mtu > 0) {
415 buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */;
416 srep->agg_enabled = 1;
417 }
418
419 srep->buffer = msgb_alloc(buffer_size, "stats buffer");
420
421 return 0;
422
423failed:
424 rc = -errno;
425 close(sock);
426
427 return rc;
428}
429
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200430/*! Closee the UDP socket for given stats_reporter.
431 * \param[in] srep stats_reporter whose UDP socket is to be closed
432 * ]returns 0 on success; negative otherwise */
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100433int osmo_stats_reporter_udp_close(struct osmo_stats_reporter *srep)
434{
435 int rc;
436 if (srep->fd == -1)
437 return -EBADF;
438
439 osmo_stats_reporter_send_buffer(srep);
440
441 rc = close(srep->fd);
442 srep->fd = -1;
443 msgb_free(srep->buffer);
444 srep->buffer = NULL;
445 return rc == -1 ? -errno : 0;
446}
447
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200448/*! Send given date to given stats_reporter.
449 * \param[in] srep stats_reporter whose UDP socket is to be opened
450 * \param[in] data string data to be sent
451 * \param[in] data_len Length of \a data in bytes
452 * \returns number of bytes on success; negative otherwise */
Jacob Erlbeckb6e6bea2015-11-09 15:33:44 +0100453int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200454 int data_len)
455{
456 int rc;
457
Arran Cudbard-Bellcc3694b2016-05-18 16:02:19 -0400458 rc = sendto(srep->fd, data, data_len,
459#ifdef MSG_NOSIGNAL
460 MSG_NOSIGNAL |
461#endif
462 MSG_DONTWAIT,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200463 &srep->dest_addr, srep->dest_addr_len);
464
465 if (rc == -1)
466 rc = -errno;
467
468 return rc;
469}
470
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200471/*! Send current accumulated buffer to given stats_reporter.
472 * \param[in] srep stats_reporter whose UDP socket is to be opened
473 * \returns number of bytes on success; negative otherwise */
Jacob Erlbeckb6e6bea2015-11-09 15:33:44 +0100474int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep)
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100475{
476 int rc;
477
478 if (!srep->buffer || msgb_length(srep->buffer) == 0)
479 return 0;
480
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100481 rc = osmo_stats_reporter_send(srep,
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100482 (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer));
483
484 msgb_trim(srep->buffer, 0);
485
486 return rc;
487}
Harald Welte67bdd802017-01-15 17:56:11 +0100488#endif /* HAVE_SYS_SOCKET_H */
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100489
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100490/*** log reporter ***/
491
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200492/*! Create a stats_reporter that logs via libosmocore logging.
493 * A stats_reporter created via this function will simply print the statistics
494 * via the libosmocore logging framework, using DLSTATS subsystem and LOGL_INFO
495 * priority. The configuration of the libosmocore log targets define where this
496 * information will end up (ignored, text file, stderr, syslog, ...).
497 * \param[in] name Name of the to-be-created stats_reporter
498 * \returns stats_reporter on success; NULL on error */
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100499struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100500{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100501 struct osmo_stats_reporter *srep;
502 srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100503
504 srep->have_net_config = 0;
505
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100506 srep->send_counter = osmo_stats_reporter_log_send_counter;
507 srep->send_item = osmo_stats_reporter_log_send_item;
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100508
509 return srep;
510}
511
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100512static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100513 const char *type,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100514 const char *name1, unsigned int index1, const char *name2, int value,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100515 const char *unit)
516{
Jacob Erlbeck79125ec2015-11-02 15:17:50 +0100517 LOGP(DLSTATS, LOGL_INFO,
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100518 "stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100519 type, srep->name_prefix ? srep->name_prefix : "",
520 name1 ? name1 : "", index1,
521 name2, value, unit ? unit : "");
522
523 return 0;
524}
525
526
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100527static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100528 const struct rate_ctr_group *ctrg,
529 const struct rate_ctr_desc *desc,
530 int64_t value, int64_t delta)
531{
532 if (ctrg)
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100533 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100534 ctrg->desc->group_name_prefix,
535 ctrg->idx,
536 desc->name, value, NULL);
537 else
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100538 return osmo_stats_reporter_log_send(srep, "c",
Jacob Erlbeck16fe8da2015-11-02 11:30:01 +0100539 NULL, 0,
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100540 desc->name, value, NULL);
541}
542
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100543static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep,
544 const struct osmo_stat_item_group *statg,
Harald Welte1554f802016-11-11 15:06:06 +0100545 const struct osmo_stat_item_desc *desc, int64_t value)
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100546{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100547 return osmo_stats_reporter_log_send(srep, "i",
Jacob Erlbeckbc4f7ae2015-10-28 21:47:45 +0100548 statg->desc->group_name_prefix, statg->idx,
549 desc->name, value, desc->unit);
550}
551
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100552/*** helper for reporting ***/
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200553
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100554static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep,
555 unsigned int index, int class_id)
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200556{
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100557 if (class_id == OSMO_STATS_CLASS_UNKNOWN)
558 class_id = index != 0 ?
559 OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200560
Jacob Erlbeck2e8f9ed2015-11-09 15:48:25 +0100561 return class_id <= srep->max_class;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200562}
563
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200564/*** generic rate counter support ***/
565
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100566static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200567 const struct rate_ctr_group *ctrg,
568 const struct rate_ctr_desc *desc,
569 int64_t value, int64_t delta)
570{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100571 if (!srep->send_counter)
572 return 0;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200573
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100574 return srep->send_counter(srep, ctrg, desc, value, delta);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200575}
576
577static int rate_ctr_handler(
578 struct rate_ctr_group *ctrg, struct rate_ctr *ctr,
579 const struct rate_ctr_desc *desc, void *sctx_)
580{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100581 struct osmo_stats_reporter *srep;
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200582 int64_t delta = rate_ctr_difference(ctr);
583
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100584 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200585 if (!srep->running)
586 continue;
587
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100588 if (delta == 0 && !srep->force_single_flush)
589 continue;
590
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100591 if (!osmo_stats_reporter_check_config(srep,
592 ctrg->idx, ctrg->desc->class_id))
Jacob Erlbeck8a97cb92015-11-09 11:39:42 +0100593 continue;
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100594
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100595 osmo_stats_reporter_send_counter(srep, ctrg, desc,
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200596 ctr->current, delta);
597
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100598 /* TODO: handle result (log?, inc counter(!)?) or remove it */
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200599 }
600
601 return 0;
602}
603
604static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
605{
606 rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_);
607
608 return 0;
609}
610
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100611/*** stat item support ***/
612
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100613static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep,
614 const struct osmo_stat_item_group *statg,
615 const struct osmo_stat_item_desc *desc,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100616 int32_t value)
617{
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100618 if (!srep->send_item)
619 return 0;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100620
Jacob Erlbeck490b38f2015-10-27 15:10:28 +0100621 return srep->send_item(srep, statg, desc, value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100622}
623
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100624static int osmo_stat_item_handler(
625 struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100626{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100627 struct osmo_stats_reporter *srep;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100628 int32_t idx = current_stat_item_index;
629 int32_t value;
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100630 int have_value;
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100631
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100632 have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
633 if (!have_value)
634 /* Send the last value in case a flush is requested */
635 value = osmo_stat_item_get_last(item);
636
637 do {
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100638 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100639 if (!srep->running)
640 continue;
641
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100642 if (!have_value && !srep->force_single_flush)
643 continue;
644
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100645 if (!osmo_stats_reporter_check_config(srep,
646 statg->idx, statg->desc->class_id))
Jacob Erlbeck8a97cb92015-11-09 11:39:42 +0100647 continue;
Jacob Erlbeckbc9d9ac2015-11-02 14:49:35 +0100648
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100649 osmo_stats_reporter_send_item(srep, statg,
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100650 item->desc, value);
651 }
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100652
653 if (!have_value)
654 break;
655
656 have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
657 } while (have_value);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100658
659 return 0;
660}
661
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100662static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_)
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100663{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100664 osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_);
Jacob Erlbeckc27671c2015-10-26 12:32:07 +0100665
666 return 0;
667}
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200668
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100669/*** osmo counter support ***/
670
671static int handle_counter(struct osmo_counter *counter, void *sctx_)
672{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100673 struct osmo_stats_reporter *srep;
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100674 struct rate_ctr_desc desc = {0};
675 /* Fake a rate counter description */
676 desc.name = counter->name;
677 desc.description = counter->description;
678
679 int delta = osmo_counter_difference(counter);
680
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100681 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100682 if (!srep->running)
683 continue;
684
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100685 if (delta == 0 && !srep->force_single_flush)
686 continue;
687
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100688 osmo_stats_reporter_send_counter(srep, NULL, &desc,
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100689 counter->value, delta);
690
Holger Hans Peter Freyther837e9402015-11-02 15:44:26 +0100691 /* TODO: handle result (log?, inc counter(!)?) */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100692 }
693
694 return 0;
695}
696
697
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200698/*** main reporting function ***/
699
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100700static void flush_all_reporters()
701{
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100702 struct osmo_stats_reporter *srep;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100703
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100704 llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100705 if (!srep->running)
706 continue;
707
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100708 osmo_stats_reporter_send_buffer(srep);
Jacob Erlbeckaed7c122015-11-09 11:25:12 +0100709 srep->force_single_flush = 0;
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100710 }
711}
712
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100713int osmo_stats_report()
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200714{
Jacob Erlbeck01e8c912015-11-09 14:13:23 +0100715 /* per group actions */
Jacob Erlbeckc8f47b62015-10-26 14:42:05 +0100716 osmo_counters_for_each(handle_counter, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200717 rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
Jacob Erlbeckfc9533d2015-10-29 00:55:58 +0100718 osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200719
Jacob Erlbeck01e8c912015-11-09 14:13:23 +0100720 /* global actions */
721 osmo_stat_item_discard_all(&current_stat_item_index);
Jacob Erlbeckd01acfc2015-10-26 16:22:45 +0100722 flush_all_reporters();
723
Jacob Erlbeck95bf82802015-10-20 19:05:52 +0200724 return 0;
725}
Harald Welte67bdd802017-01-15 17:56:11 +0100726
727#endif /* !EMBEDDED */
Harald Welteeb5b6ce2017-10-15 20:03:24 +0200728
729/*! @} */