The InspIRCd Project
Home | Developers | Wiki | Forums | Bug Tracker | SVN | Download
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

logger.cpp

Go to the documentation of this file.
00001 /*       +------------------------------------+
00002  *       | Inspire Internet Relay Chat Daemon |
00003  *       +------------------------------------+
00004  *
00005  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
00006  * See: http://www.inspircd.org/wiki/index.php/Credits
00007  *
00008  * This program is free but copyrighted software; see
00009  *            the file COPYING for details.
00010  *
00011  * ---------------------------------------------------
00012  */
00013 
00014 /* $Core */
00015 
00016 #include "inspircd.h"
00017 
00018 #include "filelogger.h"
00019 
00020 /*
00021  * Suggested implementation...
00022  *      class LogManager
00023  *              bool AddLogType(const std::string &type, enum loglevel, LogStream *)
00024  *              bool DelLogType(const std::string &type, LogStream *)
00025  *              Log(const std::string &type, enum loglevel, const std::string &msg)
00026  *              std::map<std::string, std::vector<LogStream *> > logstreams (holds a 'chain' of logstreams for each type that are all notified when a log happens)
00027  *
00028  *  class LogStream
00029  *              std::string type
00030  *      virtual void OnLog(enum loglevel, const std::string &msg)
00031  *
00032  * How it works:
00033  *  Modules create their own logstream types (core will create one for 'file logging' for example) and create instances of these logstream types
00034  *  and register interest in a certain logtype. Globbing is not here, with the exception of * - for all events.. loglevel is used to drop 
00035  *  events that are of no interest to a logstream.
00036  *
00037  *  When Log is called, the vector of logstreams for that type is iterated (along with the special vector for "*"), and all registered logstreams
00038  *  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.
00039  *  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.
00040  *
00041  * NOTE: Somehow we have to let LogManager manage the non-blocking file streams and provide an interface to share them with various LogStreams,
00042  *       as, for example, a user may want to let 'KILL' and 'XLINE' snotices go to /home/ircd/inspircd/logs/operactions.log, or whatever. How
00043  *       can we accomplish this easily? I guess with a map of pre-loved logpaths, and a pointer of FILE *..
00044  * 
00045  */
00046 
00047 void LogManager::SetupNoFork()
00048 {
00049         if (!noforkstream)
00050         {
00051                 FileWriter* fw = new FileWriter(ServerInstance, stdout);
00052                 noforkstream = new FileLogStream(ServerInstance, ServerInstance->Config->forcedebug ? DEBUG : DEFAULT, fw);
00053         }
00054         else
00055         {
00056                 noforkstream->ChangeLevel(ServerInstance->Config->forcedebug ? DEBUG : DEFAULT);
00057         }
00058         AddLogType("*", noforkstream, false);
00059 }
00060 
00061 void LogManager::OpenFileLogs()
00062 {
00063         /* Re-register the nofork stream if necessary. */
00064         if (ServerInstance->Config->nofork)
00065         {
00066                 SetupNoFork();
00067         }
00068         /* Skip rest of logfile opening if we are running -nolog. */
00069         if (!ServerInstance->Config->writelog)
00070         {
00071                 return;
00072         }
00073         ConfigReader* Conf = new ConfigReader(ServerInstance);
00074         std::map<std::string, FileWriter*> logmap;
00075         std::map<std::string, FileWriter*>::iterator i;
00076         for (int index = 0; index < Conf->Enumerate("log"); ++index)
00077         {
00078                 std::string method = Conf->ReadValue("log", "method", index);
00079                 if (method != "file")
00080                 {
00081                         continue;
00082                 }
00083                 std::string type = Conf->ReadValue("log", "type", index);
00084                 std::string level = Conf->ReadValue("log", "level", index);
00085                 int loglevel = DEFAULT;
00086                 if (level == "debug" || ServerInstance->Config->forcedebug)
00087                 {
00088                         loglevel = DEBUG;
00089                         ServerInstance->Config->debugging = true;
00090                 }
00091                 else if (level == "verbose")
00092                 {
00093                         loglevel = VERBOSE;
00094                 }
00095                 else if (level == "default")
00096                 {
00097                         loglevel = DEFAULT;
00098                 }
00099                 else if (level == "sparse")
00100                 {
00101                         loglevel = SPARSE;
00102                 }
00103                 else if (level == "none")
00104                 {
00105                         loglevel = NONE;
00106                 }
00107                 FileWriter* fw;
00108                 std::string target = Conf->ReadValue("log", "target", index);
00109                 if ((i = logmap.find(target)) == logmap.end())
00110                 {
00111                         FILE* f = fopen(target.c_str(), "a");
00112                         fw = new FileWriter(ServerInstance, f);
00113                         logmap.insert(std::make_pair(target, fw));
00114                 }
00115                 else
00116                 {
00117                         fw = i->second;
00118                 }
00119                 FileLogStream* fls = new FileLogStream(ServerInstance, loglevel, fw);
00120                 AddLogTypes(type, fls, true);
00121         }
00122 }
00123 
00124 void LogManager::CloseLogs()
00125 {
00126         std::map<std::string, std::vector<LogStream*> >().swap(LogStreams); /* Clear it */
00127         std::map<LogStream*, std::vector<std::string> >().swap(GlobalLogStreams); /* Clear it */
00128         for (std::map<LogStream*, int>::iterator i = AllLogStreams.begin(); i != AllLogStreams.end(); ++i)
00129         {
00130                 delete i->first;
00131         }
00132         std::map<LogStream*, int>().swap(AllLogStreams); /* And clear it */
00133 }
00134 
00135 void LogManager::AddLogTypes(const std::string &types, LogStream* l, bool autoclose)
00136 {
00137         irc::spacesepstream css(types);
00138         std::string tok;
00139         std::vector<std::string> excludes;
00140         while (css.GetToken(tok))
00141         {
00142                 if (tok.empty())
00143                 {
00144                         continue;
00145                 }
00146                 if (tok.at(0) == '-')
00147                 {
00148                         /* Exclude! */
00149                         excludes.push_back(tok.substr(1));
00150                 }
00151                 else
00152                 {
00153                         AddLogType(tok, l, autoclose);
00154                 }
00155         }
00156         // Handle doing things like: USERINPUT USEROUTPUT -USERINPUT should be the same as saying just USEROUTPUT.
00157         // (This is so modules could, for example, inject exclusions for logtypes they can't handle.)
00158         for (std::vector<std::string>::iterator i = excludes.begin(); i != excludes.end(); ++i)
00159         {
00160                 if (*i == "*")
00161                 {
00162                         /* -* == Exclude all. Why someone would do this, I dunno. */
00163                         DelLogStream(l);
00164                         return;
00165                 }
00166                 DelLogType(*i, l);
00167         }
00168         // Now if it's registered as a global, add the exclusions there too.
00169         std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
00170         if (gi != GlobalLogStreams.end())
00171         {
00172                 gi->second.swap(excludes); // Swap with the vector in the hash.
00173         }
00174 }
00175 
00176 bool LogManager::AddLogType(const std::string &type, LogStream *l, bool autoclose)
00177 {
00178         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
00179 
00180         if (i != LogStreams.end())
00181         {
00182                 i->second.push_back(l);
00183         }
00184         else
00185         {
00186                 std::vector<LogStream *> v;
00187                 v.push_back(l);
00188                 LogStreams[type] = v;
00189         }
00190 
00191         if (type == "*")
00192         {
00193                 GlobalLogStreams.insert(std::make_pair(l, std::vector<std::string>()));
00194         }
00195 
00196         if (autoclose)
00197         {
00198                 std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
00199                 if (ai == AllLogStreams.end())
00200                 {
00201                         AllLogStreams.insert(std::make_pair(l, 1));
00202                 }
00203                 else
00204                 {
00205                         ++ai->second;
00206                 }
00207         }
00208 
00209         return true;
00210 }
00211 
00212 void LogManager::DelLogStream(LogStream* l)
00213 {
00214         for (std::map<std::string, std::vector<LogStream*> >::iterator i = LogStreams.begin(); i != LogStreams.end(); ++i)
00215         {
00216                 std::vector<LogStream*>::iterator it;
00217                 while ((it = std::find(i->second.begin(), i->second.end(), l)) != i->second.end())
00218                 {
00219                         if (it == i->second.end())
00220                                 continue;
00221                         i->second.erase(it);
00222                 }
00223         }
00224         std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
00225         if (gi != GlobalLogStreams.end())
00226         {
00227                 GlobalLogStreams.erase(gi);
00228         }
00229         std::map<LogStream*, int>::iterator ai = AllLogStreams.begin();
00230         if (ai == AllLogStreams.end())
00231         {
00232                 return; /* Done. */
00233         }
00234         delete ai->first;
00235         AllLogStreams.erase(ai);
00236 }
00237 
00238 bool LogManager::DelLogType(const std::string &type, LogStream *l)
00239 {
00240         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
00241         if (type == "*")
00242         {
00243                 std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.find(l);
00244                 if (gi != GlobalLogStreams.end()) GlobalLogStreams.erase(gi);
00245         }
00246 
00247         if (i != LogStreams.end())
00248         {
00249                 std::vector<LogStream *>::iterator it = std::find(i->second.begin(), i->second.end(), l);
00250 
00251                 if (it != i->second.end())
00252                 {
00253                         i->second.erase(it);
00254                         if (i->second.size() == 0)
00255                         {
00256                                 LogStreams.erase(i);
00257                         }
00258                 }
00259                 else
00260                 {
00261                         return false;
00262                 }
00263         }
00264         else
00265         {
00266                 return false;
00267         }
00268 
00269         std::map<LogStream*, int>::iterator ai = AllLogStreams.find(l);
00270         if (ai == AllLogStreams.end())
00271         {
00272                 return true;
00273         }
00274 
00275         if ((--ai->second) < 1)
00276         {
00277                 AllLogStreams.erase(ai);
00278                 delete l;
00279         }
00280 
00281         return true;
00282 }
00283 
00284 void LogManager::Log(const std::string &type, int loglevel, const char *fmt, ...)
00285 {
00286         if (Logging)
00287         {
00288                 return;
00289         }
00290 
00291         va_list a;
00292         static char buf[65536];
00293 
00294         va_start(a, fmt);
00295         vsnprintf(buf, 65536, fmt, a);
00296         va_end(a);
00297 
00298         this->Log(type, loglevel, std::string(buf));
00299 }
00300 
00301 void LogManager::Log(const std::string &type, int loglevel, const std::string &msg)
00302 {
00303         if (Logging)
00304         {
00305                 return;
00306         }
00307 
00308         Logging = true;
00309 
00310         for (std::map<LogStream *, std::vector<std::string> >::iterator gi = GlobalLogStreams.begin(); gi != GlobalLogStreams.end(); ++gi)
00311         {
00312                 if (std::find(gi->second.begin(), gi->second.end(), type) != gi->second.end())
00313                 {
00314                         continue;
00315                 }
00316                 gi->first->OnLog(loglevel, type, msg);
00317         }
00318 
00319         std::map<std::string, std::vector<LogStream *> >::iterator i = LogStreams.find(type);
00320 
00321         if (i != LogStreams.end())
00322         {
00323                 for (std::vector<LogStream *>::iterator it = i->second.begin(); it != i->second.end(); ++it)
00324                 {
00325                         (*it)->OnLog(loglevel, type, msg);
00326                 }
00327         }
00328 
00329         Logging = false;
00330 }
00331 
00332 
00333 FileWriter::FileWriter(InspIRCd* Instance, FILE* logfile)
00334 : ServerInstance(Instance), log(logfile), writeops(0)
00335 {
00336 }
00337 
00338 void FileWriter::HandleEvent(EventType ev, int)
00339 {
00340 }
00341 
00342 void FileWriter::WriteLogLine(const std::string &line)
00343 {
00344         if (log)
00345         {
00346                 fprintf(log,"%s",line.c_str());
00347                 if (writeops++ % 20)
00348                 {
00349                         fflush(log);
00350                 }
00351         }
00352 }
00353 
00354 void FileWriter::Close()
00355 {
00356         if (log)
00357         {
00358                 fflush(log);
00359                 fclose(log);
00360         }
00361 }
00362 
00363 FileWriter::~FileWriter()
00364 {
00365 }
00366