3 * =============================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
33 #include "sourcemod.h"
34 #include "sourcemm_api.h"
35 #include "sm_stringutil.h"
37 #include "systems/LibrarySys.h"
38 #include "sm_version.h"
43 * :TODO: This should be creating the log folder if it doesn't exist
46 ConfigResult Logger::OnSourceModConfigChanged(const char *key,
52 if (strcasecmp(key, "Logging") == 0)
56 if (strcasecmp(value, "on") == 0)
59 } else if (strcasecmp(value, "off") == 0) {
62 UTIL_Format(error, maxlength, "Invalid value: must be \"on\" or \"off\"");
63 return ConfigResult_Reject;
66 if (source == ConfigSource_Console)
68 state ? EnableLogging() : DisableLogging();
70 m_InitialState = state;
73 return ConfigResult_Accept;
74 } else if (strcasecmp(key, "LogMode") == 0) {
75 if (strcasecmp(value, "daily") == 0)
77 m_Mode = LoggingMode_Daily;
78 } else if (strcasecmp(value, "map") == 0) {
79 m_Mode = LoggingMode_PerMap;
80 } else if (strcasecmp(value, "game") == 0) {
81 m_Mode = LoggingMode_Game;
83 UTIL_Format(error, maxlength, "Invalid value: must be [daily|map|game]");
84 return ConfigResult_Reject;
87 return ConfigResult_Accept;
90 return ConfigResult_Ignore;
93 void Logger::OnSourceModStartup(bool late)
98 void Logger::OnSourceModAllShutdown()
103 void Logger::OnSourceModLevelChange(const char *mapName)
108 void Logger::_NewMapFile()
115 /* Append "Log file closed" to previous log file */
123 tm *curtime = localtime(&t);
127 g_SourceMod.BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%02d%02d%03d.log", curtime->tm_mon + 1, curtime->tm_mday, i);
128 FILE *fp = fopen(_filename, "r");
136 m_NrmFileName.assign(_filename);
138 FILE *fp = fopen(m_NrmFileName.c_str(), "w");
141 g_SMAPI->ConPrint("[SM] Unexpected fatal logging error. SourceMod logging disabled.\n");
146 strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
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);
152 void Logger::_CloseFile()
160 if (!m_NrmFileName.empty())
162 fp = fopen(m_NrmFileName.c_str(), "r+");
165 fseek(fp, 0, SEEK_END);
166 LogMessage("Log file closed.");
169 m_NrmFileName.clear();
176 fp = fopen(m_ErrFileName.c_str(), "r+");
179 fseek(fp, 0, SEEK_END);
180 LogError("Error log file session closed.");
183 m_ErrFileName.clear();
186 void Logger::InitLogger(LoggingMode mode)
189 m_Active = m_InitialState;
193 tm *curtime = localtime(&t);
194 m_CurDay = curtime->tm_mday;
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);
198 m_ErrFileName.assign(_filename);
202 case LoggingMode_PerMap:
206 m_DelayedStart = true;
210 case LoggingMode_Daily:
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);
213 m_NrmFileName.assign(_filename);
214 m_DailyPrintHdr = true;
225 void Logger::CloseLogger()
230 void Logger::LogMessage(const char *vafmt, ...)
237 if (m_Mode == LoggingMode_Game)
241 _PrintToGameLog(vafmt, ap);
248 m_DelayedStart = false;
255 vsnprintf(msg, sizeof(msg), vafmt, ap);
261 tm *curtime = localtime(&t);
262 strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
265 if (m_Mode == LoggingMode_PerMap)
267 fp = fopen(m_NrmFileName.c_str(), "a+");
271 fp = fopen(m_NrmFileName.c_str(), "a+");
278 if (m_CurDay != curtime->tm_mday)
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);
282 m_NrmFileName.assign(_filename);
283 m_CurDay = curtime->tm_mday;
284 m_DailyPrintHdr = true;
286 fp = fopen(m_NrmFileName.c_str(), "a+");
293 m_DailyPrintHdr = false;
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);
296 fprintf(fp, "L %s: %s\n", date, msg);
302 g_SMAPI->ConPrintf("L %s: %s\n", date, msg);
305 g_SMAPI->ConPrint("[SM] Unexpected fatal logging error. SourceMod logging disabled.\n");
309 void Logger::LogError(const char *vafmt, ...)
318 tm *curtime = localtime(&t);
321 strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime);
323 if (curtime->tm_mday != m_CurDay)
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);
327 m_ErrFileName.assign(_filename);
328 m_CurDay = curtime->tm_mday;
329 m_ErrMapStart = false;
335 vsnprintf(msg, sizeof(msg), vafmt, ap);
338 FILE *fp = fopen(m_ErrFileName.c_str(), "a+");
343 fprintf(fp, "L %s: SourceMod error session started\n", date);
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);
345 m_ErrMapStart = true;
347 fprintf(fp, "L %s: %s\n", date, msg);
350 g_SMAPI->ConPrint("[SM] Unexpected fatal logging error. SourceMod logging disabled.\n");
355 g_SMAPI->ConPrintf("L %s: %s\n", date, msg);
358 void Logger::MapChange(const char *mapname)
360 m_CurMapName.assign(mapname);
364 case LoggingMode_Daily:
366 LogMessage("-------- Mapchange to %s --------", mapname);
369 case LoggingMode_PerMap:
383 LogError("Error log file session closed.");
385 m_ErrMapStart = false;
388 void Logger::_PrintToGameLog(const char *fmt, va_list ap)
393 len = vsnprintf(msg, sizeof(msg)-2, fmt, ap);
394 len = (len >= sizeof(msg)) ? (sizeof(msg) - 2) : len;
399 engine->LogPrint(msg);
402 const char *Logger::GetLogFileName(LogType type) const
408 return m_NrmFileName.c_str();
412 return m_ErrFileName.c_str();
421 LoggingMode Logger::GetLoggingMode() const
426 void Logger::EnableLogging()
433 LogMessage("[SM] Logging enabled manually by user.");
436 void Logger::DisableLogging()
442 LogMessage("[SM] Logging disabled manually by user.");
446 void Logger::LogFatal(const char *msg, ...)
448 /* :TODO: make this print all pretty-like
449 * In fact, the pretty log printing function should be abstracted.
450 * It's already implemented twice which is bad.
453 char path[PLATFORM_MAX_PATH];
454 g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log");
455 FILE *fp = fopen(path, "at");
458 /* We're just doomed, aren't we... */
464 vfprintf(fp, msg, ap);