Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
res_statsd.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2013, Digium, Inc.
5 *
6 * David M. Lee, II <dlee@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*!
20 * \brief Support for publishing to a StatsD server.
21 *
22 * \author David M. Lee, II <dlee@digium.com>
23 * \since 12
24 */
25
26/*** MODULEINFO
27 <support_level>extended</support_level>
28 ***/
29
30/*** DOCUMENTATION
31 <configInfo name="res_statsd" language="en_US">
32 <synopsis>StatsD client</synopsis>
33 <description>
34 <para>The <literal>res_statsd</literal> module provides an API that
35 allows Asterisk and its modules to send statistics to a StatsD
36 server. It only provides a means to communicate with a StatsD server
37 and does not send any metrics of its own.</para>
38 <para>An example module, <literal>res_chan_stats</literal>, is
39 provided which uses the API exposed by this module to send channel
40 statistics to the configured StatsD server.</para>
41 <para>More information about StatsD can be found at
42 https://github.com/statsd/statsd</para>
43 </description>
44 <configFile name="statsd.conf">
45 <configObject name="global">
46 <since>
47 <version>12.0.0</version>
48 </since>
49 <synopsis>Global configuration settings</synopsis>
50 <configOption name="enabled">
51 <since>
52 <version>12.0.0</version>
53 </since>
54 <synopsis>Enable/disable the StatsD module</synopsis>
55 </configOption>
56 <configOption name="server">
57 <since>
58 <version>12.0.0</version>
59 </since>
60 <synopsis>Address of the StatsD server</synopsis>
61 </configOption>
62 <configOption name="prefix">
63 <since>
64 <version>12.0.0</version>
65 </since>
66 <synopsis>Prefix to prepend to every metric</synopsis>
67 </configOption>
68 <configOption name="add_newline">
69 <since>
70 <version>12.0.0</version>
71 </since>
72 <synopsis>Append a newline to every event. This is useful if
73 you want to fake out a server using netcat
74 (nc -lu 8125)</synopsis>
75 </configOption>
76 <configOption name="meter_support">
77 <since>
78 <version>16.20.0</version>
79 <version>18.6.0</version>
80 </since>
81 <synopsis>Enable/disable the non-standard StatsD Meter type,
82 if disabled falls back to counter and will append a "_meter" suffix to the metric name</synopsis>
83 </configOption>
84 </configObject>
85 </configFile>
86 </configInfo>
87***/
88
89#include "asterisk.h"
90
92#include "asterisk/module.h"
93#include "asterisk/netsock2.h"
94
95#define AST_API_MODULE
96#include "asterisk/statsd.h"
97
98#define DEFAULT_STATSD_PORT 8125
99
100#define MAX_PREFIX 40
101
102/*! Socket for sending statd messages */
103static int socket_fd = -1;
104
105/*! \brief Global configuration options for statsd client. */
107 /*! Enabled by default, disabled if false. */
109 /*! Disabled by default, appends newlines to all messages when enabled. */
111 /*! Statsd server address[:port]. */
113 /*! Prefix to put on every stat. */
115 /*! Enabled support for non-standard Meter type by default, falls back to counter if disabled */
117};
118
119/*! \brief All configuration options for statsd client. */
120struct conf {
121 /*! The general section configuration options. */
123};
124
125/*! \brief Locking container for safe configuration access. */
127
128static void conf_server(const struct conf *cfg, struct ast_sockaddr *addr)
129{
130 *addr = cfg->global->statsd_server;
131 if (ast_sockaddr_port(addr) == 0) {
133 }
134}
135
136void AST_OPTIONAL_API_NAME(ast_statsd_log_string)(const char *metric_name,
137 const char *metric_type, const char *value, double sample_rate)
138{
139 struct conf *cfg;
140 struct ast_str *msg;
141 size_t len;
142 struct ast_sockaddr statsd_server;
143
144 if (socket_fd == -1) {
145 return;
146 }
147
148 /* Rates <= 0.0 never get logged.
149 * Rates >= 1.0 always get logged.
150 * All others leave it to chance.
151 */
152 if (sample_rate <= 0.0 ||
153 (sample_rate < 1.0 && sample_rate < ast_random_double())) {
154 return;
155 }
156
158 conf_server(cfg, &statsd_server);
159
160 msg = ast_str_create(40);
161 if (!msg) {
162 ao2_cleanup(cfg);
163 return;
164 }
165
166 if (!ast_strlen_zero(cfg->global->prefix)) {
167 ast_str_append(&msg, 0, "%s.", cfg->global->prefix);
168 }
169
170 if (!cfg->global->meter_support && strcmp(metric_type, AST_STATSD_METER)) {
171 ast_str_append(&msg, 0, "%s_meter:%s|%s", metric_name, value, AST_STATSD_COUNTER);
172 } else {
173 ast_str_append(&msg, 0, "%s:%s|%s", metric_name, value, metric_type);
174 }
175
176 if (sample_rate < 1.0) {
177 ast_str_append(&msg, 0, "|@%.2f", sample_rate);
178 }
179
180 if (cfg->global->add_newline) {
181 ast_str_append(&msg, 0, "\n");
182 }
183
184 len = ast_str_strlen(msg);
185
186 ast_debug(6, "Sending statistic %s to StatsD server\n", ast_str_buffer(msg));
187 ast_sendto(socket_fd, ast_str_buffer(msg), len, 0, &statsd_server);
188
189 ao2_cleanup(cfg);
190 ast_free(msg);
191}
192
193void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
194 const char *metric_type, intmax_t value, double sample_rate)
195{
196 char char_value[30];
197 snprintf(char_value, sizeof(char_value), "%jd", value);
198
199 ast_statsd_log_string(metric_name, metric_type, char_value, sample_rate);
200
201}
202
204
206 const char *metric_type, const char *value, double sample_rate, ...)
207{
208 struct ast_str *buf;
209 va_list ap;
210 int res;
211
213 if (!buf) {
214 return;
215 }
216
217 va_start(ap, sample_rate);
218 res = ast_str_set_va(&buf, 0, metric_name, ap);
219 va_end(ap);
220
221 if (res == AST_DYNSTR_BUILD_FAILED) {
222 return;
223 }
224
225 ast_statsd_log_string(ast_str_buffer(buf), metric_type, value, sample_rate);
226}
227
228void AST_OPTIONAL_API_NAME(ast_statsd_log_full_va)(const char *metric_name,
229 const char *metric_type, intmax_t value, double sample_rate, ...)
230{
231 struct ast_str *buf;
232 va_list ap;
233 int res;
234
236 if (!buf) {
237 return;
238 }
239
240 va_start(ap, sample_rate);
241 res = ast_str_set_va(&buf, 0, metric_name, ap);
242 va_end(ap);
243
244 if (res == AST_DYNSTR_BUILD_FAILED) {
245 return;
246 }
247
248 ast_statsd_log_full(ast_str_buffer(buf), metric_type, value, sample_rate);
249}
250
251void AST_OPTIONAL_API_NAME(ast_statsd_log)(const char *metric_name,
252 const char *metric_type, intmax_t value)
253{
254 char char_value[30];
255 snprintf(char_value, sizeof(char_value), "%jd", value);
256
257 ast_statsd_log_string(metric_name, metric_type, char_value, 1.0);
258}
259
260void AST_OPTIONAL_API_NAME(ast_statsd_log_sample)(const char *metric_name,
261 intmax_t value, double sample_rate)
262{
263 char char_value[30];
264 snprintf(char_value, sizeof(char_value), "%jd", value);
265
266 ast_statsd_log_string(metric_name, AST_STATSD_COUNTER, char_value,
267 sample_rate);
268}
269
270/*! \brief Mapping of the statsd conf struct's globals to the
271 * general context in the config file. */
272static struct aco_type global_option = {
273 .type = ACO_GLOBAL,
274 .name = "global",
275 .item_offset = offsetof(struct conf, global),
276 .category = "general",
277 .category_match = ACO_WHITELIST_EXACT,
278};
279
281
282/*! \brief Disposes of the statsd conf object */
283static void conf_destructor(void *obj)
284{
285 struct conf *cfg = obj;
286 ao2_cleanup(cfg->global);
287}
288
289/*! \brief Creates the statis http conf object. */
290static void *conf_alloc(void)
291{
292 struct conf *cfg;
293
294 if (!(cfg = ao2_alloc(sizeof(*cfg), conf_destructor))) {
295 return NULL;
296 }
297
298 if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), NULL))) {
299 ao2_ref(cfg, -1);
300 return NULL;
301 }
302 return cfg;
303}
304
305/*! \brief The conf file that's processed for the module. */
306static struct aco_file conf_file = {
307 /*! The config file name. */
308 .filename = "statsd.conf",
309 /*! The mapping object types to be processed. */
310 .types = ACO_TYPES(&global_option),
311};
312
314 .files = ACO_FILES(&conf_file));
315
316/*! \brief Helper function to check if module is enabled. */
317static char is_enabled(void)
318{
320 return cfg->global->enabled;
321}
322
323static int statsd_init(void)
324{
326 char *server;
327 struct ast_sockaddr statsd_server;
328
330
331 ast_debug(3, "Configuring StatsD client.\n");
332
333 if (socket_fd == -1) {
334 ast_debug(3, "Creating StatsD socket.\n");
335 socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
336 if (socket_fd == -1) {
337 perror("Error creating StatsD socket");
338 return -1;
339 }
340 }
341
342 conf_server(cfg, &statsd_server);
343 server = ast_sockaddr_stringify_fmt(&statsd_server,
345 ast_debug(3, " StatsD server = %s.\n", server);
346 ast_debug(3, " add newline = %s\n", AST_YESNO(cfg->global->add_newline));
347 ast_debug(3, " prefix = %s\n", cfg->global->prefix);
348
349 return 0;
350}
351
352static void statsd_shutdown(void)
353{
354 ast_debug(3, "Shutting down StatsD client.\n");
355 if (socket_fd != -1) {
356 close(socket_fd);
357 socket_fd = -1;
358 }
359}
360
361static int unload_module(void)
362{
364 aco_info_destroy(&cfg_info);
366 return 0;
367}
368
369static int load_module(void)
370{
371 if (aco_info_init(&cfg_info)) {
372 aco_info_destroy(&cfg_info);
374 }
375
376 aco_option_register(&cfg_info, "enabled", ACO_EXACT, global_options,
377 "no", OPT_BOOL_T, 1,
379
380 aco_option_register(&cfg_info, "add_newline", ACO_EXACT, global_options,
381 "no", OPT_BOOL_T, 1,
382 FLDSET(struct conf_global_options, add_newline));
383
384 aco_option_register(&cfg_info, "server", ACO_EXACT, global_options,
385 "127.0.0.1", OPT_SOCKADDR_T, 0,
386 FLDSET(struct conf_global_options, statsd_server));
387
388 aco_option_register(&cfg_info, "prefix", ACO_EXACT, global_options,
389 "", OPT_CHAR_ARRAY_T, 0,
391
392 aco_option_register(&cfg_info, "meter_support", ACO_EXACT, global_options,
393 "yes", OPT_BOOL_T, 1,
394 FLDSET(struct conf_global_options, meter_support));
395
396 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
397 struct conf *cfg;
398
399 ast_log(LOG_NOTICE, "Could not load statsd config; using defaults\n");
400 cfg = conf_alloc();
401 if (!cfg) {
402 aco_info_destroy(&cfg_info);
404 }
405
406 if (aco_set_defaults(&global_option, "general", cfg->global)) {
407 ast_log(LOG_ERROR, "Failed to initialize statsd defaults.\n");
408 ao2_ref(cfg, -1);
409 aco_info_destroy(&cfg_info);
411 }
412
414 ao2_ref(cfg, -1);
415 }
416
417 if (!is_enabled()) {
419 }
420
421 if (statsd_init()) {
424 }
425
427}
428
429static int reload_module(void)
430{
431 switch (aco_process_config(&cfg_info, 1)) {
432 case ACO_PROCESS_OK:
433 break;
437 default:
439 }
440
441 if (is_enabled()) {
442 if (statsd_init()) {
444 }
445 } else {
447 }
449}
450
451/* The priority of this module is set just after realtime, since it loads
452 * configuration and could be used by any other sort of module.
453 */
455 .support_level = AST_MODULE_SUPPORT_EXTENDED,
456 .load = load_module,
457 .unload = unload_module,
459 .load_pri = AST_MODPRI_REALTIME_DRIVER + 5,
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_log
Definition: astobj2.c:42
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
Definition: astobj2.h:901
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
Configuration option-handling.
@ ACO_EXACT
int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
Set all default options of obj.
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
@ ACO_PROCESS_UNCHANGED
The config had not been edited and no changes applied.
@ ACO_PROCESS_ERROR
Their was an error and no changes were applied.
@ ACO_PROCESS_OK
The config was processed and applied.
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
#define CHARFLDSET(type, field)
A helper macro to pass the appropriate arguments to aco_option_register for OPT_CHAR_ARRAY_T.
#define aco_option_register(info, name, matchtype, types, default_val, opt_type, flags,...)
Register a config option.
#define ACO_FILES(...)
@ OPT_BOOL_T
Type for default option handler for bools (ast_true/ast_false)
@ OPT_CHAR_ARRAY_T
Type for default option handler for character array strings.
@ OPT_SOCKADDR_T
Type for default handler for ast_sockaddrs.
@ ACO_GLOBAL
@ ACO_WHITELIST_EXACT
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
static int enabled
Definition: dnsmgr.c:91
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static char prefix[MAX_PREFIX]
Definition: http.c:144
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_REALTIME_DRIVER
Definition: module.h:337
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Network socket handling.
char * ast_sockaddr_stringify_fmt(const struct ast_sockaddr *addr, int format)
Convert a socket address to a string.
Definition: netsock2.c:65
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
#define AST_SOCKADDR_STR_DEFAULT
Definition: netsock2.h:203
ssize_t ast_sendto(int sockfd, const void *buf, size_t len, int flags, const struct ast_sockaddr *dest_addr)
Wrapper around sendto(2) that uses ast_sockaddr.
Definition: netsock2.c:614
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
#define AST_OPTIONAL_API_NAME(name)
Expands to the name of the implementation function.
Definition: optional_api.h:228
static int reload(void)
static struct aco_type * global_options[]
Definition: res_statsd.c:280
CONFIG_INFO_STANDARD(cfg_info, confs, conf_alloc,.files=ACO_FILES(&conf_file))
void AST_OPTIONAL_API_NAME() ast_statsd_log_string_va(const char *metric_name, const char *metric_type, const char *value, double sample_rate,...)
Send a stat to the configured statsd server.
Definition: res_statsd.c:205
static char is_enabled(void)
Helper function to check if module is enabled.
Definition: res_statsd.c:317
static void * conf_alloc(void)
Creates the statis http conf object.
Definition: res_statsd.c:290
static void statsd_shutdown(void)
Definition: res_statsd.c:352
static AO2_GLOBAL_OBJ_STATIC(confs)
Locking container for safe configuration access.
static struct aco_file conf_file
The conf file that's processed for the module.
Definition: res_statsd.c:306
void AST_OPTIONAL_API_NAME() ast_statsd_log_full(const char *metric_name, const char *metric_type, intmax_t value, double sample_rate)
Send a stat to the configured statsd server.
Definition: res_statsd.c:193
static int reload_module(void)
Definition: res_statsd.c:429
static void conf_destructor(void *obj)
Disposes of the statsd conf object.
Definition: res_statsd.c:283
void AST_OPTIONAL_API_NAME() ast_statsd_log_string(const char *metric_name, const char *metric_type, const char *value, double sample_rate)
Send a stat to the configured statsd server.
Definition: res_statsd.c:136
static void conf_server(const struct conf *cfg, struct ast_sockaddr *addr)
Definition: res_statsd.c:128
static struct ast_threadstorage statsd_buf
Definition: res_statsd.c:203
#define DEFAULT_STATSD_PORT
Definition: res_statsd.c:98
void AST_OPTIONAL_API_NAME() ast_statsd_log_sample(const char *metric_name, intmax_t value, double sample_rate)
Send a random sampling of a stat to the configured statsd server.
Definition: res_statsd.c:260
static int load_module(void)
Definition: res_statsd.c:369
void AST_OPTIONAL_API_NAME() ast_statsd_log_full_va(const char *metric_name, const char *metric_type, intmax_t value, double sample_rate,...)
Send a stat to the configured statsd server.
Definition: res_statsd.c:228
static int socket_fd
Definition: res_statsd.c:103
static int unload_module(void)
Definition: res_statsd.c:361
static int statsd_init(void)
Definition: res_statsd.c:323
static struct aco_type global_option
Mapping of the statsd conf struct's globals to the general context in the config file.
Definition: res_statsd.c:272
#define MAX_PREFIX
Definition: res_statsd.c:100
void AST_OPTIONAL_API_NAME() ast_statsd_log(const char *metric_name, const char *metric_type, intmax_t value)
Send a stat to the configured statsd server.
Definition: res_statsd.c:251
#define NULL
Definition: resample.c:96
#define AST_STATSD_METER
Definition: statsd.h:48
#define AST_STATSD_COUNTER
Definition: statsd.h:39
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
int ast_str_set_va(struct ast_str **buf, ssize_t max_len, const char *fmt, va_list ap)
Set a dynamic string from a va_list.
Definition: strings.h:1030
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
@ AST_DYNSTR_BUILD_FAILED
Definition: strings.h:943
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
#define AST_YESNO(x)
return Yes or No depending on the argument.
Definition: strings.h:143
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
The representation of a single configuration file to be processed.
const char * filename
Type information about a category-level configurable object.
enum aco_type_t type
Socket address structure.
Definition: netsock2.h:97
Support for dynamic strings.
Definition: strings.h:623
Global configuration options for statsd client.
Definition: res_statsd.c:106
struct ast_sockaddr statsd_server
Definition: res_statsd.c:112
char prefix[MAX_PREFIX+1]
Definition: res_statsd.c:114
All configuration options for http media cache.
struct conf_global_options * global
Definition: res_statsd.c:122
int value
Definition: syslog.c:37
static struct aco_type global
Definition: test_config.c:1445
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
#define ast_random_double()
Returns a random number between 0.0 and 1.0, inclusive.
Definition: utils.h:624