#ifdef __gnu_linux__
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <aio.h>
#include <time.h>
#include <pthread.h>
#include "log.h"
#include "../util/strbuf.h"
#include "../util/io.h"
#include "../util/atomic.h"
#include <cx/hash_map.h>
#include <cx/linked_list.h>
#include <cx/compare.h>
static int is_initialized =
0;
static int log_file_fd;
static int log_level =
0;
static uint32_t log_dup_count =
0;
static CxList *log_dup_list =
NULL;
static pthread_mutex_t mutex =
PTHREAD_MUTEX_INITIALIZER;
WSBool main_is_daemon(
void);
static sbuf_t *ui_buffer =
NULL;
static CxMap *access_log_files;
static char *log_date_month[] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
static const char *log_levels[] = {
"warning",
"config",
"security",
"failure",
"catastrophe",
"info",
"verbose",
"debug"
};
static int can_log[] = {
1,
1,
1,
1,
1,
1,
0,
0
};
int init_logging(
void) {
log_dup_list = cxLinkedListCreate(cxDefaultAllocator,
NULL,
CX_STORE_POINTERS);
return log_dup_list ==
NULL;
}
int init_log_file(LogConfig *cfg) {
if(is_initialized) {
return 0;
}
mode_t mode =
S_IRUSR |
S_IWUSR |
S_IRGRP |
S_IROTH;
log_file_fd = open(cfg->file,
O_WRONLY |
O_CREAT |
O_APPEND, mode);
if(log_file_fd == -
1) {
log_ereport(
LOG_FAILURE,
"Cannot open log file %s: %s", cfg->file, strerror(errno));
return -
1;
}
if(!strcmp(cfg->level,
"ERROR")) {
can_log[
LOG_WARN] =
0;
can_log[
LOG_INFORM] =
0;
}
else if(!strcmp(cfg->level,
"WARNING")) {
can_log[
LOG_INFORM] =
0;
}
else if(!strcmp(cfg->level,
"VERBOSE")) {
can_log[
LOG_VERBOSE] =
1;
}
else if(!strcmp(cfg->level,
"DEBUG")) {
can_log[
LOG_VERBOSE] =
1;
can_log[
LOG_DEBUG] =
1;
}
if(cfg->log_stdout) {
}
if(cfg->log_stderr) {
}
is_initialized =
1;
if(ui_buffer) {
size_t len = ui_buffer->length;
size_t r;
while(len >
0) {
r = write(log_file_fd, ui_buffer->ptr, ui_buffer->length);
len -= r;
}
sbuf_free(ui_buffer);
}
return 0;
}
void log_uninitialized_writeln(
char *str,
size_t len) {
if(ui_buffer ==
NULL) {
ui_buffer = sbuf_new(
1024);
if(ui_buffer ==
NULL) {
return;
}
}
cxstring s;
s.ptr = str;
s.length = len;
sbuf_append(ui_buffer, s);
sbuf_put(ui_buffer,
'\n');
}
void log_file_writeln(
char *str,
size_t len) {
struct iovec io[] = {
{ str, len },
{
"\n",
1}
};
WSBool write_to_stdout = !main_is_daemon();
if(is_initialized) {
writev(log_file_fd, io,
2);
}
else {
log_uninitialized_writeln(str, len);
}
if(write_to_stdout) {
writev(
STDOUT_FILENO, io,
2);
}
if(log_dup_count >
0) {
char *msg = malloc(len +
1);
memcpy(msg, str, len);
msg[len] =
'\n';
pthread_mutex_lock(&mutex);
CxIterator i = cxListIterator(log_dup_list);
cx_foreach(LogDup *, dup, i) {
dup->write(dup->cookie, msg, len +
1);
}
pthread_mutex_unlock(&mutex);
free(msg);
}
}
cxmutstr log_get_prefix_str(
const char *level) {
time_t t = time(
NULL);
cxmutstr d;
d.ptr =
NULL;
d.length =
0;
struct tm date;
localtime_r(&t, &date);
char *buf = malloc(
64);
int len = snprintf(
buf,
64,
"[%02d/%s/%d:%02d:%02d:%02d] %s : ",
date.tm_mday,
log_date_month[date.tm_mon],
1900 + date.tm_year,
date.tm_hour,
date.tm_min,
date.tm_sec,
level);
if(len >
0) {
d.ptr = buf;
d.length = len;
}
return d;
}
cxmutstr log_get_prefix(
int level) {
return log_get_prefix_str(log_levels[level]);
}
void log_add_logdup(LogDup *dup) {
pthread_mutex_lock(&mutex);
cxListAdd(log_dup_list, dup);
ws_atomic_inc32(&log_dup_count);
pthread_mutex_unlock(&mutex);
}
void log_remove_logdup(LogDup *ldup) {
pthread_mutex_lock(&mutex);
CxMutIterator i = cxListMutIterator(log_dup_list);
WSBool finished =
0;
cx_foreach(LogDup *, dup, i) {
if(finished)
break;
if(dup == ldup) {
cxIteratorFlagRemoval(i);
finished =
1;
ws_atomic_dec32(&log_dup_count);
}
}
pthread_mutex_unlock(&mutex);
}
int log_ereport(
int degree,
const char *format, ...) {
va_list args;
va_start(args, format);
int ret = log_ereport_v(degree, format, args);
va_end(args);
return ret;
}
int log_ereport_v(
int degree,
const char *format, va_list args) {
if(degree <
0 || degree >
7) {
return 0;
}
if(!can_log[degree]) {
return 0;
}
cxmutstr lmsg;
lmsg.ptr =
NULL;
cxmutstr lpre = log_get_prefix(degree);
int len = vasprintf(&lmsg.ptr, format, args);
lmsg.length = len;
cxmutstr message = cx_strcat(
2, lpre, lmsg);
log_file_writeln(message.ptr, message.length);
free(lmsg.ptr);
free(lpre.ptr);
free(message.ptr);
return 0;
}
int log_error(
int degree,
const char *func, Session *sn, Request *rq,
const char *format, ...)
{
va_list args;
va_start(args, format);
int ret = log_error_v(degree, func, sn, rq, format, args);
va_end(args);
return ret;
}
int log_error_v(
int degree,
const char *func, Session *sn, Request *rq,
const char *format, va_list args)
{
return log_ereport(degree, format, args);
}
int log_message(
const char *degree,
const char *format, ...) {
va_list args;
va_start(args, format);
int ret = log_message_v(degree, format, args);
va_end(args);
return ret;
}
int log_message_v(
const char *degree,
const char *format, va_list args) {
cxmutstr lmsg;
lmsg.ptr =
NULL;
cxmutstr lpre = log_get_prefix_str(degree);
int len = vasprintf(&lmsg.ptr, format, args);
lmsg.length = len;
cxmutstr message = cx_strcat(
2, lpre, lmsg);
log_file_writeln(message.ptr, message.length);
free(lmsg.ptr);
free(lpre.ptr);
free(message.ptr);
return 0;
}
void ws_log_assert(
const char *file,
const char *func,
int line) {
log_ereport(
LOG_CATASTROPHE,
"assertion failed: %s: %s:%d",
file,
func,
line);
exit(-
1);
}
LogFile* get_access_log_file(cxstring file) {
if(!access_log_files) {
access_log_files = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
4);
}
if(file.ptr ==
NULL || file.length ==
0) {
return NULL;
}
CxHashKey key = cx_hash_key_bytes((
unsigned const char*)file.ptr, file.length);
LogFile *log = cxMapGet(access_log_files, key);
if(log !=
NULL) {
ws_atomic_inc32(&log->ref);
return log;
}
FILE *out = fopen(file.ptr,
"a");
if(out ==
NULL) {
return NULL;
}
log = calloc(
1,
sizeof(LogFile));
log->file = out;
log->ref =
1;
cxMapPut(access_log_files, key, log);
return log;
}