/* * ============================================================================ * * Zombie:Reloaded * * File: log.inc * Type: Core * Description: Logging API. * * Copyright (C) 2009-2013 Greyscale, Richard Helgeby * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * ============================================================================ */ /* * Note: See log.h.inc for header types and defines. */ LogInit() { // Destroy existing handle to prevent memory leak. if (hLogModuleFilter != INVALID_HANDLE) { CloseHandle(hLogModuleFilter); } // Initialize module filter array. hLogModuleFilter = CreateArray(32); } /** * Convert module type to a string. * * @param buffer Destination string buffer. * @param maxlen Size of destination buffer. * @param module Module type to convert. * @param shortName Optional. Use short name instead of human readable * names. Default is false * @return Number of cells written. */ LogGetModuleNameString(String:buffer[], maxlen, LogModules:module, bool:shortName = false) { switch (module) { case LogModule_Account: { return shortName ? strcopy(buffer, maxlen, "account") : strcopy(buffer, maxlen, "Account"); } case LogModule_AntiStick: { return shortName ? strcopy(buffer, maxlen, "antistick") : strcopy(buffer, maxlen, "Anti-Stick"); } case LogModule_Config: { return shortName ? strcopy(buffer, maxlen, "config") : strcopy(buffer, maxlen, "Config"); } case LogModule_Cvars: { return shortName ? strcopy(buffer, maxlen, "cvars") : strcopy(buffer, maxlen, "CVARs"); } case LogModule_Damage: { return shortName ? strcopy(buffer, maxlen, "damage") : strcopy(buffer, maxlen, "Damage"); } case LogModule_Downloads: { return shortName ? strcopy(buffer, maxlen, "downloads") : strcopy(buffer, maxlen, "Downloads"); } case LogModule_Hitgroups: { return shortName ? strcopy(buffer, maxlen, "hitgroups") : strcopy(buffer, maxlen, "Hit Groups"); } case LogModule_Infect: { return shortName ? strcopy(buffer, maxlen, "infect") : strcopy(buffer, maxlen, "Infect"); } case LogModule_Models: { return shortName ? strcopy(buffer, maxlen, "models") : strcopy(buffer, maxlen, "Models"); } case LogModule_Napalm: { return shortName ? strcopy(buffer, maxlen, "napalm") : strcopy(buffer, maxlen, "Napalm"); } case LogModule_Playerclasses: { return shortName ? strcopy(buffer, maxlen, "playerclasses") : strcopy(buffer, maxlen, "Player Classes"); } case LogModule_VEffects: { return shortName ? strcopy(buffer, maxlen, "veffects") : strcopy(buffer, maxlen, "Visual Effects"); } case LogModule_SEffects: { return shortName ? strcopy(buffer, maxlen, "seffects") : strcopy(buffer, maxlen, "Sound Effects"); } case LogModule_Tools: { return shortName ? strcopy(buffer, maxlen, "tools") : strcopy(buffer, maxlen, "Tools"); } case LogModule_Volfeatures: { return shortName ? strcopy(buffer, maxlen, "volfeatures") : strcopy(buffer, maxlen, "Volumetric Features"); } case LogModule_Weapons: { return shortName ? strcopy(buffer, maxlen, "weapons") : strcopy(buffer, maxlen, "Weapons"); } case LogModule_Weaponrestrict: { return shortName ? strcopy(buffer, maxlen, "weaponrestrict") : strcopy(buffer, maxlen, "Weapon Restrictions"); } case LogModule_ZSpawn: { return shortName ? strcopy(buffer, maxlen, "zspawn") : strcopy(buffer, maxlen, "ZSpawn"); } case LogModule_ZTele: { return shortName ? strcopy(buffer, maxlen, "ztele") : strcopy(buffer, maxlen, "ZTele"); } } // Module mismatch. return 0; } /** * Converts a string module name into a module type. * * @param moduleName A string with the short module name. Case insensitive, * but not trimmed for white space. * @return The matcing module type or LogModules_Invalid if failed. */ LogModules:LogGetModule(const String:moduleName[]) { if (StrEqual(moduleName, "account", false)) { return LogModule_Account; } else if (StrEqual(moduleName, "antistick", false)) { return LogModule_AntiStick; } else if (StrEqual(moduleName, "config", false)) { return LogModule_Config; } else if (StrEqual(moduleName, "cvars", false)) { return LogModule_Cvars; } else if (StrEqual(moduleName, "damage", false)) { return LogModule_Damage; } else if (StrEqual(moduleName, "downloads", false)) { return LogModule_Downloads; } else if (StrEqual(moduleName, "hitgroups", false)) { return LogModule_Hitgroups; } else if (StrEqual(moduleName, "infect", false)) { return LogModule_Infect; } else if (StrEqual(moduleName, "models", false)) { return LogModule_Models; } else if (StrEqual(moduleName, "napalm", false)) { return LogModule_Napalm; } else if (StrEqual(moduleName, "playerclasses", false)) { return LogModule_Playerclasses; } else if (StrEqual(moduleName, "veffects", false)) { return LogModule_VEffects; } else if (StrEqual(moduleName, "seffects", false)) { return LogModule_SEffects; } else if (StrEqual(moduleName, "tools", false)) { return LogModule_Tools; } else if (StrEqual(moduleName, "volfeatures", false)) { return LogModule_Volfeatures; } else if (StrEqual(moduleName, "weapons", false)) { return LogModule_Weapons; } else if (StrEqual(moduleName, "weaponrestrict", false)) { return LogModule_Weaponrestrict; } else if (StrEqual(moduleName, "zspawn", false)) { return LogModule_ZSpawn; } else if (StrEqual(moduleName, "ztele", false)) { return LogModule_ZTele; } // No match. return LogModule_Invalid; } /** * Check if the specified log flag is set. * * @param eventType The log flag to check. * @return True if set, false otherwise. */ bool:LogCheckFlag(eventType) { // Check if eventType is set. if (GetConVarInt(g_hCvarsList[CVAR_LOG_FLAGS]) & eventType) { return true; } else { return false; } } /** * Check if the specified module is enabled in the log module filter cache. * * @param module Module to check. * @return True if enabled, false otherwise. */ bool:LogCheckModuleFilter(LogModules:module) { if (LogModuleFilterCache[module]) { return true; } else { return false; } } /** * Print a formatted message to logs depending on log settings. * * @param isConsole Optional. Specifies whether the log event came from * client 0. Used in console commands, do not mix with * regular log events. Default is false. * @param logType Optional. Log type and action. Default is * LogType_Normal. * @param eventType Optional. A log flag describing What kind of log event * it is. Default is LOG_CORE_EVENTS. * @param module Module the log event were executed in. * @param description Event type or function name. A short descriptive phrase * to group together similar logs. * @param text Log message. Can be formatted. * @param ... Formatting parameters. */ LogEvent(bool:isConsole = false, LogTypes:logType = LogType_Normal, eventType = LOG_CORE_EVENTS, LogModules:module, const String:description[], const String:text[], any:...) { // Check filter overrides. Always log fatal errors, and check error override setting on error log types. if ((logType != LogType_Fatal && logType != LogType_Error) || (logType == LogType_Error && !GetConVarBool(g_hCvarsList[CVAR_LOG_ERROR_OVERRIDE]))) { // Check if logging is disabled. if (!GetConVarBool(g_hCvarsList[CVAR_LOG])) { return; } // Check if console is ignored. if (isConsole && GetConVarBool(g_hCvarsList[CVAR_LOG_IGNORE_CONSOLE])) { return; } // Check event type (log flag). if (!LogCheckFlag(eventType)) { return; } // Check if module filtering is enabled. if (GetConVarBool(g_hCvarsList[CVAR_LOG_MODULE_FILTER])) { // Check if the specified module is enabled. if (!LogCheckModuleFilter(module)) { return; } } } // Format extra parameters into the log buffer. decl String:logbuffer[LOG_MAX_LENGTH_FILE]; VFormat(logbuffer, sizeof(logbuffer), text, 7); // Get human readable module name. new String:modulename[64]; LogGetModuleNameString(modulename, sizeof(modulename), module); // Format Format(logbuffer, sizeof(logbuffer), "[%s] [%s] %s", modulename, description, logbuffer); // Format other parameters onto the log text. switch (logType) { // Log type is normal. case LogType_Normal: { LogMessage(logbuffer); } // Log type is error. case LogType_Error: { LogError(logbuffer); } // Log type is fatal error. case LogType_Fatal: { SetFailState(logbuffer); } } // Note: The phrase "Literal text" is a blank phrase to pass any string we want into it. // Check if printing log events to admins is enabled. if (GetConVarBool(g_hCvarsList[CVAR_LOG_PRINT_ADMINS])) { // Print text to admins. TranslationPrintToChatAll(false, true, "Literal text", logbuffer); } // Check if printing log events to public chat is enabled. if (GetConVarBool(g_hCvarsList[CVAR_LOG_PRINT_CHAT])) { // Print text to public chat. TranslationPrintToChatAll(false, false, "Literal text", logbuffer); } } /** * Adds a module to the module filter and updates the cache. If it already * exist the command is ignored. * * @param module The module to add. * @return True if added, false otherwise. */ bool:LogModuleFilterAdd(LogModules:module) { decl String:modulename[64]; // Check if empty. if (strlen(modulename) == 0) { return false; } // Convert module name. LogGetModuleNameString(modulename, sizeof(modulename), module, true); // Check if the module isn't already is listed. if (FindStringInArray(hLogModuleFilter, modulename) < 0) { // Add module to filter. PushArrayString(hLogModuleFilter, modulename); return true; } return false; } /** * Removes a module to the module filter and updates the cache. If it doesn't * exist the command is ignored. * * @param module The module to remove. * @return True if removed, false otherwise. */ bool:LogModuleFilterRemove(LogModules:module) { decl String:modulename[64]; new moduleindex; // Check if empty. if (strlen(modulename) == 0) { return false; } // Convert module name. LogGetModuleNameString(modulename, sizeof(modulename), module, true); // Get the module index. moduleindex = FindStringInArray(hLogModuleFilter, modulename); // Check if successful. if (moduleindex >= 0) { // Remove module from filter. RemoveFromArray(hLogModuleFilter, moduleindex); return true; } return false; } /** * Update module filter cache. */ LogModuleFilterCacheUpdate() { decl String:modulename[64]; new LogModules:moduletype; new modulecount; new filtersize; // Clear all entries in module cache. modulecount = sizeof(LogModuleFilterCache); for (new module = 1; module < modulecount; module++) { LogModuleFilterCache[LogModules:module] = false; } // Loop through the module array. filtersize = GetArraySize(hLogModuleFilter); for (new index = 0; index < filtersize; index++) { // Get the module name. GetArrayString(hLogModuleFilter, index, modulename, sizeof(modulename)); // Convert to type. moduletype = LogGetModule(modulename); // Validate type. if (moduletype != LogModule_Invalid) { // Set value in cache. LogModuleFilterCache[moduletype] = true; } } } /** * Creates commands for logging module. Called when commands are created. */ LogOnCommandsCreate() { RegConsoleCmd("zr_log_list", Command_LogList, "List available logging flags and modules with their status values."); RegConsoleCmd("zr_log_add_module", Command_LogAddModule, "Add one or more modules to the module filter. Usage: zr_log_add_module [module] ..."); RegConsoleCmd("zr_log_remove_module", Command_LogRemoveModule, "Remove one or more modules from the module filter. Usage: zr_log_remove_module [module] ..."); } /** * Handles the zr_log_list command. Displays flags and module filter cache. * * @param client The client that executed the command. * @param argc Number of arguments passed. */ public Action:Command_LogList(client, argc) { decl String:buffer[2048]; decl String:linebuffer[96]; decl String:modulename[64]; decl String:modulenameshort[64]; new modulecount; // Strings to store translated phrases. Because formatting width settings // doesn't work with "%t", but "%s". decl String:phrasegenericflag[32]; decl String:phrasevalue[32]; decl String:phrasemodule[32]; decl String:phraseshortname[32]; // Quick initialize string buffer. buffer[0] = 0; // Set language. SetGlobalTransTarget(client); // Get phrases. Format(phrasegenericflag, sizeof(phrasegenericflag), "%t", "Log Generic Flag"); Format(phrasevalue, sizeof(phrasevalue), "%t", "Log Value"); Format(phrasemodule, sizeof(phrasemodule), "%t", "Log Module"); Format(phraseshortname, sizeof(phraseshortname), "%t", "Log Module Short Name"); // Log flags: Format(linebuffer, sizeof(linebuffer), "%-19s %-7s %t\n", phrasegenericflag, phrasevalue, "Log Status"); StrCat(buffer, sizeof(buffer), linebuffer); StrCat(buffer, sizeof(buffer), "--------------------------------------------------------------------------------\n"); Format(linebuffer, sizeof(linebuffer), "LOG_CORE_EVENTS 1 %t\n", LogCheckFlag(LOG_CORE_EVENTS) ? "On" : "Off"); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "LOG_GAME_EVENTS 2 %t\n", LogCheckFlag(LOG_GAME_EVENTS) ? "On" : "Off"); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "LOG_PLAYER_COMMANDS 4 %t\n", LogCheckFlag(LOG_PLAYER_COMMANDS) ? "On" : "Off"); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "LOG_DEBUG 8 %t\n", LogCheckFlag(LOG_DEBUG) ? "On" : "Off"); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "LOG_DEBUG_DETAIL 16 %t\n", LogCheckFlag(LOG_DEBUG_DETAIL) ? "On" : "Off"); StrCat(buffer, sizeof(buffer), linebuffer); ReplyToCommand(client, buffer); buffer[0] = 0; // Module filtering status: Format(linebuffer, sizeof(linebuffer), "%t %t\n\n", "Log Module Filtering", GetConVarBool(g_hCvarsList[CVAR_LOG_MODULE_FILTER]) ? "On" : "Off"); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "%-23s %-19s %t\n", phrasemodule, phraseshortname, "Log Status"); StrCat(buffer, sizeof(buffer), linebuffer); StrCat(buffer, sizeof(buffer), "--------------------------------------------------------------------------------"); ReplyToCommand(client, buffer); buffer[0] = 0; // Module status: modulecount = sizeof(LogModuleFilterCache); for (new module = 1; module < modulecount; module++) { LogGetModuleNameString(modulename, sizeof(modulename), LogModules:module); LogGetModuleNameString(modulenameshort, sizeof(modulenameshort), LogModules:module, true); Format(linebuffer, sizeof(linebuffer), "%-23s %-19s %t", modulename, modulenameshort, LogModuleFilterCache[LogModules:module] ? "On" : "Off"); ReplyToCommand(client, linebuffer); } return Plugin_Handled; } /** * Handles the zr_log_add_module command. Add one or modules to module filter. * * @param client The client that executed the command. * @param argc Number of arguments passed. */ public Action:Command_LogAddModule(client, argc) { decl String:buffer[256]; decl String:argument[32]; buffer[0] = 0; // Check if privileged. if (!ZRIsClientPrivileged(client, OperationType_Configuration)) { TranslationReplyToCommand(client, "No access to command"); return Plugin_Handled; } new LogModules:logmodule; // Check if no arguments. if (argc < 1) { // Display syntax info. StrCat(buffer, sizeof(buffer), "Add one or more modules to the module filter. Usage: zr_log_add_module [module] ...\n"); StrCat(buffer, sizeof(buffer), "See zr_log_list to list available module names (short names)."); ReplyToCommand(client, buffer); } // Loop through each argument. for (new arg = 1; arg <= argc; arg++) { // Get argument string. GetCmdArg(arg, argument, sizeof(argument)); // Convert to module type. logmodule = LogGetModule(argument); // Check if invalid. if (logmodule == LogModule_Invalid) { ReplyToCommand(client, "Invalid module name: \"%s\"", argument); // Skip to next argument. continue; } LogModuleFilterAdd(logmodule); ReplyToCommand(client, "Added \"%s\" to module filter.", argument); } // Update cache. LogModuleFilterCacheUpdate(); return Plugin_Handled; } /** * Handles the zr_log_add_module command. Remove one or modules to module filter. * * @param client The client that executed the command. * @param argc Number of arguments passed. */ public Action:Command_LogRemoveModule(client, argc) { decl String:buffer[256]; decl String:argument[32]; buffer[0] = 0; // Check if privileged. if (!ZRIsClientPrivileged(client, OperationType_Configuration)) { TranslationReplyToCommand(client, "No access to command"); return Plugin_Handled; } new LogModules:logmodule; // Check if no arguments. if (argc < 1) { // Display syntax info. StrCat(buffer, sizeof(buffer), "Remove one or more modules to the module filter. Usage: zr_log_remove_module [module] ...\n"); StrCat(buffer, sizeof(buffer), "See zr_log_list to list available module names (short names)."); ReplyToCommand(client, buffer); } // Loop through each argument. for (new arg = 1; arg <= argc; arg++) { // Get argument string. GetCmdArg(arg, argument, sizeof(argument)); // Convert to module type. logmodule = LogGetModule(argument); // Check if invalid. if (logmodule == LogModule_Invalid) { ReplyToCommand(client, "Invalid module name: \"%s\"", argument); // Skip to next argument. continue; } LogModuleFilterRemove(logmodule); ReplyToCommand(client, "Removed \"%s\" from module filter.", argument); } // Update cache. LogModuleFilterCacheUpdate(); return Plugin_Handled; }