2008-12-31 01:50:32 +00:00
|
|
|
/*
|
|
|
|
todo:
|
|
|
|
- write out logs at startup directly
|
|
|
|
- scheme:// prefix
|
2009-10-03 13:46:36 +00:00
|
|
|
- LI_LOG_LEVEL_ABORT: write message immediately, as the log write is followed by an abort()
|
2008-12-31 01:50:32 +00:00
|
|
|
|
|
|
|
*/
|
2008-06-24 19:19:20 +00:00
|
|
|
|
2008-11-16 20:33:53 +00:00
|
|
|
#include <lighttpd/base.h>
|
|
|
|
#include <lighttpd/plugin_core.h>
|
2010-09-25 12:08:56 +00:00
|
|
|
|
2008-06-24 19:19:20 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
#define LOG_DEFAULT_TS_FORMAT "%d/%b/%Y %T %Z"
|
|
|
|
#define LOG_DEFAULT_TTL 30.0
|
2010-02-13 14:29:21 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
static void log_watcher_cb(struct ev_loop *loop, ev_async *w, int revents);
|
2009-10-09 13:38:12 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
static void li_log_write_stderr(liServer *srv, const gchar *msg, gboolean newline) {
|
|
|
|
gsize s;
|
|
|
|
struct tm tm;
|
|
|
|
time_t now = (time_t) ev_time();
|
|
|
|
gchar buf[128];
|
|
|
|
|
|
|
|
UNUSED(srv);
|
|
|
|
|
|
|
|
#ifdef HAVE_LOCALTIME_R
|
|
|
|
s = strftime(buf, sizeof(buf), LOG_DEFAULT_TS_FORMAT, localtime_r(&now, &tm));
|
|
|
|
#else
|
|
|
|
s = strftime(buf, sizeof(buf), LOG_DEFAULT_TS_FORMAT, localtime(&now));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
buf[s] = '\0';
|
|
|
|
|
|
|
|
g_printerr(newline ? "%s %s\n" : "%s %s", buf, msg);
|
2009-10-09 13:38:12 +00:00
|
|
|
}
|
|
|
|
|
2012-03-16 12:24:17 +00:00
|
|
|
static liLogTarget *log_open(liServer *srv, GString *path) {
|
|
|
|
liLogTarget *log;
|
2010-08-29 10:24:30 +00:00
|
|
|
|
2010-10-07 16:31:21 +00:00
|
|
|
log = li_radixtree_lookup_exact(srv->logs.targets, path->str, path->len * 8);
|
2010-08-29 10:24:30 +00:00
|
|
|
|
2010-09-25 12:08:56 +00:00
|
|
|
if (NULL == log) {
|
2010-08-29 10:24:30 +00:00
|
|
|
/* log not open */
|
|
|
|
gint fd = -1;
|
2010-09-25 12:08:56 +00:00
|
|
|
gchar *param = NULL;
|
|
|
|
liLogType type = li_log_type_from_path(path, ¶m);
|
|
|
|
GString sparam = { param, (param != NULL ? path->len - (param - path->str) : 0), 0 };
|
2010-08-29 10:24:30 +00:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case LI_LOG_TYPE_STDERR:
|
|
|
|
fd = STDERR_FILENO;
|
|
|
|
break;
|
|
|
|
case LI_LOG_TYPE_FILE:
|
2010-09-25 12:08:56 +00:00
|
|
|
/* TODO: open via angel */
|
|
|
|
fd = li_angel_fake_log_open_file(srv, &sparam);
|
2010-08-29 10:24:30 +00:00
|
|
|
break;
|
|
|
|
case LI_LOG_TYPE_PIPE:
|
2010-09-25 12:08:56 +00:00
|
|
|
ERROR(srv, "%s", "pipe logging not supported yet");
|
|
|
|
break;
|
2010-08-29 10:24:30 +00:00
|
|
|
case LI_LOG_TYPE_SYSLOG:
|
2010-09-25 12:08:56 +00:00
|
|
|
ERROR(srv, "%s", "syslog not supported yet");
|
|
|
|
break;
|
2010-08-29 10:24:30 +00:00
|
|
|
case LI_LOG_TYPE_NONE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-09-25 12:08:56 +00:00
|
|
|
/* Even if -1 == fd we create an entry, so we don't throw an error every time */
|
2012-03-16 12:24:17 +00:00
|
|
|
log = g_slice_new0(liLogTarget);
|
2010-08-29 10:24:30 +00:00
|
|
|
log->type = type;
|
|
|
|
log->path = g_string_new_len(GSTR_LEN(path));
|
|
|
|
log->fd = fd;
|
|
|
|
log->wqelem.data = log;
|
|
|
|
li_radixtree_insert(srv->logs.targets, log->path->str, log->path->len * 8, log);
|
|
|
|
/*g_print("log_open(\"%s\")\n", log->path->str);*/
|
|
|
|
}
|
|
|
|
|
|
|
|
li_waitqueue_push(&srv->logs.close_queue, &log->wqelem);
|
|
|
|
|
|
|
|
return log;
|
2009-10-09 13:38:12 +00:00
|
|
|
}
|
|
|
|
|
2012-03-16 12:24:17 +00:00
|
|
|
static void log_close(liServer *srv, liLogTarget *log) {
|
2010-08-29 10:24:30 +00:00
|
|
|
li_radixtree_remove(srv->logs.targets, log->path->str, log->path->len * 8);
|
|
|
|
li_waitqueue_remove(&srv->logs.close_queue, &log->wqelem);
|
|
|
|
|
|
|
|
if (log->type == LI_LOG_TYPE_FILE || log->type == LI_LOG_TYPE_PIPE) {
|
2010-09-25 12:08:56 +00:00
|
|
|
if (-1 != log->fd) close(log->fd);
|
2010-08-29 10:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*g_print("log_close(\"%s\")\n", log->path->str);*/
|
|
|
|
g_string_free(log->path, TRUE);
|
|
|
|
|
2012-03-16 12:24:17 +00:00
|
|
|
g_slice_free(liLogTarget, log);
|
2010-08-29 10:24:30 +00:00
|
|
|
}
|
|
|
|
|
2010-09-12 12:19:25 +00:00
|
|
|
static void log_close_cb(liWaitQueue *wq, gpointer data) {
|
2010-08-29 10:24:30 +00:00
|
|
|
/* callback for the close queue */
|
2010-09-12 12:19:25 +00:00
|
|
|
liServer *srv = (liServer*) data;
|
2010-08-29 10:24:30 +00:00
|
|
|
liWaitQueueElem *wqe;
|
|
|
|
|
2010-09-12 12:19:25 +00:00
|
|
|
while ((wqe = li_waitqueue_pop(wq)) != NULL) {
|
2010-08-29 10:24:30 +00:00
|
|
|
log_close(srv, wqe->data);
|
|
|
|
}
|
|
|
|
|
2010-09-12 12:19:25 +00:00
|
|
|
li_waitqueue_update(wq);
|
2010-08-29 10:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void li_log_init(liServer *srv) {
|
|
|
|
srv->logs.loop = ev_loop_new(EVFLAG_AUTO);
|
|
|
|
ev_async_init(&srv->logs.watcher, log_watcher_cb);
|
|
|
|
srv->logs.watcher.data = srv;
|
|
|
|
srv->logs.targets = li_radixtree_new();
|
|
|
|
li_waitqueue_init(&srv->logs.close_queue, srv->logs.loop, log_close_cb, LOG_DEFAULT_TTL, srv);
|
2012-03-16 12:24:17 +00:00
|
|
|
srv->logs.timestamp.format = g_string_new_len(CONST_STR_LEN(LOG_DEFAULT_TS_FORMAT));
|
|
|
|
srv->logs.timestamp.cached = g_string_sized_new(255);
|
|
|
|
srv->logs.timestamp.last_ts = 0;
|
2010-08-29 10:24:30 +00:00
|
|
|
srv->logs.thread_alive = FALSE;
|
|
|
|
g_queue_init(&srv->logs.write_queue);
|
|
|
|
g_static_mutex_init(&srv->logs.write_queue_mutex);
|
2012-03-17 14:52:19 +00:00
|
|
|
srv->logs.log_context.log_map = li_log_map_new_default();
|
2010-08-29 10:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void li_log_cleanup(liServer *srv) {
|
|
|
|
/* wait for logging thread to exit */
|
|
|
|
if (g_atomic_int_get(&srv->logs.thread_alive) == TRUE)
|
|
|
|
{
|
|
|
|
li_log_thread_finish(srv);
|
|
|
|
g_thread_join(srv->logs.thread);
|
|
|
|
}
|
2008-06-24 19:19:20 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
li_radixtree_free(srv->logs.targets, NULL, NULL);
|
|
|
|
|
2012-03-16 12:24:17 +00:00
|
|
|
g_string_free(srv->logs.timestamp.format, TRUE);
|
|
|
|
g_string_free(srv->logs.timestamp.cached, TRUE);
|
2010-08-29 10:24:30 +00:00
|
|
|
|
|
|
|
ev_loop_destroy(srv->logs.loop);
|
2012-03-17 14:52:19 +00:00
|
|
|
|
|
|
|
li_log_context_set(&srv->logs.log_context, NULL);
|
2010-08-29 10:24:30 +00:00
|
|
|
}
|
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
liLogMap* li_log_map_new(void) {
|
2012-03-16 12:24:17 +00:00
|
|
|
liLogMap *log_map = g_slice_new0(liLogMap);
|
|
|
|
|
|
|
|
log_map->refcount = 1;
|
|
|
|
|
|
|
|
return log_map;
|
|
|
|
}
|
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
liLogMap* li_log_map_new_default(void) {
|
|
|
|
liLogMap *log_map = li_log_map_new();
|
|
|
|
|
|
|
|
/* default: log LI_LOG_LEVEL_WARNING, LI_LOG_LEVEL_ERROR and LI_LOG_LEVEL_BACKEND to stderr */
|
|
|
|
log_map->targets[LI_LOG_LEVEL_WARNING] = g_string_new_len(CONST_STR_LEN("stderr"));
|
|
|
|
log_map->targets[LI_LOG_LEVEL_ERROR] = g_string_new_len(CONST_STR_LEN("stderr"));
|
|
|
|
log_map->targets[LI_LOG_LEVEL_BACKEND] = g_string_new_len(CONST_STR_LEN("stderr"));
|
|
|
|
|
|
|
|
return log_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
void li_log_map_acquire(liLogMap *log_map) {
|
2012-03-16 12:24:17 +00:00
|
|
|
assert(g_atomic_int_get(&log_map->refcount) > 0);
|
|
|
|
g_atomic_int_inc(&log_map->refcount);
|
|
|
|
}
|
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
void li_log_map_release(liLogMap *log_map) {
|
2012-03-16 12:24:17 +00:00
|
|
|
if (!log_map) return;
|
|
|
|
|
|
|
|
assert(g_atomic_int_get(&log_map->refcount) > 0);
|
|
|
|
if (g_atomic_int_dec_and_test(&log_map->refcount)) {
|
2012-03-17 14:52:19 +00:00
|
|
|
for (guint i = 0; i < LI_LOG_LEVEL_COUNT; ++i) {
|
|
|
|
if (NULL != log_map->targets[i]) {
|
|
|
|
g_string_free(log_map->targets[i], TRUE);
|
|
|
|
}
|
2012-03-16 12:24:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_slice_free(liLogMap, log_map);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
void li_log_context_set(liLogContext *context, liLogMap *log_map) {
|
|
|
|
if (NULL == context || context->log_map == log_map) return;
|
|
|
|
|
|
|
|
li_log_map_release(context->log_map);
|
|
|
|
if (NULL != log_map) {
|
|
|
|
li_log_map_acquire(log_map);
|
|
|
|
context->log_map = log_map;
|
|
|
|
} else {
|
|
|
|
context->log_map = NULL;
|
|
|
|
}
|
2012-03-16 12:24:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
gboolean li_log_write_direct(liServer *srv, liWorker *wrk, GString *path, GString *msg) {
|
2010-08-29 10:24:30 +00:00
|
|
|
liLogEntry *log_entry;
|
2008-06-24 19:19:20 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
log_entry = g_slice_new(liLogEntry);
|
2010-08-29 10:24:30 +00:00
|
|
|
log_entry->path = g_string_new_len(GSTR_LEN(path));
|
|
|
|
log_entry->level = 0;
|
|
|
|
log_entry->flags = 0;
|
2008-12-02 14:58:06 +00:00
|
|
|
log_entry->msg = msg;
|
2010-08-29 10:24:30 +00:00
|
|
|
log_entry->queue_link.data = log_entry;
|
|
|
|
log_entry->queue_link.next = NULL;
|
|
|
|
log_entry->queue_link.prev = NULL;
|
|
|
|
|
2012-03-16 12:24:17 +00:00
|
|
|
if (G_LIKELY(wrk)) {
|
2010-08-29 10:24:30 +00:00
|
|
|
/* push onto local worker log queue */
|
2012-03-16 12:24:17 +00:00
|
|
|
g_queue_push_tail_link(&wrk->logs.log_queue, &log_entry->queue_link);
|
2010-08-29 10:24:30 +00:00
|
|
|
} else {
|
|
|
|
/* no worker context, push directly onto global log queue */
|
|
|
|
g_static_mutex_lock(&srv->logs.write_queue_mutex);
|
|
|
|
g_queue_push_tail_link(&srv->logs.write_queue, &log_entry->queue_link);
|
|
|
|
g_static_mutex_unlock(&srv->logs.write_queue_mutex);
|
|
|
|
ev_async_send(srv->logs.loop, &srv->logs.watcher);
|
|
|
|
}
|
2008-06-24 19:19:20 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
return TRUE;
|
2008-06-24 19:19:20 +00:00
|
|
|
}
|
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
gboolean li_log_write(liServer *srv, liWorker *wrk, liLogContext *context, liLogLevel log_level, guint flags, const gchar *fmt, ...) {
|
2008-07-18 18:23:05 +00:00
|
|
|
va_list ap;
|
|
|
|
GString *log_line;
|
2009-07-08 19:06:07 +00:00
|
|
|
liLogEntry *log_entry;
|
2012-03-17 14:52:19 +00:00
|
|
|
liLogMap *log_map = NULL;
|
2010-08-29 10:24:30 +00:00
|
|
|
GString *path;
|
2008-07-18 18:23:05 +00:00
|
|
|
|
2012-03-16 12:24:17 +00:00
|
|
|
if (!srv) srv = wrk->srv;
|
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
if (NULL != context) log_map = context->log_map;
|
|
|
|
if (NULL == log_map) log_map = srv->logs.log_context.log_map;
|
2010-01-24 20:30:41 +00:00
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
if (log_map != NULL && log_level < LI_LOG_LEVEL_COUNT) {
|
|
|
|
path = log_map->targets[log_level];
|
2010-08-29 10:24:30 +00:00
|
|
|
} else {
|
|
|
|
return FALSE;
|
2010-01-24 20:30:41 +00:00
|
|
|
}
|
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
log_line = g_string_sized_new(63);
|
2008-07-18 18:23:05 +00:00
|
|
|
va_start(ap, fmt);
|
|
|
|
g_string_vprintf(log_line, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
if (!path) {
|
|
|
|
li_log_write_stderr(srv, log_line->str, TRUE);
|
|
|
|
g_string_free(log_line, TRUE);
|
2010-01-24 20:30:41 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2009-08-30 18:43:13 +00:00
|
|
|
switch (g_atomic_int_get(&srv->state)) {
|
|
|
|
case LI_SERVER_INIT:
|
|
|
|
case LI_SERVER_LOADING:
|
|
|
|
case LI_SERVER_SUSPENDED:
|
|
|
|
case LI_SERVER_WARMUP:
|
|
|
|
case LI_SERVER_STOPPING:
|
|
|
|
case LI_SERVER_DOWN:
|
2010-08-29 10:24:30 +00:00
|
|
|
li_log_write_stderr(srv, log_line->str, TRUE);
|
|
|
|
g_string_free(log_line, TRUE);
|
2009-07-07 20:40:44 +00:00
|
|
|
return TRUE;
|
2009-08-30 18:43:13 +00:00
|
|
|
default:
|
|
|
|
break;
|
2008-12-20 15:25:02 +00:00
|
|
|
}
|
2010-01-24 20:30:41 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
log_entry = g_slice_new(liLogEntry);
|
2010-08-29 10:24:30 +00:00
|
|
|
log_entry->path = g_string_new_len(GSTR_LEN(path));
|
2008-07-22 14:00:31 +00:00
|
|
|
log_entry->level = log_level;
|
2010-08-29 10:24:30 +00:00
|
|
|
log_entry->flags = flags;
|
|
|
|
log_entry->msg = log_line;
|
|
|
|
log_entry->queue_link.data = log_entry;
|
|
|
|
log_entry->queue_link.next = NULL;
|
|
|
|
log_entry->queue_link.prev = NULL;
|
|
|
|
|
|
|
|
if (G_LIKELY(wrk)) {
|
|
|
|
/* push onto local worker log queue */
|
2012-03-16 12:24:17 +00:00
|
|
|
g_queue_push_tail_link(&wrk->logs.log_queue, &log_entry->queue_link);
|
2010-08-29 10:24:30 +00:00
|
|
|
} else {
|
|
|
|
/* no worker context, push directly onto global log queue */
|
|
|
|
g_static_mutex_lock(&srv->logs.write_queue_mutex);
|
|
|
|
g_queue_push_tail_link(&srv->logs.write_queue, &log_entry->queue_link);
|
|
|
|
g_static_mutex_unlock(&srv->logs.write_queue_mutex);
|
|
|
|
ev_async_send(srv->logs.loop, &srv->logs.watcher);
|
|
|
|
}
|
2008-07-19 20:13:32 +00:00
|
|
|
|
2008-07-18 18:23:05 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
2008-07-18 20:16:30 +00:00
|
|
|
|
2009-10-09 13:38:12 +00:00
|
|
|
static gpointer log_thread(liServer *srv) {
|
2010-08-29 10:24:30 +00:00
|
|
|
ev_loop(srv->logs.loop, 0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-16 12:24:17 +00:00
|
|
|
static GString *log_timestamp_format(liServer *srv) {
|
2010-08-29 10:24:30 +00:00
|
|
|
gsize s;
|
|
|
|
struct tm tm;
|
|
|
|
time_t now = (time_t) ev_now(srv->logs.loop);
|
|
|
|
|
|
|
|
/* cache hit */
|
2012-03-16 12:24:17 +00:00
|
|
|
if (now == srv->logs.timestamp.last_ts) {
|
|
|
|
return srv->logs.timestamp.cached;
|
|
|
|
}
|
2010-08-29 10:24:30 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_LOCALTIME_R
|
2012-03-16 12:24:17 +00:00
|
|
|
s = strftime(srv->logs.timestamp.cached->str, srv->logs.timestamp.cached->allocated_len, srv->logs.timestamp.format->str, localtime_r(&now, &tm));
|
2010-08-29 10:24:30 +00:00
|
|
|
#else
|
2012-03-16 12:24:17 +00:00
|
|
|
s = strftime(srv->logs.timestamp.cached->str, srv->logs.timestamp.cached->allocated_len, srv->logs.timestamp.format->str, localtime(&now));
|
2010-08-29 10:24:30 +00:00
|
|
|
#endif
|
|
|
|
|
2012-03-16 12:24:17 +00:00
|
|
|
g_string_set_size(srv->logs.timestamp.cached, s);
|
|
|
|
srv->logs.timestamp.last_ts = now;
|
2010-08-29 10:24:30 +00:00
|
|
|
|
2012-03-16 12:24:17 +00:00
|
|
|
return srv->logs.timestamp.cached;
|
2010-08-29 10:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void log_watcher_cb(struct ev_loop *loop, ev_async *w, int revents) {
|
|
|
|
liServer *srv = (liServer*) w->data;
|
|
|
|
GList *queue_link, *queue_link_next;
|
2008-07-18 22:11:08 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
UNUSED(loop);
|
|
|
|
UNUSED(revents);
|
2008-09-26 22:09:12 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
if (g_atomic_int_get(&srv->logs.thread_stop) == TRUE) {
|
|
|
|
liWaitQueueElem *wqe;
|
2008-09-26 22:09:12 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
while ((wqe = li_waitqueue_pop_force(&srv->logs.close_queue)) != NULL) {
|
|
|
|
log_close(srv, wqe->data);
|
2008-07-20 15:12:17 +00:00
|
|
|
}
|
2010-08-29 10:24:30 +00:00
|
|
|
li_waitqueue_stop(&srv->logs.close_queue);
|
|
|
|
ev_async_stop(srv->logs.loop, &srv->logs.watcher);
|
|
|
|
return;
|
|
|
|
}
|
2008-07-18 22:11:08 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
/* pop everything from global write queue */
|
|
|
|
g_static_mutex_lock(&srv->logs.write_queue_mutex);
|
|
|
|
queue_link = g_queue_peek_head_link(&srv->logs.write_queue);
|
|
|
|
g_queue_init(&srv->logs.write_queue);
|
|
|
|
g_static_mutex_unlock(&srv->logs.write_queue_mutex);
|
2008-07-19 20:13:32 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
while (queue_link) {
|
2012-03-16 12:24:17 +00:00
|
|
|
liLogTarget *log;
|
2010-09-25 12:08:56 +00:00
|
|
|
liLogEntry *log_entry = queue_link->data;
|
|
|
|
GString *msg = log_entry->msg;
|
|
|
|
gssize bytes_written = 0;
|
|
|
|
gssize write_res;
|
2010-08-29 10:24:30 +00:00
|
|
|
|
|
|
|
if (log_entry->flags & LOG_FLAG_TIMESTAMP) {
|
2012-03-16 12:24:17 +00:00
|
|
|
GString *ts = log_timestamp_format(srv);
|
2010-08-29 10:24:30 +00:00
|
|
|
g_string_prepend_c(msg, ' ');
|
2012-03-16 12:24:17 +00:00
|
|
|
g_string_prepend_len(msg, GSTR_LEN(ts));
|
2010-08-29 10:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_string_append_len(msg, CONST_STR_LEN("\n"));
|
|
|
|
|
2010-09-25 12:08:56 +00:00
|
|
|
log = log_open(srv, log_entry->path);
|
|
|
|
|
|
|
|
if (NULL == log || -1 == log->fd) {
|
|
|
|
li_log_write_stderr(srv, msg->str, TRUE);
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
/* todo: support for other logtargets than files */
|
2008-07-19 20:13:32 +00:00
|
|
|
while (bytes_written < (gssize)msg->len) {
|
|
|
|
write_res = write(log->fd, msg->str + bytes_written, msg->len - bytes_written);
|
2010-09-05 12:19:03 +00:00
|
|
|
/* write_res = msg->len; */
|
2008-07-18 22:11:08 +00:00
|
|
|
|
|
|
|
/* write() failed, check why */
|
|
|
|
if (write_res == -1) {
|
2010-08-29 10:24:30 +00:00
|
|
|
GString *str;
|
|
|
|
int err = errno;
|
|
|
|
|
|
|
|
switch (err) {
|
2008-07-18 22:11:08 +00:00
|
|
|
case EAGAIN:
|
|
|
|
case EINTR:
|
|
|
|
continue;
|
|
|
|
}
|
2008-07-19 13:12:32 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
str = g_string_sized_new(63);
|
|
|
|
g_string_printf(str, "could not write to log '%s': %s\n", log_entry->path->str, g_strerror(err));
|
|
|
|
li_log_write_stderr(srv, str->str, TRUE);
|
|
|
|
li_log_write_stderr(srv, msg->str, TRUE);
|
2008-07-20 14:35:04 +00:00
|
|
|
break;
|
2008-07-18 22:11:08 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
bytes_written += write_res;
|
2008-07-19 20:13:32 +00:00
|
|
|
assert(bytes_written <= (gssize) msg->len);
|
2008-07-18 22:11:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-25 12:08:56 +00:00
|
|
|
next:
|
2010-08-29 10:24:30 +00:00
|
|
|
queue_link_next = queue_link->next;
|
|
|
|
g_string_free(log_entry->path, TRUE);
|
|
|
|
g_string_free(log_entry->msg, TRUE);
|
2009-07-08 19:06:07 +00:00
|
|
|
g_slice_free(liLogEntry, log_entry);
|
2010-08-29 10:24:30 +00:00
|
|
|
queue_link = queue_link_next;
|
2008-07-18 22:11:08 +00:00
|
|
|
}
|
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
if (g_atomic_int_get(&srv->logs.thread_finish) == TRUE) {
|
|
|
|
liWaitQueueElem *wqe;
|
2008-07-20 14:35:04 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
while ((wqe = li_waitqueue_pop_force(&srv->logs.close_queue)) != NULL) {
|
|
|
|
log_close(srv, wqe->data);
|
|
|
|
}
|
|
|
|
li_waitqueue_stop(&srv->logs.close_queue);
|
|
|
|
ev_async_stop(srv->logs.loop, &srv->logs.watcher);
|
|
|
|
return;
|
2008-07-19 20:13:32 +00:00
|
|
|
}
|
2008-07-20 14:35:04 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
return;
|
2008-07-19 20:13:32 +00:00
|
|
|
}
|
|
|
|
|
2010-09-25 12:08:56 +00:00
|
|
|
#define RET(type, offset) do { if (NULL != param) *param = path->str + offset; return type; } while(0)
|
|
|
|
#define RET_PAR(type, par) do { if (NULL != param) *param = par; return type; } while(0)
|
|
|
|
#define TRY_SCHEME(scheme, type) do { if (g_str_has_prefix(path->str, scheme)) RET(type, sizeof(scheme)-1); } while(0)
|
|
|
|
liLogType li_log_type_from_path(GString *path, gchar **param) {
|
2008-08-13 17:57:19 +00:00
|
|
|
if (path->len == 0)
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_LOG_TYPE_NONE;
|
2008-08-13 17:57:19 +00:00
|
|
|
|
2010-09-25 12:08:56 +00:00
|
|
|
/* look for scheme: paths */
|
|
|
|
TRY_SCHEME("file:", LI_LOG_TYPE_FILE);
|
|
|
|
TRY_SCHEME("pipe:", LI_LOG_TYPE_PIPE);
|
|
|
|
TRY_SCHEME("stderr:", LI_LOG_TYPE_STDERR);
|
|
|
|
TRY_SCHEME("syslog:", LI_LOG_TYPE_SYSLOG);
|
2008-12-31 01:50:32 +00:00
|
|
|
|
2008-08-13 17:57:19 +00:00
|
|
|
/* targets starting with a slash are absolute paths and therefor file targets */
|
|
|
|
if (*path->str == '/')
|
2010-09-25 12:08:56 +00:00
|
|
|
RET(LI_LOG_TYPE_FILE, 0);
|
2008-08-13 17:57:19 +00:00
|
|
|
|
|
|
|
/* targets starting with a pipe are ... pipes! */
|
2010-09-25 12:08:56 +00:00
|
|
|
if (*path->str == '|') {
|
|
|
|
guint i = 1;
|
|
|
|
while (path->str[i] == ' ') i++; /* skip spaces */
|
|
|
|
RET(LI_LOG_TYPE_PIPE, i);
|
|
|
|
}
|
2008-08-13 17:57:19 +00:00
|
|
|
|
|
|
|
if (g_str_equal(path->str, "stderr"))
|
2010-09-25 12:08:56 +00:00
|
|
|
RET_PAR(LI_LOG_TYPE_STDERR, NULL);
|
2008-08-13 17:57:19 +00:00
|
|
|
|
|
|
|
if (g_str_equal(path->str, "syslog"))
|
2010-09-25 12:08:56 +00:00
|
|
|
RET_PAR(LI_LOG_TYPE_SYSLOG, NULL);
|
2008-08-13 17:57:19 +00:00
|
|
|
|
|
|
|
/* fall back to stderr */
|
2010-09-25 12:08:56 +00:00
|
|
|
RET_PAR(LI_LOG_TYPE_STDERR, NULL);
|
2008-08-13 17:57:19 +00:00
|
|
|
}
|
2010-09-25 12:08:56 +00:00
|
|
|
#undef RET
|
|
|
|
#undef RET_PAR
|
|
|
|
#undef TRY_SCHEME
|
2008-08-13 17:57:19 +00:00
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
int li_log_level_from_string(GString *str) {
|
2008-08-13 17:57:19 +00:00
|
|
|
if (g_str_equal(str->str, "debug"))
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_LOG_LEVEL_DEBUG;
|
2008-08-13 17:57:19 +00:00
|
|
|
if (g_str_equal(str->str, "info"))
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_LOG_LEVEL_INFO;
|
2008-08-13 17:57:19 +00:00
|
|
|
if (g_str_equal(str->str, "warning"))
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_LOG_LEVEL_WARNING;
|
2008-08-13 17:57:19 +00:00
|
|
|
if (g_str_equal(str->str, "error"))
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_LOG_LEVEL_ERROR;
|
2012-03-17 14:52:19 +00:00
|
|
|
if (g_str_equal(str->str, "abort"))
|
|
|
|
return LI_LOG_LEVEL_ABORT;
|
2008-12-31 01:50:32 +00:00
|
|
|
if (g_str_equal(str->str, "backend"))
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_LOG_LEVEL_BACKEND;
|
2008-08-13 17:57:19 +00:00
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
return -1;
|
2008-08-13 17:57:19 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 13:38:12 +00:00
|
|
|
gchar* li_log_level_str(liLogLevel log_level) {
|
2008-08-13 17:57:19 +00:00
|
|
|
switch (log_level) {
|
2012-03-17 14:52:19 +00:00
|
|
|
case LI_LOG_LEVEL_DEBUG: return "debug";
|
|
|
|
case LI_LOG_LEVEL_INFO: return "info";
|
|
|
|
case LI_LOG_LEVEL_WARNING: return "warning";
|
|
|
|
case LI_LOG_LEVEL_ERROR: return "error";
|
|
|
|
case LI_LOG_LEVEL_ABORT: return "abort";
|
|
|
|
case LI_LOG_LEVEL_BACKEND: return "backend";
|
2008-08-13 17:57:19 +00:00
|
|
|
}
|
2012-03-17 14:52:19 +00:00
|
|
|
return "unknown";
|
2008-08-13 17:57:19 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 13:38:12 +00:00
|
|
|
void li_log_thread_start(liServer *srv) {
|
2008-07-20 15:12:17 +00:00
|
|
|
GError *err = NULL;
|
2008-07-19 13:12:32 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
ev_async_start(srv->logs.loop, &srv->logs.watcher);
|
|
|
|
|
2008-09-24 21:43:22 +00:00
|
|
|
srv->logs.thread = g_thread_create((GThreadFunc)log_thread, srv, TRUE, &err);
|
2008-07-18 22:11:08 +00:00
|
|
|
|
2008-09-24 21:43:22 +00:00
|
|
|
if (srv->logs.thread == NULL) {
|
2010-08-29 10:24:30 +00:00
|
|
|
g_printerr("could not create logging thread: %s\n", err->message);
|
2008-09-24 21:43:22 +00:00
|
|
|
g_error_free(err);
|
|
|
|
abort();
|
2008-07-18 22:11:08 +00:00
|
|
|
}
|
2010-08-29 10:24:30 +00:00
|
|
|
|
|
|
|
g_atomic_int_set(&srv->logs.thread_alive, TRUE);
|
2008-07-18 22:11:08 +00:00
|
|
|
}
|
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
void li_log_thread_stop(liServer *srv) {
|
2008-09-26 22:09:12 +00:00
|
|
|
if (g_atomic_int_get(&srv->logs.thread_alive) == TRUE) {
|
|
|
|
g_atomic_int_set(&srv->logs.thread_stop, TRUE);
|
2009-10-09 13:38:12 +00:00
|
|
|
li_log_thread_wakeup(srv);
|
2008-09-26 22:09:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
void li_log_thread_finish(liServer *srv) {
|
2008-09-26 22:09:12 +00:00
|
|
|
if (g_atomic_int_get(&srv->logs.thread_alive) == TRUE) {
|
|
|
|
g_atomic_int_set(&srv->logs.thread_finish, TRUE);
|
2009-10-09 13:38:12 +00:00
|
|
|
li_log_thread_wakeup(srv);
|
2008-09-26 22:09:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-09 13:38:12 +00:00
|
|
|
void li_log_thread_wakeup(liServer *srv) {
|
2008-09-27 15:06:43 +00:00
|
|
|
if (!g_atomic_int_get(&srv->logs.thread_alive))
|
2009-10-09 13:38:12 +00:00
|
|
|
li_log_thread_start(srv);
|
2008-07-19 20:13:32 +00:00
|
|
|
|
2010-08-29 10:24:30 +00:00
|
|
|
ev_async_send(srv->logs.loop, &srv->logs.watcher);
|
2008-09-24 21:43:22 +00:00
|
|
|
}
|
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
void li_log_split_lines(liServer *srv, liWorker *wrk, liLogContext *context, liLogLevel log_level, guint flags, gchar *txt, const gchar *prefix) {
|
2009-02-03 14:25:14 +00:00
|
|
|
gchar *start;
|
|
|
|
|
|
|
|
start = txt;
|
|
|
|
while ('\0' != *txt) {
|
|
|
|
if ('\r' == *txt || '\n' == *txt) {
|
|
|
|
*txt = '\0';
|
|
|
|
if (txt - start > 1) { /* skip empty lines*/
|
2012-03-17 14:52:19 +00:00
|
|
|
li_log_write(srv, wrk, context, log_level, flags, "%s%s", prefix, start);
|
2009-02-03 14:25:14 +00:00
|
|
|
}
|
|
|
|
txt++;
|
|
|
|
while (*txt == '\n' || *txt == '\r') txt++;
|
|
|
|
start = txt;
|
|
|
|
} else {
|
|
|
|
txt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (txt - start > 1) { /* skip empty lines*/
|
2012-03-17 14:52:19 +00:00
|
|
|
li_log_write(srv, wrk, context, log_level, flags, "%s%s", prefix, start);
|
2009-02-03 14:25:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
void li_log_split_lines_(liServer *srv, liWorker *wrk, liLogContext *context, liLogLevel log_level, guint flags, gchar *txt, const gchar *fmt, ...) {
|
2009-02-03 14:25:14 +00:00
|
|
|
va_list ap;
|
|
|
|
GString *prefix;
|
|
|
|
|
|
|
|
prefix = g_string_sized_new(0);
|
|
|
|
va_start(ap, fmt);
|
|
|
|
g_string_vprintf(prefix, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
2012-03-17 14:52:19 +00:00
|
|
|
li_log_split_lines(srv, wrk, context, log_level, flags, txt, prefix->str);
|
2009-02-03 14:25:14 +00:00
|
|
|
|
|
|
|
g_string_free(prefix, TRUE);
|
|
|
|
}
|