Skip to content
Snippets Groups Projects
Commit bef5a541 authored by Nick Draper's avatar Nick Draper
Browse files

re #720

Loggers should not leak now and can be disabled by calling setEnabledfalse)
parent ad0cdca1
No related branches found
No related tags found
No related merge requests found
......@@ -8,6 +8,7 @@
#include "MantidGeometry/ICompAssembly.h"
#include "MantidGeometry/Detector.h"
#include <boost/shared_ptr.hpp>
#include <map>
#include <string>
#include <ostream>
......
......@@ -9,7 +9,8 @@
#include "MantidGeometry/CompAssembly.h"
#include "MantidGeometry/ObjComponent.h"
#include "MantidGeometry/Detector.h"
#include <string>
#include <string>
#include <map>
#include <ostream>
namespace Mantid
......
......@@ -8,6 +8,7 @@
#include "MantidGeometry/Component.h"
#include <boost/shared_ptr.hpp>
#include <vector>
#include <map>
namespace Mantid
{
......
......@@ -5,8 +5,8 @@
// Includes
//----------------------------------------------------------------------
#include <vector>
#include "MantidKernel/System.h"
//#include "MantidKernel/DynamicFactory.h"
#include <map>
#include "MantidKernel/System.h"
#include "MantidKernel/Instantiator.h"
#include "MantidKernel/SingletonHolder.h"
#include "MantidKernel/Logger.h"
......
#ifndef Rules_h
#define Rules_h
#include <map>
namespace Mantid
{
......
......@@ -6,8 +6,8 @@
//----------------------------------------------------------------------
#include "System.h"
#include <string>
#include <set>
#include <exception>
#include <map>
#include <ostream>
#include <streambuf>
......@@ -19,6 +19,8 @@ namespace Poco
{
class Logger;
class LogStream;
class NullChannel;
class Mutex;
}
/// @endcond
......@@ -70,6 +72,7 @@ namespace Kernel
PRIO_FATAL = 1, ///< A fatal error. The application will most likely terminate. This is the highest priority.
PRIO_ERROR = 3, ///< An error. An operation did not complete successfully, but the application as a whole is not affected.
PRIO_WARNING = 4, ///< A warning. An operation completed with an unexpected result.
PRIO_NOTICE = 5, ///< An informational message, usually denoting the successful completion of an Algorithm, These are the headlines of what we should be reporting to the user.
PRIO_INFORMATION = 6, ///< An informational message, usually denoting the successful completion of an operation.
PRIO_DEBUG = 7 ///< A debugging message.This is the lowest priority.
};
......@@ -77,36 +80,67 @@ namespace Kernel
void fatal(const std::string& msg);
void error(const std::string& msg);
void warning(const std::string& msg);
void notice(const std::string& msg);
void information(const std::string& msg);
void debug(const std::string& msg);
std::ostream& fatal();
std::ostream& error();
std::ostream& warning();
std::ostream& notice();
std::ostream& information();
std::ostream& debug();
/// Logs the given message at debug level, followed by the data in buffer.
void dump(const std::string& msg, const void* buffer, std::size_t length);
/// Sets the Logger's log level.
void setLevel(int level);
/// Returns the Logger's log level.
int getLevel() const;
/// Sets the Logger's log level using a symbolic value.
///
/// Valid values are:
/// - fatal
/// - critical
/// - error
/// - warning
/// - notice
/// - information
/// - debug
void setLevel(const std::string& level);
///returns true if the log is enabled
bool getEnabled() const;
///set if the logging is enabled
void setEnabled(const bool enabled);
/// Returns true if at least the given log level is set.
bool is(int level) const;
/// releases resources and deletes this object
void release();
/// Returns a reference to the Logger with the given name.
static Logger& get(const std::string& name);
//destroy the given logger and releases resources
static void Logger::destroy(Logger& logger);
/// Shuts down the logging framework and releases all Loggers.
static void shutdown();
~Logger();
protected:
/// Protected constructor called by static get method
Logger(const std::string& name);
/// Protected destructor - call release instead
~Logger();
private:
Logger();
/// Overload of = operator
......@@ -114,11 +148,24 @@ namespace Kernel
/// Internal handle to third party logging objects
Poco::Logger& _log;
///This pointer is owned by this class, initialized in the constructor and deleted in the destructor
///A Log stream to allow streaming operations. This pointer is owned by this class, initialized in the constructor and deleted in the destructor
Poco::LogStream* _logStream;
///A Null stream, used when the logger is disabled. This pointer is owned by this class, initialized in the constructor and deleted in the destructor
Poco::LogStream* _nullStream;
/// a null channell used to create the null stream
Poco::NullChannel* _nullChannel;
///returns the correct stream depending on the enabled status
Poco::LogStream* getStream();
/// Name of this logging object
std::string _name;
/// The state of this logger, disabled loggers send no messages
bool _enabled;
typedef std::set<Logger*> LoggerList;
static LoggerList* m_LoggerList;
static Poco::Mutex m_ListMtx;
};
} // namespace Kernel
......
......@@ -81,9 +81,9 @@ namespace Mantid
*/
ConfigServiceImpl::~ConfigServiceImpl()
{
Kernel::Logger::shutdown();
delete m_pSysConfig;
delete m_pConf; // potential double delete???
// g_log.debug() << "ConfigService destroyed." << std::endl;
}
/**
......
#include "MantidKernel/Logger.h"
#include <Poco/Logger.h>
#include <Poco/LogStream.h>
#include <Poco/Message.h>
#include <Poco/Message.h>
#include <Poco/Mutex.h>
#include <Poco/NullChannel.h>
#include <iostream>
#include <sstream>
namespace Mantid
{
namespace Kernel
{
/** Constructor
* @param name The class name invoking this logger
*/
Logger::Logger(const std::string& name): _log(Poco::Logger::get(name))
{
_name = name;
_logStream = new Poco::LogStream(_log);
}
///destructor
Logger::~Logger()
{
delete (_logStream);
}
/** If the Logger's log level is at least PRIO_FATAL, creates a Message with
* priority PRIO_FATAL and the given message text and sends it to the attached channel.
*
* @param msg The message to log.
*/
void Logger::fatal(const std::string& msg)
{
try
{
_log.fatal(msg);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
/** If the Logger's log level is at least PRIO_ERROR, creates a Message with priority
* PRIO_ERROR and the given message text and sends it to the attached channel.
*
* @param msg The message to log.
*/
void Logger::error(const std::string& msg)
namespace Kernel
{
try
Logger::LoggerList* Logger::m_LoggerList = 0;
Poco::Mutex Logger::m_ListMtx;
/** Constructor
* @param name The class name invoking this logger
*/
Logger::Logger(const std::string& name): _log(Poco::Logger::get(name)),_enabled(true)
{
_name = name;
_logStream = new Poco::LogStream(_log);
_nullChannel = new Poco::NullChannel;
Poco::Logger& nullLogger = Poco::Logger::get("NULL");
nullLogger.setChannel(_nullChannel);
_nullStream = new Poco::LogStream(nullLogger,Poco::Message::PRIO_FATAL);
}
///destructor
Logger::~Logger()
{
_log.error(msg);
}
catch (std::exception& e)
delete (_logStream);
delete (_nullStream);
delete (_nullChannel);
}
/** Returns true if the log is enabled
*
* @retval true - logging is enabled
* @retval false - all messages are ignored.
*/
bool Logger::getEnabled() const
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
return _enabled;
}
}
/** If the Logger's log level is at least PRIO_WARNING, creates a Message with
* priority PRIO_WARNING and the given message text and sends it to the attached channel.
*
* @param msg The message to log.
*/
void Logger::warning(const std::string& msg)
{
try
/** set if the logging is enabled
*
* @param enabled true - logging is enabled, false - all messages are ignored.
*/
void Logger::setEnabled(const bool enabled)
{
_log.warning(msg);
}
catch (std::exception& e)
_enabled = enabled;
}
/** If the Logger's log level is at least PRIO_FATAL, creates a Message with
* priority PRIO_FATAL and the given message text and sends it to the attached channel.
*
* @param msg The message to log.
*/
void Logger::fatal(const std::string& msg)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
if(_enabled)
{
try
{
_log.fatal(msg);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
}
}
/** If the Logger's log level is at least PRIO_INFORMATION, creates a Message with
* priority PRIO_INFORMATION and the given message text and sends it to the
* attached channel.
*
* @param msg The message to log.
*/
void Logger::information(const std::string& msg)
{
try
/** If the Logger's log level is at least PRIO_ERROR, creates a Message with priority
* PRIO_ERROR and the given message text and sends it to the attached channel.
*
* @param msg The message to log.
*/
void Logger::error(const std::string& msg)
{
_log.information(msg);
}
catch (std::exception& e)
if(_enabled)
{
try
{
_log.error(msg);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
}
/** If the Logger's log level is at least PRIO_WARNING, creates a Message with
* priority PRIO_WARNING and the given message text and sends it to the attached channel.
*
* @param msg The message to log.
*/
void Logger::warning(const std::string& msg)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
if(_enabled)
{
try
{
_log.warning(msg);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
}
}
/** If the Logger's log level is at least PRIO_DEBUG, creates a Message with priority
* PRIO_DEBUG and the given message text and sends it to the attached channel.
*
* @param msg The message to log.
*/
void Logger::debug(const std::string& msg)
{
try
{
_log.debug(msg);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
/** Logs the given message at debug level, followed by the data in buffer.
*
* The data in buffer is written in canonical hex+ASCII form:
* Offset (4 bytes) in hexadecimal, followed by sixteen space-separated,
* two column, hexadecimal bytes, followed by the same sixteen bytes as
* ASCII characters.
* For bytes outside the range 32 .. 127, a dot is printed.
* Note all Dump messages go out at Debug message level
*
* @param msg The message to log
* @param buffer the binary data to log
* @param length The length of the binaary data to log
*/
void Logger::dump(const std::string& msg, const void* buffer, std::size_t length)
{
try
/** If the Logger's log level is at least PRIO_NOTICE, creates a Message with
* priority PRIO_NOTICE and the given message text and sends it to the attached channel.
*
* @param msg The message to log.
*/
void Logger::notice(const std::string& msg)
{
_log.dump(msg,buffer,length);
}
catch (std::exception& e)
if(_enabled)
{
try
{
_log.notice(msg);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
}
/** If the Logger's log level is at least PRIO_INFORMATION, creates a Message with
* priority PRIO_INFORMATION and the given message text and sends it to the
* attached channel.
*
* @param msg The message to log.
*/
void Logger::information(const std::string& msg)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
if(_enabled)
{
try
{
_log.information(msg);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
}
}
/** Returns true if at least the given log level is set.
* @param level The logging level it is best to use the Logger::Priority enum (7=debug, 6=information, 4=warning, 3=error, 2=critical, 1=fatal)
*/
bool Logger::is(int level) const
{
bool retVal = false;
try
{
retVal = _log.is(level);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
return retVal;
}
/** This class implements an ostream interface to the Logger for fatal messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::fatal()
{
return _logStream->fatal();
}
/** This class implements an ostream interface to the Logger for error messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::error()
{
return _logStream->error();
}
/** This class implements an ostream interface to the Logger for warning messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::warning()
{
return _logStream->warning();
}
/** This class implements an ostream interface to the Logger for information messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::information()
{
return _logStream->information();
}
/** This class implements an ostream interface to the Logger for debug messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::debug()
{
return _logStream->debug();
}
/** If the Logger's log level is at least PRIO_DEBUG, creates a Message with priority
* PRIO_DEBUG and the given message text and sends it to the attached channel.
*
* @param msg The message to log.
*/
void Logger::debug(const std::string& msg)
{
if(_enabled)
{
try
{
_log.debug(msg);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
}
/// Shuts down the logging framework and releases all Loggers.
void Logger::shutdown()
{
try
/** Logs the given message at debug level, followed by the data in buffer.
*
* The data in buffer is written in canonical hex+ASCII form:
* Offset (4 bytes) in hexadecimal, followed by sixteen space-separated,
* two column, hexadecimal bytes, followed by the same sixteen bytes as
* ASCII characters.
* For bytes outside the range 32 .. 127, a dot is printed.
* Note all Dump messages go out at Debug message level
*
* @param msg The message to log
* @param buffer the binary data to log
* @param length The length of the binaary data to log
*/
void Logger::dump(const std::string& msg, const void* buffer, std::size_t length)
{
if(_enabled)
{
try
{
_log.dump(msg,buffer,length);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
}
/** Returns true if at least the given log level is set.
* @param level The logging level it is best to use the Logger::Priority enum (7=debug, 6=information, 4=warning, 3=error, 2=critical, 1=fatal)
*/
bool Logger::is(int level) const
{
Poco::Logger::shutdown();
}
catch (std::exception& e)
bool retVal = false;
try
{
retVal = _log.is(level);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
return retVal;
}
void Logger::setLevel(int level)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
try
{
_log.setLevel(level);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
}
/** Returns a reference to the Logger with the given name.
* If the Logger does not yet exist, it is created, based on its parent logger.
*
* @param name The name of the logger to use - this is usually the class name.
*/
Logger& Logger::get(const std::string& name)
{
Logger* pLogger = new Logger(name);
return *pLogger;
}
void Logger::setLevel(const std::string& level)
{
try
{
_log.setLevel(level);
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
int Logger::getLevel() const
{
return _log.getLevel();
}
Poco::LogStream* Logger::getStream()
{
return _enabled?_logStream:_nullStream;
}
/** This class implements an ostream interface to the Logger for fatal messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::fatal()
{
return getStream()->fatal();
}
/** This class implements an ostream interface to the Logger for error messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::error()
{
return getStream()->error();
}
/** This class implements an ostream interface to the Logger for warning messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::warning()
{
return getStream()->warning();
}
/** This class implements an ostream interface to the Logger for notice messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::notice()
{
return getStream()->notice();
}
/** This class implements an ostream interface to the Logger for information messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::information()
{
return getStream()->information();
}
/** This class implements an ostream interface to the Logger for debug messages.
*
* The stream's buffer appends all characters written to it
* to a string. As soon as a CR or LF (std::endl) is written,
* the string is sent to the Logger.
* @returns an std::ostream reference.
*/
std::ostream& Logger::debug()
{
return getStream()->debug();
}
/** releases resources and deletes this object.
*/
void Logger::release()
{
delete(this);
}
/** Deletes the logger and clears it from the cache.
*
* @param name The logger to destroy.
*/
void Logger::destroy(Logger& logger)
{
Poco::Mutex::ScopedLock lock(m_ListMtx);
if (m_LoggerList)
{
LoggerList::iterator it = m_LoggerList->find(&logger);
if (it != m_LoggerList->end())
{
delete(*it);
m_LoggerList->erase(it);
}
}
}
/// Shuts down the logging framework and releases all Loggers.
void Logger::shutdown()
{
Poco::Mutex::ScopedLock lock(m_ListMtx);
try
{
//first release the POCO loggers
Poco::Logger::shutdown();
//now delete our static cache of loggers
if (m_LoggerList)
{
for (LoggerList::iterator it = m_LoggerList->begin(); it != m_LoggerList->end(); ++it)
{
delete(*it);
}
delete m_LoggerList;
m_LoggerList = 0;
}
}
catch (std::exception& e)
{
//failures in logging are not allowed to throw exceptions out of the logging class
std::cerr << e.what();
}
}
/** Returns a reference to the Logger with the given name.
* This logger is stored until in a static list until it is destroyed, released or Logger::shutdown is called.
*
* @param name The name of the logger to use - this is usually the class name.
*/
Logger& Logger::get(const std::string& name)
{
Logger* pLogger = new Logger(name);
if (!m_LoggerList)
m_LoggerList = new LoggerList;
m_LoggerList->insert(pLogger);
return *pLogger;
}
} // namespace Kernel
} // namespace Kernel
} // Namespace Mantid
......@@ -6,6 +6,8 @@
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/Logger.h"
#include "Poco/Path.h"
#include "boost/shared_ptr.hpp"
#include "TestChannel.hh"
#include <string>
using namespace Mantid::Kernel;
......@@ -26,6 +28,7 @@ public:
TS_ASSERT_THROWS_NOTHING(log1.debug("a debug string"));
TS_ASSERT_THROWS_NOTHING(log1.information("an information string"));
TS_ASSERT_THROWS_NOTHING(log1.information("a notice string"));
TS_ASSERT_THROWS_NOTHING(log1.warning("a warning string"));
TS_ASSERT_THROWS_NOTHING(log1.error("an error string"));
TS_ASSERT_THROWS_NOTHING(log1.fatal("a fatal string"));
......@@ -34,17 +37,40 @@ public:
log1.fatal()<<"A fatal message from the stream operators " << 4.5 << std::endl;
log1.error()<<"A error message from the stream operators " << -0.2 << std::endl;
log1.warning()<<"A warning message from the stream operators " << 999.99 << std::endl;
log1.notice()<<"A notice message from the stream operators " << 0.0 << std::endl;
log1.information()<<"A information message from the stream operators " << -999.99 << std::endl;
log1.debug()<<"A debug message from the stream operators " << 5684568 << std::endl;
);
//checking the level - this should be set to debug in the config file
//therefore this should only return false for debug
TS_ASSERT(log1.is(Logger::PRIO_DEBUG) == false); //debug
TS_ASSERT(log1.is(Logger::PRIO_INFORMATION)); //information
TS_ASSERT(log1.is(Logger::PRIO_NOTICE)); //information
TS_ASSERT(log1.is(Logger::PRIO_WARNING)); //warning
TS_ASSERT(log1.is(Logger::PRIO_ERROR)); //error
TS_ASSERT(log1.is(Logger::PRIO_FATAL)); //fatal
}
void testEnabled()
{
//attempt some logging
Logger& log1 = Logger::get("logTestEnabled");
TS_ASSERT(log1.getEnabled());
TS_ASSERT_THROWS_NOTHING(log1.fatal("a fatal string with enabled=true"));
TS_ASSERT_THROWS_NOTHING(log1.fatal()<<"A fatal message from the stream operators with enabled=true " << 4.5 << std::endl;);
TS_ASSERT_THROWS_NOTHING(log1.setEnabled(false));
TS_ASSERT(!log1.getEnabled());
TS_ASSERT_THROWS_NOTHING(log1.fatal("YOU SHOULD NEVER SEE THIS"));
TS_ASSERT_THROWS_NOTHING(log1.fatal()<<"YOU SHOULD NEVER SEE THIS VIA A STREAM" << std::endl;);
TS_ASSERT_THROWS_NOTHING(log1.setEnabled(true));
TS_ASSERT(log1.getEnabled());
TS_ASSERT_THROWS_NOTHING(log1.fatal("you are allowed to see this"));
TS_ASSERT_THROWS_NOTHING(log1.fatal()<<"you are allowed to see this via a stream" << std::endl;);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment