Repository URL to install this package:
|
Version:
2.0.17 ▾
|
#include <uwsgi.h>
extern struct uwsgi_server uwsgi;
/*
Exceptions management
generally exceptions are printed in the logs, but if you enable
an exception manager they will be stored in a (relatively big) uwsgi packet
with the following structure.
"vars" -> keyval of request vars
"backtrace" -> list of backtrace lines. Each line is a list of 5 elements filename,line,function,text,custom
"unix" -> seconds since the epoch
"class" -> the exception class
"msg" -> a text message mapped to the extension
"repr" -> a text message mapped to the extension in language-specific gergo
"wid" -> worker id
"core" -> the core generating the exception
"pid" -> pid of the worker
"node" -> hostname
Other vars can be added, but you cannot be sure they will be used by exceptions handler.
The exception-uwsgi packet is passed "as is" to the exception handler
Exceptions hooks:
a request plugin can export that hooks:
struct uwsgi_buffer *backtrace(struct wsgi_request *);
struct uwsgi_buffer *exception_class(struct wsgi_request *);
struct uwsgi_buffer *exception_msg(struct wsgi_request *);
struct uwsgi_buffer *exception_repr(struct wsgi_request *);
void exception_log(struct wsgi_request *);
Remember to reset the exception status (if possibile) after each call
Exceptions catcher:
This is a special development-mode in which exceptions are printed
to the HTTP client.
*/
struct uwsgi_buffer *uwsgi_exception_handler_object(struct wsgi_request *wsgi_req) {
struct uwsgi_buffer *ub = uwsgi_buffer_new(4096);
if (uwsgi_buffer_append_keyval(ub, "vars", 4, wsgi_req->buffer,wsgi_req->uh->pktsize)) goto error;
if (uwsgi.p[wsgi_req->uh->modifier1]->backtrace) {
struct uwsgi_buffer *bt = uwsgi.p[wsgi_req->uh->modifier1]->backtrace(wsgi_req);
if (bt) {
if (uwsgi_buffer_append_keyval(ub, "backtrace", 9, bt->buf, bt->pos)) {
uwsgi_buffer_destroy(bt);
goto error;
}
uwsgi_buffer_destroy(bt);
}
}
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_class) {
struct uwsgi_buffer *ec = uwsgi.p[wsgi_req->uh->modifier1]->exception_class(wsgi_req);
if (ec) {
if (uwsgi_buffer_append_keyval(ub, "class", 5, ec->buf, ec->pos)) {
uwsgi_buffer_destroy(ec);
goto error;
}
uwsgi_buffer_destroy(ec);
}
}
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_msg) {
struct uwsgi_buffer *em = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
if (em) {
if (uwsgi_buffer_append_keyval(ub, "msg", 3, em->buf, em->pos)) {
uwsgi_buffer_destroy(em);
goto error;
}
uwsgi_buffer_destroy(em);
}
}
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_repr) {
struct uwsgi_buffer *er = uwsgi.p[wsgi_req->uh->modifier1]->exception_repr(wsgi_req);
if (er) {
if (uwsgi_buffer_append_keyval(ub, "repr", 4, er->buf, er->pos)) {
uwsgi_buffer_destroy(er);
goto error;
}
uwsgi_buffer_destroy(er);
}
}
if (uwsgi_buffer_append_keynum(ub, "unix", 4, uwsgi_now())) goto error;
if (uwsgi_buffer_append_keynum(ub, "wid", 3, uwsgi.mywid)) goto error;
if (uwsgi_buffer_append_keynum(ub, "pid", 3, uwsgi.mypid)) goto error;
if (uwsgi_buffer_append_keynum(ub, "core", 4, wsgi_req->async_id)) goto error;
if (uwsgi_buffer_append_keyval(ub, "node", 4, uwsgi.hostname, uwsgi.hostname_len)) goto error;
return ub;
error:
uwsgi_buffer_destroy(ub);
return NULL;
}
static void append_vars_to_ubuf(char *key, uint16_t keylen, char *val, uint16_t vallen, void *data) {
struct uwsgi_buffer *ub = (struct uwsgi_buffer *) data;
if (uwsgi_buffer_append(ub, key, keylen)) return;
if (uwsgi_buffer_append(ub, " = ", 3)) return;
if (uwsgi_buffer_append(ub, val, vallen)) return;
if (uwsgi_buffer_append(ub, "\n", 1)) return;
}
static void append_backtrace_to_ubuf(uint16_t pos, char *value, uint16_t len, void *data) {
struct uwsgi_buffer *ub = (struct uwsgi_buffer *) data;
uint16_t item = 0;
if (pos > 0) {
item = pos % 5;
}
switch(item) {
// filename
case 0:
if (uwsgi_buffer_append(ub, "filename: \"", 11)) return;
if (uwsgi_buffer_append(ub, value, len)) return;
if (uwsgi_buffer_append(ub, "\" ", 2)) return;
break;
// lineno
case 1:
if (uwsgi_buffer_append(ub, "line: ", 6)) return;
if (uwsgi_buffer_append(ub, value, len)) return;
if (uwsgi_buffer_append(ub, " ", 1)) return;
break;
// function
case 2:
if (uwsgi_buffer_append(ub, "function: \"", 11)) return;
if (uwsgi_buffer_append(ub, value, len)) return;
if (uwsgi_buffer_append(ub, "\" ", 2)) return;
break;
// text
case 3:
if (len > 0) {
if (uwsgi_buffer_append(ub, "text/code: \"", 12)) return;
if (uwsgi_buffer_append(ub, value, len)) return;
if (uwsgi_buffer_append(ub, "\" ", 2)) return;
}
break;
// custom
case 4:
if (len > 0) {
if (uwsgi_buffer_append(ub, "custom: \"", 9)) return;
if (uwsgi_buffer_append(ub, value, len)) return;
if (uwsgi_buffer_append(ub, "\" ", 2)) return;
}
if (uwsgi_buffer_append(ub, "\n", 1)) return;
break;
default:
break;
}
}
int uwsgi_exceptions_catch(struct wsgi_request *wsgi_req) {
if (uwsgi_response_prepare_headers(wsgi_req, "500 Internal Server Error", 25)) {
return -1;
}
if (uwsgi_response_add_content_type(wsgi_req, "text/plain", 10)) {
return -1;
}
struct uwsgi_buffer *ub = uwsgi_buffer_new(4096);
if (uwsgi_buffer_append(ub, "uWSGI exceptions catcher for \"", 30)) goto error;
if (uwsgi_buffer_append(ub, wsgi_req->method, wsgi_req->method_len)) goto error;
if (uwsgi_buffer_append(ub, " ", 1)) goto error;
if (uwsgi_buffer_append(ub, wsgi_req->uri, wsgi_req->uri_len)) goto error;
if (uwsgi_buffer_append(ub, "\" (request plugin: \"", 20)) goto error;
if (uwsgi_buffer_append(ub, (char *) uwsgi.p[wsgi_req->uh->modifier1]->name, strlen(uwsgi.p[wsgi_req->uh->modifier1]->name))) goto error;
if (uwsgi_buffer_append(ub, "\", modifier1: ", 14 )) goto error;
if (uwsgi_buffer_num64(ub, wsgi_req->uh->modifier1)) goto error;
if (uwsgi_buffer_append(ub, ")\n\n", 3)) goto error;
if (uwsgi_buffer_append(ub, "Exception: ", 11)) goto error;
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_repr) {
struct uwsgi_buffer *ub_exc_repr = uwsgi.p[wsgi_req->uh->modifier1]->exception_repr(wsgi_req);
if (ub_exc_repr) {
if (uwsgi_buffer_append(ub, ub_exc_repr->buf, ub_exc_repr->pos)) {
uwsgi_buffer_destroy(ub_exc_repr);
goto error;
}
uwsgi_buffer_destroy(ub_exc_repr);
}
else {
goto notavail3;
}
}
else {
notavail3:
if (uwsgi_buffer_append(ub, "-Not available-", 15)) goto error;
}
if (uwsgi_buffer_append(ub, "\n\n", 2)) goto error;
if (uwsgi_buffer_append(ub, "Exception class: ", 17)) goto error;
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_class) {
struct uwsgi_buffer *ub_exc_class = uwsgi.p[wsgi_req->uh->modifier1]->exception_class(wsgi_req);
if (ub_exc_class) {
if (uwsgi_buffer_append(ub, ub_exc_class->buf, ub_exc_class->pos)) {
uwsgi_buffer_destroy(ub_exc_class);
goto error;
}
uwsgi_buffer_destroy(ub_exc_class);
}
else {
goto notavail;
}
}
else {
notavail:
if (uwsgi_buffer_append(ub, "-Not available-", 15)) goto error;
}
if (uwsgi_buffer_append(ub, "\n\n", 2)) goto error;
if (uwsgi_buffer_append(ub, "Exception message: ", 19)) goto error;
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_msg) {
struct uwsgi_buffer *ub_exc_msg = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
if (ub_exc_msg) {
if (uwsgi_buffer_append(ub, ub_exc_msg->buf, ub_exc_msg->pos)) {
uwsgi_buffer_destroy(ub_exc_msg);
goto error;
}
uwsgi_buffer_destroy(ub_exc_msg);
}
else {
goto notavail2;
}
}
else {
notavail2:
if (uwsgi_buffer_append(ub, "-Not available-", 15)) goto error;
}
if (uwsgi_buffer_append(ub, "\n\n", 2)) goto error;
if (uwsgi_buffer_append(ub, "Backtrace:\n", 11)) goto error;
if (uwsgi.p[wsgi_req->uh->modifier1]->backtrace) {
struct uwsgi_buffer *ub_exc_bt = uwsgi.p[wsgi_req->uh->modifier1]->backtrace(wsgi_req);
if (ub_exc_bt) {
struct uwsgi_buffer *parsed_bt = uwsgi_buffer_new(4096);
if (uwsgi_hooked_parse_array(ub_exc_bt->buf, ub_exc_bt->pos, append_backtrace_to_ubuf, parsed_bt)) {
uwsgi_buffer_destroy(ub_exc_bt);
uwsgi_buffer_destroy(parsed_bt);
goto error;
}
uwsgi_buffer_destroy(ub_exc_bt);
if (uwsgi_buffer_append(ub, parsed_bt->buf, parsed_bt->pos)) {
uwsgi_buffer_destroy(parsed_bt);
goto error;
}
uwsgi_buffer_destroy(parsed_bt);
}
else {
goto notavail4;
}
}
else {
notavail4:
if (uwsgi_buffer_append(ub, "-Not available-", 15)) goto error;
}
if (uwsgi_buffer_append(ub, "\n\n", 2)) goto error;
if (uwsgi_hooked_parse(wsgi_req->buffer, wsgi_req->uh->pktsize, append_vars_to_ubuf, ub)) {
goto error;
}
if (uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos)) {
goto error;
}
uwsgi_buffer_destroy(ub);
return 0;
error:
uwsgi_buffer_destroy(ub);
return -1;
}
static void uwsgi_exception_run_handlers(struct uwsgi_buffer *ub) {
struct uwsgi_string_list *usl = uwsgi.exception_handlers_instance;
struct iovec iov[2];
iov[1].iov_base = ub->buf;
iov[1].iov_len = ub->pos;
while(usl) {
struct uwsgi_exception_handler_instance *uehi = (struct uwsgi_exception_handler_instance *) usl->custom_ptr;
iov[0].iov_base = &uehi;
iov[0].iov_len = sizeof(long);
// now send the message to the exception handler thread
if (writev(uwsgi.exception_handler_thread->pipe[0], iov, 2) != (ssize_t) (ub->pos+sizeof(long))) {
uwsgi_error("[uwsgi-exception-handler-error] uwsgi_exception_run_handlers()/writev()");
}
usl = usl->next;
}
}
void uwsgi_manage_exception(struct wsgi_request *wsgi_req,int catch) {
int do_exit = uwsgi.reload_on_exception;
if (!wsgi_req) goto log2;
if (do_exit) goto check_catch;
if (uwsgi.exception_handlers_instance) {
struct uwsgi_buffer *ehi = uwsgi_exception_handler_object(wsgi_req);
if (ehi) {
uwsgi_exception_run_handlers(ehi);
uwsgi_buffer_destroy(ehi);
}
}
uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].exceptions++;
uwsgi_apps[wsgi_req->app_id].exceptions++;
if (uwsgi.reload_on_exception_type && uwsgi.p[wsgi_req->uh->modifier1]->exception_class) {
struct uwsgi_buffer *ub = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
if (ub) {
struct uwsgi_string_list *usl = uwsgi.reload_on_exception_type;
while (usl) {
if (!uwsgi_strncmp(usl->value, usl->len, ub->buf, ub->len)) {
do_exit = 1;
uwsgi_buffer_destroy(ub);
goto check_catch;
}
usl = usl->next;
}
uwsgi_buffer_destroy(ub);
}
}
if (uwsgi.reload_on_exception_value && uwsgi.p[wsgi_req->uh->modifier1]->exception_msg) {
struct uwsgi_buffer *ub = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
if (ub) {
struct uwsgi_string_list *usl = uwsgi.reload_on_exception_value;
while (usl) {
if (!uwsgi_strncmp(usl->value, usl->len, ub->buf, ub->len)) {
do_exit = 1;
uwsgi_buffer_destroy(ub);
goto check_catch;
}
usl = usl->next;
}
uwsgi_buffer_destroy(ub);
}
}
if (uwsgi.reload_on_exception_repr && uwsgi.p[wsgi_req->uh->modifier1]->exception_repr) {
struct uwsgi_buffer *ub = uwsgi.p[wsgi_req->uh->modifier1]->exception_msg(wsgi_req);
if (ub) {
struct uwsgi_string_list *usl = uwsgi.reload_on_exception_repr;
while (usl) {
if (!uwsgi_strncmp(usl->value, usl->len, ub->buf, ub->len)) {
do_exit = 1;
uwsgi_buffer_destroy(ub);
goto check_catch;
}
usl = usl->next;
}
uwsgi_buffer_destroy(ub);
}
}
check_catch:
if (catch && wsgi_req) {
if (uwsgi_exceptions_catch(wsgi_req)) {
// for now, just goto, new features could be added
goto log;
}
}
log:
if (uwsgi.p[wsgi_req->uh->modifier1]->exception_log) {
uwsgi.p[wsgi_req->uh->modifier1]->exception_log(wsgi_req);
}
log2:
if (do_exit) {
exit(UWSGI_EXCEPTION_CODE);
}
}
struct uwsgi_exception_handler *uwsgi_register_exception_handler(char *name, int (*func)(struct uwsgi_exception_handler_instance *, char *, size_t)) {
struct uwsgi_exception_handler *old_ueh = NULL, *ueh = uwsgi.exception_handlers;
while(ueh) {
if (!strcmp(name, ueh->name)) {
return NULL;
}
old_ueh = ueh;
ueh = ueh->next;
}
ueh = uwsgi_calloc(sizeof(struct uwsgi_exception_handler));
ueh->name = name;
ueh->func = func;
if (old_ueh) {
old_ueh->next = ueh;
}
else {
uwsgi.exception_handlers = ueh;
}
return ueh;
}
struct uwsgi_exception_handler *uwsgi_exception_handler_by_name(char *name) {
struct uwsgi_exception_handler *ueh = uwsgi.exception_handlers;
while(ueh) {
if (!strcmp(name, ueh->name)) {
return ueh;
}
ueh = ueh->next;
}
return NULL;
}
static void uwsgi_exception_handler_thread_loop(struct uwsgi_thread *ut) {
char *buf = uwsgi_malloc(uwsgi.exception_handler_msg_size + sizeof(long));
for (;;) {
int interesting_fd = -1;
int ret = event_queue_wait(ut->queue, -1, &interesting_fd);
if (ret > 0) {
ssize_t len = read(ut->pipe[1], buf, uwsgi.exception_handler_msg_size + sizeof(long));
if (len > (ssize_t)(sizeof(long) + 1)) {
size_t msg_size = len - sizeof(long);
char *msg = buf + sizeof(long);
long ptr = 0;
memcpy(&ptr, buf, sizeof(long));
struct uwsgi_exception_handler_instance *uehi = (struct uwsgi_exception_handler_instance *) ptr;
if (!uehi)
break;
if (uehi->handler->func(uehi, msg, msg_size)) {
uwsgi_log("[uwsgi-exception] error running the handler \"%s\" args: \"%s\"\n", uehi->handler->name, uehi->arg ? uehi->arg : "");
}
}
}
}
free(buf);
}
void uwsgi_exception_setup_handlers() {
struct uwsgi_string_list *usl = uwsgi.exception_handlers_instance;
while(usl) {
// do not free handler !!!
char *handler = uwsgi_str(usl->value);
char *colon = strchr(handler, ':');
if (colon) {
*colon = 0;
}
struct uwsgi_exception_handler *ueh = uwsgi_exception_handler_by_name(handler);
if (!ueh) {
uwsgi_log("unable to find exception handler: %s\n", handler);
exit(1);
}
struct uwsgi_exception_handler_instance *uehi = uwsgi_calloc(sizeof(struct uwsgi_exception_handler_instance));
uehi->handler = ueh;
if (colon) {
uehi->arg = colon+1;
}
usl->custom_ptr = uehi;
usl = usl->next;
}
}
void uwsgi_exceptions_handler_thread_start() {
if (!uwsgi.exception_handlers_instance) return;
// start the exception_handler_thread
uwsgi.exception_handler_thread = uwsgi_thread_new(uwsgi_exception_handler_thread_loop);
if (!uwsgi.exception_handler_thread) {
uwsgi_log("unable to spawn exception handler thread\n");
exit(1);
}
}