added new multi log system
[NeonServV5.git] / src / log.c
1 /* log.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "log.h"
19 #include "ChanNode.h"
20 #include "IRCEvents.h"
21 #include "ClientSocket.h"
22 #include "bots.h"
23 #include "IOHandler.h"
24 #include "tools.h"
25 #include "ConfigParser.h"
26
27 #define MAXLOGLEN 1024
28
29 static void write_log(const char *module, const int section, const char *line, int len);
30 static void load_log_targets();
31 static void unload_log_targets();
32 static int reload_log_targets(int init);
33
34
35 #define LOG_TARGET_TYPE_FILE   1
36 #define LOG_TARGET_TYPE_STDOUT 2
37 #define LOG_TARGET_TYPE_STDERR 3
38 #define LOG_TARGET_TYPE_IRC    4
39
40 static const struct {
41     int id;
42     char *name;
43 } log_sections[] = {
44     {LOG_INFO, "info"},
45     {LOG_DEBUG, "debug"},
46     {LOG_IRCRAW, "raw"},
47     {LOG_MYSQL, "mysql"},
48     {LOG_OVERRIDE, "override"},
49     {LOG_WARNING, "warning"},
50     {LOG_ERROR, "error"},
51     {LOG_FATAL, "fatal"},
52     {0, NULL}
53 };
54
55 struct log_target {
56     char *module;
57     int section;
58     int type : 8;
59     union {
60         char *channel;
61         FILE *fptr;
62     } target;
63     struct log_target *next;
64 };
65
66 static struct log_target *log_targets = NULL;
67
68 static char *get_section_name(int section_id) {
69     int i = -1;
70     while(log_sections[++i].id) {
71         if(log_sections[i].id == section_id)
72             return log_sections[i].name;
73     }
74     return NULL;
75 }
76
77 static int get_section_id(char *section_name) {
78     int i = -1;
79     while(log_sections[++i].id) {
80         if(!stricmp(log_sections[i].name, section_name))
81             return log_sections[i].id;
82     }
83     return 0;
84 }
85
86
87 void printf_log(const char *module, const int section, const char *text, ...) {
88     va_list arg_list;
89     char logBuf[MAXLOGLEN];
90     int pos;
91     logBuf[0] = '\0';
92     va_start(arg_list, text);
93     pos = vsnprintf(logBuf, MAXLOGLEN - 1, text, arg_list);
94     va_end(arg_list);
95     if (pos < 0 || pos > (MAXLOGLEN - 1)) pos = MAXLOGLEN - 1;
96     logBuf[pos] = '\0';
97     write_log(module, section, logBuf, pos);
98 }
99
100 static void write_log(const char *module, const int section, const char *line, int len) {
101     if(!log_targets)
102         return;
103     SYNCHRONIZE(log_sync);
104     struct log_target *target;
105     char lineBuf[MAXLOGLEN];
106     char timeBuf[80];
107     int lineBufPos, timeBufPos;
108     time_t rawtime;
109     struct tm *timeinfo;
110     int i, j;
111     
112     time(&rawtime);
113     timeinfo = localtime(&rawtime);
114     timeBufPos = strftime(timeBuf, 80, "[%X %x] ", timeinfo);
115     
116     lineBufPos = sprintf(lineBuf, "(%s:", module);
117     for(i = 0, j = 0; i < LOG_SECTIONS; i++) {
118         if((section & (1 << i))) {
119             char *section_name = get_section_name((1 << i));
120             if(!section_name)
121                 continue;
122             if(j++)
123                 lineBuf[lineBufPos++] = ',';
124             lineBufPos += sprintf(lineBuf + lineBufPos, "%s", section_name);
125         }
126     }
127     lineBufPos += sprintf(lineBuf + lineBufPos, ") %s", line);
128     //cut off \n \r
129     while(lineBuf[lineBufPos-1] == '\r' || lineBuf[lineBufPos-1] == '\n') {
130         lineBuf[lineBufPos-1] = '\0';
131         lineBufPos--;
132     }
133     
134     j = 0;
135     for(target = log_targets; target; target = target->next) {
136         if(strcmp(target->module, "*") && stricmp(target->module, module))
137             continue;
138         if(!(target->section & section))
139             continue;
140         if(target->type == LOG_TARGET_TYPE_IRC) {
141             if(section == LOG_IRCRAW || (!stricmp(module, "iohandler") && section == LOG_DEBUG))
142                 continue; //endless loop ;)
143             struct ChanNode *channel = getChanByName(target->target.channel);
144             struct ClientSocket *client;
145             if(channel && (client = getChannelBot(channel, 0))) {
146                 putsock(client, "PRIVMSG %s :%s", channel->name, lineBuf);
147             }
148         } else if(target->type == LOG_TARGET_TYPE_FILE) {
149             fwrite(timeBuf, 1, timeBufPos, target->target.fptr);
150             fwrite(lineBuf, 1, lineBufPos, target->target.fptr);
151             fwrite("\n", 1, 1, target->target.fptr);
152         } else if(target->type == LOG_TARGET_TYPE_STDOUT || target->type == LOG_TARGET_TYPE_STDERR) {
153             if(process_state.daemonized)
154                 continue; //block stdout / stderr as daemon
155             fprintf((target->type == LOG_TARGET_TYPE_STDOUT ? stdout : stderr), "%s%s\n", timeBuf, lineBuf);
156             j = 1;
157         }
158     }
159     if((process_state.loglevel & section) && !process_state.daemonized && !j) {
160         fprintf(stdout, "%s%s\n", timeBuf, lineBuf);
161     }
162     
163     DESYNCHRONIZE(log_sync);
164 }
165
166 static void load_log_targets() {
167     if(log_targets)
168         return;
169     char **targetlist = get_all_fieldnames("logs");
170     if(!targetlist) return;
171     int i = 0;
172     char tmp[MAXLEN];
173     struct log_target *ctarget;
174     while(targetlist[i]) {
175         sprintf(tmp, "logs.%s", targetlist[i]);
176         char *type = get_string_field(tmp);
177         char *target = strchr(type, ':');
178         char *module = targetlist[i];
179         char *section = strchr(module, ':');
180         if(!target || !section) {
181             i++;
182             continue;
183         }
184         *target = '\0';
185         target++;
186         *section = '\0';
187         section++;
188         ctarget = malloc(sizeof(*ctarget));
189         if(!stricmp(type, "file")) {
190             ctarget->type = LOG_TARGET_TYPE_FILE;
191             ctarget->target.fptr = fopen(target, "a");
192             if(!ctarget->target.fptr) {
193                 free(ctarget);
194                 ctarget = NULL;
195             }
196         } else if(!stricmp(type, "std")) {
197             if(!stricmp(target, "out"))
198                 ctarget->type = LOG_TARGET_TYPE_STDOUT;
199             else if(!stricmp(target, "err"))
200                 ctarget->type = LOG_TARGET_TYPE_STDERR;
201             else {
202                 free(ctarget);
203                 ctarget = NULL;
204             }
205         } else if(!stricmp(type, "irc")) {
206             if(is_valid_chan(target)) {
207                 ctarget->type = LOG_TARGET_TYPE_IRC;
208                 ctarget->target.channel = strdup(target);
209             } else {
210                 free(ctarget);
211                 ctarget = NULL;
212             }
213         } else {
214             free(ctarget);
215             ctarget = NULL;
216         }
217         if(ctarget) {
218             ctarget->module = strdup(module);
219             ctarget->section = 0;
220             char *csection = section;
221             while(1) {
222                 char *next_section = strchr(csection, ',');
223                 if(next_section)
224                     *next_section = '\0';
225                 if(!strcmp(csection, "*"))
226                     ctarget->section |= LOG_ALL;
227                 else
228                     ctarget->section |= get_section_id(csection);
229                 if(next_section) {
230                     *next_section = ',';
231                     csection = next_section + 1;
232                 } else
233                     break;
234             }
235             ctarget->next = log_targets;
236             log_targets = ctarget;
237         }
238         target--;
239         *target = ':';
240         section--;
241         *section = ':';
242         i++;
243     }
244 }
245
246 static void unload_log_targets() {
247     struct log_target *target, *next_target;
248     for(target = log_targets; target; target = next_target) {
249         next_target = target->next;
250         if(target->type == LOG_TARGET_TYPE_IRC)
251             free(target->target.channel);
252         else if(target->type == LOG_TARGET_TYPE_FILE)
253             fclose(target->target.fptr);
254         free(target);
255     }
256     log_targets = NULL;
257 }
258
259 static int reload_log_targets(int init) {
260     unload_log_targets();
261     load_log_targets();
262     return 1;
263 }
264
265 static IOHANDLER_LOG_BACKEND(log_iohandler_backend) {
266     int log_level;
267     switch(type) {
268         case IOLOG_DEBUG:
269             log_level = LOG_DEBUG;
270             break;
271         case IOLOG_WARNING:
272             log_level = LOG_WARNING;
273             break;
274         case IOLOG_ERROR:
275             log_level = LOG_ERROR;
276             break;
277         case IOLOG_FATAL:
278             log_level = LOG_FATAL;
279             break;
280         default:
281             log_level = 0;
282     }
283     write_log("iohandler", log_level, line, strlen(line));
284 }
285
286 void init_log() {
287     load_log_targets();
288     bind_reload(reload_log_targets, 0);
289     iolog_backend = log_iohandler_backend;
290     printf_log("log", LOG_INFO, "initialized log system.");
291 }