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