656 lines
18 KiB
C++
656 lines
18 KiB
C++
// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3
|
|
|
|
#pragma once
|
|
|
|
#include <string>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdarg>
|
|
#include <cstring>
|
|
#include <cerrno>
|
|
#include <ctime>
|
|
#include <cstdlib>
|
|
#include <sys/stat.h>
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <atomic>
|
|
#include <memory>
|
|
#include <unistd.h>
|
|
|
|
#include "types.hpp"
|
|
#include "xml_parser.hpp"
|
|
#include "../WorldServer/World.h"
|
|
#include "../WorldServer/client.h"
|
|
#include "../WorldServer/zoneserver.h"
|
|
|
|
extern ZoneList zone_list;
|
|
|
|
#define LOG_BUFFER_SIZE 4096
|
|
|
|
// Log output destinations
|
|
#define LOG_LOGFILE 1
|
|
#define LOG_CONSOLE 2
|
|
#define LOG_CLIENT 4
|
|
|
|
// Console color definitions using ANSI escape codes
|
|
#define FOREGROUND_WHITE 37
|
|
#define FOREGROUND_WHITE_BOLD 137
|
|
#define FOREGROUND_RED 31
|
|
#define FOREGROUND_RED_BOLD 131
|
|
#define FOREGROUND_GREEN 32
|
|
#define FOREGROUND_GREEN_BOLD 132
|
|
#define FOREGROUND_BLUE 34
|
|
#define FOREGROUND_BLUE_BOLD 134
|
|
#define FOREGROUND_YELLOW 33
|
|
#define FOREGROUND_YELLOW_BOLD 133
|
|
#define FOREGROUND_CYAN 36
|
|
#define FOREGROUND_CYAN_BOLD 136
|
|
#define FOREGROUND_MAGENTA 35
|
|
#define FOREGROUND_MAGENTA_BOLD 135
|
|
|
|
#define LOG_CATEGORY(category) LOG_ ##category ,
|
|
enum LogCategory
|
|
{
|
|
#include "log_types.hpp"
|
|
NUMBER_OF_LOG_CATEGORIES
|
|
};
|
|
|
|
#define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) category##__##type ,
|
|
enum LogType
|
|
{
|
|
#include "log_types.hpp"
|
|
NUMBER_OF_LOG_TYPES
|
|
};
|
|
|
|
#define LOG_CATEGORY(category) #category,
|
|
const char *log_category_names[NUMBER_OF_LOG_CATEGORIES] = {
|
|
#include "log_types.hpp"
|
|
};
|
|
|
|
// Structure to hold log type configuration and status
|
|
struct LogTypeStatus
|
|
{
|
|
int8_t level; // Minimum log level for this type
|
|
int color; // Console color code
|
|
bool enabled; // Whether this log type is active
|
|
bool logfile; // Write to log file
|
|
bool console; // Write to console
|
|
bool client; // Send to connected clients
|
|
LogCategory category; // Category this log type belongs to
|
|
const char *name; // Internal name identifier
|
|
const char *display_name; // Human-readable display name
|
|
};
|
|
|
|
#define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) { level, color, enabled, logfile, console, client, LOG_ ##category, #category "__" #type, ( strlen(str)>0 ) ? str : #category "__" #type },
|
|
static LogTypeStatus real_log_type_info[NUMBER_OF_LOG_TYPES+1] =
|
|
{
|
|
#include "log_types.hpp"
|
|
{ 0, 0, false, false, false, false, NUMBER_OF_LOG_CATEGORIES, "BAD TYPE", "Bad Name" } /* dummy trailing record */
|
|
};
|
|
|
|
LogTypeStatus *log_type_info = real_log_type_info;
|
|
|
|
// Logging system configuration constants
|
|
#define LOG_CYCLE 100 // milliseconds between each batch of log writes
|
|
#define LOGS_PER_CYCLE 50 // amount of logs to write per cycle
|
|
#define LOG_DIR "logs" // directory for log files
|
|
#define DATE_MAX 8 // maximum date string length
|
|
#define LOG_NAME_MAX 32 // maximum log name length
|
|
|
|
// Determine executable name for log file naming
|
|
#if defined LOGIN
|
|
#define EXE_NAME "login"
|
|
#elif defined WORLD
|
|
#define EXE_NAME "world"
|
|
#elif defined PARSER
|
|
#define EXE_NAME "parser"
|
|
#elif defined PATCHER
|
|
#define EXE_NAME "patcher"
|
|
#else
|
|
#define EXE_NAME "unknown"
|
|
#endif
|
|
|
|
// Structure for queued log entries in doubly-linked list
|
|
struct logq_t
|
|
{
|
|
LogType log_type; // Type of log entry
|
|
char date[DATE_MAX + 1]; // Timestamp string
|
|
char name[LOG_NAME_MAX + 1]; // Log category name
|
|
char *text; // Log message text
|
|
struct logq_t *next; // Next entry in queue
|
|
struct logq_t *prev; // Previous entry in queue
|
|
};
|
|
|
|
// Global logging system state
|
|
static logq_t head; // Head of log queue
|
|
static logq_t tail; // Tail of log queue
|
|
static std::atomic<int> num_logqs{0}; // Number of queued logs
|
|
static std::mutex log_mutex; // Mutex for log queue access
|
|
static std::atomic<bool> looping{false}; // Thread loop control
|
|
static std::atomic<bool> start_called{false}; // Initialization flag
|
|
|
|
extern const char* log_category_names[NUMBER_OF_LOG_CATEGORIES];
|
|
extern LogTypeStatus* log_type_info;
|
|
|
|
// Function declarations
|
|
void LogStart();
|
|
void LogStop();
|
|
int8_t GetLoggerLevel(LogType type);
|
|
void LogWrite(LogType type, int8_t log_level, const char *cat_text, const char *fmt, ...);
|
|
bool LogParseConfigs();
|
|
|
|
#ifdef PARSER
|
|
void ColorizeLog(int color, char *date, const char *display_name, const char *category, std::string buffer);
|
|
#endif
|
|
|
|
// Set console text color using ANSI escape codes
|
|
static void SetConsoleColor(int color)
|
|
{
|
|
switch (color) {
|
|
case FOREGROUND_WHITE:
|
|
case FOREGROUND_WHITE_BOLD:
|
|
case FOREGROUND_RED:
|
|
case FOREGROUND_RED_BOLD:
|
|
case FOREGROUND_GREEN:
|
|
case FOREGROUND_GREEN_BOLD:
|
|
case FOREGROUND_BLUE:
|
|
case FOREGROUND_BLUE_BOLD:
|
|
case FOREGROUND_YELLOW:
|
|
case FOREGROUND_YELLOW_BOLD:
|
|
case FOREGROUND_CYAN:
|
|
case FOREGROUND_CYAN_BOLD:
|
|
case FOREGROUND_MAGENTA:
|
|
case FOREGROUND_MAGENTA_BOLD:
|
|
printf("\033[%i;%i;40m", color > 100 ? 1 : 0, color > 100 ? color - 100 : color);
|
|
break;
|
|
default:
|
|
printf("\033[0;37;40m");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Open a log file for writing with date-based naming
|
|
static FILE * OpenLogFile()
|
|
{
|
|
char file[FILENAME_MAX + 1];
|
|
struct stat st;
|
|
struct tm *tm;
|
|
time_t now;
|
|
FILE *f;
|
|
|
|
now = time(nullptr);
|
|
tm = localtime(&now);
|
|
|
|
// Create logs directory if it doesn't exist
|
|
if (stat(LOG_DIR, &st) != 0) {
|
|
if (mkdir(LOG_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
|
|
fprintf(stderr, "Unable to create directory '%s': %s\n", LOG_DIR, strerror(errno));
|
|
return stderr;
|
|
}
|
|
}
|
|
|
|
// Generate filename with date and process ID
|
|
#ifdef NO_PIDLOG
|
|
snprintf(file, FILENAME_MAX, LOG_DIR"/%04i-%02i-%02i_eq2" EXE_NAME ".log",
|
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
|
|
#else
|
|
snprintf(file, FILENAME_MAX, LOG_DIR"/%04i-%02i-%02i_eq2" EXE_NAME "_%04i.log",
|
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, getpid());
|
|
#endif
|
|
|
|
if ((f = fopen(file, "a")) == nullptr) {
|
|
fprintf(stderr, "Could not open '%s' for writing: %s\n", file, strerror(errno));
|
|
return stderr;
|
|
}
|
|
|
|
return f;
|
|
}
|
|
|
|
// Write a batch of queued log entries to their destinations
|
|
static void WriteQueuedLogs(int count)
|
|
{
|
|
logq_t pending_head, pending_tail, *logq, *tmp;
|
|
int i = 0;
|
|
FILE *f;
|
|
|
|
// Initialize temporary list for processing
|
|
pending_head.next = &pending_tail;
|
|
pending_tail.prev = &pending_head;
|
|
|
|
// Move logs from main queue to temporary list for processing
|
|
{
|
|
std::lock_guard<std::mutex> lock(log_mutex);
|
|
|
|
while (head.next != &tail) {
|
|
// Remove from main list
|
|
logq = head.next;
|
|
logq->next->prev = &head;
|
|
head.next = logq->next;
|
|
|
|
// Add to temporary list
|
|
tmp = pending_tail.prev;
|
|
tmp->next = logq;
|
|
logq->prev = tmp;
|
|
logq->next = &pending_tail;
|
|
pending_tail.prev = logq;
|
|
--num_logqs;
|
|
|
|
// Check count limit
|
|
if (count > 0 && ++i == count)
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Process logs from temporary list
|
|
logq = pending_head.next;
|
|
if (logq == &pending_tail)
|
|
return;
|
|
|
|
while (logq != &pending_tail) {
|
|
// Write to console if enabled
|
|
if (log_type_info[logq->log_type].console) {
|
|
SetConsoleColor(FOREGROUND_WHITE_BOLD);
|
|
printf("%s ", logq->date);
|
|
SetConsoleColor(log_type_info[logq->log_type].color);
|
|
printf("%s ", log_type_info[logq->log_type].display_name);
|
|
SetConsoleColor(FOREGROUND_WHITE_BOLD);
|
|
printf("%-10s: ", logq->name);
|
|
SetConsoleColor(log_type_info[logq->log_type].color);
|
|
printf("%s\n", logq->text);
|
|
SetConsoleColor(-1);
|
|
fflush(stdout);
|
|
}
|
|
|
|
// Write to log file if enabled
|
|
if (log_type_info[logq->log_type].logfile) {
|
|
f = OpenLogFile();
|
|
|
|
if (f != stderr || (f == stderr && !log_type_info[logq->log_type].console)) {
|
|
fprintf(f, "%s %s %s: %s\n", logq->date,
|
|
log_type_info[logq->log_type].display_name, logq->name, logq->text);
|
|
fflush(f);
|
|
if (f != stderr)
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
#if defined WORLD
|
|
// Send to subscribed clients if enabled
|
|
if (log_type_info[logq->log_type].client) {
|
|
// TODO: Implement client logging subscription system
|
|
}
|
|
#endif
|
|
|
|
// Move to next log entry and clean up current
|
|
tmp = logq;
|
|
logq = logq->next;
|
|
free(tmp->text);
|
|
free(tmp);
|
|
}
|
|
}
|
|
|
|
// Main logging thread function - processes queued logs continuously
|
|
void LogLoop()
|
|
{
|
|
while (looping.load()) {
|
|
WriteQueuedLogs(LOGS_PER_CYCLE);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(LOG_CYCLE));
|
|
}
|
|
}
|
|
|
|
// Initialize the logging system and start the logging thread
|
|
void LogStart()
|
|
{
|
|
if (start_called.load())
|
|
return;
|
|
|
|
// Initialize doubly-linked list
|
|
head.prev = nullptr;
|
|
head.next = &tail;
|
|
tail.prev = &head;
|
|
tail.next = nullptr;
|
|
|
|
looping.store(true);
|
|
|
|
// Start logging thread
|
|
std::thread log_thread(LogLoop);
|
|
log_thread.detach();
|
|
|
|
start_called.store(true);
|
|
}
|
|
|
|
// Stop the logging system and flush remaining logs
|
|
void LogStop()
|
|
{
|
|
looping.store(false);
|
|
WriteQueuedLogs(-1); // Write all remaining logs
|
|
start_called.store(false);
|
|
}
|
|
|
|
// Add a log entry to the queue for processing
|
|
static void LogQueueAdd(LogType log_type, char *text, int len, const char *cat_text = nullptr)
|
|
{
|
|
logq_t *logq;
|
|
struct tm *tm;
|
|
time_t now;
|
|
|
|
// Allocate log queue entry
|
|
if ((logq = static_cast<logq_t*>(calloc(1, sizeof(logq_t)))) == nullptr) {
|
|
free(text);
|
|
fprintf(stderr, "%s: %u: Unable to allocate %zu bytes\n", __FUNCTION__, __LINE__, sizeof(logq_t));
|
|
return;
|
|
}
|
|
|
|
// Allocate text buffer
|
|
if ((logq->text = static_cast<char*>(calloc(len + 1, sizeof(char)))) == nullptr) {
|
|
free(text);
|
|
free(logq);
|
|
fprintf(stderr, "%s: %u: Unable to allocate %i bytes\n", __FUNCTION__, __LINE__, len + 1);
|
|
return;
|
|
}
|
|
|
|
// Set timestamp
|
|
now = time(nullptr);
|
|
tm = localtime(&now);
|
|
|
|
// Initialize log entry
|
|
logq->log_type = log_type;
|
|
snprintf(logq->date, DATE_MAX + 1, "%02i:%02i:%02i", tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
strncpy(logq->name, cat_text == nullptr || cat_text[0] == '\0' ?
|
|
log_type_info[log_type].name : cat_text, LOG_NAME_MAX);
|
|
strncpy(logq->text, text, len);
|
|
free(text);
|
|
|
|
// Ensure logging system is started
|
|
if (!start_called.load())
|
|
LogStart();
|
|
|
|
// Add to queue
|
|
{
|
|
std::lock_guard<std::mutex> lock(log_mutex);
|
|
tail.prev->next = logq;
|
|
logq->prev = tail.prev;
|
|
logq->next = &tail;
|
|
tail.prev = logq;
|
|
++num_logqs;
|
|
}
|
|
}
|
|
|
|
// Get the minimum log level for a specific log type
|
|
int8_t GetLoggerLevel(LogType type)
|
|
{
|
|
return log_type_info[type].level;
|
|
}
|
|
|
|
#ifndef PARSER
|
|
// Main logging function - formats message and queues for output
|
|
void LogWrite(LogType type, int8_t log_level, const char *cat_text, const char *fmt, ...)
|
|
{
|
|
int count, size = 64;
|
|
char *buf;
|
|
va_list ap;
|
|
|
|
// Check if logging is enabled and level is appropriate
|
|
if (!log_type_info[type].enabled || (log_level > 0 && log_type_info[type].level < log_level))
|
|
return;
|
|
|
|
// Dynamically allocate buffer for formatted message
|
|
while (true) {
|
|
if ((buf = static_cast<char*>(malloc(size))) == nullptr) {
|
|
fprintf(stderr, "%s: %i: Unable to allocate %i bytes\n", __FUNCTION__, __LINE__, size);
|
|
return;
|
|
}
|
|
|
|
va_start(ap, fmt);
|
|
count = vsnprintf(buf, size, fmt, ap);
|
|
va_end(ap);
|
|
|
|
if (count > -1 && count < size)
|
|
break;
|
|
|
|
free(buf);
|
|
if (count > 1)
|
|
size = count + 1;
|
|
else
|
|
size *= 2;
|
|
}
|
|
|
|
LogQueueAdd(type, buf, count, cat_text);
|
|
}
|
|
|
|
#else
|
|
// Parser-specific logging function - writes directly without queueing
|
|
void LogWrite(LogType type, int8_t log_level, const char *cat_text, const char *format, ...)
|
|
{
|
|
// Check if logging should proceed
|
|
if (!format || !log_type_info[type].enabled ||
|
|
(log_level > 0 && log_type_info[type].level < log_level))
|
|
return;
|
|
|
|
time_t clock;
|
|
struct tm *tm;
|
|
char buffer[LOG_BUFFER_SIZE], date[32];
|
|
va_list args;
|
|
FILE *f;
|
|
size_t cat_text_len = 0;
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
memset(date, 0, sizeof(date));
|
|
|
|
// Format the log message
|
|
va_start(args, format);
|
|
vsnprintf(buffer, sizeof(buffer) - 1, format, args);
|
|
va_end(args);
|
|
|
|
// Generate timestamp
|
|
time(&clock);
|
|
tm = localtime(&clock);
|
|
snprintf(date, sizeof(date)-1, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
|
|
cat_text_len = strlen(cat_text);
|
|
|
|
// Write to log file if enabled
|
|
if (log_type_info[type].logfile) {
|
|
char exename[200] = "";
|
|
|
|
#ifdef LOGIN
|
|
snprintf(exename, sizeof(exename), "login");
|
|
#elif defined WORLD
|
|
snprintf(exename, sizeof(exename), "world");
|
|
#elif defined PARSER
|
|
snprintf(exename, sizeof(exename), "parser");
|
|
#elif defined PATCHER
|
|
snprintf(exename, sizeof(exename), "patcher");
|
|
#endif
|
|
|
|
char filename[200], log_header[200] = "";
|
|
|
|
// Generate filename with or without PID
|
|
#ifndef NO_PIDLOG
|
|
snprintf(filename, sizeof(filename)-1, "logs/%04d-%02d-%02d_eq2%s_%04i.log",
|
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, exename, getpid());
|
|
#else
|
|
snprintf(filename, sizeof(filename)-1, "logs/%04d-%02d-%02d_eq2%s.log",
|
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, exename);
|
|
#endif
|
|
|
|
// Check if new log file and create header
|
|
f = fopen(filename, "r");
|
|
if (!f)
|
|
snprintf(log_header, sizeof(log_header), "===[ New log '%s' started ]===\n\n", filename);
|
|
else
|
|
fclose(f);
|
|
|
|
// Write log entry
|
|
f = fopen(filename, "a");
|
|
if (f) {
|
|
if (strlen(log_header) > 0)
|
|
fprintf(f, "%s\n", log_header);
|
|
fprintf(f, "%s %s %s: %s\n", date, log_type_info[type].display_name, cat_text, buffer);
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
// Write to console if enabled
|
|
if (log_type_info[type].console) {
|
|
printf("%s %s %s: %s\n", date, log_type_info[type].display_name,
|
|
cat_text_len == 0 ? log_type_info[type].name : cat_text, buffer);
|
|
}
|
|
}
|
|
|
|
// Colorize console output for parser (simplified for Linux)
|
|
void ColorizeLog(int color, char *date, const char *display_name, const char *category, std::string buffer)
|
|
{
|
|
printf("%s ", date);
|
|
SetConsoleColor(color);
|
|
printf("%s ", display_name);
|
|
SetConsoleColor(FOREGROUND_WHITE_BOLD);
|
|
printf("%s: ", category);
|
|
SetConsoleColor(color);
|
|
printf("%s\n", buffer.c_str());
|
|
SetConsoleColor(FOREGROUND_WHITE);
|
|
}
|
|
#endif
|
|
|
|
// Find log type status by category and type name
|
|
LogTypeStatus* GetLogTypeStatus(const char *category, const char *type)
|
|
{
|
|
char combined[256];
|
|
int i;
|
|
|
|
memset(combined, 0, sizeof(combined));
|
|
snprintf(combined, sizeof(combined) - 1, "%s__%s", category, type);
|
|
|
|
for (i = 0; i < NUMBER_OF_LOG_TYPES; i++) {
|
|
if (strcasecmp(log_type_info[i].name, combined) == 0)
|
|
return &log_type_info[i];
|
|
}
|
|
|
|
return &log_type_info[NUMBER_OF_LOG_TYPES];
|
|
}
|
|
|
|
// Process XML configuration for a log category
|
|
void ProcessLogConfig(XMLNode node)
|
|
{
|
|
int i;
|
|
const char *category, *type, *level, *color, *enabled, *logs;
|
|
LogTypeStatus *lfs;
|
|
XMLNode child;
|
|
|
|
// Get category attribute
|
|
category = node.getAttribute("Category");
|
|
if (!category) {
|
|
LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing a Category");
|
|
return;
|
|
}
|
|
|
|
// Process each ConfigType child node
|
|
for (i = 0; i < node.nChildNode("ConfigType"); i++) {
|
|
child = node.getChildNode("ConfigType", i);
|
|
type = child.getAttribute("Type");
|
|
|
|
if (!type) {
|
|
LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing a Type");
|
|
continue;
|
|
}
|
|
|
|
// Get log type status and configuration attributes
|
|
lfs = GetLogTypeStatus(category, type);
|
|
level = child.getAttribute("Level");
|
|
enabled = child.getAttribute("Enabled");
|
|
color = child.getAttribute("Color");
|
|
logs = child.getAttribute("Logs");
|
|
|
|
// Validate logs attribute
|
|
if (!logs) {
|
|
LogWrite(MISC__WARNING, 0, "Misc",
|
|
"Error parsing log config. Config missing 'Logs' attribute to specify which log(s) to write to");
|
|
continue;
|
|
}
|
|
if (!IsNumber(logs)) {
|
|
LogWrite(MISC__WARNING, 0, "Misc",
|
|
"Error parsing log config. Attribute 'Logs' must be a number. See LogTypes.h for the valid types.");
|
|
continue;
|
|
}
|
|
|
|
// Set enabled status
|
|
if (enabled) {
|
|
if (!strcasecmp("true", enabled) || !strcasecmp("on", enabled))
|
|
lfs->enabled = true;
|
|
else if (!strcasecmp("false", enabled) || !strcasecmp("off", enabled))
|
|
lfs->enabled = false;
|
|
else
|
|
LogWrite(MISC__WARNING, 0, "Misc",
|
|
"Error parsing log config. Log setting 'Enabled' has invalid value '%s'. "
|
|
"'true'/'on' or 'false'/'off' are valid values", enabled);
|
|
}
|
|
|
|
// Set log level
|
|
if (IsNumber(level))
|
|
lfs->level = atoi(level);
|
|
else
|
|
lfs->level = 0;
|
|
|
|
// Set color
|
|
if (color) {
|
|
if (IsNumber(color))
|
|
lfs->color = atoi(color);
|
|
else if (!strcasecmp("White", color))
|
|
lfs->color = FOREGROUND_WHITE;
|
|
else if (!strcasecmp("Green", color))
|
|
lfs->color = FOREGROUND_GREEN;
|
|
else if (!strcasecmp("Yellow", color))
|
|
lfs->color = FOREGROUND_YELLOW;
|
|
else if (!strcasecmp("Red", color))
|
|
lfs->color = FOREGROUND_RED;
|
|
else if (!strcasecmp("Blue", color))
|
|
lfs->color = FOREGROUND_BLUE;
|
|
else if (!strcasecmp("Cyan", color))
|
|
lfs->color = FOREGROUND_CYAN;
|
|
else if (!strcasecmp("Magenta", color))
|
|
lfs->color = FOREGROUND_MAGENTA;
|
|
else if (!strcasecmp("WhiteBold", color))
|
|
lfs->color = FOREGROUND_WHITE_BOLD;
|
|
else if (!strcasecmp("GreenBold", color))
|
|
lfs->color = FOREGROUND_GREEN_BOLD;
|
|
else if (!strcasecmp("YellowBold", color))
|
|
lfs->color = FOREGROUND_YELLOW_BOLD;
|
|
else if (!strcasecmp("RedBold", color))
|
|
lfs->color = FOREGROUND_RED_BOLD;
|
|
else if (!strcasecmp("BlueBold", color))
|
|
lfs->color = FOREGROUND_BLUE_BOLD;
|
|
else if (!strcasecmp("CyanBold", color))
|
|
lfs->color = FOREGROUND_CYAN_BOLD;
|
|
else if (!strcasecmp("MagentaBold", color))
|
|
lfs->color = FOREGROUND_MAGENTA_BOLD;
|
|
else
|
|
LogWrite(MISC__WARNING, 0, "Misc",
|
|
"Error parsing log config. Log setting 'Color' has invalid value '%s'", color);
|
|
}
|
|
|
|
// Set output destinations
|
|
lfs->logfile = (atoi(logs) & LOG_LOGFILE);
|
|
lfs->console = (atoi(logs) & LOG_CONSOLE);
|
|
lfs->client = (atoi(logs) & LOG_CLIENT);
|
|
}
|
|
}
|
|
|
|
// Parse log configuration from XML file
|
|
bool LogParseConfigs()
|
|
{
|
|
XMLNode main_node;
|
|
int i;
|
|
|
|
main_node = XMLNode::openFileHelper("log_config.xml", "EQ2EmuLogConfigs");
|
|
if (main_node.isEmpty()) {
|
|
LogWrite(MISC__WARNING, 0, "Misc",
|
|
"Unable to parse the file 'log_config.xml' or it does not exist. Default values will be used");
|
|
return false;
|
|
}
|
|
|
|
// Process each LogConfig node
|
|
for (i = 0; i < main_node.nChildNode("LogConfig"); i++)
|
|
ProcessLogConfig(main_node.getChildNode("LogConfig", i));
|
|
|
|
return true;
|
|
} |