...
 
Commits (5)
......@@ -82,6 +82,7 @@ dune_add_library(dunepdelab
dune/pdelab/common/hostname.cc
dune/pdelab/common/logtag.cc
dune/pdelab/logging/logger.cc
dune/pdelab/logging/loggingstreambuffer.cc
dune/pdelab/logging/logmessage.cc
dune/pdelab/logging/patternformatsink.cc
dune/pdelab/logging/sink.cc
......
......@@ -16,6 +16,7 @@
#include <dune/pdelab/common/utility.hh>
#include <dune/pdelab/logging.hh>
#include <dune/pdelab/logging/loggerbackend.hh>
#include <dune/pdelab/logging/loggingstreambuffer.hh>
#include <dune/pdelab/logging/filesinks.hh>
namespace Dune::PDELab {
......@@ -165,6 +166,9 @@ namespace Dune::PDELab {
// to state() that got passed a pointer to a comm object, which only happens in
// init(). This allows us to catch uninitialized usage of the system.
State(const CollectiveCommunication* comm_)
: cout_buf(true)
, cerr_buf(true)
, clog_buf(true)
{
if (not comm_)
DUNE_THROW(LoggingError,"You must call Dune::PDELab::Logging::init() before using the logging system");
......@@ -183,6 +187,12 @@ namespace Dune::PDELab {
LogLevel unmuted_cerr = LogLevel::all;
std::optional<CollectiveCommunication> comm;
LogMessage::Time startup_time = LogMessage::Clock::now();
LoggingStreamBuffer cout_buf;
LoggingStreamBuffer cerr_buf;
LoggingStreamBuffer clog_buf;
std::streambuf* orig_cout_buf = nullptr;
std::streambuf* orig_cerr_buf = nullptr;
std::streambuf* orig_clog_buf = nullptr;
};
......@@ -394,6 +404,55 @@ namespace Dune::PDELab {
logger().info("Muted console log sinks on MPI ranks > 0"_fmt);
}
if (params.hasKey("redirect"))
{
auto level = parseLogLevel(params["redirect"]);
if (not (s.backends.count("cout") > 0))
Logging::registerBackend("cout",s.default_backend->_default_level);
Logging::redirectCout("cout",level);
if (not (s.backends.count("cerr") > 0))
Logging::registerBackend("cerr",s.default_backend->_default_level);
Logging::redirectCerr("cerr",level);
if (not (s.backends.count("clog") > 0))
Logging::registerBackend("clog",s.default_backend->_default_level);
Logging::redirectClog("clog",level);
}
else if (params.hasSub("redirect"))
{
auto& redirects = params.sub("redirect");
if (redirects.hasSub("cout"))
{
auto& config = params.sub("cout");
auto backend = config.get<std::string>("backend","");
auto level = parseLogLevel(config.get<std::string>("level","notice"));
auto buffered = config.get("buffered",true);
Logging::redirectCout(backend,level,buffered);
}
if (redirects.hasSub("cerr"))
{
auto& config = params.sub("cerr");
auto backend = config.get<std::string>("backend","");
auto level = parseLogLevel(config.get<std::string>("level","notice"));
auto buffered = config.get("buffered",true);
Logging::redirectCerr(backend,level,buffered);
}
if (redirects.hasSub("clog"))
{
auto& config = params.sub("clog");
auto backend = config.get<std::string>("backend","");
auto level = parseLogLevel(config.get<std::string>("level","notice"));
auto buffered = config.get("buffered",true);
Logging::redirectClog(backend,level,buffered);
}
}
logger().notice("Logging system initialized"_fmt);
}
......@@ -499,7 +558,7 @@ namespace Dune::PDELab {
}
}
Logger Logging::tryLogger(const ParameterTree& params, std::string_view preferred)
Logger Logging::componentLogger(const ParameterTree& params, std::string_view preferred)
{
auto& s = state();
LoggerBackend* backend = nullptr;
......@@ -640,6 +699,104 @@ namespace Dune::PDELab {
s.muted = false;
}
////////////////////////////////////////////////////////////////////////////////
// Standard C++ stream redirection
////////////////////////////////////////////////////////////////////////////////
void Logging::redirectCout(std::string_view backend, LogLevel level, bool buffered)
{
auto& s = state();
Logger logger = Logging::logger(backend);
logger.setDefaultLevel(level);
s.cout_buf.setLogger(logger);
s.cout_buf.setLineBuffered(buffered);
if (not s.orig_cout_buf)
s.orig_cout_buf = std::cout.rdbuf();
std::cout.rdbuf(&s.cout_buf);
Logging::logger().notice("Redirected std::cout to backend {} with level {}, buffered: {}"_fmt,backend,level,buffered);
}
void Logging::redirectCerr(std::string_view backend, LogLevel level, bool buffered)
{
auto& s = state();
Logger logger = Logging::logger(backend);
logger.setDefaultLevel(level);
s.cerr_buf.setLogger(logger);
s.cerr_buf.setLineBuffered(buffered);
if (not s.orig_cerr_buf)
s.orig_cerr_buf = std::cerr.rdbuf();
std::cerr.rdbuf(&s.cerr_buf);
Logging::logger().notice("Redirected std::cerr to backend {} with level {}, buffered: {}"_fmt,backend,level,buffered);
}
void Logging::redirectClog(std::string_view backend, LogLevel level, bool buffered)
{
auto& s = state();
Logger logger = Logging::logger(backend);
logger.setDefaultLevel(level);
s.clog_buf.setLogger(logger);
s.clog_buf.setLineBuffered(buffered);
if (not s.orig_clog_buf)
s.orig_clog_buf = std::clog.rdbuf();
std::clog.rdbuf(&s.clog_buf);
Logging::logger().notice("Redirected std::clog to backend {} with level {}, buffered: {}"_fmt,backend,level,buffered);
}
void Logging::restoreCout()
{
auto& s = state();
if (s.orig_cout_buf)
{
std::cout.rdbuf(s.orig_cout_buf);
s.orig_cout_buf = nullptr;
Logging::logger().notice("Stopped redirection of std::cout"_fmt);
}
else
Logging::logger().warning("Cannot stop redirection of std::cout, not redirected at the moment"_fmt);
}
void Logging::restoreCerr()
{
auto& s = state();
if (s.orig_cerr_buf)
{
std::cerr.rdbuf(s.orig_cerr_buf);
s.orig_cerr_buf = nullptr;
Logging::logger().notice("Stopped redirection of std::cerr"_fmt);
}
else
Logging::logger().warning("Cannot stop redirection of std::cerr, not redirected at the moment"_fmt);
}
void Logging::restoreClog()
{
auto& s = state();
if (s.orig_clog_buf)
{
std::clog.rdbuf(s.orig_clog_buf);
s.orig_clog_buf = nullptr;
Logging::logger().notice("Stopped redirection of std::clog"_fmt);
}
else
Logging::logger().warning("Cannot stop redirection of std::clog, not redirected at the moment"_fmt);
}
bool Logging::isCoutRedirected()
{
return state().orig_cout_buf;
}
bool Logging::isCerrRedirected()
{
return state().orig_cerr_buf;
}
bool Logging::isClogRedirected()
{
return state().orig_clog_buf;
}
////////////////////////////////////////////////////////////////////////////////
// Utilities
////////////////////////////////////////////////////////////////////////////////
......
This diff is collapsed.
......@@ -5,6 +5,7 @@ install(
fmt.hh
logger.hh
loggerbackend.hh
loggingstreambuffer.hh
logmessage.hh
patternmessagesink.hh
sink.hh
......
......@@ -87,11 +87,11 @@ namespace Dune::PDELab {
* \{
*/
//! Logs the given log message with the default LogLevel::notice.
//! Logs the given log message with the default level of the Logger.
template<typename... Args>
void operator()(format_string_view format, Args&&... args)
{
if (_level >= LogLevel::notice) {
if (_default_level <= _level) {
handle(LogLevel::notice,_indent,format,fmt::make_format_args(std::forward<Args>(args)...));
}
}
......@@ -127,10 +127,10 @@ namespace Dune::PDELab {
// but never run it.
if (false)
fmt::format(format,std::forward<Args>(args)...);
if (_level >= LogLevel::info)
if (_default_level <= _level)
{
std::string_view raw_format(format);
handle(LogLevel::info,_indent,raw_format,fmt::make_format_args(std::forward<Args>(args)...));
handle(_default_level,_indent,raw_format,fmt::make_format_args(std::forward<Args>(args)...));
}
}
......@@ -262,6 +262,12 @@ namespace Dune::PDELab {
return _level;
}
//! Returns the default log level at which this Logger will log messages.
LogLevel defaultLevel() const
{
return _default_level;
}
//! Returns the default indentation of messages logged with this Logger.
int indent() const
{
......@@ -317,6 +323,12 @@ namespace Dune::PDELab {
_level = level;
}
//! Sets tthe default log level at which this Logger will log messages.
void setDefaultLevel(LogLevel default_level)
{
_default_level = default_level;
}
//! Sets the default indentation of messages logged with this Logger.
void setIndent(int indent)
{
......@@ -367,10 +379,11 @@ namespace Dune::PDELab {
private:
//! Internal constructor from a backend.
Logger(LoggerBackend& backend, LogLevel level, int indent)
Logger(LoggerBackend& backend, LogLevel level, int indent, LogLevel default_level = LogLevel::notice)
: _level(level)
, _indent(indent)
, _backend(&backend)
, _default_level(default_level)
{}
//! Method for handling the actual logging.
......@@ -384,6 +397,7 @@ namespace Dune::PDELab {
LogLevel _level = LogLevel::all;
int _indent = 0;
LoggerBackend* _backend = nullptr;
LogLevel _default_level = LogLevel::notice;
};
......
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string_view>
#include <dune/pdelab/common/exceptions.hh>
#include <dune/pdelab/logging/loggingstreambuffer.hh>
namespace Dune::PDELab {
LoggingStreamBuffer::LoggingStreamBuffer(bool line_buffered)
: std::stringbuf(std::ios::out)
, _line_buffered(line_buffered)
{}
LoggingStreamBuffer::LoggingStreamBuffer(bool line_buffered, Logger stream_logger)
: std::stringbuf(std::ios::out)
, _stream_log(stream_logger)
, _line_buffered(line_buffered)
{}
int LoggingStreamBuffer::sync()
{
// Get a view of the currently available data
std::string_view buf(pbase(),pptr()-pbase());
if (_logging)
{
std::fprintf(stderr,"\
======================================================================================================\n\
FATAL LOGGING ERROR: Recursive use of redirected stream detected during logging of a redirected stream\n\
======================================================================================================\n"
);
std::abort();
}
if (not buf.empty())
{
_logging = true;
// Log one message per line of output
std::string_view::size_type first = 0;
for (auto last = buf.find_first_of('\n',first) ; last != buf.npos ; last = buf.find_first_of('\n',first))
{
// Ignore empty lines in unbuffered mode
if (_line_buffered or first < last)
_stream_log("{}"_fmt,buf.substr(first,last-first));
// skip the current character, which is a newline
first = last + 1;
}
if (_line_buffered)
{
if (first == buf.size())
{
// We have logged the entire buffer and can clear it
seekpos(0,std::ios::out);
}
else if (first > 0 and buf.size() > 1024)
{
// There is still data in the buffer, and the buffer starts to grow a little large
// Purge logged data from the buffer
std::memmove(pbase(),pbase()+first,buf.size()-first);
seekpos(buf.size()-first,std::ios::out);
}
// else do nothing
}
else
{
// Always print out incomplete lines as well
if (first + 1 < buf.size() or buf[buf.size() - 1] != '\n' )
_stream_log("{}"_fmt,buf.substr(first));
// clear buffer
seekpos(0,std::ios::out);
}
_logging = false;
}
// Forward to base class
return std::stringbuf::sync();
}
} // namespace Dune::PDELab
#ifndef DUNE_PDELAB_LOGGING_LOGGINGSTREAMBUFFER_HH
#define DUNE_PDELAB_LOGGING_LOGGINGSTREAMBUFFER_HH
#include <sstream>
#include <string>
#include <string_view>
#include <dune/pdelab/logging/logger.hh>
namespace Dune::PDELab {
/**
* \addtogroup logging
* \{
*/
//! An output-only std::streambuf that forwards to a Logger.
/**
* This buffer can be used to make standard C++ `std::ostream`s feed their data into the logging system.
* It can operate in two different modes:
*
* - In line-buffered mode, the buffer will only output complete lines that have been terminated
* by a newline character. In particular, this mode will ignore any explicit flushing of the C++
* stream. This mode is capable of exactly reproducing the original output layout as designed by
* the user of the `std::ostream`, but messages may appear later than expected when the user
* explicitly flushes the C++ stream.
*
* - In unbuffered mode, the buffer will always forward all pending data everytime the C++ stream
* is flushed. As most logging sinks are line-oriented and insert an additional newline after
* each log message, this will not correctly reproduce the original layout of the output. As a
* lot of people use `std::endl` instead of just `"\n"` for ending their lines, this mode will
* not forward empty lines to the logging system to avoid empty lines after every regular line
* printed to the C++ stream.
*/
class LoggingStreamBuffer
: public std::stringbuf
{
public:
//! Constructs a LoggingStreamBuffer without a working logger.
LoggingStreamBuffer(bool line_buffered);
//! Constructs a LoggingStreamBuffer.
LoggingStreamBuffer(bool line_buffered, Logger stream_logger);
//! Handles the log message generation.
int sync() override;
//! Returns a copy of the Logger used by this buffer.
Logger logger() const
{
return _stream_log;
}
//! Sets the logger used by this buffer.
void setLogger(Logger logger)
{
_stream_log = logger;
}
//! Returns whether this buffer is line-buffered.
bool isLineBuffered() const
{
return _line_buffered;
}
//! Enavbles or disables the line-buffered mode of this buffer.
void setLineBuffered(bool enabled)
{
_line_buffered = enabled;
}
private:
Logger _stream_log;
bool _line_buffered = true;
bool _logging = false;
};
} // namespace Dune::PDELab
#endif // DUNE_PDELAB_LOGGING_LOGGINGSTREAMBUFFER_HH
......@@ -229,4 +229,31 @@ namespace Dune::PDELab {
} // end namespace Dune::PDELab
#ifndef DOXYGEN
namespace fmt {
// make it possible to format a LogLevel in a log message
template <>
struct formatter<Dune::PDELab::LogLevel>
: public formatter<std::string_view>
{
using Base = formatter<std::string_view>;
// we just inherit the parsing from the original formatter
template <typename FormatContext>
auto format(Dune::PDELab::LogLevel level, FormatContext& ctx) {
return Base::format(name(level),ctx);
}
};
} // namespace fmt
#endif // DOXYGEN
#endif // DUNE_PDELAB_LOGGING_LOGMESSAGE_HH