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