logger.cpp

Go to the documentation of this file.
00001 /* Logging routines.
00002  *
00003  * (C) 2003-2013 Anope Team
00004  * Contact us at team@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church.
00010  *
00011  */
00012 
00013 #include "services.h"
00014 #include "modules.h"
00015 #include "commands.h"
00016 #include "channels.h"
00017 #include "users.h"
00018 #include "logger.h"
00019 #include "config.h"
00020 #include "bots.h"
00021 #include "servers.h"
00022 #include "uplink.h"
00023 #include "protocol.h"
00024 #include "global.h"
00025 
00026 #ifndef _WIN32
00027 #include <sys/time.h>
00028 #include <unistd.h>
00029 #endif
00030 
00031 static Anope::string GetTimeStamp()
00032 {
00033         char tbuf[256];
00034         time_t t;
00035 
00036         if (time(&t) < 0)
00037                 t = Anope::CurTime;
00038 
00039         tm tm = *localtime(&t);
00040         if (Anope::Debug)
00041         {
00042                 char *s;
00043                 struct timeval tv;
00044                 gettimeofday(&tv, NULL);
00045                 strftime(tbuf, sizeof(tbuf) - 1, "[%b %d %H:%M:%S", &tm);
00046                 s = tbuf + strlen(tbuf);
00047                 s += snprintf(s, sizeof(tbuf) - (s - tbuf), ".%06d", static_cast<int>(tv.tv_usec));
00048                 strftime(s, sizeof(tbuf) - (s - tbuf) - 1, " %Y]", &tm);
00049         }
00050         else
00051                 strftime(tbuf, sizeof(tbuf) - 1, "[%b %d %H:%M:%S %Y]", &tm);
00052 
00053         return tbuf;
00054 }
00055 
00056 static inline Anope::string CreateLogName(const Anope::string &file, time_t t = Anope::CurTime)
00057 {
00058         char timestamp[32];
00059 
00060         tm *tm = localtime(&t);
00061         
00062         strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm);
00063 
00064         return Anope::LogDir + "/" + file + "." + timestamp;
00065 }
00066 
00067 LogFile::LogFile(const Anope::string &name) : filename(name), stream(name.c_str(), std::ios_base::out | std::ios_base::app)
00068 {
00069 }
00070 
00071 Anope::string LogFile::GetName() const
00072 {
00073         return this->filename;
00074 }
00075 
00076 Log::Log(LogType t, const Anope::string &cat, const BotInfo *b) : bi(b), u(NULL), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(NULL), type(t), category(cat)
00077 {
00078         if (!bi && Config)
00079                 bi = Global;
00080         if (bi)
00081                 this->sources.push_back(bi->nick);
00082 }
00083 
00084 Log::Log(LogType t, CommandSource &source, Command *_c, const ChannelInfo *_ci) : nick(source.GetNick()), u(source.GetUser()), nc(source.nc), c(_c), chan(NULL), ci(_ci), s(NULL), m(NULL), type(t)
00085 {
00086         if (!c)
00087                 throw CoreException("Invalid pointers passed to Log::Log");
00088         
00089         if (type != LOG_COMMAND && type != LOG_OVERRIDE && type != LOG_ADMIN)
00090                 throw CoreException("This constructor does not support this log type");
00091 
00092         size_t sl = c->name.find('/');
00093         this->bi = NULL;
00094         if (sl != Anope::string::npos)
00095                 this->bi = BotInfo::Find(c->name.substr(0, sl));
00096         if (this->bi == NULL && Config)
00097                 this->bi = Global;
00098         this->category = c->name;
00099         if (this->bi)
00100                 this->sources.push_back(this->bi->nick);
00101         if (u)
00102                 this->sources.push_back(u->nick);
00103         this->sources.push_back(c->name);
00104         if (ci)
00105                 this->sources.push_back(ci->name);
00106 }
00107 
00108 Log::Log(const User *_u, Channel *ch, const Anope::string &cat) : bi(NULL), u(_u), nc(NULL), c(NULL), chan(ch), ci(chan ? *chan->ci : NULL), s(NULL), m(NULL), type(LOG_CHANNEL), category(cat)
00109 {
00110         if (!chan)
00111                 throw CoreException("Invalid pointers passed to Log::Log");
00112         
00113         if (Config)
00114                 this->bi = ChanServ;
00115         if (this->bi)
00116                 this->sources.push_back(this->bi->nick);
00117         if (u)
00118                 this->sources.push_back(u->nick);
00119         this->sources.push_back(chan->name);
00120 }
00121 
00122 Log::Log(const User *_u, const Anope::string &cat, const BotInfo *_bi) : bi(_bi), u(_u), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_USER), category(cat)
00123 {
00124         if (!u)
00125                 throw CoreException("Invalid pointers passed to Log::Log");
00126         
00127         if (!this->bi && Config)
00128                 this->bi = Global;
00129         if (this->bi)
00130                 this->sources.push_back(this->bi->nick);
00131         this->sources.push_back(u->nick);
00132 }
00133 
00134 Log::Log(Server *serv, const Anope::string &cat, const BotInfo *_bi) : bi(_bi), u(NULL), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(serv), m(NULL), type(LOG_SERVER), category(cat)
00135 {
00136         if (!s)
00137                 throw CoreException("Invalid pointer passed to Log::Log");
00138         
00139         if (!this->bi && Config)
00140                 this->bi = OperServ;
00141         if (!this->bi && Config)
00142                 this->bi = Global;
00143         if (this->bi)
00144                 this->sources.push_back(this->bi->nick);
00145         this->sources.push_back(s->GetName());
00146 }
00147 
00148 Log::Log(const BotInfo *b, const Anope::string &cat) : bi(b), u(NULL), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_NORMAL), category(cat)
00149 {
00150         if (!this->bi && Config)
00151                 this->bi = Global;
00152         if (this->bi)
00153                 this->sources.push_back(bi->nick);
00154 }
00155 
00156 Log::Log(Module *mod, const Anope::string &cat) : bi(NULL), u(NULL), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(NULL), m(mod), type(LOG_MODULE), category(cat)
00157 {
00158         if (m)
00159                 this->sources.push_back(m->name);
00160 }
00161 
00162 Log::~Log()
00163 {
00164         if (Anope::NoFork && Anope::Debug && this->type >= LOG_NORMAL && this->type <= LOG_DEBUG + Anope::Debug - 1)
00165                 std::cout << GetTimeStamp() << " Debug: " << this->BuildPrefix() << this->buf.str() << std::endl;
00166         else if (Anope::NoFork && this->type <= LOG_TERMINAL)
00167                 std::cout << GetTimeStamp() << " " << this->BuildPrefix() << this->buf.str() << std::endl;
00168         else if (this->type == LOG_TERMINAL)
00169                 std::cout << this->BuildPrefix() << this->buf.str() << std::endl;
00170         for (unsigned i = 0; Config && i < Config->LogInfos.size(); ++i)
00171         {
00172                 LogInfo *l = Config->LogInfos[i];
00173                 l->ProcessMessage(this);
00174         }
00175         FOREACH_MOD(I_OnLog, OnLog(this));
00176 }
00177 
00178 Anope::string Log::BuildPrefix() const
00179 {
00180         Anope::string buffer;
00181 
00182         switch (this->type)
00183         {
00184                 case LOG_ADMIN:
00185                 {
00186                         if (!this->c && !(this->u || this->nc))
00187                                 break;
00188                         buffer += "ADMIN: ";
00189                         size_t sl = this->c->name.find('/');
00190                         Anope::string cname = sl != Anope::string::npos ? this->c->name.substr(sl + 1) : this->c->name;
00191                         if (this->u)
00192                                 buffer += this->u->GetMask() + " used " + cname + " ";
00193                         else if (this->nc)
00194                                 buffer += this->nc->display + " used " + cname + " ";
00195                         if (this->ci)
00196                                 buffer += "on " + this->ci->name + " ";
00197                         break;
00198                 }
00199                 case LOG_OVERRIDE:
00200                 {
00201                         if (!this->c && !(this->u || this->nc))
00202                                 break;
00203                         buffer += "OVERRIDE: ";
00204                         size_t sl = this->c->name.find('/');
00205                         Anope::string cname = sl != Anope::string::npos ? this->c->name.substr(sl + 1) : this->c->name;
00206                         if (this->u)
00207                                 buffer += this->u->GetMask() + " used " + cname + " ";
00208                         else if (this->nc)
00209                                 buffer += this->nc->display + " used " + cname + " ";
00210                         if (this->ci)
00211                                 buffer += "on " + this->ci->name + " ";
00212                         break;
00213                 }
00214                 case LOG_COMMAND:
00215                 {
00216                         if (!this->c)
00217                                 break;
00218                         buffer += "COMMAND: ";
00219                         size_t sl = this->c->name.find('/');
00220                         Anope::string cname = sl != Anope::string::npos ? this->c->name.substr(sl + 1) : this->c->name;
00221                         if (this->u)
00222                                 buffer += this->u->GetMask() + " used " + cname + " ";
00223                         else if (this->nc)
00224                                 buffer += this->nc->display + " used " + cname + " ";
00225                         else
00226                                 buffer += this->nick + " used " + cname + " ";
00227                         if (this->ci)
00228                                 buffer += "on " + this->ci->name + " ";
00229                         break;
00230                 }
00231                 case LOG_CHANNEL:
00232                 {
00233                         if (!this->chan)
00234                                 break;
00235                         buffer += "CHANNEL: ";
00236                         if (this->u)
00237                                 buffer += this->u->GetMask() + " " + this->category + " " + this->chan->name + " ";
00238                         else
00239                                 buffer += this->category + " " + this->chan->name + " ";
00240                         break;
00241                 }
00242                 case LOG_USER:
00243                 {
00244                         if (this->u)
00245                                 buffer += "USERS: " + this->u->GetMask() + " ";
00246                         break;
00247                 }
00248                 case LOG_SERVER:
00249                 {
00250                         if (this->s)
00251                                 buffer += "SERVER: " + this->s->GetName() + " (" + this->s->GetDescription() + ") ";
00252                         break;
00253                 }
00254                 case LOG_MODULE:
00255                 {
00256                         if (this->m)
00257                                 buffer += this->m->name.upper() + ": ";
00258                         break;
00259                 }
00260                 default:
00261                         break;
00262         }
00263 
00264         return buffer;
00265 }
00266 
00267 LogInfo::LogInfo(int la, bool rio, bool ldebug) : log_age(la), raw_io(rio), debug(ldebug)
00268 {
00269 }
00270 
00271 LogInfo::~LogInfo()
00272 {
00273         for (std::map<Anope::string, LogFile *>::iterator it = this->logfiles.begin(), it_end = this->logfiles.end(); it != it_end; ++it)
00274         {
00275                 LogFile *f = it->second;
00276 
00277                 if (f && f->stream.is_open())
00278                         f->stream.close();
00279                 delete f;
00280         }
00281         this->logfiles.clear();
00282 }
00283 
00284 void LogInfo::AddType(std::list<Anope::string> &list, const Anope::string &type)
00285 {
00286         for (std::list<Anope::string>::iterator it = list.begin(), it_end = list.end(); it != it_end; ++it)
00287         {
00288                 if (Anope::Match(type, *it))
00289                 {
00290                         Log() << "Log: Type " << type << " is already covered by " << *it;
00291                         return;
00292                 }
00293         }
00294 
00295         list.push_back(type);
00296 }
00297 
00298 bool LogInfo::HasType(LogType ltype, const Anope::string &type) const
00299 {
00300         const std::list<Anope::string> *list = NULL;
00301         switch (ltype)
00302         {
00303                 case LOG_ADMIN:
00304                         list = &this->admin;
00305                         break;
00306                 case LOG_OVERRIDE:
00307                         list = &this->override;
00308                         break;
00309                 case LOG_COMMAND:
00310                         list = &this->commands;
00311                         break;
00312                 case LOG_SERVER:
00313                         list = &this->servers;
00314                         break;
00315                 case LOG_CHANNEL:
00316                         list = &this->channels;
00317                         break;
00318                 case LOG_USER:
00319                         list = &this->users;
00320                         break;
00321                 case LOG_TERMINAL:
00322                         return true;
00323                 case LOG_RAWIO:
00324                         return (Anope::Debug || this->debug) ? true : this->raw_io;
00325                 case LOG_DEBUG:
00326                         return Anope::Debug ? true : this->debug;
00327                 case LOG_DEBUG_2:
00328                 case LOG_DEBUG_3:
00329                 case LOG_DEBUG_4:
00330                         break;
00331                 case LOG_MODULE:
00332                 case LOG_NORMAL:
00333                 default:
00334                         list = &this->normal;
00335                         break;
00336         }
00337 
00338         if (list == NULL)
00339                 return false;
00340 
00341         for (std::list<Anope::string>::const_iterator it = list->begin(), it_end = list->end(); it != it_end; ++it)
00342         {
00343                 Anope::string cat = *it;
00344                 bool inverse = false;
00345                 if (cat[0] == '~')
00346                 {
00347                         cat.erase(cat.begin());
00348                         inverse = true;
00349                 }
00350                 if (Anope::Match(type, cat))
00351                 {
00352                         return !inverse;
00353                 }
00354         }
00355 
00356         return false;
00357 }
00358 
00359 void LogInfo::ProcessMessage(const Log *l)
00360 {
00361         static time_t lastwarn = Anope::CurTime;
00362 
00363         if (!l)
00364                 throw CoreException("Bad values passed to LogInfo::ProcessMessages");
00365         else if (!this->HasType(l->type, l->category))
00366                 return;
00367         
00368         if (!this->sources.empty())
00369         {
00370                 bool log = false;
00371                 for (std::list<Anope::string>::const_iterator it = this->sources.begin(), it_end = this->sources.end(); it != it_end; ++it)
00372                 {
00373                         if (std::find(l->sources.begin(), l->sources.end(), *it) != l->sources.end())
00374                         {
00375                                 log = true;
00376                                 break;
00377                         }
00378                 }
00379                 if (!log)
00380                         return;
00381         }
00382 
00383         for (std::list<Anope::string>::iterator it = this->targets.begin(), it_end = this->targets.end(); it != it_end; ++it)
00384         {
00385                 const Anope::string &target = *it;
00386                 Anope::string buffer = l->BuildPrefix() + l->buf.str();
00387 
00388                 if (target[0] == '#')
00389                 {
00390                         if (UplinkSock && l->type <= LOG_NORMAL && Me && Me->IsSynced())
00391                         {
00392                                 Channel *c = Channel::Find(target);
00393                                 if (!c || !l->bi)
00394                                         continue;
00395                                 IRCD->SendPrivmsg(l->bi, c->name, "%s", buffer.c_str());
00396                         }
00397                 }
00398                 else if (target == "globops")
00399                 {
00400                         if (UplinkSock && l->bi && l->type <= LOG_NORMAL && Me && Me->IsSynced())
00401                         {
00402                                 IRCD->SendGlobops(l->bi, "%s", buffer.c_str());
00403                         }
00404                 }
00405                 else
00406                 {
00407                         LogFile *log = NULL;
00408                         std::map<Anope::string, LogFile *>::iterator lit = this->logfiles.find(target);
00409                         if (lit != this->logfiles.end())
00410                         {
00411                                 log = lit->second;
00412                                 if (log && log->GetName() != CreateLogName(target))
00413                                 {
00414                                         delete log;
00415                                         this->logfiles.erase(lit);
00416                                         log = new LogFile(CreateLogName(target));
00417                                         this->logfiles[target] = log;
00418 
00419                                         if (this->log_age)
00420                                         {
00421                                                 Anope::string oldlog = CreateLogName(target, Anope::CurTime - 86400 * this->log_age);
00422                                                 if (IsFile(oldlog))
00423                                                 {
00424                                                         unlink(oldlog.c_str());
00425                                                         Log(LOG_DEBUG) << "Deleted old logfile " << oldlog;
00426                                                 }
00427                                         }
00428                                 }
00429                                 if (!log || !log->stream.is_open())
00430                                 {
00431                                         if (log && lastwarn + 300 < Anope::CurTime)
00432                                         {
00433                                                 lastwarn = Anope::CurTime;
00434                                                 Log() << "Unable to open logfile " << log->GetName();
00435                                         }
00436                                         delete log;
00437                                         this->logfiles.erase(lit);
00438                                         log = NULL;
00439                                 }
00440                         }
00441                         else if (lit == this->logfiles.end())
00442                         {
00443                                 log = new LogFile(CreateLogName(target));
00444 
00445                                 if (!log->stream.is_open())
00446                                 {
00447                                         if (lastwarn + 300 < Anope::CurTime)
00448                                         {
00449                                                 lastwarn = Anope::CurTime;
00450                                                 Log() << "Unable to open logfile " << log->GetName();
00451                                         }
00452                                         delete log;
00453                                         log = NULL;
00454                                 }
00455                                 else
00456                                         this->logfiles[target] = log;
00457                         }
00458 
00459                         if (log)
00460                                 log->stream << GetTimeStamp() << " " << buffer << std::endl;
00461                 }
00462         }
00463 }
00464