summaryrefslogtreecommitdiffstats
path: root/src/utils
diff options
context:
space:
mode:
authorYohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer>2010-09-29 20:39:56 +0200
committerYohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer>2010-09-29 20:41:41 +0200
commitf438a7bc612e94bfcd1ef2b61a7d5b61ce2eaaa6 (patch)
treee4be8a023eaecc238c72f2eca9f3883816151863 /src/utils
parent992cd35b0d1c0e6a6bc543960d6d6720b5429e89 (diff)
downloadmanaserv-f438a7bc612e94bfcd1ef2b61a7d5b61ce2eaaa6.tar.gz
manaserv-f438a7bc612e94bfcd1ef2b61a7d5b61ce2eaaa6.tar.xz
manaserv-f438a7bc612e94bfcd1ef2b61a7d5b61ce2eaaa6.zip
Add log file rotation support based on ExceptionFault's work.
This patch adds options to enable log rotations base on files size and or change of date. Note: Zip support will be added in a second commit. Reviewed-by: CodyMartin, Thorbjorn.
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/logger.cpp154
-rw-r--r--src/utils/logger.h43
-rw-r--r--src/utils/string.hpp14
3 files changed, 192 insertions, 19 deletions
diff --git a/src/utils/logger.cpp b/src/utils/logger.cpp
index 71f92a8..45a68eb 100644
--- a/src/utils/logger.cpp
+++ b/src/utils/logger.cpp
@@ -1,6 +1,7 @@
/*
* The Mana Server
* Copyright (C) 2004-2010 The Mana World Development Team
+ * Copyright (C) 2010 The Mana Development Team
*
* This file is part of The Mana Server.
*
@@ -19,6 +20,8 @@
*/
#include "logger.h"
+#include "common/resourcemanager.hpp"
+#include "utils/string.hpp"
#include <ctime>
#include <fstream>
@@ -31,11 +34,24 @@
namespace utils
{
-
-static std::ofstream mLogFile; /**< Log file. */
-bool Logger::mHasTimestamp = true; /**< Timestamp flag. */
-bool Logger::mTeeMode = false; /**< Tee mode flag. */
-Logger::Level Logger::mVerbosity = Logger::Info; /**< Verbosity level. */
+/** Log file. */
+static std::ofstream mLogFile;
+/** current log filename */
+std::string Logger::mFilename = "";
+/** Timestamp flag. */
+bool Logger::mHasTimestamp = true;
+/** Tee mode flag. */
+bool Logger::mTeeMode = false;
+/** Verbosity level. */
+Logger::Level Logger::mVerbosity = Logger::Info;
+/** Enables logrotation by size of the logfile. */
+bool Logger::mLogRotation = false;
+/** Maximum size of current logfile. */
+long Logger::mMaxFileSize = 1024; // 1 Mb
+/** Switch log file each day. */
+bool Logger::mSwitchLogEachDay = false;
+/** Last call date */
+static std::string mLastCallDate = "";
/**
* Gets the current time.
@@ -47,29 +63,72 @@ static std::string getCurrentTime()
time_t now;
tm local;
- // get current time_t value
+ // Get current time_t value
time(&now);
- // convert time_t to tm struct to break the time into individual
- // constituents
+ // Convert time_t to tm struct to break the time into individual
+ // constituents.
local = *(localtime(&now));
- // stringify the time, the format is: [hh:mm:ss]
+ // Stringify the time, the format is: [hh-mm-ss]
using namespace std;
ostringstream os;
- os << "[" << setw(2) << setfill('0') << local.tm_hour
- << ":" << setw(2) << setfill('0') << local.tm_min
- << ":" << setw(2) << setfill('0') << local.tm_sec
- << "]";
+ os << setw(2) << setfill('0') << local.tm_hour
+ << "-" << setw(2) << setfill('0') << local.tm_min
+ << "-" << setw(2) << setfill('0') << local.tm_sec;
return os.str();
}
+/**
+ * Gets the current date.
+ *
+ * @return the current date as string.
+ */
+static std::string getCurrentDate()
+{
+ time_t now;
+ tm local;
+
+ // Get current time_t value
+ time(&now);
+
+ // Convert time_t to tm struct to break the time into individual
+ // constituents.
+ local = *(localtime(&now));
+
+ // Stringify the time, the format is: yyyy-mm-dd
+ using namespace std;
+ ostringstream os;
+ os << setw(4) << setfill('0') << (local.tm_year + 1900)
+ << "-" << setw(2) << setfill('0') << local.tm_mon
+ << "-" << setw(2) << setfill('0') << local.tm_mday;
+
+ return os.str();
+}
+
+/**
+ * Check whether the day has changed since the last call.
+ *
+ * @return whether the day has changed.
+ */
+static bool getDayChanged()
+{
+ static std::string date = getCurrentDate();
+ if (mLastCallDate.compare(date))
+ {
+ // Reset the current date for next call.
+ mLastCallDate = date;
+ return true;
+ }
+ return false;
+}
+
void Logger::output(std::ostream &os, const std::string &msg, const char *prefix)
{
if (mHasTimestamp)
{
- os << getCurrentTime() << ' ';
+ os << "[" << getCurrentTime() << "]" << ' ';
}
if (prefix)
@@ -80,7 +139,7 @@ void Logger::output(std::ostream &os, const std::string &msg, const char *prefix
os << msg << std::endl;
}
-void Logger::setLogFile(const std::string &logFile)
+void Logger::setLogFile(const std::string &logFile, bool append)
{
// Close the current log file.
if (mLogFile.is_open())
@@ -88,8 +147,12 @@ void Logger::setLogFile(const std::string &logFile)
mLogFile.close();
}
- // Open the file for output and remove the former file contents.
- mLogFile.open(logFile.c_str(), std::ios::trunc);
+ // Open the file for output
+ // and remove the former file contents depending on the append flag.
+ mLogFile.open(logFile.c_str(),
+ append ? std::ios::ate : std::ios::trunc);
+ mFilename = logFile;
+ mLastCallDate = getCurrentDate();
if (!mLogFile.is_open())
{
@@ -97,7 +160,7 @@ void Logger::setLogFile(const std::string &logFile)
}
else
{
- // by default the streams do not throw any exception
+ // By default the streams do not throw any exception
// let std::ios::failbit and std::ios::badbit throw exceptions.
mLogFile.exceptions(std::ios::failbit | std::ios::badbit);
}
@@ -127,6 +190,7 @@ void Logger::output(const std::string &msg, Level atVerbosity)
if (open)
{
output(mLogFile, msg, prefixes[atVerbosity]);
+ switchLogs();
}
if (!open || mTeeMode)
@@ -137,4 +201,58 @@ void Logger::output(const std::string &msg, Level atVerbosity)
}
}
+void Logger::switchLogs()
+{
+ // Handles logswitch if enabled
+ // and if at least one switch condition permits it.
+ if (!mLogRotation || (mMaxFileSize <= 0 && !mSwitchLogEachDay))
+ return;
+
+ // Update current filesize
+ long mFileSize = mLogFile.tellp();
+
+ if ((mFileSize >= mMaxFileSize * 1024)
+ || (mSwitchLogEachDay && getDayChanged()))
+ {
+ // Close logfile, rename it and open a new one
+ mLogFile.flush();
+ mLogFile.close();
+
+ // Stringify the time, the format is: yyyy-mm-dd_hh-mm-ss-logFilename.
+ using namespace std;
+ ostringstream os;
+ os << getCurrentDate();
+
+ int fileNum = 1;
+ std::string newFileName = os.str() + "-" + toString<int>(fileNum)
+ + "_" + mFilename;
+ // Keeping a hard limit of 100 files per day.
+ while (ResourceManager::exists(newFileName) && fileNum < 100)
+ {
+ fileNum++;
+ newFileName = os.str() + "-" + toString<int>(fileNum)
+ + "_" + mFilename;
+ }
+
+ if (rename(mFilename.c_str(), newFileName.c_str()))
+ {
+ ostringstream errorOs;
+ errorOs << "Error renaming file: " << mFilename << " to: "
+ << newFileName << std::endl << "Continuing on the same log file.";
+ perror(errorOs.str().c_str());
+
+ // Continue appending on the original file.
+ setLogFile(mFilename, true);
+ }
+ else
+ {
+ // Keep the logging after emptying the original log file.
+ setLogFile(mFilename);
+ }
+
+ mLogFile << "---- Continue logging from former file " << os.str()
+ << " ----" << std::endl;
+ }
+}
+
} // namespace utils
diff --git a/src/utils/logger.h b/src/utils/logger.h
index 473e025..ba64b10 100644
--- a/src/utils/logger.h
+++ b/src/utils/logger.h
@@ -1,6 +1,7 @@
/*
* The Mana Server
* Copyright (C) 2004-2010 The Mana World Development Team
+ * Copyright (C) 2010 The Mana Development Team
*
* This file is part of The Mana Server.
*
@@ -88,10 +89,11 @@ class Logger
* contents are removed.
*
* @param logFile the log file name (may include path).
+ * @param append whether the file is cleaned up before logging in.
*
* @exception std::ios::failure if the log file could not be opened.
*/
- static void setLogFile(const std::string &logFile);
+ static void setLogFile(const std::string &logFile, bool append = false);
/**
* Add/removes the timestamp.
@@ -120,6 +122,31 @@ class Logger
{ mVerbosity = verbosity; }
/**
+ * Enable logrotation based on the maximum filesize given in
+ * setMaxLogfileSize.
+ *
+ * @param enable Set to true to enable logrotation.
+ */
+ static void enableLogRotation(bool enable = true)
+ { mLogRotation = enable; }
+
+ /**
+ * Sets the maximum size of a logfile before logrotation occurs.
+ *
+ * @param kiloBytes Maximum size of logfile in bytes. Defaults to 1MB.
+ */
+ static void setMaxLogfileSize(long kiloBytes = 1024)
+ { mMaxFileSize = kiloBytes; }
+
+ /**
+ * Sets whether the logfile switches when changing date.
+ *
+ * @param switchLogEachDay Keeps whether the parameter is activated.
+ */
+ static void setSwitchLogEachDay(bool switchLogEachDay)
+ { mSwitchLogEachDay = switchLogEachDay; }
+
+ /**
* Logs a generic message.
*
* @param msg the message to log.
@@ -136,6 +163,14 @@ class Logger
static bool mHasTimestamp; /**< Timestamp flag. */
static bool mTeeMode; /**< Tee mode flag. */
+ static std::string mFilename; /**< Name of the current logfile. */
+ /** Enable rotation of logfiles by size. */
+ static bool mLogRotation;
+ /** Maximum size of current logfile in bytes */
+ static long mMaxFileSize;
+ /** Sets whether the logfile switches when changing date. */
+ static bool mSwitchLogEachDay;
+
/**
* Logs a generic message.
*
@@ -147,6 +182,12 @@ class Logger
*/
static void output(std::ostream &os, const std::string &msg,
const char *prefix);
+
+ /**
+ * Switch the log file based on a maximum size
+ * and/or and a date change.
+ */
+ static void switchLogs();
};
diff --git a/src/utils/string.hpp b/src/utils/string.hpp
index 9f2b4ac..6127bfe 100644
--- a/src/utils/string.hpp
+++ b/src/utils/string.hpp
@@ -22,6 +22,7 @@
#define UTILS_STRING_H
#include <string>
+#include <sstream>
namespace utils
{
@@ -67,6 +68,19 @@ namespace utils
* @param str the string to trim spaces off
*/
void trim(std::string &str);
+
+ /**
+ * Converts the given value to a string using std::stringstream.
+ *
+ * @param arg the value to convert to a string
+ * @return the string representation of arg
+ */
+ template<typename T> std::string toString(const T &arg)
+ {
+ std::stringstream ss;
+ ss << arg;
+ return ss.str();
+ }
}
#endif // UTILS_STRING_H