Skip to content

Commit

Permalink
Break it up into Impl and hpp, use a Meyers singleton
Browse files Browse the repository at this point in the history
  • Loading branch information
jmarrec committed Mar 22, 2024
1 parent c162c04 commit 442cd38
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 132 deletions.
5 changes: 3 additions & 2 deletions src/utilities/core/LogSink.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ namespace openstudio {

namespace detail {
class LogSink_Impl;
}
class Logger_Impl;
} // namespace detail

/// LogSink is a class for managing sinks for log messages, e.g. files, streams, etc.
class UTILITIES_API LogSink
Expand Down Expand Up @@ -76,7 +77,7 @@ class UTILITIES_API LogSink
void setFormatter(const boost::log::formatter& fmter);

protected:
friend class LoggerImpl;
friend class detail::Logger_Impl;

// does not register in the global logger
LogSink();
Expand Down
201 changes: 121 additions & 80 deletions src/utilities/core/Logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,134 +4,175 @@
***********************************************************************************************************************/

#include "Logger.hpp"
#include "Logger_Impl.hpp"

#include <boost/log/common.hpp>
#include <boost/log/attributes/function.hpp>
#include <boost/log/attributes/clock.hpp>

#include <boost/core/null_deleter.hpp>
#include <utility>

namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;

namespace openstudio {

std::shared_ptr<LoggerImpl> Logger::obj = nullptr;

/// convenience function for SWIG, prefer macros in C++
void logFree(LogLevel level, const std::string& channel, const std::string& message) {
BOOST_LOG_SEV(openstudio::Logger::instance().loggerFromChannel(channel), level) << message;
}

LoggerImpl::LoggerImpl() {
// Make current thread id attribute available to logging
boost::log::core::get()->add_global_attribute("ThreadId", boost::log::attributes::make_function(&std::this_thread::get_id));

// We have to provide an null deleter to avoid destroying the global stream
boost::shared_ptr<std::ostream> stdOut(&std::cout, boost::null_deleter());
m_standardOutLogger.setStream(stdOut);
m_standardOutLogger.setLogLevel(Warn);
this->addSink(m_standardOutLogger.sink());

// We have to provide an empty deleter to avoid destroying the global stream
boost::shared_ptr<std::ostream> stdErr(&std::cerr, boost::null_deleter());
m_standardErrLogger.setStream(stdErr);
m_standardErrLogger.setLogLevel(Warn);
//this->addSink(m_standardErrLogger.sink());
}
namespace detail {

LoggerImpl::~LoggerImpl() {
// unregister Qt message handler
//qInstallMsgHandler(consoleLogQtMessage);
}
Logger_Impl::Logger_Impl() {
// Make current thread id attribute available to logging
boost::log::core::get()->add_global_attribute("ThreadId", boost::log::attributes::make_function(&std::this_thread::get_id));

LogSink LoggerImpl::standardOutLogger() const {
std::shared_lock l{m_mutex};
// We have to provide an null deleter to avoid destroying the global stream
boost::shared_ptr<std::ostream> stdOut(&std::cout, boost::null_deleter());
m_standardOutLogger.setStream(stdOut);
m_standardOutLogger.setLogLevel(Warn);
this->addSink(m_standardOutLogger.sink());

return m_standardOutLogger;
}
// We have to provide an empty deleter to avoid destroying the global stream
boost::shared_ptr<std::ostream> stdErr(&std::cerr, boost::null_deleter());
m_standardErrLogger.setStream(stdErr);
m_standardErrLogger.setLogLevel(Warn);
//this->addSink(m_standardErrLogger.sink());
}

LogSink LoggerImpl::standardErrLogger() const {
std::shared_lock l{m_mutex};
LogSink Logger_Impl::standardOutLogger() const {
std::shared_lock l{m_mutex};

return m_standardErrLogger;
}
return m_standardOutLogger;
}

LogSink Logger_Impl::standardErrLogger() const {
std::shared_lock l{m_mutex};

LoggerType& LoggerImpl::loggerFromChannel(const LogChannel& logChannel) {
std::shared_lock l{m_mutex};
return m_standardErrLogger;
}

LoggerType& Logger_Impl::loggerFromChannel(const LogChannel& logChannel) {
std::shared_lock l{m_mutex};

auto it = m_loggerMap.find(logChannel);
if (it == m_loggerMap.end()) {
//LoggerType newLogger(keywords::channel = logChannel, keywords::severity = Debug);
LoggerType newLogger(keywords::channel = logChannel);
auto it = m_loggerMap.find(logChannel);
if (it == m_loggerMap.end()) {
//LoggerType newLogger(keywords::channel = logChannel, keywords::severity = Debug);
LoggerType newLogger(keywords::channel = logChannel);

std::pair<LogChannel, LoggerType> newPair(logChannel, newLogger);
std::pair<LogChannel, LoggerType> newPair(logChannel, newLogger);

// Drop the read lock and grab a write lock - we need to add the new file to the map
// this will reduce contention when multiple threads trying to log at once.
l.unlock();
std::unique_lock l2{m_mutex};
// Drop the read lock and grab a write lock - we need to add the new file to the map
// this will reduce contention when multiple threads trying to log at once.
l.unlock();
std::unique_lock l2{m_mutex};

std::pair<LoggerMapType::iterator, bool> inserted = m_loggerMap.insert(newPair);
std::pair<LoggerMapType::iterator, bool> inserted = m_loggerMap.insert(newPair);

return inserted.first->second;
return inserted.first->second;
}

return it->second;
}

return it->second;
}
bool Logger_Impl::findSink(boost::shared_ptr<LogSinkBackend> sink) {
std::unique_lock l{m_mutex};

bool LoggerImpl::findSink(boost::shared_ptr<LogSinkBackend> sink) {
std::unique_lock l{m_mutex};
auto it = m_sinks.find(sink);

auto it = m_sinks.find(sink);
return (it != m_sinks.end());
}

return (it != m_sinks.end());
}
void Logger_Impl::addSink(boost::shared_ptr<LogSinkBackend> sink) {
std::shared_lock l{m_mutex};

void LoggerImpl::addSink(boost::shared_ptr<LogSinkBackend> sink) {
std::shared_lock l{m_mutex};
auto it = m_sinks.find(sink);
if (it == m_sinks.end()) {

auto it = m_sinks.find(sink);
if (it == m_sinks.end()) {
// Drop the read lock and grab a write lock - we need to add the new file to the map
// this will reduce contention when multiple threads trying to log at once.
l.unlock();
std::unique_lock l2{m_mutex};

// Drop the read lock and grab a write lock - we need to add the new file to the map
// this will reduce contention when multiple threads trying to log at once.
l.unlock();
std::unique_lock l2{m_mutex};
m_sinks.insert(sink);

// Register the sink in the logging core
boost::log::core::get()->add_sink(sink);
}
}

m_sinks.insert(sink);
void Logger_Impl::removeSink(boost::shared_ptr<LogSinkBackend> sink) {
std::shared_lock l{m_mutex};

// Register the sink in the logging core
boost::log::core::get()->add_sink(sink);
auto it = m_sinks.find(sink);
if (it != m_sinks.end()) {

// Drop the read lock and grab a write lock - we need to add the new file to the map
// this will reduce contention when multiple threads trying to log at once.
l.unlock();
std::unique_lock l2{m_mutex};

m_sinks.erase(it);

// Register the sink in the logging core
boost::log::core::get()->remove_sink(sink);
}
}

void Logger_Impl::addTimeStampToLogger() {
std::unique_lock l{m_mutex};

// Add a TimeStamp attribute, same as boost::log::add_common_attributes() would do
[[maybe_unused]] auto [it, inserted] = boost::log::core::get()->add_global_attribute("TimeStamp", boost::log::attributes::local_clock{});
// if (!use) {
// boost::log::core::get()->remove_global_attribute(it);
// }
}

} // namespace detail

Logger::Logger() : m_impl(std::shared_ptr<detail::Logger_Impl>(new detail::Logger_Impl())) {}

Logger& Logger::instance() {
static Logger instance;
return instance;
}

void LoggerImpl::removeSink(boost::shared_ptr<LogSinkBackend> sink) {
std::shared_lock l{m_mutex};
/// get logger for standard out
LogSink Logger::standardOutLogger() const {
return m_impl->standardOutLogger();
}

auto it = m_sinks.find(sink);
if (it != m_sinks.end()) {
LogSink Logger::standardErrLogger() const {
return m_impl->standardErrLogger();
}

// Drop the read lock and grab a write lock - we need to add the new file to the map
// this will reduce contention when multiple threads trying to log at once.
l.unlock();
std::unique_lock l2{m_mutex};
/// get a logger from a LogChannel enumeration, if logChannel does not
/// exist a new logger will be set up at the default level
LoggerType& Logger::loggerFromChannel(const LogChannel& logChannel) {
return m_impl->loggerFromChannel(logChannel);
}

m_sinks.erase(it);
/// is the sink found in the logging core
bool Logger::findSink(boost::shared_ptr<LogSinkBackend> sink) {
return m_impl->findSink(std::move(sink));
}

// Register the sink in the logging core
boost::log::core::get()->remove_sink(sink);
}
/// adds a sink to the logging core, equivalent to logSink.enable()
void Logger::addSink(boost::shared_ptr<LogSinkBackend> sink) {
m_impl->addSink(std::move(sink));
}

void LoggerImpl::addTimeStampToLogger() {
std::unique_lock l{m_mutex};
/// removes a sink to the logging core, equivalent to logSink.disable()
void Logger::removeSink(boost::shared_ptr<LogSinkBackend> sink) {
m_impl->removeSink(std::move(sink));
}

// Add a TimeStamp attribute, same as boost::log::add_common_attributes() would do
[[maybe_unused]] auto [it, inserted] = boost::log::core::get()->add_global_attribute("TimeStamp", boost::log::attributes::local_clock{});
// if (!use) {
// boost::log::core::get()->remove_global_attribute(it);
// }
// cppcheck-suppress functionConst
void Logger::addTimeStampToLogger() {
m_impl->addTimeStampToLogger();
}

} // namespace openstudio
69 changes: 19 additions & 50 deletions src/utilities/core/Logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@

#include "../UtilitiesAPI.hpp"

#include "Singleton.hpp"
#include "Exception.hpp"
#include "Compare.hpp"
#include "Compare.hpp" // NOTE that this this is not needed for this header but so many compilation errors if I omit it
// since other files transitively use Compare
#include "LogMessage.hpp"
#include "LogSink.hpp"

#include <boost/shared_ptr.hpp>
#include "Exception.hpp"

#include <sstream>
#include <set>
#include <map>
#include <memory>
#include <shared_mutex>

/// defines method logChannel() to get a logger for a class
#define REGISTER_LOGGER(__logChannel__) \
Expand Down Expand Up @@ -51,21 +47,24 @@
namespace openstudio {

class OSWorkflow;
class LogSink;
namespace detail {
class Logger_Impl;
class LogSink_Impl;
} // namespace detail

/// convenience function for SWIG, prefer macros in C++
UTILITIES_API void logFree(LogLevel level, const std::string& channel, const std::string& message);

class Logger;

/** Primary logging class.
*/
class UTILITIES_API LoggerImpl
class UTILITIES_API Logger
{
friend class Logger;

public:
/// destructor, cleans up, writes xml file footers, etc
~LoggerImpl();
static Logger& instance();

Logger(const Logger& other) = delete;
Logger(Logger&& other) = delete;
Logger& operator=(const Logger&) = delete;
Logger& operator=(Logger&&) = delete;

/// get logger for standard out
LogSink standardOutLogger() const;
Expand Down Expand Up @@ -94,40 +93,10 @@ class UTILITIES_API LoggerImpl
void addTimeStampToLogger();

private:
/// private constructor
LoggerImpl();

mutable std::shared_mutex m_mutex;

/// standard out logger
LogSink m_standardOutLogger;

/// standard err logger
LogSink m_standardErrLogger;
Logger();
~Logger() = default;

/// map of std::string to logger
using LoggerMapType = std::map<std::string, LoggerType, openstudio::IstringCompare>;
LoggerMapType m_loggerMap;

/// current sinks, kept here so don't destruct when LogSink wrapper goes out of scope
using SinkSetType = std::set<boost::shared_ptr<LogSinkBackend>>;
SinkSetType m_sinks;
};

class UTILITIES_API Logger
{
public:
Logger() = delete;

static LoggerImpl& instance() {
if (!obj) {
obj = std::shared_ptr<LoggerImpl>(new LoggerImpl());
}
return *obj;
}

private:
static std::shared_ptr<LoggerImpl> obj;
std::shared_ptr<detail::Logger_Impl> m_impl;
};

} // namespace openstudio
Expand Down
Loading

0 comments on commit 442cd38

Please sign in to comment.