core/Logger.cpp
author Michael McKoy <ferret99gt@gmail.com>
Wed Aug 08 23:45:24 2007 +0000 (2007-08-08)
changeset 1284 ac293d8351a3
parent 1231 17badaf66ce4
child 1330 ab94316bb52b
permissions -rw-r--r--
Import of baseplugins.sp
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * ================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or 
     9  * modify it under the terms of the GNU General Public License, 
    10  * version 3.0, as published by the Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful,
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  * GNU General Public License for more details.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to 
    21  * link the code of this program (as well as its derivative works) to 
    22  * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
    23  * Game MODs that run on software by the Valve Corporation.  You must 
    24  * obey the GNU General Public License in all respects for all other 
    25  * code used. Additionally, AlliedModders LLC grants this exception 
    26  * to all derivative works. AlliedModders LLC defines further 
    27  * exceptions, found in LICENSE.txt (as of this writing, version 
    28  * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
    29  *
    30  * Version: $Id$
    31  */
    32 
    33 #include <time.h>
    34 #include "sourcemod.h"
    35 #include "sourcemm_api.h"
    36 #include "sm_stringutil.h"
    37 #include "Logger.h"
    38 #include "systems/LibrarySys.h"
    39 #include "sm_version.h"
    40 
    41 Logger g_Logger;
    42 
    43 /**
    44  * :TODO: This should be creating the log folder if it doesn't exist
    45  */
    46 
    47 ConfigResult Logger::OnSourceModConfigChanged(const char *key, 
    48 									  const char *value, 
    49 									  ConfigSource source,
    50 									  char *error, 
    51 									  size_t maxlength)
    52 {
    53 	if (strcasecmp(key, "Logging") == 0)
    54 	{
    55 		bool state;
    56 
    57 		if (strcasecmp(value, "on") == 0)
    58 		{
    59 			state = true;
    60 		} else if (strcasecmp(value, "off") == 0) {
    61 			state = false;
    62 		} else {
    63 			UTIL_Format(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
    64 			return ConfigResult_Reject;
    65 		}
    66 
    67 		if (source == ConfigSource_Console)
    68 		{
    69 			state ? EnableLogging() : DisableLogging();
    70 		} else {
    71 			m_InitialState = state;
    72 		}
    73 
    74 		return ConfigResult_Accept;
    75 	} else if (strcasecmp(key, "LogMode") == 0) {
    76 		if (strcasecmp(value, "daily") == 0) 
    77 		{
    78 			m_Mode = LoggingMode_Daily;
    79 		} else if (strcasecmp(value, "map") == 0) {
    80 			m_Mode = LoggingMode_PerMap;
    81 		} else if (strcasecmp(value, "game") == 0) {
    82 			m_Mode = LoggingMode_Game;
    83 		} else {
    84 			UTIL_Format(error, maxlength, "Invalid value: must be [daily|map|game]");
    85 			return ConfigResult_Reject;
    86 		}
    87 
    88 		return ConfigResult_Accept;
    89 	}
    90 
    91 	return ConfigResult_Ignore;
    92 }
    93 
    94 void Logger::OnSourceModStartup(bool late)
    95 {
    96 	InitLogger(m_Mode);
    97 }
    98 
    99 void Logger::OnSourceModAllShutdown()
   100 {
   101 	CloseLogger();
   102 }
   103 
   104 void Logger::OnSourceModLevelChange(const char *mapName)
   105 {
   106 	MapChange(mapName);
   107 }
   108 
   109 void Logger::_NewMapFile()
   110 {
   111 	if (!m_Active)
   112 	{
   113 		return;
   114 	}
   115 
   116 	/* Append "Log file closed" to previous log file */
   117 	_CloseFile();
   118 	
   119 	char _filename[256];
   120 	int i = 0;
   121 
   122 	time_t t;
   123 	time(&t);
   124 	tm *curtime = localtime(&t);
   125 
   126 	while (true)
   127 	{
   128 		g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%02d%02d%03d.log", curtime->tm_mon + 1, curtime->tm_mday, i);
   129 		FILE *fp = fopen(_filename, "r");
   130 		if (!fp)
   131 		{
   132 			break;
   133 		}
   134 		fclose(fp);
   135 		i++;
   136 	}
   137 	m_NrmFileName.assign(_filename);
   138 
   139 	FILE *fp = fopen(m_NrmFileName.c_str(), "w");
   140 	if (!fp)
   141 	{
   142 		g_SMAPI->ConPrint("[SM] Unexpected fatal logging error. SourceMod logging disabled.\n");
   143 		m_Active = false;
   144 		return;
   145 	} else {
   146 		char date[32];
   147 		strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
   148 		fprintf(fp, "L %s: SourceMod log file started (file \"L%02d%02d%03d.log\") (Version \"%s\")\n", date, curtime->tm_mon + 1, curtime->tm_mday, i, SVN_FULL_VERSION);
   149 		fclose(fp);
   150 	}
   151 }
   152 
   153 void Logger::_CloseFile()
   154 {
   155 	if (!m_Active)
   156 	{
   157 		return;
   158 	}
   159 
   160 	FILE *fp = NULL;
   161 	if (!m_NrmFileName.empty())
   162 	{
   163 		fp = fopen(m_NrmFileName.c_str(), "r+");
   164 		if (fp)
   165 		{
   166 			fseek(fp, 0, SEEK_END);
   167 			LogMessage("Log file closed.");
   168 			fclose(fp);
   169 		}
   170 		m_NrmFileName.clear();
   171 	}
   172 
   173 	if (!m_ErrMapStart)
   174 	{
   175 		return;
   176 	}
   177 	fp = fopen(m_ErrFileName.c_str(), "r+");
   178 	if (fp)
   179 	{
   180 		fseek(fp, 0, SEEK_END);
   181 		LogError("Error log file session closed.");
   182 		fclose(fp);
   183 	}
   184 	m_ErrFileName.clear();
   185 }
   186 
   187 void Logger::InitLogger(LoggingMode mode)
   188 {
   189 	m_Mode = mode;
   190 	m_Active = m_InitialState;
   191 
   192 	time_t t;
   193 	time(&t);
   194 	tm *curtime = localtime(&t);
   195 	m_CurDay = curtime->tm_mday;
   196 
   197 	char _filename[256];
   198 	g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
   199 	m_ErrFileName.assign(_filename);
   200 
   201 	switch (m_Mode)
   202 	{
   203 	case LoggingMode_PerMap:
   204 		{
   205 			if (!m_Active)
   206 			{
   207 				m_DelayedStart = true;
   208 			}
   209 			break;
   210 		}
   211 	case LoggingMode_Daily:
   212 		{
   213 			g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
   214 			m_NrmFileName.assign(_filename);
   215 			m_DailyPrintHdr = true;
   216 			break;
   217 		}
   218 	default:
   219 		{
   220 			/* do nothing... */
   221 			break;
   222 		}
   223 	}
   224 }
   225 
   226 void Logger::CloseLogger()
   227 {
   228 	_CloseFile();
   229 }
   230 
   231 void Logger::LogMessage(const char *vafmt, ...)
   232 {
   233 	if (!m_Active)
   234 	{
   235 		return;
   236 	}
   237 
   238 	if (m_Mode == LoggingMode_Game)
   239 	{
   240 		va_list ap;
   241 		va_start(ap, vafmt);
   242 		_PrintToGameLog(vafmt, ap);
   243 		va_end(ap);
   244 		return;
   245 	}
   246 
   247 	if (m_DelayedStart)
   248 	{
   249 		m_DelayedStart = false;
   250 		_NewMapFile();
   251 	}
   252 
   253 	char msg[3072];
   254 	va_list ap;
   255 	va_start(ap, vafmt);
   256 	vsnprintf(msg, sizeof(msg), vafmt, ap);
   257 	va_end(ap);
   258 
   259 	char date[32];
   260 	time_t t;
   261 	time(&t);
   262 	tm *curtime = localtime(&t);
   263 	strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
   264 
   265 	FILE *fp = NULL;
   266 	if (m_Mode == LoggingMode_PerMap)
   267 	{
   268 		fp = fopen(m_NrmFileName.c_str(), "a+");
   269 		if (!fp)
   270 		{
   271 			_NewMapFile();
   272 			fp = fopen(m_NrmFileName.c_str(), "a+");
   273 			if (!fp)
   274 			{
   275 				goto print_error;
   276 			}
   277 		}
   278 	} else {
   279 		if (m_CurDay != curtime->tm_mday)
   280 		{
   281 			char _filename[256];
   282 			g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
   283 			m_NrmFileName.assign(_filename);
   284 			m_CurDay = curtime->tm_mday;
   285 			m_DailyPrintHdr = true;
   286 		}
   287 		fp = fopen(m_NrmFileName.c_str(), "a+");
   288 	}
   289 
   290 	if (fp)
   291 	{
   292 		if (m_DailyPrintHdr)
   293 		{
   294 			m_DailyPrintHdr = false;
   295 			fprintf(fp, "L %s: SourceMod log file session started (file \"L%04d%02d%02d.log\") (Version \"%s\")\n", date, curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday, SVN_FULL_VERSION);
   296 	 	}
   297 		fprintf(fp, "L %s: %s\n", date, msg);
   298 		fclose(fp);
   299 	} else {
   300 		goto print_error;
   301 	}
   302 
   303 	g_SMAPI->ConPrintf("L %s: %s\n", date, msg);
   304 	return;
   305 print_error:
   306 	g_SMAPI->ConPrint("[SM] Unexpected fatal logging error. SourceMod logging disabled.\n");
   307 	m_Active = false;
   308 }
   309 
   310 void Logger::LogError(const char *vafmt, ...)
   311 {
   312 	if (!m_Active)
   313 	{
   314 		return;
   315 	}
   316 
   317 	time_t t;
   318 	time(&t);
   319 	tm *curtime = localtime(&t);
   320 
   321 	char date[32];
   322 	strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
   323 
   324 	if (curtime->tm_mday != m_CurDay)
   325 	{
   326 		char _filename[256];
   327 		g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
   328 		m_ErrFileName.assign(_filename);
   329 		m_CurDay = curtime->tm_mday;
   330 		m_ErrMapStart = false;
   331 	}
   332 
   333 	char msg[3072];
   334 	va_list ap;
   335 	va_start(ap, vafmt);
   336 	vsnprintf(msg, sizeof(msg), vafmt, ap);
   337 	va_end(ap);
   338 
   339 	FILE *fp = fopen(m_ErrFileName.c_str(), "a+");
   340 	if (fp)
   341 	{
   342 		if (!m_ErrMapStart)
   343 		{
   344 			fprintf(fp, "L %s: SourceMod error session started\n", date);
   345 			fprintf(fp, "L %s: Info (map \"%s\") (file \"errors_%04d%02d%02d.log\")\n", date, m_CurMapName.c_str(), curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday);
   346 			m_ErrMapStart = true;
   347 		}
   348 		fprintf(fp, "L %s: %s\n", date, msg);
   349 		fclose(fp);
   350 	} else {
   351 		g_SMAPI->ConPrint("[SM] Unexpected fatal logging error. SourceMod logging disabled.\n");
   352 		m_Active = false;
   353 		return;
   354 	}
   355 
   356 	g_SMAPI->ConPrintf("L %s: %s\n", date, msg);
   357 }
   358 
   359 void Logger::MapChange(const char *mapname)
   360 {
   361 	m_CurMapName.assign(mapname);
   362 
   363 	switch (m_Mode)
   364 	{
   365 	case LoggingMode_Daily:
   366 		{
   367 			LogMessage("-------- Mapchange to %s --------", mapname);
   368 			break;
   369 		}
   370 	case LoggingMode_PerMap:
   371 		{
   372 			_NewMapFile();
   373 			break;
   374 		}
   375 	default:
   376 		{
   377 			/* Do nothing... */
   378 			break;
   379 		}
   380 	}
   381 
   382 	if (m_ErrMapStart)
   383 	{
   384 		LogError("Error log file session closed.");
   385 	}
   386 	m_ErrMapStart = false;
   387 }
   388 
   389 void Logger::_PrintToGameLog(const char *fmt, va_list ap)
   390 {
   391 	char msg[3072];
   392 	size_t len;
   393 
   394 	len = vsnprintf(msg, sizeof(msg)-2, fmt, ap);
   395 	len = (len >= sizeof(msg)) ? (sizeof(msg) - 2) : len;
   396 
   397 	msg[len++] = '\n';
   398 	msg[len] = '\0';
   399 
   400 	engine->LogPrint(msg);
   401 }
   402 
   403 const char *Logger::GetLogFileName(LogType type) const
   404 {
   405 	switch (type)
   406 	{
   407 	case LogType_Normal:
   408 		{
   409 			return m_NrmFileName.c_str();
   410 		}
   411 	case LogType_Error:
   412 		{
   413 			return m_ErrFileName.c_str();
   414 		}
   415 	default:
   416 		{
   417 			return "";
   418 		}
   419 	}
   420 }
   421 
   422 LoggingMode Logger::GetLoggingMode() const
   423 {
   424 	return m_Mode;
   425 }
   426 
   427 void Logger::EnableLogging()
   428 {
   429 	if (m_Active)
   430 	{
   431 		return;
   432 	}
   433 	m_Active = true;
   434 	LogMessage("[SM] Logging enabled manually by user.");
   435 }
   436 
   437 void Logger::DisableLogging()
   438 {
   439 	if (!m_Active)
   440 	{
   441 		return;
   442 	}
   443 	LogMessage("[SM] Logging disabled manually by user.");
   444 	m_Active = false;
   445 }
   446 
   447 void Logger::LogFatal(const char *msg, ...)
   448 {
   449 	/* :TODO: make this print all pretty-like
   450 	 * In fact, the pretty log printing function should be abstracted. 
   451 	 * It's already implemented twice which is bad.
   452 	 */
   453 
   454 	char path[PLATFORM_MAX_PATH];
   455 	g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
   456 	FILE *fp = fopen(path, "at");
   457 	if (!fp)
   458 	{
   459 		/* We're just doomed, aren't we... */
   460 		return;
   461 	}
   462 
   463 	va_list ap;
   464 	va_start(ap, msg);
   465 	vfprintf(fp, msg, ap);
   466 	va_end(ap);
   467 
   468 	fputs("\n", fp);
   469 	
   470 	fclose(fp);
   471 }