]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/logger.cpp
Use ConfigTagList as a faster access method for access to configuration
[user/henk/code/inspircd.git] / src / logger.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15
16 #include "filelogger.h"
17
18 /*
19  * Suggested implementation...
20  *      class LogManager
21  *              bool AddLogType(const std::string &type, enum loglevel, LogStream *)
22  *              bool DelLogType(const std::string &type, LogStream *)
23  *              Log(const std::string &type, enum loglevel, const std::string &msg)
24  *              std::map<std::string, std::vector<LogStream *> > logstreams (holds a 'chain' of logstreams for each type that are all notified when a log happens)
25  *
26  *  class LogStream
27  *              std::string type
28  *      virtual void OnLog(enum loglevel, const std::string &msg)
29  *
30  * How it works:
31  *  Modules create their own logstream types (core will create one for 'file logging' for example) and create instances of these logstream types
32  *  and register interest in a certain logtype. Globbing is not here, with the exception of * - for all events.. loglevel is used to drop
33  *  events that are of no interest to a logstream.
34  *
35  *  When Log is called, the vector of logstreams for that type is iterated (along with the special vector for "*"), and all registered logstreams
36  *  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.
37  *  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.
38  *
39  * NOTE: Somehow we have to let LogManager manage the non-blocking file streams and provide an interface to share them with various LogStreams,
40  *       as, for example, a user may want to let 'KILL' and 'XLINE' snotices go to /home/ircd/inspircd/logs/operactions.log, or whatever. How
41  *       can we accomplish this easily? I guess with a map of pre-loved logpaths, and a pointer of FILE *..
42  *
43  */
44
45 LogManager::LogManager()
46 {
47         noforkstream = NULL;
48         Logging = false;
49 }
50
51 LogManager::~LogManager()
52 {
53         if (noforkstream)
54         {
55                 ServerInstance->Logs = this;
56                 delete noforkstream;
57                 ServerInstance->Logs = NULL;
58         }
59 }
60
61 void LogManager::SetupNoFork()
62 {
63         if (!noforkstream)
64         {
65                 FileWriter* fw = new FileWriter(stdout);
66                 noforkstream = new FileLogStream(ServerInstance->Config->forcedebug ? DEBUG : DEFAULT, fw);
67         }
68         else
69         {
70                 noforkstream->ChangeLevel(ServerInstance->Config->forcedebug ? DEBUG : DEFAULT);
71         }
72         AddLogType("*", noforkstream, false);
73 }
74
75 void LogManager::OpenFileLogs()
76 {
77         /* Re-register the nofork stream if necessary. */
78         if (ServerInstance->Config->nofork)
79         {
80                 SetupNoFork();
81         }
82         /* Skip rest of logfile opening if we are running -nolog. */
83         if (!ServerInstance->Config->writelog)
84         {
85                 return;
86         }
87         ConfigReader Conf;
88         std::map<std::string, FileWriter*> logmap;
89         ConfigTagList tags = ServerInstance->Config->ConfTags("log");
90         for(ConfigIter i = tags.first; i != tags.second; ++i)
91         {
92                 ConfigTag* tag = i->second;
93                 std::string method = tag->getString("method");
94                 if (method != "file")
95                 {
96                         continue;
97                 }
98                 std::string type = tag->getString("type");
99                 std::string level = tag->getString("level");
100                 int loglevel = DEFAULT;
101                 if (level == "debug" || ServerInstance->Config->forcedebug)
102                 {
103                         loglevel = DEBUG;
104                         ServerInstance->Config->debugging = true;
105                 }
106                 else if (level == "verbose")
107                 {
108                         loglevel = VERBOSE;
109                 }
110                 else if (level == "default")
111                 {
112                         loglevel = DEFAULT;
113                 }
114                 else if (level == "sparse")
115                 {
116                         loglevel = SPARSE;
117                 }
118                 else if (level == "none")
119                 {
120                         loglevel = NONE;
121                 }
122                 FileWriter* fw;
123                 std::string target = tag->getString("target");
124                 std::map<std::string, FileWriter*>::iterator fwi = logmap.find(target);
125                 if (fwi == logmap.end())
126                 {
127                         FILE* f = fopen(target.c_str(), "a");
128                         fw = new FileWriter(f);
129                         logmap.insert(std::make_pair(target, fw));
130                 }
131                 else
132                 {
133                         fw = fwi->second;
134                 }
135                 FileLogStream* fls = new FileLogStream(loglevel, fw);
136                 AddLogTypes(type, fls, true);
137         }
138 }
139
140 void LogManager::CloseLogs()
141 {
142         std::map<std::string, std::vector<LogStream*> >().swap(LogStreams); /* Clear it */
143         std::map<LogStream*, std::vector<std::string> >().swap(GlobalLogStreams); /* Clear it */
144         for (std::map<LogStream*, int>::iterator i = AllLogStreams.begin(); i != AllLogStreams.end(); ++i)
145         {
146                 delete i->first;
147         }
148         std::map<LogStream*, int>().swap(AllLogStreams); /* And clear it */
149 }
150
151 void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autoclose)
152 {
153         irc::spacesepstream css(types);
154         std::string tok;
155         std::vector<std::string> excludes;
156         while (css.GetToken(tok))
157         {
158                 if (tok.empty())
159                 {
160                         continue;
161                 }
162                 if (tok.at(0) == '-')
163                 {
164                         /* Exclude! */
165                         excludes.push_back(tok.substr(1));
166                 }
167                 else
168                 {
169                         AddLogType(tok, l, autoclose);
170                 }
171         }
172         // Handle doing things like: USERINPUT USEROUTPUT -USERINPUT should be the same as saying just USEROUTPUT.
173         // (This is so modules could, for example, inject exclusions for logtypes they can't handle.)
174         for (std::vector<std::string>::iterator i = excludes.begin(); i != excludes.end(); ++i)
175         {
176                 if (*i == "*")
177                 {
178                         /* -* == Exclude all. Why someone would do this, I dunno. */
179                         DelLogStream(l);
180                         return;
181                 }
182                 DelLogType(*i, l);
183         }
184         // Now if it's registered as a global, add the exclusions there too.
185         std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
186         if (gi != GlobalLogStreams.end())
187         {
188                 gi->second.swap(excludes); // Swap with the vector in the hash.
189         }
190 }
191
192 bool LogManager::AddLogType(const std::string &type, LogStream *l, bool autoclose)
193 {
194         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
195
196         if (i != LogStreams.end())
197         {
198                 i->second.push_back(l);
199         }
200         else
201         {
202                 std::vector<LogStream *> v;
203                 v.push_back(l);
204                 LogStreams[type] = v;
205         }
206
207         if (type == "*")
208         {
209                 GlobalLogStreams.insert(std::make_pair(l, std::vector<std::string>()));
210         }
211
212         if (autoclose)
213         {
214                 std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
215                 if (ai == AllLogStreams.end())
216                 {
217                         AllLogStreams.insert(std::make_pair(l, 1));
218                 }
219                 else
220                 {
221                         ++ai->second;
222                 }
223         }
224
225         return true;
226 }
227
228 void LogManager::DelLogStream(LogStream* l)
229 {
230         for (std::map<std::string, std::vector<LogStream*> >::iterator i = LogStreams.begin(); i != LogStreams.end(); ++i)
231         {
232                 std::vector<LogStream*>::iterator it;
233                 while ((it = std::find(i->second.begin(), i->second.end(), l)) != i->second.end())
234                 {
235                         if (it == i->second.end())
236                                 continue;
237                         i->second.erase(it);
238                 }
239         }
240         std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
241         if (gi != GlobalLogStreams.end())
242         {
243                 GlobalLogStreams.erase(gi);
244         }
245         std::map<LogStream*, int>::iterator ai = AllLogStreams.begin();
246         if (ai == AllLogStreams.end())
247         {
248                 return; /* Done. */
249         }
250         delete ai->first;
251         AllLogStreams.erase(ai);
252 }
253
254 bool LogManager::DelLogType(const std::string &type, LogStream *l)
255 {
256         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
257         if (type == "*")
258         {
259                 std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
260                 if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
261         }
262
263         if (i != LogStreams.end())
264         {
265                 std::vector<LogStream *>::iterator it = std::find(i->second.begin(), i->second.end(), l);
266
267                 if (it != i->second.end())
268                 {
269                         i->second.erase(it);
270                         if (i->second.size() == 0)
271                         {
272                                 LogStreams.erase(i);
273                         }
274                 }
275                 else
276                 {
277                         return false;
278                 }
279         }
280         else
281         {
282                 return false;
283         }
284
285         std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
286         if (ai == AllLogStreams.end())
287         {
288                 return true;
289         }
290
291         if ((--ai->second) < 1)
292         {
293                 AllLogStreams.erase(ai);
294                 delete l;
295         }
296
297         return true;
298 }
299
300 void LogManager::Log(const std::string &type, int loglevel, const char *fmt, ...)
301 {
302         if (Logging)
303         {
304                 return;
305         }
306
307         va_list a;
308         static char buf[65536];
309
310         va_start(a, fmt);
311         vsnprintf(buf, 65536, fmt, a);
312         va_end(a);
313
314         this->Log(type, loglevel, std::string(buf));
315 }
316
317 void LogManager::Log(const std::string &type, int loglevel, const std::string &msg)
318 {
319         if (Logging)
320         {
321                 return;
322         }
323
324         Logging = true;
325
326         for (std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.begin(); gi != GlobalLogStreams.end(); ++gi)
327         {
328                 if (std::find(gi->second.begin(), gi->second.end(), type) != gi->second.end())
329                 {
330                         continue;
331                 }
332                 gi->first->OnLog(loglevel, type, msg);
333         }
334
335         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
336
337         if (i != LogStreams.end())
338         {
339                 for (std::vector<LogStream *>::iterator it = i->second.begin(); it != i->second.end(); ++it)
340                 {
341                         (*it)->OnLog(loglevel, type, msg);
342                 }
343         }
344
345         Logging = false;
346 }
347
348
349 FileWriter::FileWriter(FILE* logfile)
350 : log(logfile), writeops(0)
351 {
352 }
353
354 void FileWriter::WriteLogLine(const std::string &line)
355 {
356         if (log == NULL)
357                 return;
358 // XXX: For now, just return. Don't throw an exception. It'd be nice to find out if this is happening, but I'm terrified of breaking so close to final release. -- w00t
359 //              throw CoreException("FileWriter::WriteLogLine called with a closed logfile");
360
361         fprintf(log,"%s",line.c_str());
362         if (writeops++ % 20)
363         {
364                 fflush(log);
365         }
366 }
367
368 FileWriter::~FileWriter()
369 {
370         if (log)
371         {
372                 fflush(log);
373                 fclose(log);
374                 log = NULL;
375         }
376 }