changeset 2015:bcc575732aef

New option "--logfile" to output the Orthanc log to the given file
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 13 Jun 2016 15:07:53 +0200
parents e39a2657f1c6
children 0ae26237569a
files Core/Logging.cpp Core/Logging.h NEWS OrthancServer/main.cpp
diffstat 4 files changed, 153 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Logging.cpp	Mon Jun 13 13:39:10 2016 +0200
+++ b/Core/Logging.cpp	Mon Jun 13 15:07:53 2016 +0200
@@ -47,6 +47,14 @@
     {
     }
 
+    void Reset()
+    {
+    }
+
+    void Flush()
+    {
+    }
+
     void EnableInfoLevel(bool enabled)
     {
     }
@@ -55,6 +63,10 @@
     {
     }
 
+    void SetTargetFile(const std::string& path)
+    {
+    }
+
     void SetTargetFolder(const std::string& path)
     {
     }
@@ -85,10 +97,12 @@
 
 namespace
 {
-  struct LoggingState
+  struct LoggingContext
   {
     bool infoEnabled_;
     bool traceEnabled_;
+    std::string  targetFile_;
+    std::string  targetFolder_;
 
     std::ostream* error_;
     std::ostream* warning_;
@@ -96,7 +110,7 @@
 
     std::auto_ptr<std::ofstream> file_;
 
-    LoggingState() : 
+    LoggingContext() : 
       infoEnabled_(false),
       traceEnabled_(false),
       error_(&std::cerr),
@@ -109,7 +123,7 @@
 
 
 
-static std::auto_ptr<LoggingState> loggingState_;
+static std::auto_ptr<LoggingContext> loggingContext_;
 static boost::mutex  loggingMutex_;
 
 
@@ -161,9 +175,9 @@
     }
 
 
-    static void PrepareLogFile(std::auto_ptr<std::ofstream>& file,
-                               const std::string& suffix,
-                               const std::string& directory)
+    static void PrepareLogFolder(std::auto_ptr<std::ofstream>& file,
+                                 const std::string& suffix,
+                                 const std::string& directory)
     {
       boost::filesystem::path log, link;
       GetLogPath(log, link, suffix, directory);
@@ -180,56 +194,125 @@
     void Initialize()
     {
       boost::mutex::scoped_lock lock(loggingMutex_);
-      loggingState_.reset(new LoggingState);
+      loggingContext_.reset(new LoggingContext);
     }
 
     void Finalize()
     {
       boost::mutex::scoped_lock lock(loggingMutex_);
-      loggingState_.reset(NULL);
+      loggingContext_.reset(NULL);
+    }
+
+    void Reset()
+    {
+      // Recover the old logging context
+      std::auto_ptr<LoggingContext> old;
+
+      {
+        boost::mutex::scoped_lock lock(loggingMutex_);
+        if (loggingContext_.get() == NULL)
+        {
+          return;
+        }
+        else
+        {
+          old = loggingContext_;
+
+          // Create a new logging context, 
+          loggingContext_.reset(new LoggingContext);
+        }
+      }
+      
+      EnableInfoLevel(old->infoEnabled_);
+      EnableTraceLevel(old->traceEnabled_);
+
+      if (!old->targetFolder_.empty())
+      {
+        SetTargetFolder(old->targetFolder_);
+      }
+      else if (!old->targetFile_.empty())
+      {
+        SetTargetFile(old->targetFile_);
+      }
     }
 
     void EnableInfoLevel(bool enabled)
     {
       boost::mutex::scoped_lock lock(loggingMutex_);
-      assert(loggingState_.get() != NULL);
+      assert(loggingContext_.get() != NULL);
 
-      loggingState_->infoEnabled_ = enabled;
+      loggingContext_->infoEnabled_ = enabled;
+      
+      if (!enabled)
+      {
+        // Also disable the "TRACE" level when info-level debugging is disabled
+        loggingContext_->traceEnabled_ = false;
+      }
     }
 
     void EnableTraceLevel(bool enabled)
     {
       boost::mutex::scoped_lock lock(loggingMutex_);
-      assert(loggingState_.get() != NULL);
+      assert(loggingContext_.get() != NULL);
 
-      loggingState_->traceEnabled_ = enabled;
+      loggingContext_->traceEnabled_ = enabled;
       
       if (enabled)
       {
         // Also enable the "INFO" level when trace-level debugging is enabled
-        loggingState_->infoEnabled_ = true;
+        loggingContext_->infoEnabled_ = true;
+      }
+    }
+
+
+    static void CheckFile(std::auto_ptr<std::ofstream>& f)
+    {
+      if (loggingContext_->file_.get() == NULL ||
+          !loggingContext_->file_->is_open())
+      {
+        throw OrthancException(ErrorCode_CannotWriteFile);
       }
     }
 
     void SetTargetFolder(const std::string& path)
     {
       boost::mutex::scoped_lock lock(loggingMutex_);
-      assert(loggingState_.get() != NULL);
+      assert(loggingContext_.get() != NULL);
+
+      PrepareLogFolder(loggingContext_->file_, "" /* no suffix */, path);
+      CheckFile(loggingContext_->file_);
 
-      PrepareLogFile(loggingState_->file_, "" /* no suffix */, path);
+      loggingContext_->targetFile_.clear();
+      loggingContext_->targetFolder_ = path;
+      loggingContext_->warning_ = loggingContext_->file_.get();
+      loggingContext_->error_ = loggingContext_->file_.get();
+      loggingContext_->info_ = loggingContext_->file_.get();
+    }
+
 
-      loggingState_->warning_ = loggingState_->file_.get();
-      loggingState_->error_ = loggingState_->file_.get();
-      loggingState_->info_ = loggingState_->file_.get();
+    void SetTargetFile(const std::string& path)
+    {
+      boost::mutex::scoped_lock lock(loggingMutex_);
+      assert(loggingContext_.get() != NULL);
+
+      loggingContext_->file_.reset(new std::ofstream(path.c_str(), std::fstream::app));
+      CheckFile(loggingContext_->file_);
+
+      loggingContext_->targetFile_ = path;
+      loggingContext_->targetFolder_.clear();
+      loggingContext_->warning_ = loggingContext_->file_.get();
+      loggingContext_->error_ = loggingContext_->file_.get();
+      loggingContext_->info_ = loggingContext_->file_.get();
     }
 
+
     InternalLogger::InternalLogger(const char* level,
                                    const char* file,
                                    int line) : 
       lock_(loggingMutex_), 
       stream_(&null_)  // By default, logging to "/dev/null" is simulated
     {
-      if (loggingState_.get() == NULL)
+      if (loggingContext_.get() == NULL)
       {
         fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n");
         return;
@@ -237,8 +320,8 @@
 
       LogLevel l = StringToLogLevel(level);
       
-      if ((l == LogLevel_Info  && !loggingState_->infoEnabled_) ||
-          (l == LogLevel_Trace && !loggingState_->traceEnabled_))
+      if ((l == LogLevel_Info  && !loggingContext_->infoEnabled_) ||
+          (l == LogLevel_Trace && !loggingContext_->traceEnabled_))
       {
         // This logging level is disabled, directly exit and unlock
         // the mutex to speed-up things. The stream is set to "/dev/null"
@@ -292,12 +375,12 @@
 
 
       // The header is computed, we now re-lock the mutex to access
-      // the stream objects. Pay attention that "loggingState_",
+      // the stream objects. Pay attention that "loggingContext_",
       // "infoEnabled_" or "traceEnabled_" might have changed while
       // the mutex was unlocked.
       lock_.lock();
 
-      if (loggingState_.get() == NULL)
+      if (loggingContext_.get() == NULL)
       {
         fprintf(stderr, "ERROR: Trying to log a message after the finalization of the logging engine\n");
         return;
@@ -306,25 +389,25 @@
       switch (l)
       {
         case LogLevel_Error:
-          stream_ = loggingState_->error_;
+          stream_ = loggingContext_->error_;
           break;
 
         case LogLevel_Warning:
-          stream_ = loggingState_->warning_;
+          stream_ = loggingContext_->warning_;
           break;
 
         case LogLevel_Info:
-          if (loggingState_->infoEnabled_)
+          if (loggingContext_->infoEnabled_)
           {
-            stream_ = loggingState_->info_;
+            stream_ = loggingContext_->info_;
           }
 
           break;
 
         case LogLevel_Trace:
-          if (loggingState_->traceEnabled_)
+          if (loggingContext_->traceEnabled_)
           {
-            stream_ = loggingState_->info_;
+            stream_ = loggingContext_->info_;
           }
 
           break;
@@ -360,6 +443,16 @@
     }
       
 
+    void Flush()
+    {
+      boost::mutex::scoped_lock lock(loggingMutex_);
+
+      if (loggingContext_.get() != NULL &&
+          loggingContext_->file_.get() != NULL)
+      {
+        loggingContext_->file_->flush();
+      }
+    }
   }
 }
 
--- a/Core/Logging.h	Mon Jun 13 13:39:10 2016 +0200
+++ b/Core/Logging.h	Mon Jun 13 15:07:53 2016 +0200
@@ -42,10 +42,16 @@
 
     void Finalize();
 
+    void Reset();
+
+    void Flush();
+
     void EnableInfoLevel(bool enabled);
 
     void EnableTraceLevel(bool enabled);
 
+    void SetTargetFile(const std::string& path);
+
     void SetTargetFolder(const std::string& path);
 
     struct NullStream : public std::ostream 
--- a/NEWS	Mon Jun 13 13:39:10 2016 +0200
+++ b/NEWS	Mon Jun 13 15:07:53 2016 +0200
@@ -43,6 +43,7 @@
 Maintenance
 -----------
 
+* New option "--logfile" to output the Orthanc log to the given file
 * Support of SIGHUP signal (restart Orthanc only if the configuration files have changed)
 * New logo of Orthanc
 * Fix issue 11 (is_regular_file() fails for FILE_ATTRIBUTE_REPARSE_POINT)
--- a/OrthancServer/main.cpp	Mon Jun 13 13:39:10 2016 +0200
+++ b/OrthancServer/main.cpp	Mon Jun 13 15:07:53 2016 +0200
@@ -443,7 +443,9 @@
     << "Command-line options:" << std::endl
     << "  --help\t\tdisplay this help and exit" << std::endl
     << "  --logdir=[dir]\tdirectory where to store the log files" << std::endl
-    << "\t\t\t(if not used, the logs are dumped to stderr)" << std::endl
+    << "\t\t\t(by default, the log is dumped to stderr)" << std::endl
+    << "  --logfile=[file]\tfile where to store the log of Orthanc" << std::endl
+    << "\t\t\t(by default, the log is dumped to stderr)" << std::endl
     << "  --config=[file]\tcreate a sample configuration file and exit" << std::endl
     << "  --errors\t\tprint the supported error codes and exit" << std::endl
     << "  --verbose\t\tbe verbose in logs" << std::endl
@@ -661,15 +663,19 @@
     if (!restart && 
         event == ServerBarrierEvent_Reload)
     {
+      // Handling of SIGHUP
+
       if (Configuration::HasConfigurationChanged())
       {
         LOG(WARNING) << "A SIGHUP signal has been received, resetting Orthanc";
+        Logging::Flush();
         restart = true;
         break;
       }
       else
       {
         LOG(WARNING) << "A SIGHUP signal has been received, but is ignored as the configuration has not changed";
+        Logging::Flush();
         continue;
       }
     }
@@ -1148,6 +1154,21 @@
         return -1;
       }
     }
+    else if (boost::starts_with(argument, "--logfile="))
+    {
+      std::string file = argument.substr(10);
+
+      try
+      {
+        Logging::SetTargetFile(file);
+      }
+      catch (OrthancException&)
+      {
+        LOG(ERROR) << "Cannot write to the specified log file (" 
+                   << file << "), aborting.";
+        return -1;
+      }
+    }
     else if (argument == "--upgrade")
     {
       allowDatabaseUpgrade = true;
@@ -1208,6 +1229,8 @@
       if (restart)
       {
         OrthancFinalize();
+        LOG(WARNING) << "Logging system is resetting";
+        Logging::Reset();
       }
       else
       {