reworked logging functions; added 'log' and 'log.timestamp' options, removed 'log.level' and 'log.target' options

personal/stbuehler/wip
Thomas Porzelt 15 years ago
parent 6fe8eb603d
commit 292d013134

@ -1,3 +1,6 @@
#include "base.h"
void string_destroy_notify(gpointer *str) {
g_string_free((GString*)str, TRUE);
}

@ -36,6 +36,8 @@ typedef struct server server;
struct connection;
typedef struct connection connection;
LI_API void string_destroy_notify(gpointer *str);
#include "server.h"
#include "worker.h"

@ -86,8 +86,8 @@ int main(int argc, char *argv[]) {
s = d / 1000000;
millis = (d - s) / 1000;
micros = (d - s - millis) %1000;
g_print("parsed config file in %lu seconds, %lu milliseconds, %lu microseconds\n", s, millis, micros);
g_print("option_stack: %u action_list_stack: %u (should be 0:1)\n", g_queue_get_length(ctx->option_stack), g_queue_get_length(ctx->action_list_stack));
log_debug(srv, NULL, "parsed config file in %lu seconds, %lu milliseconds, %lu microseconds", s, millis, micros);
log_debug(srv, NULL, "option_stack: %u action_list_stack: %u (should be 0:1)", g_queue_get_length(ctx->option_stack), g_queue_get_length(ctx->action_list_stack));
/* TODO config_parser_finish(srv, ctx_stack); */
}
@ -107,12 +107,6 @@ int main(int argc, char *argv[]) {
TRACE(srv, "%s", "Test!");
/* srv->log_stderr = log_new(srv, LOG_TYPE_FILE, g_string_new("lightytest.log")); */
log_write_(srv, NULL, LOG_LEVEL_WARNING, "test %s", "foo1");
log_warning(srv, NULL, "test %s", "foo1"); /* duplicate won't be logged */
log_warning(srv, NULL, "test %s", "foo2");
log_debug(srv, NULL, "test %s", "message");
server_loop_init(srv);
server_start(srv);

@ -1,11 +1,12 @@
#include "log.h"
#include "base.h"
#include "plugin_core.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
/* from server.h */
#if REMOVE_PATH_FROM_FILE
const char *remove_path(const char *path) {
char *p = strrchr(path, DIR_SEPERATOR);
@ -34,51 +35,91 @@ int log_write(server* UNUSED_PARAM(srv), connection* UNUSED_PARAM(con), const ch
}
gboolean log_write_(server *srv, connection *con, log_level_t log_level, const gchar *fmt, ...) {
gboolean log_write_(server *srv, connection *con, log_level_t log_level, guint flags, const gchar *fmt, ...) {
va_list ap;
GString *log_line;
log_t *log;
log_t *log = NULL;
log_entry_t *log_entry;
log_level_t log_level_want;
log_timestamp_t *ts = NULL;
if (con != NULL) {
if (!srv) srv = con->srv;
/* get log index from connection */
log = CORE_OPTION(CORE_OPTION_LOG_TARGET) ? CORE_OPTION(CORE_OPTION_LOG_TARGET) : srv->log_stderr;
log_level_want = (log_level_t) CORE_OPTION(CORE_OPTION_LOG_LEVEL);
/* get log from connection */
log = g_array_index((GArray*)CORE_OPTION(CORE_OPTION_LOG), log_t*, log_level);
if (log == NULL)
return TRUE;
ts = CORE_OPTION(CORE_OPTION_LOG_TS_FORMAT);
if (!ts)
ts = &g_array_index(srv->logs.timestamps, log_timestamp_t, 0);
}
else {
log = srv->log_stderr;
log_level_want = LOG_LEVEL_DEBUG;
log = srv->logs.stderr;
ts = &g_array_index(srv->logs.timestamps, log_timestamp_t, 0);
}
/* ingore messages we are not interested in */
if (log_level < log_level_want)
return TRUE;
log_ref(srv, log);
log_line = g_string_sized_new(0);
va_start(ap, fmt);
g_string_vprintf(log_line, fmt, ap);
va_end(ap);
/* check if last message for this log was the same */
if (g_string_equal(log->lastmsg, log_line)) {
log->lastmsg_count++;
log_unref(srv, log);
g_string_free(log_line, TRUE);
return TRUE;
if (!(flags & LOG_FLAG_NOLOCK))
log_lock(log);
if (!(flags & LOG_FLAG_ALLOW_REPEAT)) {
/* check if last message for this log was the same */
if (g_string_equal(log->lastmsg, log_line)) {
log->lastmsg_count++;
if (!(flags & LOG_FLAG_NOLOCK))
log_unlock(log);
log_unref(srv, log);
g_string_free(log_line, TRUE);
return TRUE;
}
else {
if (log->lastmsg_count > 0) {
guint count = log->lastmsg_count;
log->lastmsg_count = 0;
log_write_(srv, con, log_level, flags | LOG_FLAG_NOLOCK | LOG_FLAG_ALLOW_REPEAT, "last message repeated %d times", count);
}
}
}
else {
if (log->lastmsg_count > 0) {
guint count = log->lastmsg_count;
log->lastmsg_count = 0;
log_write_(srv, con, log_level, "last message repeated %d times", count);
g_string_assign(log->lastmsg, log_line->str);
/* for normal error messages, we prepend a timestamp */
if (flags & LOG_FLAG_TIMETAMP) {
time_t cur_ts;
g_mutex_lock(srv->logs.mutex);
/* if we have a worker context, we can use its timestamp to save us a call to time() */
if (con != NULL)
cur_ts = CUR_TS(con->wrk);
else
cur_ts = time(NULL);
if (cur_ts != ts->last_ts) {
gsize s;
g_string_set_size(ts->cached, 255);
s = strftime(ts->cached->str, ts->cached->allocated_len,
ts->format->str, localtime(&cur_ts));
g_string_set_size(ts->cached, s);
ts->last_ts = cur_ts;
}
g_string_prepend_c(log_line, ' ');
g_string_prepend_len(log_line, GSTR_LEN(ts->cached));
g_mutex_unlock(srv->logs.mutex);
}
log->lastmsg = g_string_assign(log->lastmsg, log_line->str);
if (!(flags & LOG_FLAG_NOLOCK))
log_unlock(log);
g_string_append_len(log_line, CONST_STR_LEN("\r\n"));
@ -88,11 +129,12 @@ gboolean log_write_(server *srv, connection *con, log_level_t log_level, const g
log_entry->msg = log_line;
log_entry->level = log_level;
g_async_queue_push(srv->log_queue, log_entry);
g_async_queue_push(srv->logs.queue, log_entry);
/* on critical error, exit */
if (log_level == LOG_LEVEL_ERROR) {
if (log_level == LOG_LEVEL_ABORT) {
g_atomic_int_set(&srv->exiting, TRUE);
g_atomic_int_set(&srv->logs.stop_thread, TRUE);
log_thread_wakeup(srv); /* just in case the logging thread is sleeping at this point */
}
@ -108,24 +150,30 @@ gpointer log_thread(server *srv) {
gssize bytes_written;
gssize write_res;
queue = srv->log_queue;
queue = srv->logs.queue;
while (TRUE) {
/* do we need to rotate logs? */
/*
if (g_atomic_int_get(&srv->rotate_logs)) {
g_atomic_int_set(&srv->rotate_logs, FALSE);
g_mutex_lock(srv->log_mutex);
g_hash_table_foreach(srv->logs, (GHFunc) log_rotate, srv);
g_mutex_unlock(srv->log_mutex);
g_mutex_lock(srv->logs.mutex);
g_hash_table_foreach(srv->logs.targets, (GHFunc) log_rotate, srv);
g_mutex_unlock(srv->logs.mutex);
}
*/
log_entry = g_async_queue_pop(srv->log_queue);
log_entry = g_async_queue_pop(srv->logs.queue);
/* if log_entry->log is NULL, it means that the logger thread has been woken up probably because it should exit */
if (log_entry->log == NULL) {
g_slice_free(log_entry_t, log_entry);
/* lighty is exiting, end logging thread */
if (g_atomic_int_get(&srv->state) == SERVER_STOPPING && g_async_queue_length(srv->log_queue) == 0)
if (g_atomic_int_get(&srv->state) == SERVER_STOPPING && g_async_queue_length(srv->logs.queue) == 0)
break;
if (g_atomic_int_get(&srv->logs.stop_thread) == TRUE)
break;
continue;
@ -188,7 +236,9 @@ void log_rotate(gchar * path, log_t *log, server * UNUSED_PARAM(srv)) {
}
void log_rotate_logs(server *srv) {
g_atomic_int_set(&srv->rotate_logs, TRUE);
UNUSED(srv);
/*g_atomic_int_set(&srv->rotate_logs, TRUE);*/
}
@ -198,12 +248,12 @@ void log_ref(server *srv, log_t *log) {
}
void log_unref(server *srv, log_t *log) {
g_mutex_lock(srv->log_mutex);
g_mutex_lock(srv->logs.mutex);
if (g_atomic_int_dec_and_test(&log->refcount))
log_free_unlocked(srv, log);
g_mutex_unlock(srv->log_mutex);
g_mutex_unlock(srv->logs.mutex);
}
log_type_t log_type_from_path(GString *path) {
@ -233,8 +283,6 @@ log_level_t log_level_from_string(GString *str) {
return LOG_LEVEL_DEBUG;
if (g_str_equal(str->str, "info"))
return LOG_LEVEL_INFO;
if (g_str_equal(str->str, "message"))
return LOG_LEVEL_MESSAGE;
if (g_str_equal(str->str, "warning"))
return LOG_LEVEL_WARNING;
if (g_str_equal(str->str, "error"))
@ -248,7 +296,6 @@ gchar* log_level_str(log_level_t log_level) {
switch (log_level) {
case LOG_LEVEL_DEBUG: return "debug";
case LOG_LEVEL_INFO: return "info";
case LOG_LEVEL_MESSAGE: return "message";
case LOG_LEVEL_WARNING: return "warning";
case LOG_LEVEL_ERROR: return "error";
default: return "unknown";
@ -260,14 +307,14 @@ log_t *log_new(server *srv, log_type_t type, GString *path) {
log_t *log;
gint fd = -1;
g_mutex_lock(srv->log_mutex);
log = g_hash_table_lookup(srv->logs, path->str);
g_mutex_lock(srv->logs.mutex);
log = g_hash_table_lookup(srv->logs.targets, path->str);
/* log already open, inc refcount */
if (log != NULL)
{
g_atomic_int_inc(&log->refcount);
g_mutex_unlock(srv->log_mutex);
g_mutex_unlock(srv->logs.mutex);
return log;
}
@ -295,19 +342,20 @@ log_t *log_new(server *srv, log_type_t type, GString *path) {
log->fd = fd;
log->path = path;
log->refcount = 1;
log->mutex = g_mutex_new();
g_hash_table_insert(srv->logs, path->str, log);
g_hash_table_insert(srv->logs.targets, path->str, log);
g_mutex_unlock(srv->log_mutex);
g_mutex_unlock(srv->logs.mutex);
return log;
}
/* only call this if srv->log_mutex is NOT locked */
/* only call this if srv->logs.mutex is NOT locked */
void log_free(server *srv, log_t *log) {
g_mutex_lock(srv->log_mutex);
g_mutex_lock(srv->logs.mutex);
log_free_unlocked(srv, log);
g_mutex_unlock(srv->log_mutex);
g_mutex_unlock(srv->logs.mutex);
}
/* only call this if srv->log_mutex IS locked */
@ -315,44 +363,92 @@ void log_free_unlocked(server *srv, log_t *log) {
if (log->type == LOG_TYPE_FILE || log->type == LOG_TYPE_PIPE)
close(log->fd);
g_hash_table_remove(srv->logs, log->path);
g_hash_table_remove(srv->logs.targets, log->path);
g_string_free(log->path, TRUE);
g_string_free(log->lastmsg, TRUE);
g_mutex_free(log->mutex);
g_slice_free(log_t, log);
}
void log_init(server *srv) {
GString *str;
srv->logs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
srv->log_queue = g_async_queue_new();
srv->log_mutex = g_mutex_new();
srv->logs.targets = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
srv->logs.queue = g_async_queue_new();
srv->logs.mutex = g_mutex_new();
srv->logs.timestamps = g_array_new(FALSE, FALSE, sizeof(log_timestamp_t));
/* first entry in srv->logs.timestamps is the default timestamp */
log_timestamp_new(srv, g_string_new_len(CONST_STR_LEN("%d/%b/%Y %T %Z")));
/* first entry in srv->logs is the plain good old stderr */
/* first entry in srv->logs.targets is the plain good old stderr */
str = g_string_new_len(CONST_STR_LEN("stderr"));
srv->log_stderr = log_new(srv, LOG_TYPE_STDERR, str);
srv->log_syslog = NULL;
srv->logs.stderr = log_new(srv, LOG_TYPE_STDERR, str);
}
void log_thread_start(server *srv) {
GError *err = NULL;
srv->log_thread = g_thread_create((GThreadFunc)log_thread, srv, TRUE, &err);
srv->logs.thread = g_thread_create((GThreadFunc)log_thread, srv, TRUE, &err);
if (srv->log_thread == NULL) {
if (srv->logs.thread == NULL) {
g_printerr("could not create loggin thread: %s\n", err->message);
assert(NULL);
g_error_free(err);
abort();
}
}
void log_thread_wakeup(server *srv) {
log_entry_t *e;
e = g_slice_new(log_entry_t);
e->log = NULL;
e->msg = NULL;
e = g_slice_new0(log_entry_t);
g_async_queue_push(srv->logs.queue, e);
}
void log_lock(log_t *log) {
g_mutex_lock(log->mutex);
}
g_async_queue_push(srv->log_queue, e);
void log_unlock(log_t *log) {
g_mutex_unlock(log->mutex);
}
log_timestamp_t *log_timestamp_new(server *srv, GString *format) {
log_timestamp_t *ts;
/* check if there already exists a timestamp entry with the same format */
for (guint i = 0; i < srv->logs.timestamps->len; i++) {
ts = &g_array_index(srv->logs.timestamps, log_timestamp_t, i);
if (g_string_equal(ts->format, format)) {
g_atomic_int_inc(&(ts->refcount));
return ts;
}
}
ts = g_slice_new(log_timestamp_t);
ts->cached = g_string_sized_new(0);
ts->last_ts = 0;
ts->refcount = 1;
ts->format = format;
g_array_append_val(srv->logs.timestamps, *ts);
return ts;
}
void log_timestamp_free(server *srv, log_timestamp_t *ts) {
if (g_atomic_int_dec_and_test(&(ts->refcount))) {
for (guint i = 0; i < srv->logs.timestamps->len; i++) {
if (g_string_equal(g_array_index(srv->logs.timestamps, log_timestamp_t, i).format, ts->format)) {
g_array_remove_index_fast(srv->logs.timestamps, i);
break;
}
}
g_slice_free(log_timestamp_t, ts);
}
}

@ -43,6 +43,28 @@ LI_API const char *remove_path(const char *path);
} while(0)
#undef ERROR
#define ERROR(srv, fmt, ...) \
log_write_(srv, NULL, LOG_LEVEL_ERROR, LOG_FLAG_TIMETAMP, "(error) %s.%d: "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
/*#undef INFO
#define INFO(srv, fmt, ...) \
log_write_(srv, NULL, LOG_LEVEL_INFO, LOG_FLAG_TIMETAMP, "%s.%d: (info) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
*/
#undef TRACE
#define TRACE(srv, fmt, ...) \
log_write_(srv, NULL, LOG_LEVEL_INFO, LOG_FLAG_TIMETAMP, "(trace) %s.%d: "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
/*
#undef CON_ERROR
#define CON_ERROR(con, fmt, ...) \
log_write_(con->srv, con, LOG_LEVEL_ERROR, LOG_FLAG_TIMETAMP, "%s.%d: (error) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
*/
#undef CON_TRACE
#define CON_TRACE(con, fmt, ...) \
log_write_(con->srv, con, LOG_LEVEL_DEBUG, LOG_FLAG_TIMETAMP, "%s.%d: (debug) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
/* TODO: perhaps make portable (detect if cc supports) */
#define __ATTRIBUTE_PRINTF_FORMAT(fmt, arg) __attribute__ ((__format__ (__printf__, fmt, arg)))
@ -52,19 +74,19 @@ LI_API int log_write(server *srv, connection *con, const char *fmt, ...) __ATTRI
/* convenience makros */
#define log_error(srv, con, fmt, ...) \
log_write_(srv, con, LOG_LEVEL_ERROR, fmt, __VA_ARGS__)
log_write_(srv, con, LOG_LEVEL_ERROR, LOG_FLAG_TIMETAMP, "%s.%d: (error) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
#define log_warning(srv, con, fmt, ...) \
log_write_(srv, con, LOG_LEVEL_WARNING, fmt, __VA_ARGS__)
log_write_(srv, con, LOG_LEVEL_WARNING, LOG_FLAG_TIMETAMP, "%s.%d: (warning) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
#define log_info(srv, con, fmt, ...) \
log_write_(srv, con, LOG_LEVEL_INFO, fmt, __VA_ARGS__)
log_write_(srv, con, LOG_LEVEL_INFO, LOG_FLAG_TIMETAMP, "%s.%d: (info) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
#define log_message(srv, con, fmt, ...) \
log_write_(srv, con, LOG_LEVEL_MESSAGE, fmt, __VA_ARGS__)
log_write_(srv, con, LOG_LEVEL_MESSAGE, LOG_FLAG_TIMETAMP, "%s.%d: (message) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
#define log_debug(srv, con, fmt, ...) \
log_write_(srv, con, LOG_LEVEL_DEBUG, fmt, __VA_ARGS__)
log_write_(srv, con, LOG_LEVEL_DEBUG, LOG_FLAG_TIMETAMP, "%s.%d: (debug) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__)
@ -74,12 +96,15 @@ typedef struct log_t log_t;
struct log_entry_t;
typedef struct log_entry_t log_entry_t;
struct log_timestamp_t;
typedef struct log_timestamp_t log_timestamp_t;
typedef enum {
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_MESSAGE,
LOG_LEVEL_WARNING,
LOG_LEVEL_ERROR
LOG_LEVEL_ERROR,
LOG_LEVEL_ABORT
} log_level_t;
typedef enum {
@ -89,16 +114,31 @@ typedef enum {
LOG_TYPE_SYSLOG
} log_type_t;
/* flags for log_write */
#define LOG_FLAG_NONE (0x0) /* default flag */
#define LOG_FLAG_TIMETAMP (0x1) /* prepend a timestamp to the log message */
#define LOG_FLAG_NOLOCK (0x1 << 1) /* for internal use only */
#define LOG_FLAG_ALLOW_REPEAT (0x1 << 2) /* allow writing of multiple equal entries after each other */
struct log_t {
log_type_t type;
GString *path;
gint refcount;
gint fd;
GString *lastmsg;
guint lastmsg_count;
GMutex *mutex;
};
struct log_timestamp_t {
gint refcount;
time_t last_ts;
GString *format;
GString *cached;
};
struct log_entry_t {
log_t *log;
log_level_t level;
@ -120,6 +160,9 @@ void log_free_unlocked(server *srv, log_t *log);
void log_ref(server *srv, log_t *log);
void log_unref(server *srv, log_t *log);
void log_lock(log_t *log);
void log_unlock(log_t *log);
/* do not call directly, use log_rotate_logs instead */
void log_rotate(gchar *path, log_t *log, server *srv);
@ -130,6 +173,10 @@ void log_thread_start(server *srv);
void log_thread_wakeup(server *srv);
void log_init(server *srv);
gboolean log_write_(server *srv, connection *con, log_level_t log_level, const gchar *fmt, ...);
LI_API gboolean log_write_(server *srv, connection *con, log_level_t log_level, guint flags, const gchar *fmt, ...) __ATTRIBUTE_PRINTF_FORMAT(5, 6);
log_timestamp_t *log_timestamp_new(server *srv, GString *format);
void log_timestamp_free(server *srv, log_timestamp_t *ts);
#endif

@ -3,9 +3,12 @@
#include "plugin_core.h"
#include "utils.h"
#include <sys/stat.h>
#include <fcntl.h>
static action* core_list(server *srv, plugin* p, option *opt) {
action *a;
guint i;
@ -196,9 +199,9 @@ static action* core_static(server *srv, plugin* p, option *opt) {
static action_result core_handle_test(connection *con, gpointer param) {
server *srv = con->srv;
GHashTableIter iter;
/*GHashTableIter iter;
gpointer k, v;
GList *hv;
GList *hv;*/
GString *str;
gchar *backend;
guint64 uptime;
@ -260,11 +263,6 @@ static action_result core_handle_test(connection *con, gpointer param) {
chunkqueue_append_mem(con->out, CONST_STR_LEN("\r\n"));
connection_handle_direct(con);
CON_TRACE(con, "core_handle_test: %s%s%s log_level: %s",
con->request.uri.path->str, con->request.uri.query->len ? "?" : "", con->request.uri.query->len ? con->request.uri.query->str : "",
log_level_str((log_level_t)CORE_OPTION(CORE_OPTION_LOG_LEVEL))
);
return ACTION_GO_ON;
}
@ -420,71 +418,104 @@ static gboolean core_workers(server *srv, plugin* p, option *opt) {
return TRUE;
}
gboolean core_option_log_target_parse(server *srv, plugin *p, size_t ndx, option *opt, gpointer *value) {
log_t *log;
log_type_t log_type;
UNUSED(ndx);
gpointer core_option_max_keep_alive_idle_default(server *srv, plugin *p, gsize ndx) {
UNUSED(srv);
UNUSED(p);
UNUSED(ndx);
assert(opt->type == OPTION_STRING);
log_type = log_type_from_path(opt->value.opt_string);
log = log_new(srv, log_type, opt->value.opt_string);
return GINT_TO_POINTER(5);
}
*value = (gpointer)log;
gpointer core_option_server_tag_default(server *srv, plugin *p, gsize ndx) {
UNUSED(srv);
UNUSED(p);
UNUSED(ndx);
return TRUE;
return g_string_new_len(CONST_STR_LEN("lighttpd-2.0~sandbox")); /* TODO: fix mem leak */
}
void core_option_log_target_free(server *srv, plugin *p, size_t ndx, gpointer value) {
gpointer core_option_log_default(server *srv, plugin *p, gsize ndx) {
UNUSED(srv);
UNUSED(p);
UNUSED(ndx);
UNUSED(value);
GArray *arr = g_array_sized_new(FALSE, TRUE, sizeof(log_t*), 5);
return arr;
}
gboolean core_option_log_level_parse(server *srv, plugin *p, size_t ndx, option *opt, gpointer *value) {
UNUSED(srv);
gboolean core_option_log_parse(server *srv, plugin *p, size_t ndx, option *opt, gpointer *value) {
UNUSED(p);
UNUSED(ndx);
UNUSED(opt);
GHashTableIter iter;
gpointer k, v;
log_level_t level;
GString *path;
GString *level_str;
GArray *arr = g_array_sized_new(FALSE, TRUE, sizeof(log_t*), 5);
g_array_set_size(arr, 5);
assert(opt->type == OPTION_STRING);
g_hash_table_iter_init(&iter, opt->value.opt_hash);
while (g_hash_table_iter_next(&iter, &k, &v)) {
path = ((option*)v)->value.opt_string;
level_str = (GString*)k;
if (g_str_equal(level_str->str, "*")) {
for (guint i = 0; i < arr->len; i++) {
if (NULL != g_array_index(arr, log_t*, i))
continue;
log_t *log = log_new(srv, log_type_from_path(path), path);
g_array_index(arr, log_t*, i) = log;
}
}
else {
log_t *log = log_new(srv, log_type_from_path(path), path);
level = log_level_from_string(level_str);
g_array_index(arr, log_t*, level) = log;
}
}
*value = (gpointer)log_level_from_string(opt->value.opt_string);
*value = (gpointer)arr;
return TRUE;
}
void core_option_log_level_free(server *srv, plugin *p, size_t ndx, gpointer value) {
void core_option_log_free(server *srv, plugin *p, size_t ndx, gpointer value) {
UNUSED(srv);
UNUSED(p);
UNUSED(ndx);
UNUSED(value);
GArray *arr = value;
for (guint i = 0; i < arr->len; i++) {
if (NULL != g_array_index(arr, log_t*, i))
log_unref(srv, g_array_index(arr, log_t*, i));
}
}
gpointer core_option_max_keep_alive_idle_default(server *srv, plugin *p, gsize ndx) {
gboolean core_option_log_timestamp_parse(server *srv, plugin *p, size_t ndx, option *opt, gpointer *value) {
UNUSED(srv);
UNUSED(p);
UNUSED(ndx);
return GINT_TO_POINTER(5);
*value = (gpointer)log_timestamp_new(srv, opt->value.opt_string);
return TRUE;
}
gpointer core_option_server_tag_default(server *srv, plugin *p, gsize ndx) {
void core_option_log_timestamp_free(server *srv, plugin *p, size_t ndx, gpointer value) {
UNUSED(srv);
UNUSED(p);
UNUSED(ndx);
return g_string_new_len(CONST_STR_LEN("lighttpd-2.0~sandbox")); /* TODO: fix mem leak */
log_timestamp_free(srv, value);
}
static const plugin_option options[] = {
{ "debug.log_request_handling", OPTION_BOOLEAN, NULL, NULL, NULL },
{ "log.target", OPTION_STRING, NULL, core_option_log_target_parse, core_option_log_target_free },
{ "log.level", OPTION_STRING, NULL, core_option_log_level_parse, core_option_log_level_free },
{ "log.timestamp", OPTION_STRING, NULL, core_option_log_timestamp_parse, core_option_log_timestamp_free },
{ "log", OPTION_HASH, core_option_log_default, core_option_log_parse, core_option_log_free },
{ "static-file.exclude", OPTION_LIST, NULL, NULL, NULL },

@ -4,13 +4,13 @@
enum core_options_t {
CORE_OPTION_DEBUG_REQUEST_HANDLING = 0,
CORE_OPTION_LOG_TARGET = 1,
CORE_OPTION_LOG_LEVEL = 2,
CORE_OPTION_LOG_TS_FORMAT,
CORE_OPTION_LOG,
CORE_OPTION_STATIC_FILE_EXCLUDE = 3,
CORE_OPTION_STATIC_FILE_EXCLUDE,
CORE_OPTION_SERVER_TAG = 4,
CORE_OPTION_MAX_KEEP_ALIVE_IDLE = 5
CORE_OPTION_SERVER_TAG,
CORE_OPTION_MAX_KEEP_ALIVE_IDLE
};
/* the core plugin always has base index 0, as it is the first plugin loaded */

@ -128,19 +128,19 @@ void server_free(server* srv) {
g_slice_free1(srv->option_count * sizeof(*srv->option_def_values), srv->option_def_values);
/* free logs */
g_thread_join(srv->log_thread);
g_thread_join(srv->logs.thread);
{
GHashTableIter iter;
gpointer k, v;
g_hash_table_iter_init(&iter, srv->logs);
g_hash_table_iter_init(&iter, srv->logs.targets);
while (g_hash_table_iter_next(&iter, &k, &v)) {
log_free(srv, v);
}
g_hash_table_destroy(srv->logs);
g_hash_table_destroy(srv->logs.targets);
}
g_mutex_free(srv->log_mutex);
g_async_queue_unref(srv->log_queue);
g_mutex_free(srv->logs.mutex);
g_async_queue_unref(srv->logs.queue);
g_slice_free(server, srv);
}
@ -332,3 +332,35 @@ void server_exit(server *srv) {
void joblist_append(connection *con) {
connection_state_machine(con);
}
/* cache timestamp */
GString *server_current_timestamp() {
static GStaticPrivate last_ts_key = G_STATIC_PRIVATE_INIT;
static GStaticPrivate ts_str_key = G_STATIC_PRIVATE_INIT;
time_t *last_ts = g_static_private_get(&last_ts_key);
GString *ts_str = g_static_private_get(&ts_str_key);
time_t cur_ts = time(NULL);
if (last_ts == NULL) {
last_ts = g_new0(time_t, 1);
g_static_private_set(&last_ts_key, last_ts, g_free);
}
if (ts_str == NULL) {
ts_str = g_string_sized_new(255);
g_static_private_set(&ts_str_key, ts_str, (GDestroyNotify)string_destroy_notify);
}
if (cur_ts != *last_ts) {
gsize s;
g_string_set_size(ts_str, 255);
s = strftime(ts_str->str, ts_str->allocated_len,
"%a, %d %b %Y %H:%M:%S GMT", gmtime(&(cur_ts)));
g_string_set_size(ts_str, s);
*last_ts = cur_ts;
}
return ts_str;
}

@ -66,14 +66,15 @@ struct server {
gboolean exiting; /** atomic access */
/* logs */
gboolean rotate_logs; /** atomic access */
GHashTable *logs;
struct log_t *log_stderr;
struct log_t *log_syslog;
GAsyncQueue *log_queue;
GThread *log_thread;
GMutex *log_mutex; /* manage access for the logs hashtable */
struct {
GMutex *mutex;
GHashTable *targets; /** const gchar* path => (log_t*) */
GAsyncQueue *queue;
GThread *thread;
gboolean stop_thread; /** access with atomic functions */
GArray *timestamps; /** array of log_timestamp_t */
struct log_t *stderr;
} logs;
ev_tstamp started;
statistics_t stats; /** TODO: sync/worker split */
@ -98,4 +99,6 @@ LI_API void server_exit(server *srv);
LI_API void joblist_append(connection *con);
LI_API GString *server_current_timestamp();
#endif

Loading…
Cancel
Save