eqemu-server/common/eqemu_logsys.h

410 lines
10 KiB
C++

/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2018 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* 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; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_LOGSYS_H
#define EQEMU_LOGSYS_H
#include <fmt/format.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <functional>
#include "types.h"
namespace Logs {
enum DebugLevel {
General = 1, /* 1 - Low-Level general debugging, useful info on single line */
Moderate, /* 2 - Informational based, used in functions, when particular things load */
Detail /* 3 - Use this for extreme detail in logging, usually in extreme debugging in the stack or interprocess communication */
};
/**
* If you add to this, make sure you update LogCategoryName
*
* NOTE: Only add to the bottom of the enum because that is the type ID assignment
*/
enum LogCategory {
None = 0,
AA,
AI,
Aggro,
Attack,
Client_Server_Packet,
Combat,
Commands,
Crash,
Debug,
Doors,
Error,
Guilds,
Inventory,
Launcher,
Netcode,
Normal,
Object,
Pathing,
QS_Server,
Quests,
Rules,
Skills,
Spawns,
Spells,
Status,
TCP_Connection,
Tasks,
Tradeskills,
Trading,
Tribute,
UCS_Server,
WebInterface_Server,
World_Server,
Zone_Server,
MySQLError,
MySQLQuery,
Mercenaries,
QuestDebug,
Server_Client_Packet,
Client_Server_Packet_Unhandled,
Server_Client_Packet_With_Dump,
Client_Server_Packet_With_Dump,
Login_Server,
Client_Login,
Headless_Client,
HP_Update,
FixZ,
Food,
Traps,
NPCRoamBox,
NPCScaling,
MobAppearance,
Info,
Warning,
Critical,
Emergency,
Alert,
Notice,
MaxCategoryID /* Don't Remove this */
};
/**
* If you add to this, make sure you update LogCategory
*/
static const char* LogCategoryName[LogCategory::MaxCategoryID] = {
"",
"AA",
"AI",
"Aggro",
"Attack",
"Packet :: Client -> Server",
"Combat",
"Commands",
"Crash",
"Debug",
"Doors",
"Error",
"Guilds",
"Inventory",
"Launcher",
"Netcode",
"Normal",
"Object",
"Pathing",
"QS Server",
"Quests",
"Rules",
"Skills",
"Spawns",
"Spells",
"Status",
"TCP Connection",
"Tasks",
"Tradeskills",
"Trading",
"Tribute",
"UCS Server",
"WebInterface Server",
"World Server",
"Zone Server",
"MySQL Error",
"MySQL Query",
"Mercenaries",
"Quest Debug",
"Packet :: Server -> Client",
"Packet :: Client -> Server Unhandled",
"Packet :: Server -> Client (Dump)",
"Packet :: Client -> Server (Dump)",
"Login Server",
"Client Login",
"Headless Client",
"HP Update",
"FixZ",
"Food",
"Traps",
"NPC Roam Box",
"NPC Scaling",
"Mob Appearance",
"Info",
"Warning",
"Critical",
"Emergency",
"Alert",
"Notice"
};
}
/**
* RFC 5424
*/
#define LogEmergency(message, ...) do {\
if (LogSys.log_settings[Logs::Emergency].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Emergency, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogAlert(message, ...) do {\
if (LogSys.log_settings[Logs::Alert].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Alert, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogCritical(message, ...) do {\
if (LogSys.log_settings[Logs::Critical].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Critical, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogError(message, ...) do {\
if (LogSys.log_settings[Logs::Error].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Error, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogWarning(message, ...) do {\
if (LogSys.log_settings[Logs::Warning].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Warning, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogNotice(message, ...) do {\
if (LogSys.log_settings[Logs::Notice].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Notice, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogInfo(message, ...) do {\
if (LogSys.log_settings[Logs::Info].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Info, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogDebug(message, ...) do {\
if (LogSys.log_settings[Logs::Debug].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Debug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
/**
* Other
*/
#define LogStatus(message, ...) do {\
if (LogSys.log_settings[Logs::Status].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogF(debug_level, log_category, message, ...) do {\
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
OutF(LogSys, debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
class EQEmuLogSys {
public:
EQEmuLogSys();
~EQEmuLogSys();
/**
* Close File Logs wherever necessary, either at zone shutdown or entire process shutdown for everything else.
* This should be handled on deconstructor but to be safe we use it anyways.
*/
void CloseFileLogs();
void LoadLogSettingsDefaults();
/**
* @param directory_name
*/
void MakeDirectory(const std::string &directory_name);
/**
* The one and only Logging function that uses a debug level as a parameter, as well as a log_category
* log_category - defined in Logs::LogCategory::[]
* log_category name resolution works by passing the enum int ID to Logs::LogCategoryName[category_id]
*
* Example: EQEmuLogSys::Out(Logs::General, Logs::Guilds, "This guild has no leader present");
* - This would pipe the same category and debug level to all output formats, but the internal memory reference of log_settings would
* be checked against to see if that piped output is set to actually process it for the category and debug level
*/
void Out(
Logs::DebugLevel debug_level,
uint16 log_category,
const char *file,
const char *func,
int line,
const std::string &message,
...
);
/**
* Used in file logs to prepend a timestamp entry for logs
* @param time_stamp
*/
void SetCurrentTimeStamp(char* time_stamp);
/**
* @param log_name
*/
void StartFileLogs(const std::string &log_name = "");
/**
* LogSettings Struct
*
* This struct is the master reference for all settings for each category, and for each output
*
* log_to_file[category_id] = [1-3] - Sets debug level for category to output to file
* log_to_console[category_id] = [1-3] - Sets debug level for category to output to console
* log_to_gmsay[category_id] = [1-3] - Sets debug level for category to output to gmsay
*
*/
struct LogSettings {
uint8 log_to_file;
uint8 log_to_console;
uint8 log_to_gmsay;
uint8 is_category_enabled; /* When any log output in a category > 0, set this to 1 as (Enabled) */
};
/**
* Internally used memory reference for all log settings per category
* These are loaded via DB and have defaults loaded in LoadLogSettingsDefaults
* Database loaded via Database::LoadLogSettings(log_settings)
*/
LogSettings log_settings[Logs::LogCategory::MaxCategoryID]{};
bool file_logs_enabled = false;
/**
* Sets Executable platform (Zone/World/UCS) etc.
*/
int log_platform = 0;
/**
* File name used in writing logs
*/
std::string platform_file_name;
/**
* GMSay Client Message colors mapped by category
*
* @param log_category
* @return
*/
uint16 GetGMSayColorFromCategory(uint16 log_category);
/**
* @param f
*/
void SetGMSayHandler(std::function<void(uint16 log_type, const std::string&)> f) { on_log_gmsay_hook = f; }
/**
* @param f
*/
void SetConsoleHandler(std::function<void(uint16 debug_level, uint16 log_type, const std::string&)> f) { on_log_console_hook = f; }
private:
/**
* Callback pointer to zone process for hooking logs to zone using GMSay
*/
std::function<void(uint16 log_category, const std::string&)> on_log_gmsay_hook;
std::function<void(uint16 debug_level, uint16 log_category, const std::string&)> on_log_console_hook;
/**
* Formats log messages like '[Category] This is a log message'
*/
std::string FormatOutMessageString(uint16 log_category, const std::string &in_message);
/**
* Linux console color messages mapped by category
*
* @param log_category
* @return
*/
std::string GetLinuxConsoleColorFromCategory(uint16 log_category);
/**
* Windows console color messages mapped by category
*/
uint16 GetWindowsConsoleColorFromCategory(uint16 log_category);
/**
* @param debug_level
* @param log_category
* @param message
*/
void ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message);
/**
* @param debug_level
* @param log_category
* @param message
*/
void ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message);
/**
* @param debug_level
* @param log_category
* @param message
*/
void ProcessLogWrite(uint16 debug_level, uint16 log_category, const std::string &message);
/**
* @param log_category
* @return
*/
bool IsRfc5424LogCategory(uint16 log_category);
};
extern EQEmuLogSys LogSys;
template<typename... Args>
void OutF(
EQEmuLogSys &ls,
Logs::DebugLevel debug_level,
uint16 log_category,
const char *file,
const char *func,
int line,
const char *fmt,
const Args &... args
)
{
std::string log_str = fmt::format(fmt, args...);
ls.Out(debug_level, log_category, file, func, line, log_str);
}
#endif