]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/logger.cpp
More <log> stuff
[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         std::map<std::string, std::vector<LogStream*> >().swap(LogStreams); /* Clear it */
105         std::vector<LogStream*>().swap(GlobalLogStreams); /* Clear it */
106         for (std::map<LogStream*, int>::iterator i = AllLogStreams.begin(); i != AllLogStreams.end(); ++i)
107         {
108                 delete i->first;
109         }
110         std::map<LogStream*, int>().swap(AllLogStreams); /* And clear it */
111
112         /* Now close FileLoggers, for those logstreams that neglected to properly free their stuff. */
113         for (FileLogMap::iterator it = FileLogs.begin(); it != FileLogs.end(); ++it)
114         {
115                 delete it->first;
116         }
117
118         FileLogMap().swap(FileLogs);
119 }
120
121 bool LogManager::AddLogType(const std::string &type, LogStream *l)
122 {
123         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
124
125         if (i != LogStreams.end())
126                 i->second.push_back(l);
127         else
128         {
129                 std::vector<LogStream *> v;
130                 v.push_back(l);
131                 LogStreams[type] = v;
132         }
133
134         if (type == "*")
135                 GlobalLogStreams.push_back(l);
136
137         std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
138         if (ai == AllLogStreams.end())
139         {
140                 AllLogStreams.insert(std::make_pair(l, 1));
141         }
142         else
143         {
144                 ++ai->second;
145         }
146
147         return true;
148 }
149
150 void LogManager::DelLogStream(LogStream* l)
151 {
152         for (std::map<std::string, std::vector<LogStream*> >::iterator i = LogStreams.begin(); i != LogStreams.end(); ++i)
153         {
154                 std::vector<LogStream*>::iterator it;
155                 while ((it = std::find(i->second.begin(), i->second.end(), l)) != i->second.end())
156                 {
157                         if (it == i->second.end()) continue;
158                         i->second.erase(it);
159                 }
160         }
161         std::vector<LogStream *>::iterator gi = std::find(GlobalLogStreams.begin(), GlobalLogStreams.end(), l);
162         if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
163         std::map<LogStream*, int>::iterator ai = AllLogStreams.begin();
164         if (ai == AllLogStreams.end()) return; /* Done. */
165         delete ai->first;
166         AllLogStreams.erase(ai);
167 }
168
169 bool LogManager::DelLogType(const std::string &type, LogStream *l)
170 {
171         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
172         if (type == "*")
173         {
174                 std::vector<LogStream *>::iterator gi = std::find(GlobalLogStreams.begin(), GlobalLogStreams.end(), l);
175                 if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
176         }
177
178         if (i != LogStreams.end())
179         {
180                 std::vector<LogStream *>::iterator it = std::find(i->second.begin(), i->second.end(), l);
181
182                 if (it != i->second.end())
183                 {
184                         i->second.erase(it);
185                         if (i->second.size() == 0)
186                         {
187                                 LogStreams.erase(i);
188                         }
189                 }
190                 else
191                 {
192                         return false;
193                 }
194         }
195         else
196         {
197                 return false;
198         }
199
200         std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
201         if (ai == AllLogStreams.end()) return true;
202
203         if ((--ai->second) < 1)
204         {
205                 AllLogStreams.erase(ai);
206                 delete l;
207         }
208
209         return true;
210 }
211
212 void LogManager::Log(const std::string &type, int loglevel, const char *fmt, ...)
213 {
214         if (Logging)
215                 return;
216
217         va_list a;
218         static char buf[65536];
219
220         va_start(a, fmt);
221         vsnprintf(buf, 65536, fmt, a);
222         va_end(a);
223
224         this->Log(type, loglevel, std::string(buf));
225 }
226
227 void LogManager::Log(const std::string &type, int loglevel, const std::string &msg)
228 {
229         if (Logging)
230                 return;
231
232         Logging = true;
233
234         std::vector<LogStream *>::iterator gi = GlobalLogStreams.begin();
235
236         while (gi != GlobalLogStreams.end())
237         {
238                 (*gi)->OnLog(loglevel, type, msg);
239                 gi++;
240         }
241
242         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
243
244         if (i != LogStreams.end())
245         {
246                 std::vector<LogStream *>::iterator it = i->second.begin();
247
248                 while (it != i->second.end())
249                 {
250                         (*it)->OnLog(loglevel, type, msg);
251                         it++;
252                 }
253         }
254
255         Logging = false;
256 }
257
258
259 FileWriter::FileWriter(InspIRCd* Instance, FILE* logfile)
260 : ServerInstance(Instance), log(logfile), writeops(0)
261 {
262         if (log)
263         {
264                 Instance->SE->NonBlocking(fileno(log));
265                 SetFd(fileno(log));
266                 buffer.clear();
267         }
268 }
269
270 bool FileWriter::Readable()
271 {
272         return false;
273 }
274     
275 void FileWriter::HandleEvent(EventType, int)
276 {
277         WriteLogLine("");
278         if (log)
279                 ServerInstance->SE->DelFd(this);
280 }
281
282 void FileWriter::WriteLogLine(const std::string &line)
283 {
284         if (line.length())
285                 buffer.append(line);
286
287         if (log)
288         {
289                 int written = fprintf(log,"%s",buffer.c_str());
290 #ifdef WINDOWS
291                 buffer.clear();
292 #else
293                 if ((written >= 0) && (written < (int)buffer.length()))
294                 {
295                         buffer.erase(0, buffer.length());
296                         ServerInstance->SE->AddFd(this);
297                 }
298                 else if (written == -1)
299                 {
300                         if (errno == EAGAIN)
301                                 ServerInstance->SE->AddFd(this);
302                 }
303                 else
304                 {
305                         /* Wrote the whole buffer, and no need for write callback */
306                         buffer.clear();
307                 }
308 #endif
309                 if (writeops++ % 20)
310                 {
311                         fflush(log);
312                 }
313         }
314 }
315
316 void FileWriter::Close()
317 {
318         if (log)
319         {
320                 ServerInstance->SE->Blocking(fileno(log));
321
322                 if (buffer.size())
323                         fprintf(log,"%s",buffer.c_str());
324
325 #ifndef WINDOWS
326                 ServerInstance->SE->DelFd(this);
327 #endif
328
329                 fflush(log);
330                 fclose(log);
331         }
332
333         buffer.clear();
334 }
335
336 FileWriter::~FileWriter()
337 {
338         this->Close();
339 }
340
341