From 5adcab2223c1f64550f24c2b1d49d1299ceb69d5 Mon Sep 17 00:00:00 2001 From: brain Date: Fri, 18 Aug 2006 22:01:26 +0000 Subject: NONBLOCKING LOGGER! git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@4971 e03df62e-2008-0410-955e-edbf42e46eb7 --- include/inspircd.h | 53 ++++++++++++++++++++++++++++++++++++++-- src/helperfuncs.cpp | 8 +++++-- src/inspircd.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 4 deletions(-) diff --git a/include/inspircd.h b/include/inspircd.h index 44f5b519c..d72b5f5b7 100644 --- a/include/inspircd.h +++ b/include/inspircd.h @@ -125,6 +125,55 @@ class serverstats : public classbase } }; +class InspIRCd; + +/** This class implements a nonblocking log-writer. + * Most people writing an ircd give little thought to their disk + * i/o. On a congested system, disk writes can block for long + * periods of time (e.g. if the system is busy and/or swapping + * a lot). If we just use a blocking fprintf() call, this could + * block for undesirable amounts of time (half of a second through + * to whole seconds). We DO NOT want this, so we make our logfile + * nonblocking and hook it into the SocketEngine. + */ +class FileLogger : public EventHandler +{ + protected: + /** The creator/owner of this object + */ + InspIRCd* ServerInstance; + /** The log file (fd is inside this somewhere, + * we get it out with fileno()) + */ + FILE* log; + /** Buffer of pending log lines to be written + */ + std::string buffer; + /** Number of write operations that have occured + */ + int writeops; + public: + /** The constructor takes an already opened logfile + */ + FileLogger(InspIRCd* Instance, FILE* logfile); + /** This returns false, logfiles are writeable + */ + bool Readable(); + /** Handle pending write events + */ + void HandleEvent(EventType et); + /** Write one or more preformatted log lines + */ + void WriteLogLine(const std::string &line); + /** Close the log file and cancel any events. + */ + void Close(); + /** Close the log file and cancel any events. + * (indirectly call Close() + */ + ~FileLogger(); +}; + class XLineManager; /** The main singleton class of the irc server. @@ -279,9 +328,9 @@ class InspIRCd : public classbase */ socklen_t length; - /** Used to count iterations around the mainloop + /** Nonblocking file writer */ - int iterations; + FileLogger* Logger; public: /** Time this ircd was booted diff --git a/src/helperfuncs.cpp b/src/helperfuncs.cpp index cc4b8355f..e4be94ec0 100644 --- a/src/helperfuncs.cpp +++ b/src/helperfuncs.cpp @@ -83,8 +83,8 @@ void InspIRCd::Log(int level, const std::string &text) if (Config->log_file && Config->writelog) { - fprintf(Config->log_file,"%s %s\n",TIMESTR,text.c_str()); - fflush(Config->log_file); + std::string out = std::string(TIMESTR) + text.c_str() + "\n"; + this->Logger->WriteLogLine(out); } if (Config->nofork) @@ -446,6 +446,8 @@ void InspIRCd::OpenLog(char** argv, int argc) printf("ERROR: Could not write to logfile %s, bailing!\n\n",Config->logpath.c_str()); Exit(ERROR); } + + this->Logger = new FileLogger(this, Config->log_file); return; } @@ -456,6 +458,8 @@ void InspIRCd::OpenLog(char** argv, int argc) printf("ERROR: Could not write to logfile %s, bailing!\n\n",Config->logpath.c_str()); Exit(ERROR); } + + this->Logger = new FileLogger(this, Config->log_file); } void InspIRCd::CheckRoot() diff --git a/src/inspircd.cpp b/src/inspircd.cpp index 18d289381..5b2d5aac2 100644 --- a/src/inspircd.cpp +++ b/src/inspircd.cpp @@ -50,6 +50,7 @@ #include "command_parse.h" using irc::sockets::NonBlocking; +using irc::sockets::Blocking; using irc::sockets::insp_ntoa; using irc::sockets::insp_inaddr; using irc::sockets::insp_sockaddr; @@ -792,3 +793,71 @@ time_t InspIRCd::Time() return TIME; } +bool FileLogger::Readable() +{ + return false; +} + +void FileLogger::HandleEvent(EventType et) +{ + this->WriteLogLine(""); + ServerInstance->SE->DelFd(this); +} + +void FileLogger::WriteLogLine(const std::string &line) +{ + if (line.length()) + buffer.append(line); + + if (log) + { + int written = fprintf(log,"%s",buffer.c_str()); + if ((written >= 0) && (written < (int)buffer.length())) + { + buffer.erase(0, buffer.length()); + ServerInstance->SE->AddFd(this); + } + else if (written == -1) + { + if (errno == EAGAIN) + ServerInstance->SE->AddFd(this); + } + else + { + /* Wrote the whole buffer, and no need for write callback */ + buffer = ""; + } + } + if (writeops++ % 20) + { + fflush(log); + } +} + +void FileLogger::Close() +{ + if (log) + { + int flags = fcntl(fileno(log), F_GETFL, 0); + fcntl(fileno(log), F_SETFL, flags ^ O_NONBLOCK); + if (buffer.size()) + fprintf(log,"%s",buffer.c_str()); + fflush(log); + fclose(log); + } + buffer = ""; + ServerInstance->SE->DelFd(this); +} + +FileLogger::FileLogger(InspIRCd* Instance, FILE* logfile) : ServerInstance(Instance), log(logfile), writeops(0) +{ + irc::sockets::NonBlocking(fileno(log)); + this->SetFd(fileno(log)); + buffer = ""; +} + +FileLogger::~FileLogger() +{ + this->Close(); +} + -- cgit v1.2.3