added new multi log system
[NeonServV5.git] / src / log.c
diff --git a/src/log.c b/src/log.c
new file mode 100644 (file)
index 0000000..6e1ba87
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,291 @@
+/* log.c - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+
+#include "log.h"
+#include "ChanNode.h"
+#include "IRCEvents.h"
+#include "ClientSocket.h"
+#include "bots.h"
+#include "IOHandler.h"
+#include "tools.h"
+#include "ConfigParser.h"
+
+#define MAXLOGLEN 1024
+
+static void write_log(const char *module, const int section, const char *line, int len);
+static void load_log_targets();
+static void unload_log_targets();
+static int reload_log_targets(int init);
+
+
+#define LOG_TARGET_TYPE_FILE   1
+#define LOG_TARGET_TYPE_STDOUT 2
+#define LOG_TARGET_TYPE_STDERR 3
+#define LOG_TARGET_TYPE_IRC    4
+
+static const struct {
+    int id;
+    char *name;
+} log_sections[] = {
+    {LOG_INFO, "info"},
+    {LOG_DEBUG, "debug"},
+    {LOG_IRCRAW, "raw"},
+    {LOG_MYSQL, "mysql"},
+    {LOG_OVERRIDE, "override"},
+    {LOG_WARNING, "warning"},
+    {LOG_ERROR, "error"},
+    {LOG_FATAL, "fatal"},
+    {0, NULL}
+};
+
+struct log_target {
+    char *module;
+    int section;
+    int type : 8;
+    union {
+        char *channel;
+        FILE *fptr;
+    } target;
+    struct log_target *next;
+};
+
+static struct log_target *log_targets = NULL;
+
+static char *get_section_name(int section_id) {
+    int i = -1;
+    while(log_sections[++i].id) {
+        if(log_sections[i].id == section_id)
+            return log_sections[i].name;
+    }
+    return NULL;
+}
+
+static int get_section_id(char *section_name) {
+    int i = -1;
+    while(log_sections[++i].id) {
+        if(!stricmp(log_sections[i].name, section_name))
+            return log_sections[i].id;
+    }
+    return 0;
+}
+
+
+void printf_log(const char *module, const int section, const char *text, ...) {
+    va_list arg_list;
+    char logBuf[MAXLOGLEN];
+    int pos;
+    logBuf[0] = '\0';
+    va_start(arg_list, text);
+    pos = vsnprintf(logBuf, MAXLOGLEN - 1, text, arg_list);
+    va_end(arg_list);
+    if (pos < 0 || pos > (MAXLOGLEN - 1)) pos = MAXLOGLEN - 1;
+    logBuf[pos] = '\0';
+    write_log(module, section, logBuf, pos);
+}
+
+static void write_log(const char *module, const int section, const char *line, int len) {
+    if(!log_targets)
+        return;
+    SYNCHRONIZE(log_sync);
+    struct log_target *target;
+    char lineBuf[MAXLOGLEN];
+    char timeBuf[80];
+    int lineBufPos, timeBufPos;
+    time_t rawtime;
+    struct tm *timeinfo;
+    int i, j;
+    
+    time(&rawtime);
+    timeinfo = localtime(&rawtime);
+    timeBufPos = strftime(timeBuf, 80, "[%X %x] ", timeinfo);
+    
+    lineBufPos = sprintf(lineBuf, "(%s:", module);
+    for(i = 0, j = 0; i < LOG_SECTIONS; i++) {
+        if((section & (1 << i))) {
+            char *section_name = get_section_name((1 << i));
+            if(!section_name)
+                continue;
+            if(j++)
+                lineBuf[lineBufPos++] = ',';
+            lineBufPos += sprintf(lineBuf + lineBufPos, "%s", section_name);
+        }
+    }
+    lineBufPos += sprintf(lineBuf + lineBufPos, ") %s", line);
+    //cut off \n \r
+    while(lineBuf[lineBufPos-1] == '\r' || lineBuf[lineBufPos-1] == '\n') {
+        lineBuf[lineBufPos-1] = '\0';
+        lineBufPos--;
+    }
+    
+    j = 0;
+    for(target = log_targets; target; target = target->next) {
+        if(strcmp(target->module, "*") && stricmp(target->module, module))
+            continue;
+        if(!(target->section & section))
+            continue;
+        if(target->type == LOG_TARGET_TYPE_IRC) {
+            if(section == LOG_IRCRAW || (!stricmp(module, "iohandler") && section == LOG_DEBUG))
+                continue; //endless loop ;)
+            struct ChanNode *channel = getChanByName(target->target.channel);
+            struct ClientSocket *client;
+            if(channel && (client = getChannelBot(channel, 0))) {
+                putsock(client, "PRIVMSG %s :%s", channel->name, lineBuf);
+            }
+        } else if(target->type == LOG_TARGET_TYPE_FILE) {
+            fwrite(timeBuf, 1, timeBufPos, target->target.fptr);
+            fwrite(lineBuf, 1, lineBufPos, target->target.fptr);
+            fwrite("\n", 1, 1, target->target.fptr);
+        } else if(target->type == LOG_TARGET_TYPE_STDOUT || target->type == LOG_TARGET_TYPE_STDERR) {
+            if(process_state.daemonized)
+                continue; //block stdout / stderr as daemon
+            fprintf((target->type == LOG_TARGET_TYPE_STDOUT ? stdout : stderr), "%s%s\n", timeBuf, lineBuf);
+            j = 1;
+        }
+    }
+    if((process_state.loglevel & section) && !process_state.daemonized && !j) {
+        fprintf(stdout, "%s%s\n", timeBuf, lineBuf);
+    }
+    
+    DESYNCHRONIZE(log_sync);
+}
+
+static void load_log_targets() {
+    if(log_targets)
+        return;
+    char **targetlist = get_all_fieldnames("logs");
+    if(!targetlist) return;
+    int i = 0;
+    char tmp[MAXLEN];
+    struct log_target *ctarget;
+    while(targetlist[i]) {
+        sprintf(tmp, "logs.%s", targetlist[i]);
+        char *type = get_string_field(tmp);
+        char *target = strchr(type, ':');
+        char *module = targetlist[i];
+        char *section = strchr(module, ':');
+        if(!target || !section) {
+            i++;
+            continue;
+        }
+        *target = '\0';
+        target++;
+        *section = '\0';
+        section++;
+        ctarget = malloc(sizeof(*ctarget));
+        if(!stricmp(type, "file")) {
+            ctarget->type = LOG_TARGET_TYPE_FILE;
+            ctarget->target.fptr = fopen(target, "a");
+            if(!ctarget->target.fptr) {
+                free(ctarget);
+                ctarget = NULL;
+            }
+        } else if(!stricmp(type, "std")) {
+            if(!stricmp(target, "out"))
+                ctarget->type = LOG_TARGET_TYPE_STDOUT;
+            else if(!stricmp(target, "err"))
+                ctarget->type = LOG_TARGET_TYPE_STDERR;
+            else {
+                free(ctarget);
+                ctarget = NULL;
+            }
+        } else if(!stricmp(type, "irc")) {
+            if(is_valid_chan(target)) {
+                ctarget->type = LOG_TARGET_TYPE_IRC;
+                ctarget->target.channel = strdup(target);
+            } else {
+                free(ctarget);
+                ctarget = NULL;
+            }
+        } else {
+            free(ctarget);
+            ctarget = NULL;
+        }
+        if(ctarget) {
+            ctarget->module = strdup(module);
+            ctarget->section = 0;
+            char *csection = section;
+            while(1) {
+                char *next_section = strchr(csection, ',');
+                if(next_section)
+                    *next_section = '\0';
+                if(!strcmp(csection, "*"))
+                    ctarget->section |= LOG_ALL;
+                else
+                    ctarget->section |= get_section_id(csection);
+                if(next_section) {
+                    *next_section = ',';
+                    csection = next_section + 1;
+                } else
+                    break;
+            }
+            ctarget->next = log_targets;
+            log_targets = ctarget;
+        }
+        target--;
+        *target = ':';
+        section--;
+        *section = ':';
+        i++;
+    }
+}
+
+static void unload_log_targets() {
+    struct log_target *target, *next_target;
+    for(target = log_targets; target; target = next_target) {
+        next_target = target->next;
+        if(target->type == LOG_TARGET_TYPE_IRC)
+            free(target->target.channel);
+        else if(target->type == LOG_TARGET_TYPE_FILE)
+            fclose(target->target.fptr);
+        free(target);
+    }
+    log_targets = NULL;
+}
+
+static int reload_log_targets(int init) {
+    unload_log_targets();
+    load_log_targets();
+    return 1;
+}
+
+static IOHANDLER_LOG_BACKEND(log_iohandler_backend) {
+    int log_level;
+    switch(type) {
+        case IOLOG_DEBUG:
+            log_level = LOG_DEBUG;
+            break;
+        case IOLOG_WARNING:
+            log_level = LOG_WARNING;
+            break;
+        case IOLOG_ERROR:
+            log_level = LOG_ERROR;
+            break;
+        case IOLOG_FATAL:
+            log_level = LOG_FATAL;
+            break;
+        default:
+            log_level = 0;
+    }
+    write_log("iohandler", log_level, line, strlen(line));
+}
+
+void init_log() {
+    load_log_targets();
+    bind_reload(reload_log_targets, 0);
+    iolog_backend = log_iohandler_backend;
+    printf_log("log", LOG_INFO, "initialized log system.");
+}