diff --git a/lnm/include/lnm/log.h b/lnm/include/lnm/log.h new file mode 100644 index 0000000..42f27de --- /dev/null +++ b/lnm/include/lnm/log.h @@ -0,0 +1,47 @@ +#ifndef LNM_LOG +#define LOG + +#include + +#include "lnm/common.h" + +typedef struct lnm_logger lnm_logger; + +typedef enum lnm_log_level { + lnm_log_level_debug = 0, + lnm_log_level_info, + lnm_log_level_notice, + lnm_log_level_warning, + lnm_log_level_error, + lnm_log_level_critical +} lnm_log_level; + +extern const char *lnm_log_level_names[]; + +/** + * Initialize the global logger. + */ +lnm_err lnm_log_init_global(); + +/** + * Register stdout as one of the streams for the global logger. + */ +lnm_err lnm_log_register_stdout(lnm_log_level level); + +void lnm_log(lnm_log_level level, const char *section, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); + +#define lnm_ldebug(section, fmt, ...) \ + lnm_log(lnm_log_level_debug, section, fmt, __VA_ARGS__) +#define lnm_linfo(section, fmt, ...) \ + lnm_log(lnm_log_level_info, section, fmt, __VA_ARGS__) +#define lnm_lnotice(section, fmt, ...) \ + lnm_log(lnm_log_level_notice, section, fmt, __VA_ARGS__) +#define lnm_lwarning(section, fmt, ...) \ + lnm_log(lnm_log_level_warning, section, fmt, __VA_ARGS__) +#define lnm_lerror(section, fmt, ...) \ + lnm_log(lnm_log_level_error, section, fmt, __VA_ARGS__) +#define lnm_lcritical(section, fmt, ...) \ + lnm_log(lnm_log_level_critical, section, fmt, __VA_ARGS__) + +#endif diff --git a/lnm/src/_include/lnm/log_internal.h b/lnm/src/_include/lnm/log_internal.h new file mode 100644 index 0000000..ebcd243 --- /dev/null +++ b/lnm/src/_include/lnm/log_internal.h @@ -0,0 +1,26 @@ +#ifndef LNM_LOG_INTERNAL +#define LNM_LOG_INTERNAL + +#include "lnm/log.h" + +typedef enum lnm_logger_stream_type { + lnm_logger_stream_type_file = 0 +} lnm_logger_stream_type; + +typedef struct lnm_logger_stream { + void *ptr; + lnm_logger_stream_type type; + lnm_log_level level; +} lnm_logger_stream; + +struct lnm_logger { + struct { + lnm_logger_stream **arr; + size_t len; + } streams; +}; + +lnm_err lnm_logger_stream_register(lnm_logger *logger, + lnm_logger_stream *stream); + +#endif diff --git a/lnm/src/http/lnm_http_loop_process.c b/lnm/src/http/lnm_http_loop_process.c index e98c00b..4cfa6f7 100644 --- a/lnm/src/http/lnm_http_loop_process.c +++ b/lnm/src/http/lnm_http_loop_process.c @@ -6,9 +6,12 @@ #include "lnm/http/loop.h" #include "lnm/http/loop_internal.h" #include "lnm/http/req.h" +#include "lnm/log.h" #include "lnm/loop.h" #include "lnm/loop_internal.h" +static const char *section = "http"; + /* static const lnm_http_loop_state lnm_http_loop_state_first_req = * lnm_http_loop_state_parse_req; */ static const lnm_http_loop_state lnm_http_loop_state_first_res = @@ -29,10 +32,15 @@ void lnm_http_loop_process_parse_req(lnm_http_conn *conn) { // If the request is already the size of the read buffer, we close the // request. Otherwise, we wait for anything read if (conn->r.size - conn->r.read == LNM_LOOP_BUF_SIZE) { + lnm_linfo(section, "Received request larger than buffer (%i bytes)", + LNM_LOOP_BUF_SIZE); + conn->state = lnm_loop_state_end; } break; case lnm_http_parse_err_invalid: + lnm_linfo(section, "%s", "Received invalid request"); + conn->state = lnm_loop_state_end; break; case lnm_http_parse_err_unknown_method: @@ -40,6 +48,10 @@ void lnm_http_loop_process_parse_req(lnm_http_conn *conn) { ctx->state = lnm_http_loop_state_first_res; break; } + + lnm_linfo(section, "%s %.*s HTTP/1.%i", + lnm_http_method_names[ctx->req.method], (int)ctx->req.path.len, + ctx->req.path.s, ctx->req.minor_version); } void lnm_http_loop_process_route(lnm_http_conn *conn) { @@ -163,6 +175,13 @@ void lnm_http_loop_state_process_add_headers(lnm_http_conn *conn) { false); } + if (res->status == 0) { + res->status = lnm_http_status_ok; + } + + lnm_linfo(section, "%i %s", res->status, + lnm_http_status_names[res->status / 100 - 1][res->status % 100]); + ctx->state = lnm_http_loop_state_write_status_line; } @@ -172,10 +191,6 @@ void lnm_http_loop_process_write_status_line(lnm_http_conn *conn) { lnm_http_loop_ctx *ctx = conn->ctx; lnm_http_res *res = &ctx->res; - if (res->status == 0) { - res->status = lnm_http_status_ok; - } - const char *response_type_name = lnm_http_status_names[res->status / 100 - 1][res->status % 100]; diff --git a/lnm/src/lnm_log.c b/lnm/src/lnm_log.c new file mode 100644 index 0000000..15fb00f --- /dev/null +++ b/lnm/src/lnm_log.c @@ -0,0 +1,86 @@ +#include +#include + +#include "lnm/common.h" +#include "lnm/log_internal.h" + +const char *lnm_log_level_names[] = {"DEBUG ", "INFO ", "NOTICE ", + "WARNING ", "ERROR ", "CRITICAL"}; + +lnm_logger *global_logger = NULL; + +lnm_err lnm_log_init_global() { + global_logger = calloc(1, sizeof(lnm_logger)); + + return global_logger == NULL ? lnm_err_failed_alloc : lnm_err_ok; +} + +lnm_err lnm_logger_stream_register(lnm_logger *logger, + lnm_logger_stream *stream) { + lnm_logger_stream **new = + logger->streams.len == 0 + ? malloc(sizeof(lnm_logger_stream *)) + : realloc(logger->streams.arr, + (logger->streams.len + 1) * sizeof(lnm_logger_stream *)); + + if (new == NULL) { + return lnm_err_failed_alloc; + } + + new[logger->streams.len] = stream; + logger->streams.arr = new; + logger->streams.len++; + + return lnm_err_ok; +} + +lnm_err lnm_log_register_stdout(lnm_log_level level) { + lnm_logger_stream *stream = malloc(sizeof(lnm_logger_stream)); + + if (stream == NULL) { + return lnm_err_failed_alloc; + } + + stream->type = lnm_logger_stream_type_file; + stream->ptr = stdout; + stream->level = level; + + LNM_RES2(lnm_logger_stream_register(global_logger, stream), free(stream)); + + return lnm_err_ok; +} + +void lnm_vlog(lnm_log_level level, const char *section, const char *fmt, + va_list ap) { + char date_str[32]; + + time_t now = time(NULL); + strftime(date_str, sizeof(date_str) - 1, "%Y-%m-%d %H:%M:%S", + localtime(&now)); + + for (size_t i = 0; i < global_logger->streams.len; i++) { + lnm_logger_stream *stream = global_logger->streams.arr[i]; + + if (level < stream->level) { + continue; + } + + switch (stream->type) { + case lnm_logger_stream_type_file: + fprintf(stream->ptr, "[%s][%s][%s] ", date_str, + lnm_log_level_names[level], section); + vfprintf(stream->ptr, fmt, ap); + fprintf(stream->ptr, "\n"); + break; + } + + va_end(ap); + } +} + +void lnm_log(lnm_log_level level, const char *section, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + lnm_vlog(level, section, fmt, ap); + va_end(ap); +} diff --git a/lnm/src/loop/lnm_loop.c b/lnm/src/loop/lnm_loop.c index a5839d4..4126eb0 100644 --- a/lnm/src/loop/lnm_loop.c +++ b/lnm/src/loop/lnm_loop.c @@ -6,8 +6,11 @@ #include #include "lnm/common.h" +#include "lnm/log.h" #include "lnm/loop_internal.h" +static const char *section = "loop"; + lnm_err lnm_loop_init(lnm_loop **out, void *gctx, lnm_err (*ctx_init)(void **out, void *gctx), void (*ctx_free)(void *ctx), @@ -34,6 +37,8 @@ lnm_err lnm_loop_accept(lnm_loop *l) { int conn_fd = accept(l->listen_fd, NULL, NULL); if (conn_fd < 0) { + lnm_lcritical(section, "accept failed: %i", conn_fd); + return lnm_err_failed_network; } @@ -72,6 +77,8 @@ lnm_err lnm_loop_accept(lnm_loop *l) { l->conns.open++; + lnm_ldebug(section, "connection opened with fd %i", conn_fd); + return lnm_err_ok; } @@ -130,6 +137,8 @@ lnm_err lnm_loop_run(lnm_loop *l) { poll_args[0].fd = l->listen_fd; poll_args[0].events = POLLIN; + lnm_linfo(section, "started on fd %i", l->listen_fd); + while (1) { nfds_t poll_args_len = 1; @@ -171,6 +180,8 @@ lnm_err lnm_loop_run(lnm_loop *l) { close(conn->fd); l->conns.open--; + lnm_ldebug(section, "connection closed with fd %i", conn->fd); + lnm_loop_conn_free(l, conn); } diff --git a/src/lander/lander_get.c b/src/lander/lander_get.c index bb3ca08..24e93ba 100644 --- a/src/lander/lander_get.c +++ b/src/lander/lander_get.c @@ -1,12 +1,13 @@ #include +#include #include "lnm/http/consts.h" #include "lnm/http/loop.h" #include "lnm/loop.h" +#include "lnm/log.h" #include "lsm/store.h" #include "lander.h" -#include "log.h" static const char index_page[] = "\n" @@ -39,7 +40,7 @@ lnm_http_step_err lander_get_redirect(lnm_http_conn *conn) { // This shouldn't be able to happen if (lsm_entry_attr_get(&url_attr_val, c_ctx->entry, lander_attr_type_url) != lsm_error_ok) { - error("Entry of type redirect detected without URL attribute"); + lnm_lerror("lander", "%s", "Entry of type redirect detected without URL attribute"); ctx->res.status = lnm_http_status_internal_server_error; lsm_entry_close(c_ctx->entry); diff --git a/src/lander/lander_post.c b/src/lander/lander_post.c index 4bda832..1244cb7 100644 --- a/src/lander/lander_post.c +++ b/src/lander/lander_post.c @@ -1,8 +1,9 @@ +#include + #include "lnm/loop.h" #include "lsm/store.h" #include "lander.h" -#include "log.h" static void randomize_key(char *key, int len) { size_t charset_len = strlen(lander_key_charset); diff --git a/src/log.c b/src/log.c deleted file mode 100644 index 1ca0bc7..0000000 --- a/src/log.c +++ /dev/null @@ -1,31 +0,0 @@ -#include - -#include "log.h" - -const char *log_level_names[] = {"DEBUG", "INFO ", "WARN ", "ERROR", - "CRITICAL"}; - -log_level _log_level = log_level_debug; - -void _lander_log(log_level level, FILE *f, const char *fmt, ...) { - if (level < _log_level) { - return; - } - - // Log to stdout by default - f = (f == NULL) ? stdout : f; - - char date_str[32]; - - time_t now = time(NULL); - strftime(date_str, sizeof(date_str) - 1, "%Y-%m-%d %H:%M:%S", - localtime(&now)); - fprintf(f, "[%s][%s] ", date_str, log_level_names[level]); - - va_list ap; - va_start(ap, fmt); - vfprintf(f, fmt, ap); - va_end(ap); - - fprintf(f, "\n"); -} diff --git a/src/main.c b/src/main.c index 4970d95..d25aec3 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,7 @@ #include #include "lnm/http/loop.h" +#include "lnm/log.h" #include "lander.h" #include "log.h" @@ -61,7 +62,8 @@ lnm_http_loop *loop_init(lander_gctx *gctx, const char *api_key) { #define ENV(var, env_var) \ const char *var = getenv(env_var); \ if (var == NULL) { \ - critical(1, "Missing environment variable %s", env_var); \ + lnm_lcritical("main", "Missing environment variable %s", env_var); \ + exit(1); \ } #define ENV_OPT(var, env_var, default) \ @@ -71,9 +73,11 @@ lnm_http_loop *loop_init(lander_gctx *gctx, const char *api_key) { } int main() { - setvbuf(stdout, NULL, _IONBF, 0); srand(time(NULL)); + lnm_log_init_global(); + lnm_log_register_stdout(lnm_log_level_debug); + ENV(api_key, "LANDER_API_KEY"); ENV_OPT(port_str, "LANDER_PORT", "18080"); ENV_OPT(data_dir_s, "LANDER_DATA_DIR", "."); @@ -81,7 +85,8 @@ int main() { int port = atoi(port_str); if (port <= 0 || port >= 1 << 16) { - critical(1, "Invalid TCP port %s", port_str); + lnm_lcritical("main", "Invalid TCP port %s", port_str); + exit(1); } lander_gctx *c_gctx = lander_gctx_init(); @@ -90,13 +95,14 @@ int main() { lsm_str *data_dir; lsm_str_init_copy(&data_dir, (char *)data_dir_s); - info("Initializing store from path '%s'", data_dir_s); + lnm_linfo("main", "Initializing store from path '%s'", data_dir_s); if (lsm_store_load(&c_gctx->store, data_dir) != lsm_error_ok) { - critical(2, "Failed to load existing store."); + lnm_lcritical("main", "%s", "Failed to load existing store."); } - info("Store loaded containing %lu entries", lsm_store_size(c_gctx->store)); + lnm_linfo("main", "Store loaded containing %lu entries", + lsm_store_size(c_gctx->store)); lnm_http_loop *hl = loop_init(c_gctx, api_key); lnm_http_loop_run(hl, port);