1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2010, James Martelletti <james@nerdc0re.com>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * James Martelletti <james@nerdc0re.com>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Tamas Cseke <tamas.cseke@vcc.live>
27 *
28 * mod_raven.c -- Raven Logging
29 *
30 */
31 #include <switch.h>
32 #include <zlib.h>
33 #include <switch_curl.h>
34
35 #define RAVEN_ZLIB_CHUNK 16384
36 #define RAVEN_VERSION "5"
37 #define RAVEN_UA "freeswitch-raven/1.0"
38
39 SWITCH_MODULE_LOAD_FUNCTION(mod_raven_load);
40 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_raven_shutdown);
41 SWITCH_MODULE_DEFINITION(mod_raven, mod_raven_load, mod_raven_shutdown, NULL);
42
43 static switch_status_t load_config(void);
44
45 static struct {
46 char *uri;
47 char *key;
48 char *secret;
49 char *project;
50 switch_bool_t log_uuid;
51 switch_log_level_t log_level;
52 } globals;
53
54 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_uri, globals.uri);
55 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_key, globals.key);
56 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_secret, globals.secret);
57 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_project, globals.project);
58
59 static switch_loadable_module_interface_t raven_module_interface = {
60 /*.module_name */ modname,
61 /*.endpoint_interface */ NULL,
62 /*.timer_interface */ NULL,
63 /*.dialplan_interface */ NULL,
64 /*.codec_interface */ NULL,
65 /*.application_interface */ NULL,
66 /*.api_interface */ NULL,
67 /*.file_interface */ NULL,
68 /*.speech_interface */ NULL,
69 /*.directory_interface */ NULL
70 };
71
encode(const char * raw,int raw_len,char ** encoded_out)72 static switch_status_t encode(const char *raw, int raw_len, char **encoded_out)
73 {
74 z_stream stream;
75 unsigned char *encoded = NULL, *compressed = NULL;
76 int ret;
77 switch_size_t compressed_size = 0, compressed_len = 0, need_bytes;
78
79 stream.zalloc = Z_NULL;
80 stream.zfree = Z_NULL;
81 stream.opaque = Z_NULL;
82 ret = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
83 if (ret != Z_OK) {
84 return SWITCH_STATUS_FALSE;
85 }
86
87 stream.next_in = (unsigned char *)raw;
88 stream.avail_in = raw_len;
89
90 do {
91 compressed_size += RAVEN_ZLIB_CHUNK;
92 compressed = realloc(compressed, compressed_size + 1);
93 switch_assert(compressed != NULL);
94
95 stream.avail_out = compressed_size - compressed_len;
96 stream.next_out = compressed + compressed_len;
97
98 ret = deflate(&stream, Z_FINISH);
99 assert(ret != Z_STREAM_ERROR);
100 compressed_len = compressed_size - stream.avail_out;
101 } while (stream.avail_in != 0);
102
103 deflateEnd(&stream);
104
105 need_bytes = compressed_len * 3 + 1;
106 encoded = malloc(need_bytes);
107 switch_assert(encoded);
108 memset(encoded, 0, need_bytes);
109 switch_b64_encode(compressed, compressed_len, encoded, need_bytes);
110
111 switch_safe_free(compressed);
112
113 *encoded_out = (char *)encoded;
114
115 return SWITCH_STATUS_SUCCESS;
116 }
117
raven_capture(const char * userdata,const char * message,const char * level,const char * file,const char * func,int line)118 static switch_status_t raven_capture(const char *userdata, const char *message, const char *level, const char *file, const char *func, int line)
119 {
120 cJSON* json, *fingerprint;
121 char *raw_body;
122 char *encoded_body = NULL;
123 switch_time_t timestamp = switch_micro_time_now();
124 switch_status_t status = SWITCH_STATUS_SUCCESS;
125 char uuid[SWITCH_UUID_FORMATTED_LENGTH + 1];
126
127 switch_uuid_str(uuid, sizeof(uuid));
128 json = cJSON_CreateObject();
129 cJSON_AddStringToObject(json, "event_id", (const char *)uuid);
130 cJSON_AddNumberToObject(json, "timestamp", timestamp);
131 cJSON_AddStringToObject(json, "platform", RAVEN_UA);
132 cJSON_AddStringToObject(json, "project", globals.project);
133 cJSON_AddStringToObject(json, "server_name", switch_core_get_hostname());
134 cJSON_AddStringToObject(json, "level", level);
135
136 if (globals.log_uuid && !zstr(userdata)) {
137 cJSON_AddItemToObject(json, "message", cJSON_CreateStringPrintf("%s %s", userdata, message));
138 } else {
139 cJSON_AddStringToObject(json, "message", message);
140 }
141
142 fingerprint = cJSON_CreateArray();
143 cJSON_AddItemToArray(fingerprint, cJSON_CreateString(file));
144 cJSON_AddItemToArray(fingerprint, cJSON_CreateString(func));
145 cJSON_AddItemToArray(fingerprint, cJSON_CreateNumber(line));
146 cJSON_AddItemToObject(json, "fingerprint", fingerprint);
147
148 raw_body = cJSON_PrintUnformatted(json);
149
150 if ((status = encode(raw_body, strlen(raw_body), &encoded_body)) == SWITCH_STATUS_SUCCESS) {
151 int response;
152 CURL *curl_handle = switch_curl_easy_init();
153 switch_curl_slist_t * list = NULL;
154 char *auth_header = switch_mprintf("X-Sentry-Auth: Sentry sentry_version=%s,"
155 " sentry_client=%s,"
156 " sentry_timestamp=%d,"
157 " sentry_key=%s,"
158 " sentry_secret=%s",
159 RAVEN_VERSION, RAVEN_UA,
160 timestamp, globals.key, globals.secret);
161
162 char *url = switch_mprintf( "%s/api/%s/store/", globals.uri, globals.project);
163 switch_curl_easy_setopt(curl_handle, CURLOPT_URL,url);
164 switch_curl_easy_setopt(curl_handle, CURLOPT_POST, 1);
165 switch_curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
166 switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, encoded_body);
167 switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, strlen(encoded_body));
168
169 switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, RAVEN_UA);
170
171 list = switch_curl_slist_append(list, auth_header);
172 list = switch_curl_slist_append(list, "Content-Type: application/octet-stream");
173 switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
174
175 if (!strncasecmp(globals.uri, "https", 5)) {
176 switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
177 switch_curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
178 }
179
180 switch_curl_easy_perform(curl_handle);
181 switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response);
182
183 if (response != 200) {
184 status = SWITCH_STATUS_FALSE;
185 }
186
187 switch_curl_easy_cleanup(curl_handle);
188 switch_curl_slist_free_all(list);
189 switch_safe_free(url);
190 switch_safe_free(auth_header);
191 }
192
193 switch_safe_free(raw_body);
194 switch_safe_free(encoded_body);
195 cJSON_Delete(json);
196
197 return status;
198 }
199
200
mod_raven_logger(const switch_log_node_t * node,switch_log_level_t level)201 static switch_status_t mod_raven_logger(const switch_log_node_t *node, switch_log_level_t level)
202 {
203 switch_status_t status = SWITCH_STATUS_SUCCESS;
204
205 if (level != SWITCH_LOG_CONSOLE && !zstr(node->data)) {
206 const char * raven_level;
207
208 switch (level) {
209 case SWITCH_LOG_DEBUG:
210 raven_level = "debug";
211 break;
212 case SWITCH_LOG_INFO:
213 raven_level = "info";
214 break;
215 case SWITCH_LOG_NOTICE:
216 case SWITCH_LOG_WARNING:
217 raven_level = "warning";
218 break;
219 case SWITCH_LOG_ERROR:
220 raven_level = "error";
221 break;
222 case SWITCH_LOG_CRIT:
223 case SWITCH_LOG_ALERT:
224 raven_level = "fatal";
225 break;
226 default:
227 raven_level = "debug";
228 break;
229 }
230
231 status = raven_capture(node->userdata, node->data, raven_level, node->file, node->func, node->line);
232 }
233
234 return status;
235 }
236
load_config(void)237 static switch_status_t load_config(void)
238 {
239 char *cf = "raven.conf";
240 switch_xml_t cfg, xml, settings, param;
241
242 globals.log_level = SWITCH_LOG_WARNING;
243 globals.log_uuid = SWITCH_TRUE;
244
245 if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
246 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
247 return SWITCH_STATUS_FALSE;
248 }
249
250 if ((settings = switch_xml_child(cfg, "settings"))) {
251 for (param = switch_xml_child(settings, "param"); param; param = param->next) {
252 char *var = (char *) switch_xml_attr_soft(param, "name");
253 char *val = (char *) switch_xml_attr_soft(param, "value");
254
255 if (!strcmp(var, "uri")) {
256 set_global_uri(val);
257 } else if (!strcmp(var, "key")) {
258 set_global_key(val);
259 } else if (!strcmp(var, "secret")) {
260 set_global_secret(val);
261 } else if (!strcmp(var, "project")) {
262 set_global_project(val);
263 } else if (!strcasecmp(var, "loglevel") && !zstr(val)) {
264 globals.log_level = switch_log_str2level(val);
265 if (globals.log_level == SWITCH_LOG_INVALID) {
266 globals.log_level = SWITCH_LOG_WARNING;
267 }
268 } else if (!strcasecmp(var, "uuid")) {
269 globals.log_uuid = switch_true(val);
270 }
271 }
272 }
273 switch_xml_free(xml);
274
275 if (zstr(globals.uri) || zstr(globals.project) || zstr(globals.key) || zstr(globals.secret)) {
276 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing parameter\n");
277 return SWITCH_STATUS_FALSE;
278 }
279
280 return SWITCH_STATUS_SUCCESS;
281 }
282
SWITCH_MODULE_LOAD_FUNCTION(mod_raven_load)283 SWITCH_MODULE_LOAD_FUNCTION(mod_raven_load)
284 {
285 switch_status_t status;
286 *module_interface = &raven_module_interface;
287
288 memset(&globals, 0, sizeof(globals));
289
290 if ((status = load_config()) != SWITCH_STATUS_SUCCESS) {
291 return status;
292 }
293
294 switch_log_bind_logger(mod_raven_logger, globals.log_level, SWITCH_FALSE);
295
296 return SWITCH_STATUS_SUCCESS;
297 }
298
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_raven_shutdown)299 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_raven_shutdown)
300 {
301 switch_safe_free(globals.uri);
302 switch_safe_free(globals.key);
303 switch_safe_free(globals.secret);
304 switch_safe_free(globals.project);
305
306 switch_log_unbind_logger(mod_raven_logger);
307
308 return SWITCH_STATUS_SUCCESS;
309 }
310
311 /* For Emacs:
312 * Local Variables:
313 * mode:c
314 * indent-tabs-mode:t
315 * tab-width:4
316 * c-basic-offset:4
317 * End:
318 * For VIM:
319 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
320 */
321