anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

logger.cpp (9989B)

      1 /* Logging routines.
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  */
     11 
     12 #include "services.h"
     13 #include "modules.h"
     14 #include "commands.h"
     15 #include "channels.h"
     16 #include "users.h"
     17 #include "logger.h"
     18 #include "config.h"
     19 #include "bots.h"
     20 #include "servers.h"
     21 #include "uplink.h"
     22 #include "protocol.h"
     23 
     24 #ifndef _WIN32
     25 #include <sys/time.h>
     26 #include <unistd.h>
     27 #endif
     28 
     29 static Anope::string GetTimeStamp()
     30 {
     31 	char tbuf[256];
     32 	time_t t;
     33 
     34 	if (time(&t) < 0)
     35 		t = Anope::CurTime;
     36 
     37 	tm tm = *localtime(&t);
     38 	if (Anope::Debug)
     39 	{
     40 		char *s;
     41 		struct timeval tv;
     42 		gettimeofday(&tv, NULL);
     43 		strftime(tbuf, sizeof(tbuf) - 1, "[%b %d %H:%M:%S", &tm);
     44 		s = tbuf + strlen(tbuf);
     45 		s += snprintf(s, sizeof(tbuf) - (s - tbuf), ".%06d", static_cast<int>(tv.tv_usec));
     46 		strftime(s, sizeof(tbuf) - (s - tbuf) - 1, " %Y]", &tm);
     47 	}
     48 	else
     49 		strftime(tbuf, sizeof(tbuf) - 1, "[%b %d %H:%M:%S %Y]", &tm);
     50 
     51 	return tbuf;
     52 }
     53 
     54 static inline Anope::string CreateLogName(const Anope::string &file, time_t t = Anope::CurTime)
     55 {
     56 	char timestamp[32];
     57 	tm *tm = localtime(&t);
     58 	strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm);
     59 
     60 	return Anope::LogDir + "/" + file + "." + timestamp;
     61 }
     62 
     63 LogFile::LogFile(const Anope::string &name) : filename(name), stream(name.c_str(), std::ios_base::out | std::ios_base::app)
     64 {
     65 }
     66 
     67 LogFile::~LogFile()
     68 {
     69 	this->stream.close();
     70 }
     71 
     72 const Anope::string &LogFile::GetName() const
     73 {
     74 	return this->filename;
     75 }
     76 
     77 Log::Log(LogType t, const Anope::string &cat, BotInfo *b) : bi(b), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(t), category(cat)
     78 {
     79 }
     80 
     81 Log::Log(LogType t, CommandSource &src, Command *_c, ChannelInfo *_ci) : u(src.GetUser()), nc(src.nc), c(_c), source(&src), chan(NULL), ci(_ci), s(NULL), m(NULL), type(t)
     82 {
     83 	if (!c)
     84 		throw CoreException("Invalid pointers passed to Log::Log");
     85 
     86 	if (type != LOG_COMMAND && type != LOG_OVERRIDE && type != LOG_ADMIN)
     87 		throw CoreException("This constructor does not support this log type");
     88 
     89 	size_t sl = c->name.find('/');
     90 	this->bi = NULL;
     91 	if (sl != Anope::string::npos)
     92 		this->bi = BotInfo::Find(c->name.substr(0, sl), true);
     93 	this->category = c->name;
     94 }
     95 
     96 Log::Log(User *_u, Channel *ch, const Anope::string &cat) : bi(NULL), u(_u), nc(NULL), c(NULL), source(NULL), chan(ch), ci(chan ? *chan->ci : NULL), s(NULL), m(NULL), type(LOG_CHANNEL), category(cat)
     97 {
     98 	if (!chan)
     99 		throw CoreException("Invalid pointers passed to Log::Log");
    100 }
    101 
    102 Log::Log(User *_u, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(_u), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_USER), category(cat)
    103 {
    104 	if (!u)
    105 		throw CoreException("Invalid pointers passed to Log::Log");
    106 }
    107 
    108 Log::Log(Server *serv, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(serv), m(NULL), type(LOG_SERVER), category(cat)
    109 {
    110 	if (!s)
    111 		throw CoreException("Invalid pointer passed to Log::Log");
    112 }
    113 
    114 Log::Log(BotInfo *b, const Anope::string &cat) : bi(b), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_NORMAL), category(cat)
    115 {
    116 }
    117 
    118 Log::Log(Module *mod, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(mod), type(LOG_MODULE), category(cat)
    119 {
    120 }
    121 
    122 Log::~Log()
    123 {
    124 	if (Anope::NoFork && Anope::Debug && this->type >= LOG_NORMAL && this->type <= LOG_DEBUG + Anope::Debug - 1)
    125 		std::cout << GetTimeStamp() << " Debug: " << this->BuildPrefix() << this->buf.str() << std::endl;
    126 	else if (Anope::NoFork && this->type <= LOG_TERMINAL)
    127 		std::cout << GetTimeStamp() << " " << this->BuildPrefix() << this->buf.str() << std::endl;
    128 	else if (this->type == LOG_TERMINAL)
    129 		std::cout << this->BuildPrefix() << this->buf.str() << std::endl;
    130 
    131 	FOREACH_MOD(OnLog, (this));
    132 
    133 	if (Config)
    134 		for (unsigned i = 0; i < Config->LogInfos.size(); ++i)
    135 			if (Config->LogInfos[i].HasType(this->type, this->category))
    136 				Config->LogInfos[i].ProcessMessage(this);
    137 }
    138 
    139 Anope::string Log::FormatSource() const
    140 {
    141 	if (u)
    142 		if (nc)
    143 			return this->u->GetMask() + " (" + this->nc->display + ")";
    144 		else
    145 			return this->u->GetMask();
    146 	else if (nc)
    147 		return nc->display;
    148 	else if (source)
    149 	{
    150 		Anope::string nickbuf = source->GetNick();
    151 		if (!nickbuf.empty() && !source->ip.empty())
    152 			nickbuf += " (" + source->ip + ")";
    153 		return nickbuf;
    154 	}
    155 	return "";
    156 }
    157 
    158 Anope::string Log::FormatCommand() const
    159 {
    160 	Anope::string buffer = FormatSource() + " used " + (source != NULL && !source->command.empty() ? source->command : this->c->name) + " ";
    161 	if (this->ci)
    162 		buffer += "on " + this->ci->name + " ";
    163 
    164 	return buffer;
    165 }
    166 
    167 Anope::string Log::BuildPrefix() const
    168 {
    169 	Anope::string buffer;
    170 
    171 	switch (this->type)
    172 	{
    173 		case LOG_ADMIN:
    174 		{
    175 			if (!this->c)
    176 				break;
    177 			buffer += "ADMIN: " + FormatCommand();
    178 			break;
    179 		}
    180 		case LOG_OVERRIDE:
    181 		{
    182 			if (!this->c)
    183 				break;
    184 			buffer += "OVERRIDE: " + FormatCommand();
    185 			break;
    186 		}
    187 		case LOG_COMMAND:
    188 		{
    189 			if (!this->c)
    190 				break;
    191 			buffer += "COMMAND: " + FormatCommand();
    192 			break;
    193 		}
    194 		case LOG_CHANNEL:
    195 		{
    196 			if (!this->chan)
    197 				break;
    198 			buffer += "CHANNEL: ";
    199 			Anope::string src = FormatSource();
    200 			if (!src.empty())
    201 				buffer += src + " ";
    202 			buffer += this->category + " " + this->chan->name + " ";
    203 			break;
    204 		}
    205 		case LOG_USER:
    206 		{
    207 			if (this->u)
    208 				buffer += "USERS: " + FormatSource() + " ";
    209 			break;
    210 		}
    211 		case LOG_SERVER:
    212 		{
    213 			if (this->s)
    214 				buffer += "SERVER: " + this->s->GetName() + " (" + this->s->GetDescription() + ") ";
    215 			break;
    216 		}
    217 		case LOG_MODULE:
    218 		{
    219 			if (this->m)
    220 				buffer += this->m->name.upper() + ": ";
    221 			break;
    222 		}
    223 		default:
    224 			break;
    225 	}
    226 
    227 	return buffer;
    228 }
    229 
    230 LogInfo::LogInfo(int la, bool rio, bool ldebug) : bot(NULL), last_day(0), log_age(la), raw_io(rio), debug(ldebug)
    231 {
    232 }
    233 
    234 LogInfo::~LogInfo()
    235 {
    236 	for (unsigned i = 0; i < this->logfiles.size(); ++i)
    237 		delete this->logfiles[i];
    238 	this->logfiles.clear();
    239 }
    240 
    241 bool LogInfo::HasType(LogType ltype, const Anope::string &type) const
    242 {
    243 	const std::vector<Anope::string> *list = NULL;
    244 	switch (ltype)
    245 	{
    246 		case LOG_ADMIN:
    247 			list = &this->admin;
    248 			break;
    249 		case LOG_OVERRIDE:
    250 			list = &this->override;
    251 			break;
    252 		case LOG_COMMAND:
    253 			list = &this->commands;
    254 			break;
    255 		case LOG_SERVER:
    256 			list = &this->servers;
    257 			break;
    258 		case LOG_CHANNEL:
    259 			list = &this->channels;
    260 			break;
    261 		case LOG_USER:
    262 			list = &this->users;
    263 			break;
    264 		case LOG_TERMINAL:
    265 			return true;
    266 		case LOG_RAWIO:
    267 			return (Anope::Debug || this->debug) ? true : this->raw_io;
    268 		case LOG_DEBUG:
    269 			return Anope::Debug ? true : this->debug;
    270 		case LOG_DEBUG_2:
    271 		case LOG_DEBUG_3:
    272 		case LOG_DEBUG_4:
    273 			break;
    274 		case LOG_MODULE:
    275 		case LOG_NORMAL:
    276 		default:
    277 			list = &this->normal;
    278 			break;
    279 	}
    280 
    281 	if (list == NULL)
    282 		return false;
    283 
    284 	for (unsigned i = 0; i < list->size(); ++i)
    285 	{
    286 		Anope::string cat = list->at(i);
    287 		bool inverse = false;
    288 		if (cat[0] == '~')
    289 		{
    290 			cat.erase(cat.begin());
    291 			inverse = true;
    292 		}
    293 		if (Anope::Match(type, cat))
    294 		{
    295 			return !inverse;
    296 		}
    297 	}
    298 
    299 	return false;
    300 }
    301 
    302 void LogInfo::OpenLogFiles()
    303 {
    304 	for (unsigned i = 0; i < this->logfiles.size(); ++i)
    305 		delete this->logfiles[i];
    306 	this->logfiles.clear();
    307 
    308 	for (unsigned i = 0; i < this->targets.size(); ++i)
    309 	{
    310 		const Anope::string &target = this->targets[i];
    311 
    312 		if (target.empty() || target[0] == '#' || target == "globops" || target.find(":") != Anope::string::npos)
    313 			continue;
    314 
    315 		LogFile *lf = new LogFile(CreateLogName(target));
    316 		if (!lf->stream.is_open())
    317 		{
    318 			Log() << "Unable to open logfile " << lf->GetName();
    319 			delete lf;
    320 		}
    321 		else
    322 			this->logfiles.push_back(lf);
    323 	}
    324 }
    325 
    326 void LogInfo::ProcessMessage(const Log *l)
    327 {
    328 	if (!this->sources.empty())
    329 	{
    330 		bool log = false;
    331 		for (unsigned i = 0; i < this->sources.size() && !log; ++i)
    332 		{
    333 			const Anope::string &src = this->sources[i];
    334 
    335 			if (l->bi && src == l->bi->nick)
    336 				log = true;
    337 			else if (l->u && src == l->u->nick)
    338 				log = true;
    339 			else if (l->nc && src == l->nc->display)
    340 				log = true;
    341 			else if (l->ci && src == l->ci->name)
    342 				log = true;
    343 			else if (l->m && src == l->m->name)
    344 				log = true;
    345 			else if (l->s && src == l->s->GetName())
    346 				log = true;
    347 		}
    348 		if (!log)
    349 			return;
    350 	}
    351 
    352 	const Anope::string &buffer = l->BuildPrefix() + l->buf.str();
    353 
    354 	FOREACH_MOD(OnLogMessage, (this, l, buffer));
    355 
    356 	for (unsigned i = 0; i < this->targets.size(); ++i)
    357 	{
    358 		const Anope::string &target = this->targets[i];
    359 
    360 		if (!target.empty() && target[0] == '#')
    361 		{
    362 			if (UplinkSock && l->type <= LOG_NORMAL && Me && Me->IsSynced())
    363 			{
    364 				Channel *c = Channel::Find(target);
    365 				if (!c)
    366 					continue;
    367 
    368 				BotInfo *bi = l->bi;
    369 				if (!bi)
    370 					bi = this->bot;
    371 				if (!bi)
    372 					bi = c->ci->WhoSends();
    373 				if (bi)
    374 					IRCD->SendPrivmsg(bi, c->name, "%s", buffer.c_str());
    375 			}
    376 		}
    377 		else if (target == "globops")
    378 		{
    379 			if (UplinkSock && l->type <= LOG_NORMAL && Me && Me->IsSynced())
    380 			{
    381 				BotInfo *bi = l->bi;
    382 				if (!bi)
    383 					bi = this->bot;
    384 				if (bi)
    385 					IRCD->SendGlobops(bi, "%s", buffer.c_str());
    386 			}
    387 		}
    388 	}
    389 
    390 	tm *tm = localtime(&Anope::CurTime);
    391 	if (tm->tm_mday != this->last_day)
    392 	{
    393 		this->last_day = tm->tm_mday;
    394 		this->OpenLogFiles();
    395 
    396 		if (this->log_age)
    397 			for (unsigned i = 0; i < this->targets.size(); ++i)
    398 			{
    399 				const Anope::string &target = this->targets[i];
    400 
    401 				if (target.empty() || target[0] == '#' || target == "globops" || target.find(":") != Anope::string::npos)
    402 					continue;
    403 
    404 				Anope::string oldlog = CreateLogName(target, Anope::CurTime - 86400 * this->log_age);
    405 				if (IsFile(oldlog))
    406 				{
    407 					unlink(oldlog.c_str());
    408 					Log(LOG_DEBUG) << "Deleted old logfile " << oldlog;
    409 				}
    410 			}
    411 	}
    412 
    413 	for (unsigned i = 0; i < this->logfiles.size(); ++i)
    414 	{
    415 		LogFile *lf = this->logfiles[i];
    416 		lf->stream << GetTimeStamp() << " " << buffer << std::endl;
    417 	}
    418 }