فهرست منبع

Add statsd module.

Changes that I did before merge from my github project:
- Remove Readme.md and doc/statsd.txt
- Add README in the module.
- Change sprintf to snprintf
- Doc: Add valid ids into the sections.
Eloy Coto 11 سال پیش
والد
کامیت
595909e8ac

+ 15 - 0
modules/statsd/Makefile

@@ -0,0 +1,15 @@
+# $Id$
+#
+# example module makefile
+#
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=statsd.so
+LIBS=
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 200 - 0
modules/statsd/README

@@ -0,0 +1,200 @@
+Statsd Module
+
+Eloy Coto Pereiro
+
+   <[email protected]>
+
+Edited by
+
+Eloy Coto Pereiro
+
+   <[email protected]>
+
+   Copyright © 2014 Eloy Coto
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Parameters
+
+              2.1. ip(string)
+              2.2. port(int)
+
+        3. Functions
+
+              3.1. statsd_set(key, value)
+              3.2. statsd_gauge(key, value)
+              3.3. statsd_start(key)
+              3.4. statsd_stop(key)
+              3.5. statsd_incr(key)
+              3.6. statsd_decr(key)
+
+   List of Examples
+
+   1.1. Set ip parameter
+   1.2. Set ip parameter
+   1.3. statsd_set usage
+   1.4. statsd_set usage
+   1.5. statsd_start usage
+   1.6. statsd_stop usage
+   1.7. statsd_incr usage
+   1.8. statsd_decr usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Parameters
+
+        2.1. ip(string)
+        2.2. port(int)
+
+   3. Functions
+
+        3.1. statsd_set(key, value)
+        3.2. statsd_gauge(key, value)
+        3.3. statsd_start(key)
+        3.4. statsd_stop(key)
+        3.5. statsd_incr(key)
+        3.6. statsd_decr(key)
+
+1. Overview
+
+   This is the overview for the module statsd of kamailio
+
+2. Parameters
+
+   2.1. ip(string)
+   2.2. port(int)
+
+2.1. ip(string)
+
+   Statsd server ip
+
+   Example 1.1. Set ip parameter
+...
+modparam("statsd", "ip", "127.0.0.1")
+...
+
+2.2. port(int)
+
+   Statsd server ip
+
+   Example 1.2. Set ip parameter
+...
+modparam("statsd", "port", "8125")
+...
+
+3. Functions
+
+   3.1. statsd_set(key, value)
+   3.2. statsd_gauge(key, value)
+   3.3. statsd_start(key)
+   3.4. statsd_stop(key)
+   3.5. statsd_incr(key)
+   3.6. statsd_decr(key)
+
+3.1.  statsd_set(key, value)
+
+   Sets count the number of unique values passed to a key.
+
+   If that method is called multiple times with the same userid in the
+   same sample period, that userid will only be counted once.
+
+   This function can be used in ALL ROUTES.
+
+   Example 1.3. statsd_set usage
+...
+failure_route[tryagain] {
+...
+    statsd_set("customerFailure", 1);
+...
+}
+...
+
+3.2.  statsd_gauge(key, value)
+
+   Gauges are a constant data type. They are not subject to averaging, and
+   they don’t change unless you change them. That is, once you set a
+   gauge value, it will be a flat line on the graph until you change it
+   again.
+
+   Gauges are useful for things that are already averaged, or don’t need
+   to reset periodically
+
+   This function can be used in ALL ROUTES.
+
+   The statsd server collects gauges under the stats.gauges prefix.
+
+   Example 1.4. statsd_set usage
+route [gauge_method]{
+    statsd_gauge("method"+$rm, "+1");
+    statsd_gauge("customer_credit"+$var(customer),"$var(customer_credit)");
+}
+
+3.3.  statsd_start(key)
+
+   statsd start set a avp with the key name, and when you use
+   statsd_stop(key), module will send to statsd the difference in
+   milliseconds. this is useful to know the time of a sql query, or how
+   many time take your replies.
+
+   this function can be used in all routes.
+
+   the statsd server collects all timers under the stats.timers prefix,
+   and will calculate the lower bound, mean, 90th percentile, upper bound,
+   and count of each timer for each period (by the time you see it in
+   graphite, that’s usually per minute).
+
+   Example 1.5. statsd_start usage
+...
+statsd_start("long_mysql_query");
+sql_query("ca", "select sleep(0.2)", "ra");
+statsd_stop("long_mysql_query");
+...
+
+3.4.  statsd_stop(key)
+
+   statsd_stop(key) get the avp string with the key and calculate the
+   difference from the start time. When finish app send the milliseconds
+   to statsd.
+
+   This function can be used in all routes.
+
+   Example 1.6. statsd_stop usage
+...
+statsd_start("long_mysql_query");
+sql_query("ca", "select sleep(0.2)", "ra");
+statsd_stop("long_mysql_query");
+...
+
+3.5.  statsd_incr(key)
+
+   Increment a counter
+
+   This function can be used in all routes.
+
+   Example 1.7. statsd_incr usage
+...
+if(geoip_match("$si", "src")){
+    statsd_incr("country."+$(gip(src=>cc)));
+}
+...
+
+3.6.  statsd_decr(key)
+
+   Decrement a counter
+
+   This function can be used in all routes.
+
+   Example 1.8. statsd_decr usage
+...
+if (t_check_status("408")) {
+    statsd_decr("kamailio.successfulCalls");
+    statsd_incr("kamailio.reply.busy");
+}
+...

+ 6 - 0
modules/statsd/doc/Makefile

@@ -0,0 +1,6 @@
+docs = statsd.xml
+
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module
+

+ 36 - 0
modules/statsd/doc/statsd.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+    <title>Statsd Module</title>
+    <productname class="trade"></productname>
+    <authorgroup>
+        <author>
+        <firstname>Eloy</firstname>
+        <surname>Coto Pereiro</surname>
+        <email>[email protected]</email>
+        </author>
+        <editor>
+        <firstname>Eloy</firstname>
+        <surname>Coto Pereiro</surname>
+        <email>[email protected]</email>
+        </editor>
+    </authorgroup>
+    <copyright>
+        <year>2014</year>
+        <holder>Eloy Coto</holder>
+    </copyright>
+   </bookinfo>
+   <toc></toc>
+
+    <xi:include href="statsd_admin.xml"/>
+</book>
+

+ 204 - 0
modules/statsd/doc/statsd_admin.xml

@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter xmlns:xi="http://www.w3.org/2001/XInclude">
+
+    <title>&adminguide;</title>
+
+    <section>
+        <title>Overview</title>
+        <para> This is the overview for the module statsd of kamailio</para>
+    </section>
+
+    <section>
+        <title>Parameters</title>
+        <section id="statsd.p.serverIP">
+            <title><varname>ip</varname>(string)</title>
+            <para>
+            Statsd server ip
+            </para>
+            <example>
+                <title>Set ip parameter</title>
+                <programlisting format="linespecific">
+...
+modparam("statsd", "ip", "127.0.0.1")
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section id="statsd.p.serverPort">
+            <title><varname>port</varname>(int)</title>
+            <para>
+            Statsd server ip
+            </para>
+            <example>
+                <title>Set ip parameter</title>
+                <programlisting format="linespecific">
+...
+modparam("statsd", "port", "8125")
+...
+                </programlisting>
+            </example>
+        </section>
+    </section>
+
+    <section>
+        <title>Functions</title>
+        <section id="statsd.f.statsd_set">
+            <title>
+                <function moreinfo="none">statsd_set(key, value)</function>
+            </title>
+            <para>
+                Sets count the number of unique values passed to a key.
+            </para>
+            <para>
+                If that method is called multiple times with the same userid in the same sample period, that userid will only be counted once.
+            </para>
+            <para>
+                This function can be used in ALL ROUTES.
+            </para>
+            <example>
+                <title><function>statsd_set</function> usage</title>
+                <programlisting format="linespecific">
+...
+failure_route[tryagain] {
+...
+    statsd_set("customerFailure", 1);
+...
+}
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section id="statsd.f.statsd_gauge">
+            <title>
+                <function moreinfo="none">statsd_gauge(key, value)</function>
+            </title>
+            <para>
+                Gauges are a constant data type. They are not subject to averaging, and they don’t change unless you change them. That is, once you set a gauge value, it will be a flat line on the graph until you change it again.
+            </para>
+            <para>
+                Gauges are useful for things that are already averaged, or don’t need to reset periodically
+            </para>
+            <para>
+                This function can be used in ALL ROUTES.
+            </para>
+            <para>
+                The statsd server collects gauges under the stats.gauges prefix.
+            </para>
+            <example>
+                <title><function>statsd_set</function> usage</title>
+                <programlisting format="linespecific">
+route [gauge_method]{
+    statsd_gauge("method"+$rm, "+1");
+    statsd_gauge("customer_credit"+$var(customer),"$var(customer_credit)");
+}
+                </programlisting>
+            </example>
+        </section>
+
+        <section id="statsd.f.statsd_start">
+            <title>
+                <function moreinfo="none">statsd_start(key)</function>
+            </title>
+            <para>
+statsd start set a avp with the key name, and when you use statsd_stop(key), module will send to statsd the difference in milliseconds. this is useful to know the time of a sql query, or how many time take your replies.
+            </para>
+            <para>
+                this function can be used in all routes.
+            </para>
+            <para>
+                the statsd server collects all timers under the stats.timers prefix, and will calculate the lower bound, mean, 90th percentile, upper bound, and count of each timer for each period (by the time you see it in graphite, that’s usually per minute).
+            </para>
+            <example>
+                <title><function>statsd_start</function> usage</title>
+                <programlisting format="linespecific">
+...
+statsd_start("long_mysql_query");
+sql_query("ca", "select sleep(0.2)", "ra");
+statsd_stop("long_mysql_query");
+...
+                </programlisting>
+            </example>
+        </section>
+
+
+        <section id="statsd.f.statsd_stop">
+            <title>
+                <function moreinfo="none">statsd_stop(key)</function>
+            </title>
+            <para>
+                statsd_stop(key) get the avp string with the key and calculate the difference from the start time. When finish app send the milliseconds to statsd.
+            </para>
+            <para>
+                This function can be used in all routes.
+            </para>
+            <example>
+                <title><function>statsd_stop</function> usage</title>
+                <programlisting format="linespecific">
+...
+statsd_start("long_mysql_query");
+sql_query("ca", "select sleep(0.2)", "ra");
+statsd_stop("long_mysql_query");
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section id="statsd.f.statsd_incr">
+            <title>
+                <function moreinfo="none">statsd_incr(key)</function>
+            </title>
+            <para>
+            Increment a counter
+            </para>
+            <para>
+                This function can be used in all routes.
+            </para>
+            <example>
+                <title><function>statsd_incr</function> usage</title>
+                <programlisting format="linespecific">
+...
+if(geoip_match("$si", "src")){
+    statsd_incr("country."+$(gip(src=>cc)));
+}
+...
+                </programlisting>
+            </example>
+        </section>
+
+        <section id="statsd.f.statsd_decr">
+            <title>
+                <function moreinfo="none">statsd_decr(key)</function>
+            </title>
+            <para>
+                Decrement a counter
+            </para>
+            <para>
+                This function can be used in all routes.
+            </para>
+            <example>
+                <title><function>statsd_decr</function> usage</title>
+                <programlisting format="linespecific">
+...
+if (t_check_status("408")) {
+    statsd_decr("kamailio.successfulCalls");
+    statsd_incr("kamailio.reply.busy");
+}
+...
+                </programlisting>
+            </example>
+        </section>
+    </section>
+</chapter>
+

+ 152 - 0
modules/statsd/lib_statsd.c

@@ -0,0 +1,152 @@
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <math.h>
+#include <errno.h>
+
+#include "../../sr_module.h"
+
+#define BUFFER_SIZE 8192
+typedef int Bool;
+#define True 1
+#define False 0
+
+typedef struct StatsConnection{
+    char *ip;
+    char *port;
+} StatsConnection;
+
+
+typedef struct StatsdSocket {
+    char *name; // name
+    int sock; // socket
+    int timeout; // how many miliseconds to wait for an answer
+    time_t last_failure; // time of the last failure
+    char data[BUFFER_SIZE]; // buffer for the answer data
+} StatsdSocket;
+
+static StatsdSocket statsd_socket = {
+    "/var/run/statsd/statsd.sock",
+    -1,
+    500, // timeout 500ms if no answer
+    0,
+    ""
+};
+
+static StatsConnection statsd_connection = {
+    "127.0.0.1",
+    "8125"
+};
+
+static Bool statsd_connect(void){
+
+    struct addrinfo *serverAddr;
+    int rc, error;
+
+    if (statsd_socket.sock > 0){
+        return True;
+    }
+
+    error = getaddrinfo(
+        statsd_connection.ip, statsd_connection.port,
+        NULL, &serverAddr);
+    if (error != 0)
+    {
+        LM_ERR("could not initiate server information (%s)\n",gai_strerror(error));
+        return False;
+    }
+
+    statsd_socket.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    if (statsd_socket.sock == 0 ){
+        LM_ERR("could not initiate a connect to statsd\n");
+        return False;
+    }
+
+    rc = connect(
+        statsd_socket.sock, serverAddr->ai_addr, serverAddr->ai_addrlen);
+    if (rc <0){
+        LM_ERR("could not initiate a connect to statsd\n");
+        return False;
+    }
+    return True;
+}
+
+static int send_command(char *command){
+    int send_result;
+
+    if (!statsd_connect()){
+        LM_ERR("Connection lost to statsd");
+        return False;
+    }
+
+    send_result = send(statsd_socket.sock, command, strlen(command), 0);
+    if ( send_result < 0){
+        LM_ERR("could not send the correct info to statsd (%i| %s)",
+            send_result, strerror(errno));
+        return True;
+    }
+    LM_DBG("Sent to statsd (%s)", command);
+    return True;
+}
+
+static int statsd_set(char *key, char *value){
+   char* end = 0;
+   char command[254];
+   int val;
+   val = strtol(value, &end, 0);
+   if (*end){
+       LM_ERR("statsd_count could not  use the provide value(%s)", value);
+       return False;
+   }
+   snprintf(command, sizeof command, "%s:%i|s\n", key, val);
+   return send_command(command);
+}
+
+
+static int statsd_gauge(char *key, char *value){
+   char command[254];
+   snprintf(command, sizeof command, "%s:%s|g\n", key, value);
+   return send_command(command);
+}
+
+static int statsd_count(char *key, char *value){
+   char* end = 0;
+   char command[254];
+   int val;
+
+   val = strtol(value, &end, 0);
+   if (*end){
+       LM_ERR("statsd_count could not  use the provide value(%s)", value);
+       return False;
+   }
+   snprintf(command, sizeof command, "%s:%i|c\n", key, val);
+   return send_command(command);
+}
+
+static int statsd_timing(char *key, int value){
+   char command[254];
+   snprintf(command, sizeof command, "%s:%i|ms\n", key, value);
+   return send_command(command);
+}
+
+static int statsd_init(char *ip, char *port){
+
+    if (ip != NULL){
+        statsd_connection.ip = ip;
+    }
+    if (port != NULL ){
+       statsd_connection.port = port;
+    }
+    LM_ERR("Statsd_init ip %s", statsd_connection.ip);
+    LM_ERR("Statsd_init port %s", statsd_connection.port);
+    return statsd_connect();
+}
+
+static int statsd_destroy(void){
+    statsd_socket.sock = 0;
+    return True;
+}

+ 167 - 0
modules/statsd/statsd.c

@@ -0,0 +1,167 @@
+
+#include <sys/time.h>
+#include <stdlib.h>
+
+#include "../../sr_module.h"
+#include "../../usr_avp.c"
+#include "../../pvar.h"
+#include "../../lvalue.h"
+#include "lib_statsd.c"
+
+
+MODULE_VERSION
+
+static int mod_init(void);
+static int mod_destroy(void);
+static int func_gauge(struct sip_msg *msg, char *key, char* val);
+static int func_set(struct sip_msg *msg, char *key, char* val);
+static int func_time_start(struct sip_msg *msg, char *key);
+static int func_time_end(struct sip_msg *msg, char *key);
+static int func_incr(struct sip_msg *msg, char *key);
+static int func_decr(struct sip_msg *msg, char *key);
+static char* get_milliseconds(char *dst);
+
+typedef struct StatsdParams{
+    char *ip;
+    char *port;
+} StatsdParams;
+
+static StatsdParams statsd_params= {};
+
+static cmd_export_t commands[] = {
+	{"statsd_gauge", (cmd_function)func_gauge, 2, 0, 0, ANY_ROUTE},
+	{"statsd_start", (cmd_function)func_time_start, 1, 0, 0, ANY_ROUTE},
+	{"statsd_stop", (cmd_function)func_time_end, 1, 0, 0, ANY_ROUTE},
+	{"statsd_incr", (cmd_function)func_incr, 1, 0, 0, ANY_ROUTE},
+	{"statsd_decr", (cmd_function)func_decr, 1, 0, 0, ANY_ROUTE},
+	{"statsd_set", (cmd_function)func_set, 2, 0, 0, ANY_ROUTE},
+    {0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t parameters[] = {
+    {"ip", STR_PARAM, &(statsd_params.ip)},
+    {"port", STR_PARAM, &(statsd_params.port)},
+    {0, 0, 0}
+};
+
+struct module_exports exports = {
+    "statsd",    // module name
+    DEFAULT_DLFLAGS, // dlopen flags
+    commands,        // exported functions
+    parameters,      // exported parameters
+    NULL,            // exported statistics
+    NULL,            // exported MI functions
+    NULL,            // exported seudo-variables
+    NULL,            // extra processes
+    mod_init,        // module init function (before fork. kids will inherit)
+    NULL,            // reply processing function
+    (destroy_function) mod_destroy,     // destroy function
+    NULL       // child init function
+};
+
+
+static int mod_init(void)
+{
+    LM_ERR("mod_init_values ip  %s", statsd_params.ip);
+    LM_ERR("mod_init_values port %s", statsd_params.port);
+    int rc = statsd_init(statsd_params.ip, statsd_params.port);
+    LM_ERR("Error code in mod_init is %i",rc);
+    return 0;
+}
+
+/**
+* destroy module function
+*/
+static int mod_destroy(void)
+{
+    statsd_destroy();
+    return 0;
+}
+
+static int func_gauge(struct sip_msg* msg, char* key, char* val)
+{
+    return statsd_gauge(key, val);
+}
+
+static int func_set(struct sip_msg* msg, char* key, char* val)
+{
+    return statsd_set(key, val);
+}
+
+static int func_time_start(struct sip_msg *msg, char *key)
+{
+    int_str avp_key, avp_val;
+    char unix_time[20];
+    get_milliseconds(unix_time);
+    avp_key.s.s = key;
+    avp_key.s.len = strlen(avp_key.s.s);
+
+    avp_val.s.s = unix_time;
+    avp_val.s.len = strlen(avp_val.s.s);
+
+	if (add_avp(AVP_NAME_STR|AVP_VAL_STR, avp_key, avp_val) < 0) {
+        LM_ERR("failed to create AVP\n");
+        return -1;
+    }
+    return 1;
+}
+
+
+static int func_time_end(struct sip_msg *msg, char *key)
+{
+    char unix_time[20];
+    char *endptr;
+    long int start_time;
+    int result;
+
+    struct search_state st;
+
+    get_milliseconds(unix_time);
+    LM_ERR("STOP %s",unix_time);
+    avp_t* prev_avp;
+
+    int_str avp_value, avp_name;
+    avp_name.s.s = key;
+    avp_name.s.len = strlen(avp_name.s.s);
+
+    prev_avp = search_first_avp(
+        AVP_NAME_STR|AVP_VAL_STR, avp_name, &avp_value, &st);
+    if(avp_value.s.len == 0){
+        LM_ERR("Not valid key(%s)",key);
+        return 1;
+    }
+
+    start_time = strtol(avp_value.s.s, &endptr,10);
+    if(strlen(endptr) >0){
+      LM_ERR("Not valid key(%s) it's not a number value=%s",key,avp_value.s.s);
+      return 0;
+    }
+
+    result = atol(unix_time) - start_time;
+    LM_DBG("Start_time=%ld unix_time=%ld (%i)",start_time, atol(unix_time),result);
+    destroy_avp(prev_avp);
+    return statsd_timing(key, result);
+}
+
+
+static int func_incr(struct sip_msg *msg, char *key)
+{
+    return statsd_count(key, "+1");
+}
+
+
+static int func_decr(struct sip_msg *msg, char *key)
+{
+    return statsd_count(key, "-1");
+}
+
+
+char* get_milliseconds(char *dst){
+    struct timeval tv;
+    long int millis;
+
+    gettimeofday(&tv, NULL);
+    millis = (tv.tv_sec * (int)1000) + (tv.tv_usec / 1000);
+    snprintf(dst, 21, "%ld", millis);
+    return dst;
+}