]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/logger.cpp
Code for loading <log method=file> tags from config, but this still needs to be fit...
[user/henk/code/inspircd.git] / src / logger.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $Core: libIRCDlogger */
15
16 #include "inspircd.h"
17
18 #include "filelogger.h"
19
20 /*
21  * Suggested implementation...
22  *      class LogManager
23  *              bool AddLogType(const std::string &type, enum loglevel, LogStream *)
24  *              bool DelLogType(const std::string &type, LogStream *)
25  *              Log(const std::string &type, enum loglevel, const std::string &msg)
26  *              std::map<std::string, std::vector<LogStream *> > logstreams (holds a 'chain' of logstreams for each type that are all notified when a log happens)
27  *
28  *  class LogStream
29  *              std::string type
30  *      virtual void OnLog(enum loglevel, const std::string &msg)
31  *
32  * How it works:
33  *  Modules create their own logstream types (core will create one for 'file logging' for example) and create instances of these logstream types
34  *  and register interest in a certain logtype. Globbing is not here, with the exception of * - for all events.. loglevel is used to drop 
35  *  events that are of no interest to a logstream.
36  *
37  *  When Log is called, the vector of logstreams for that type is iterated (along with the special vector for "*"), and all registered logstreams
38  *  are called back ("OnLog" or whatever) to do whatever they like with the message. In the case of the core, this will write to a file.
39  *  In the case of the module I plan to write (m_logtochannel or something), it will log to the channel(s) for that logstream, etc.
40  *
41  * NOTE: Somehow we have to let LogManager manage the non-blocking file streams and provide an interface to share them with various LogStreams,
42  *       as, for example, a user may want to let 'KILL' and 'XLINE' snotices go to /home/ircd/inspircd/logs/operactions.log, or whatever. How
43  *       can we accomplish this easily? I guess with a map of pre-loved logpaths, and a pointer of FILE *..
44  * 
45  */
46
47 void LogManager::OpenFileLogs()
48 {
49         ConfigReader* Conf = new ConfigReader(ServerInstance);
50         std::map<std::string, FileWriter*> logmap;
51         std::map<std::string, FileWriter*>::iterator i;
52         for (int index = 0; index < Conf->Enumerate("log"); ++index)
53         {
54                 std::string method = Conf->ReadValue("log", "method", index);
55                 if (method != "file") continue;
56                 std::string type = Conf->ReadValue("log", "type", index);
57                 std::string level = Conf->ReadValue("log", "level", index);
58                 int loglevel = DEFAULT;
59                 if (level == "debug")
60                 {
61                         loglevel = DEBUG;
62                         ServerInstance->Config->debugging = true;
63                 }
64                 else if (level == "verbose")
65                 {
66                         loglevel = VERBOSE;
67                 }
68                 else if (level == "default")
69                 {
70                         loglevel = DEFAULT;
71                 }
72                 else if (level == "sparse")
73                 {
74                         loglevel = SPARSE;
75                 }
76                 else if (level == "none")
77                 {
78                         loglevel = NONE;
79                 }
80                 FileWriter* fw;
81                 std::string target = Conf->ReadValue("log", "target", index);
82                 if ((i = logmap.find(target)) == logmap.end())
83                 {
84                         FILE* f = fopen(target.c_str(), "a");
85                         fw = new FileWriter(ServerInstance, f);
86                         logmap.insert(std::make_pair(target, fw));
87                 }
88                 else
89                 {
90                         fw = i->second;
91                 }
92                 FileLogStream* fls = new FileLogStream(ServerInstance, loglevel, fw);
93                 irc::commasepstream css(type);
94                 std::string tok;
95                 while (css.GetToken(tok))
96                 {
97                         AddLogType(tok, fls);
98                 }
99         }
100 }
101
102 void LogManager::CloseLogs()
103 {
104         /*
105          * This doesn't remove logstreams from the map/vector etc, because if this is called, shit is hitting the fan
106          * and we're going down anyway - this just provides a "nice" way for logstreams to clean up. -- w
107          */
108         std::map<std::string, std::vector<LogStream *> >::iterator i;
109
110         while (LogStreams.begin() != LogStreams.end())
111         {
112                 i = LogStreams.begin();
113
114                 while (i->second.begin() != i->second.end())
115                 {
116                         std::vector<LogStream *>::iterator it = i->second.begin();
117
118                         delete (*it);
119                         i->second.erase(it);
120                 }
121
122                 LogStreams.erase(i);
123         }
124         /* Now close FileLoggers, for those logstreams that neglected to properly free their stuff. */
125         for (FileLogMap::iterator i = FileLogs.begin(); i != FileLogs.end(); ++i)
126         {
127                 delete i->first;
128         }
129         FileLogMap().swap(FileLogs); /* Swap with empty map to clear */
130 }
131
132 bool LogManager::AddLogType(const std::string &type, LogStream *l)
133 {
134         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
135
136         if (i != LogStreams.end())
137                 i->second.push_back(l);
138         else
139         {
140                 std::vector<LogStream *> v;
141                 v.push_back(l);
142                 LogStreams[type] = v;
143         }
144
145         if (type == "*")
146                 GlobalLogStreams.push_back(l);
147
148         return true;
149 }
150
151 bool LogManager::DelLogType(const std::string &type, LogStream *l)
152 {
153         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
154         std::vector<LogStream *>::iterator gi = GlobalLogStreams.begin();
155
156         while (gi != GlobalLogStreams.end())
157         {
158                 if ((*gi) == l)
159                 {
160                         GlobalLogStreams.erase(gi);
161                         break;
162                 }
163         }
164
165         if (i != LogStreams.end())
166         {
167                 std::vector<LogStream *>::iterator it = i->second.begin();
168
169                 while (it != i->second.end())
170                 {
171                         if (*it == l)
172                         {
173                                 i->second.erase(it);
174
175                                 if (i->second.size() == 0)
176                                 {
177                                         LogStreams.erase(i);
178                                 }
179
180                                 delete l;
181                                 return true;
182                         }
183
184                         it++;
185                 }
186         }
187
188         return false;
189 }
190
191 void LogManager::Log(const std::string &type, int loglevel, const char *fmt, ...)
192 {
193         if (Logging)
194                 return;
195
196         va_list a;
197         static char buf[65536];
198
199         va_start(a, fmt);
200         vsnprintf(buf, 65536, fmt, a);
201         va_end(a);
202
203         this->Log(type, loglevel, std::string(buf));
204 }
205
206 void LogManager::Log(const std::string &type, int loglevel, const std::string &msg)
207 {
208         if (Logging)
209                 return;
210
211         Logging = true;
212
213         std::vector<LogStream *>::iterator gi = GlobalLogStreams.begin();
214
215         while (gi != GlobalLogStreams.end())
216         {
217                 (*gi)->OnLog(loglevel, type, msg);
218                 gi++;
219         }
220
221         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
222
223         if (i != LogStreams.end())
224         {
225                 std::vector<LogStream *>::iterator it = i->second.begin();
226
227                 while (it != i->second.end())
228                 {
229                         (*it)->OnLog(loglevel, type, msg);
230                         it++;
231                 }
232         }
233
234         Logging = false;
235 }
236
237
238 FileWriter::FileWriter(InspIRCd* Instance, FILE* logfile)
239 : ServerInstance(Instance), log(logfile), writeops(0)
240 {
241         if (log)
242         {
243                 Instance->SE->NonBlocking(fileno(log));
244                 SetFd(fileno(log));
245                 buffer.clear();
246         }
247 }
248
249 bool FileWriter::Readable()
250 {
251         return false;
252 }
253     
254 void FileWriter::HandleEvent(EventType, int)
255 {
256         WriteLogLine("");
257         if (log)
258                 ServerInstance->SE->DelFd(this);
259 }
260
261 void FileWriter::WriteLogLine(const std::string &line)
262 {
263         if (line.length())
264                 buffer.append(line);
265
266         if (log)
267         {
268                 int written = fprintf(log,"%s",buffer.c_str());
269 #ifdef WINDOWS
270                 buffer.clear();
271 #else
272                 if ((written >= 0) && (written < (int)buffer.length()))
273                 {
274                         buffer.erase(0, buffer.length());
275                         ServerInstance->SE->AddFd(this);
276                 }
277                 else if (written == -1)
278                 {
279                         if (errno == EAGAIN)
280                                 ServerInstance->SE->AddFd(this);
281                 }
282                 else
283                 {
284                         /* Wrote the whole buffer, and no need for write callback */
285                         buffer.clear();
286                 }
287 #endif
288                 if (writeops++ % 20)
289                 {
290                         fflush(log);
291                 }
292         }
293 }
294
295 void FileWriter::Close()
296 {
297         if (log)
298         {
299                 ServerInstance->SE->Blocking(fileno(log));
300
301                 if (buffer.size())
302                         fprintf(log,"%s",buffer.c_str());
303
304 #ifndef WINDOWS
305                 ServerInstance->SE->DelFd(this);
306 #endif
307
308                 fflush(log);
309                 fclose(log);
310         }
311
312         buffer.clear();
313 }
314
315 FileWriter::~FileWriter()
316 {
317         this->Close();
318 }
319
320