Add request specific statistics to all CURL-based plugins.
[collectd.git] / src / utils_curl_stats.c
1 /**
2  * collectd - src/utils_curl_stats.c
3  * Copyright (C) 2015       Sebastian 'tokkee' Harl
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Sebastian Harl <sh@tokkee.org>
25  **/
26
27 #include "collectd.h"
28 #include "common.h"
29 #include "utils_curl_stats.h"
30
31 #include <stdbool.h>
32 #include <stddef.h>
33
34 struct curl_stats_s
35 {
36         bool total_time;
37         bool namelookup_time;
38         bool connect_time;
39         bool pretransfer_time;
40         bool size_upload;
41         bool size_download;
42         bool speed_download;
43         bool speed_upload;
44         bool header_size;
45         bool request_size;
46         bool content_length_download;
47         bool content_length_upload;
48         bool starttransfer_time;
49         bool redirect_time;
50         bool redirect_count;
51         bool num_connects;
52         bool appconnect_time;
53 };
54
55 /*
56  * Private functions
57  */
58
59 static int dispatch_gauge (CURL *curl, CURLINFO info, value_list_t *vl)
60 {
61         CURLcode code;
62         value_t v;
63
64         code = curl_easy_getinfo (curl, info, &v.gauge);
65         if (code != CURLE_OK)
66                 return -1;
67
68         vl->values = &v;
69         vl->values_len = 1;
70
71         return plugin_dispatch_values (vl);
72 } /* dispatch_gauge */
73
74 /* dispatch a speed, in bytes/second */
75 static int dispatch_speed (CURL *curl, CURLINFO info, value_list_t *vl)
76 {
77         CURLcode code;
78         value_t v;
79
80         code = curl_easy_getinfo (curl, info, &v.gauge);
81         if (code != CURLE_OK)
82                 return -1;
83
84         v.gauge *= 8;
85
86         vl->values = &v;
87         vl->values_len = 1;
88
89         return plugin_dispatch_values (vl);
90 } /* dispatch_speed */
91
92 /* dispatch a size/count, reported as a long value */
93 static int dispatch_size (CURL *curl, CURLINFO info, value_list_t *vl)
94 {
95         CURLcode code;
96         value_t v;
97         long raw;
98
99         code = curl_easy_getinfo (curl, info, &raw);
100         if (code != CURLE_OK)
101                 return -1;
102
103         v.gauge = (double)raw;
104
105         vl->values = &v;
106         vl->values_len = 1;
107
108         return plugin_dispatch_values (vl);
109 } /* dispatch_size */
110
111 static struct {
112         const char *name;
113         size_t offset;
114
115         int (*dispatcher)(CURL *, CURLINFO, value_list_t *);
116         const char *type;
117         CURLINFO info;
118 } field_specs[] = {
119 #define SPEC(name, dispatcher, type, info) \
120         { #name, offsetof (curl_stats_t, name), dispatcher, type, info }
121
122         SPEC (total_time,              dispatch_gauge, "duration", CURLINFO_TOTAL_TIME),
123         SPEC (namelookup_time,         dispatch_gauge, "duration", CURLINFO_NAMELOOKUP_TIME),
124         SPEC (connect_time,            dispatch_gauge, "duration", CURLINFO_CONNECT_TIME),
125         SPEC (pretransfer_time,        dispatch_gauge, "duration", CURLINFO_PRETRANSFER_TIME),
126         SPEC (size_upload,             dispatch_gauge, "bytes",    CURLINFO_SIZE_UPLOAD),
127         SPEC (size_download,           dispatch_gauge, "bytes",    CURLINFO_SIZE_DOWNLOAD),
128         SPEC (speed_download,          dispatch_speed, "bitrate",  CURLINFO_SPEED_DOWNLOAD),
129         SPEC (speed_upload,            dispatch_speed, "bitrate",  CURLINFO_SPEED_UPLOAD),
130         SPEC (header_size,             dispatch_size,  "bytes",    CURLINFO_HEADER_SIZE),
131         SPEC (request_size,            dispatch_size,  "bytes",    CURLINFO_REQUEST_SIZE),
132         SPEC (content_length_download, dispatch_gauge, "bytes",    CURLINFO_CONTENT_LENGTH_DOWNLOAD),
133         SPEC (content_length_upload,   dispatch_gauge, "bytes",    CURLINFO_CONTENT_LENGTH_UPLOAD),
134         SPEC (starttransfer_time,      dispatch_gauge, "duration", CURLINFO_STARTTRANSFER_TIME),
135         SPEC (redirect_time,           dispatch_gauge, "duration", CURLINFO_REDIRECT_TIME),
136         SPEC (redirect_count,          dispatch_size,  "count",    CURLINFO_REDIRECT_COUNT),
137         SPEC (num_connects,            dispatch_size,  "count",    CURLINFO_NUM_CONNECTS),
138         SPEC (appconnect_time,         dispatch_gauge, "duration", CURLINFO_APPCONNECT_TIME),
139
140 #undef SPEC
141 };
142
143 static void enable_field (curl_stats_t *s, size_t offset)
144 {
145         *(bool *)((char *)s + offset) = true;
146 } /* enable_field */
147
148 static bool field_enabled (curl_stats_t *s, size_t offset)
149 {
150         return *(bool *)((char *)s + offset);
151 } /* field_enabled */
152
153 /*
154  * Public API
155  */
156 curl_stats_t *curl_stats_from_config (oconfig_item_t *ci)
157 {
158         curl_stats_t *s;
159         int i;
160
161         if (ci == NULL)
162                 return NULL;
163
164         s = calloc (sizeof (*s), 1);
165         if (s == NULL)
166                 return NULL;
167
168         for (i = 0; i < ci->children_num; ++i)
169         {
170                 oconfig_item_t *c = ci->children + i;
171                 size_t field;
172
173                 for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field)
174                         if (! strcasecmp (c->key, field_specs[field].name))
175                                 break;
176                 if (field >= STATIC_ARRAY_SIZE (field_specs))
177                 {
178                         ERROR ("curl stats: Unknown field name %s", c->key);
179                         free (s);
180                         return NULL;
181                 }
182
183                 if ((c->values_num != 1)
184                                 || ((c->values[0].type != OCONFIG_TYPE_STRING)
185                                         && (c->values[0].type != OCONFIG_TYPE_BOOLEAN))) {
186                         ERROR ("curl stats: `%s' expects a single boolean argument", c->key);
187                         free (s);
188                         return NULL;
189                 }
190
191                 if (((c->values[0].type == OCONFIG_TYPE_STRING)
192                                         && IS_TRUE (c->values[0].value.string))
193                                 || ((c->values[0].type == OCONFIG_TYPE_BOOLEAN)
194                                         && c->values[0].value.boolean))
195                         enable_field (s, field_specs[field].offset);
196         }
197
198         return s;
199 } /* curl_stats_from_config */
200
201 void curl_stats_destroy (curl_stats_t *s)
202 {
203         if (s != NULL)
204                 free (s);
205 } /* curl_stats_destroy */
206
207 int curl_stats_dispatch (curl_stats_t *s, CURL *curl,
208                 const char *hostname, const char *plugin, const char *plugin_instance,
209                 const char *instance_prefix)
210 {
211         value_list_t vl = VALUE_LIST_INIT;
212         size_t field;
213
214         if (s == NULL)
215                 return 0;
216         if (curl == NULL)
217                 return -1;
218
219         if (hostname != NULL)
220                 sstrncpy (vl.host, hostname, sizeof (vl.host));
221         if (plugin != NULL)
222                 sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
223         if (plugin_instance != NULL)
224                 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
225
226         for (field = 0; field < STATIC_ARRAY_SIZE (field_specs); ++field)
227         {
228                 int status;
229
230                 if (! field_enabled (s, field_specs[field].offset))
231                         continue;
232
233                 sstrncpy (vl.type, field_specs[field].type, sizeof (vl.type));
234                 ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s%s",
235                                 instance_prefix ? instance_prefix : "", field_specs[field].name);
236
237                 vl.values = NULL;
238                 vl.values_len = 0;
239                 status = field_specs[field].dispatcher (curl, field_specs[field].info, &vl);
240                 if (status < 0)
241                         return status;
242         }
243
244         return 0;
245 } /* curl_stats_dispatch */