core/Logger.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1231 17badaf66ce4
child 1371 d207f0e17532
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

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