# HG changeset patch # User Sebastien Jodogne # Date 1465823273 -7200 # Node ID bcc575732aefd275de5b9c30f458b632321106eb # Parent e39a2657f1c6c01003b52443f4dd2569d8a99c0b New option "--logfile" to output the Orthanc log to the given file diff -r e39a2657f1c6 -r bcc575732aef Core/Logging.cpp --- 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 file_; - LoggingState() : + LoggingContext() : infoEnabled_(false), traceEnabled_(false), error_(&std::cerr), @@ -109,7 +123,7 @@ -static std::auto_ptr loggingState_; +static std::auto_ptr loggingContext_; static boost::mutex loggingMutex_; @@ -161,9 +175,9 @@ } - static void PrepareLogFile(std::auto_ptr& file, - const std::string& suffix, - const std::string& directory) + static void PrepareLogFolder(std::auto_ptr& 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 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& 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(); + } + } } } diff -r e39a2657f1c6 -r bcc575732aef Core/Logging.h --- 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 diff -r e39a2657f1c6 -r bcc575732aef NEWS --- 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) diff -r e39a2657f1c6 -r bcc575732aef OrthancServer/main.cpp --- 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 {