Asterisk - The Open Source Telephony Project GIT-master-2de1a68
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 <synopsis>Global configuration settings</synopsis>
47 <configOption name="enabled">
48 <synopsis>Enable/disable the StatsD module</synopsis>
49 </configOption>
50 <configOption name="server">
51 <synopsis>Address of the StatsD server</synopsis>
52 </configOption>
53 <configOption name="prefix">
54 <synopsis>Prefix to prepend to every metric</synopsis>
55 </configOption>
56 <configOption name="add_newline">
57 <synopsis>Append a newline to every event. This is useful if
58 you want to fake out a server using netcat
59 (nc -lu 8125)</synopsis>
60 </configOption>
61 <configOption name="meter_support">
62 <synopsis>Enable/disable the non-standard StatsD Meter type,
63 if disabled falls back to counter and will append a "_meter" suffix to the metric name</synopsis>
64 </configOption>
65 </configObject>
66 </configFile>
67 </configInfo>
68***/
69
70#include "asterisk.h"
71
73#include "asterisk/module.h"
74#include "asterisk/netsock2.h"
75
76#define AST_API_MODULE
77#include "asterisk/statsd.h"
78
79#define DEFAULT_STATSD_PORT 8125
80
81#define MAX_PREFIX 40
82
83/*! Socket for sending statd messages */
84static int socket_fd = -1;
85
86/*! \brief Global configuration options for statsd client. */
88 /*! Enabled by default, disabled if false. */
90 /*! Disabled by default, appends newlines to all messages when enabled. */
92 /*! Statsd server address[:port]. */
94 /*! Prefix to put on every stat. */
95 char prefix[MAX_PREFIX + 1];
96 /*! Enabled support for non-standard Meter type by default, falls back to counter if disabled */
98};
99
100/*! \brief All configuration options for statsd client. */
101struct conf {
102 /*! The general section configuration options. */
104};
105
106/*! \brief Locking container for safe configuration access. */
108
109static void conf_server(const struct conf *cfg, struct ast_sockaddr *addr)
110{
111 *addr = cfg->global->statsd_server;
112 if (ast_sockaddr_port(addr) == 0) {
114 }
115}
116
117void AST_OPTIONAL_API_NAME(ast_statsd_log_string)(const char *metric_name,
118 const char *metric_type, const char *value, double sample_rate)
119{
120 struct conf *cfg;
121 struct ast_str *msg;
122 size_t len;
123 struct ast_sockaddr statsd_server;
124
125 if (socket_fd == -1) {
126 return;
127 }
128
129 /* Rates <= 0.0 never get logged.
130 * Rates >= 1.0 always get logged.
131 * All others leave it to chance.
132 */
133 if (sample_rate <= 0.0 ||
134 (sample_rate < 1.0 && sample_rate < ast_random_double())) {
135 return;
136 }
137
139 conf_server(cfg, &statsd_server);
140
141 msg = ast_str_create(40);
142 if (!msg) {
143 ao2_cleanup(cfg);
144 return;
145 }
146
147 if (!ast_strlen_zero(cfg->global->prefix)) {
148 ast_str_append(&msg, 0, "%s.", cfg->global->prefix);
149 }
150
151 if (!cfg->global->meter_support && strcmp(metric_type, AST_STATSD_METER)) {
152 ast_str_append(&msg, 0, "%s_meter:%s|%s", metric_name, value, AST_STATSD_COUNTER);
153 } else {
154 ast_str_append(&msg, 0, "%s:%s|%s", metric_name, value, metric_type);
155 }
156
157 if (sample_rate < 1.0) {
158 ast_str_append(&msg, 0, "|@%.2f", sample_rate);
159 }
160
161 if (cfg->global->add_newline) {
162 ast_str_append(&msg, 0, "\n");
163 }
164
165 len = ast_str_strlen(msg);
166
167 ast_debug(6, "Sending statistic %s to StatsD server\n", ast_str_buffer(msg));
168 ast_sendto(socket_fd, ast_str_buffer(msg), len, 0, &statsd_server);
169
170 ao2_cleanup(cfg);
171 ast_free(msg);
172}
173
174void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name,
175 const char *metric_type, intmax_t value, double sample_rate)
176{
177 char char_value[30];
178 snprintf(char_value, sizeof(char_value), "%jd", value);
179
180 ast_statsd_log_string(metric_name, metric_type, char_value, sample_rate);
181
182}
183
185
187 const char *metric_type, const char *value, double sample_rate, ...)
188{
189 struct ast_str *buf;
190 va_list ap;
191 int res;
192
194 if (!buf) {
195 return;
196 }
197
198 va_start(ap, sample_rate);
199 res = ast_str_set_va(&buf, 0, metric_name, ap);
200 va_end(ap);
201
202 if (res == AST_DYNSTR_BUILD_FAILED) {
203 return;
204 }
205
206 ast_statsd_log_string(ast_str_buffer(buf), metric_type, value, sample_rate);
207}
208
209void AST_OPTIONAL_API_NAME(ast_statsd_log_full_va)(const char *metric_name,
210 const char *metric_type, intmax_t value, double sample_rate, ...)
211{
212 struct ast_str *buf;
213 va_list ap;
214 int res;
215
217 if (!buf) {
218 return;
219 }
220
221 va_start(ap, sample_rate);
222 res = ast_str_set_va(&buf, 0, metric_name, ap);
223 va_end(ap);
224
225 if (res == AST_DYNSTR_BUILD_FAILED) {
226 return;
227 }
228
229 ast_statsd_log_full(ast_str_buffer(buf), metric_type, value, sample_rate);
230}
231
232void AST_OPTIONAL_API_NAME(ast_statsd_log)(const char *metric_name,
233 const char *metric_type, intmax_t value)
234{
235 char char_value[30];
236 snprintf(char_value, sizeof(char_value), "%jd", value);
237
238 ast_statsd_log_string(metric_name, metric_type, char_value, 1.0);
239}
240
241void AST_OPTIONAL_API_NAME(ast_statsd_log_sample)(const char *metric_name,
242 intmax_t value, double sample_rate)
243{
244 char char_value[30];
245 snprintf(char_value, sizeof(char_value), "%jd", value);
246
247 ast_statsd_log_string(metric_name, AST_STATSD_COUNTER, char_value,
248 sample_rate);
249}
250
251/*! \brief Mapping of the statsd conf struct's globals to the
252 * general context in the config file. */
253static struct aco_type global_option = {
254 .type = ACO_GLOBAL,
255 .name = "global",
256 .item_offset = offsetof(struct conf, global),
257 .category = "general",
258 .category_match = ACO_WHITELIST_EXACT,
259};
260
262
263/*! \brief Disposes of the statsd conf object */
264static void conf_destructor(void *obj)
265{
266 struct conf *cfg = obj;
267 ao2_cleanup(cfg->global);
268}
269
270/*! \brief Creates the statis http conf object. */
271static void *conf_alloc(void)
272{
273 struct conf *cfg;
274
275 if (!(cfg = ao2_alloc(sizeof(*cfg), conf_destructor))) {
276 return NULL;
277 }
278
279 if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), NULL))) {
280 ao2_ref(cfg, -1);
281 return NULL;
282 }
283 return cfg;
284}
285
286/*! \brief The conf file that's processed for the module. */
287static struct aco_file conf_file = {
288 /*! The config file name. */
289 .filename = "statsd.conf",
290 /*! The mapping object types to be processed. */
291 .types = ACO_TYPES(&global_option),
292};
293
295 .files = ACO_FILES(&conf_file));
296
297/*! \brief Helper function to check if module is enabled. */
298static char is_enabled(void)
299{
301 return cfg->global->enabled;
302}
303
304static int statsd_init(void)
305{
307 char *server;
308 struct ast_sockaddr statsd_server;
309
311
312 ast_debug(3, "Configuring StatsD client.\n");
313
314 if (socket_fd == -1) {
315 ast_debug(3, "Creating StatsD socket.\n");
316 socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
317 if (socket_fd == -1) {
318 perror("Error creating StatsD socket");
319 return -1;
320 }
321 }
322
323 conf_server(cfg, &statsd_server);
324 server = ast_sockaddr_stringify_fmt(&statsd_server,
326 ast_debug(3, " StatsD server = %s.\n", server);
327 ast_debug(3, " add newline = %s\n", AST_YESNO(cfg->global->add_newline));
328 ast_debug(3, " prefix = %s\n", cfg->global->prefix);
329
330 return 0;
331}
332
333static void statsd_shutdown(void)
334{
335 ast_debug(3, "Shutting down StatsD client.\n");
336 if (socket_fd != -1) {
337 close(socket_fd);
338 socket_fd = -1;
339 }
340}
341
342static int unload_module(void)
343{
345 aco_info_destroy(&cfg_info);
347 return 0;
348}
349
350static int load_module(void)
351{
352 if (aco_info_init(&cfg_info)) {
353 aco_info_destroy(&cfg_info);
355 }
356
357 aco_option_register(&cfg_info, "enabled", ACO_EXACT, global_options,
358 "no", OPT_BOOL_T, 1,
360
361 aco_option_register(&cfg_info, "add_newline", ACO_EXACT, global_options,
362 "no", OPT_BOOL_T, 1,
363 FLDSET(struct conf_global_options, add_newline));
364
365 aco_option_register(&cfg_info, "server", ACO_EXACT, global_options,
366 "127.0.0.1", OPT_SOCKADDR_T, 0,
367 FLDSET(struct conf_global_options, statsd_server));
368
369 aco_option_register(&cfg_info, "prefix", ACO_EXACT, global_options,
370 "", OPT_CHAR_ARRAY_T, 0,
372
373 aco_option_register(&cfg_info, "meter_support", ACO_EXACT, global_options,
374 "yes", OPT_BOOL_T, 1,
375 FLDSET(struct conf_global_options, meter_support));
376
377 if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
378 struct conf *cfg;
379
380 ast_log(LOG_NOTICE, "Could not load statsd config; using defaults\n");
381 cfg = conf_alloc();
382 if (!cfg) {
383 aco_info_destroy(&cfg_info);
385 }
386
387 if (aco_set_defaults(&global_option, "general", cfg->global)) {
388 ast_log(LOG_ERROR, "Failed to initialize statsd defaults.\n");
389 ao2_ref(cfg, -1);
390 aco_info_destroy(&cfg_info);
392 }
393
395 ao2_ref(cfg, -1);
396 }
397
398 if (!is_enabled()) {
400 }
401
402 if (statsd_init()) {
405 }
406
408}
409
410static int reload_module(void)
411{
412 switch (aco_process_config(&cfg_info, 1)) {
413 case ACO_PROCESS_OK:
414 break;
418 default:
420 }
421
422 if (is_enabled()) {
423 if (statsd_init()) {
425 }
426 } else {
428 }
430}
431
432/* The priority of this module is set just after realtime, since it loads
433 * configuration and could be used by any other sort of module.
434 */
436 .support_level = AST_MODULE_SUPPORT_EXTENDED,
437 .load = load_module,
438 .unload = unload_module,
440 .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:317
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:316
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_REALTIME_DRIVER
Definition: module.h:323
@ 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:261
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:186
static char is_enabled(void)
Helper function to check if module is enabled.
Definition: res_statsd.c:298
static void * conf_alloc(void)
Creates the statis http conf object.
Definition: res_statsd.c:271
static void statsd_shutdown(void)
Definition: res_statsd.c:333
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:287
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:174
static int reload_module(void)
Definition: res_statsd.c:410
static void conf_destructor(void *obj)
Disposes of the statsd conf object.
Definition: res_statsd.c:264
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:117
static void conf_server(const struct conf *cfg, struct ast_sockaddr *addr)
Definition: res_statsd.c:109
static struct ast_threadstorage statsd_buf
Definition: res_statsd.c:184
#define DEFAULT_STATSD_PORT
Definition: res_statsd.c:79
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:241
static int load_module(void)
Definition: res_statsd.c:350
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:209
static int socket_fd
Definition: res_statsd.c:84
static int unload_module(void)
Definition: res_statsd.c:342
static int statsd_init(void)
Definition: res_statsd.c:304
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:253
#define MAX_PREFIX
Definition: res_statsd.c:81
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:232
#define NULL
Definition: resample.c:96
#define AST_STATSD_METER
Definition: statsd.h:48
#define AST_STATSD_COUNTER
Definition: statsd.h:39
@ AST_DYNSTR_BUILD_FAILED
Definition: strings.h:943
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
#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:87
struct ast_sockaddr statsd_server
Definition: res_statsd.c:93
char prefix[MAX_PREFIX+1]
Definition: res_statsd.c:95
All configuration options for http media cache.
struct conf_global_options * global
Definition: res_statsd.c:103
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