Skip to content

Commit

Permalink
Merge pull request #1161 from vsg-dev/RedirectCoutCerrToLogger
Browse files Browse the repository at this point in the history
Redirect std::cout & std::cerr to vsg::Logger
  • Loading branch information
robertosfield authored Apr 16, 2024
2 parents 909ebcc + cdcc8e1 commit c39d2db
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 25 deletions.
10 changes: 9 additions & 1 deletion include/vsg/io/Logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ namespace vsg
/// Logger singleton, defaults to using vsg::StdLogger
static ref_ptr<Logger>& instance();

/// redirect std::cout and std::cerr output to vsg::Logger at level LOGGER_INFO and LOGGER_ERROR respectively.
virtual void redirect_std();

virtual void flush() {}

inline void debug(char* message) { debug(std::string_view(message)); }
Expand Down Expand Up @@ -234,6 +237,11 @@ namespace vsg
std::mutex _mutex;
std::ostringstream _stream;

std::unique_ptr<std::streambuf> _override_cout;
std::unique_ptr<std::streambuf> _override_cerr;
std::streambuf* _original_cout = nullptr;
std::streambuf* _original_cerr = nullptr;

virtual void debug_implementation(const std::string_view& message) = 0;
virtual void info_implementation(const std::string_view& message) = 0;
virtual void warn_implementation(const std::string_view& message) = 0;
Expand Down Expand Up @@ -365,7 +373,7 @@ namespace vsg
void flush() override;

protected:
void print_id(std::ostream& out, std::thread::id id);
void print_id(FILE* out, std::thread::id id);

void debug_implementation(const std::string_view& message) override;
void info_implementation(const std::string_view& message) override;
Expand Down
109 changes: 85 additions & 24 deletions src/vsg/io/Logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,55 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI

using namespace vsg;

namespace vsg
{

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// intercept_streambuf takes std::cout/cerr output and redirects it the Logger
//

class intercept_streambuf : public std::streambuf
{
public:
explicit intercept_streambuf(Logger* in_logger, Logger::Level in_level) :
logger(in_logger),
level(in_level)
{
}

Logger* logger = nullptr;
Logger::Level level = Logger::LOGGER_INFO;

std::streamsize xsputn(const char_type* s, std::streamsize n) override
{
std::scoped_lock<std::mutex> lock(_mutex);
_line.append(s, static_cast<std::size_t>(n));
return n;
}

std::streambuf::int_type overflow(std::streambuf::int_type c) override
{
std::scoped_lock<std::mutex> lock(_mutex);
if (c=='\n')
{
logger->log(level, _line);
_line.clear();
}
else
{
_line.push_back(static_cast<char>(c));
}
return c;
}

protected:
std::string _line;
std::mutex _mutex;
};

} // namespace vsg

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Logger
Expand All @@ -40,6 +89,10 @@ Logger::Logger(const Logger& rhs) :

Logger::~Logger()
{
flush();

if (_original_cout) std::cout.rdbuf(_original_cout);
if (_original_cerr) std::cerr.rdbuf(_original_cerr);
}

ref_ptr<Logger>& Logger::instance()
Expand All @@ -49,6 +102,15 @@ ref_ptr<Logger>& Logger::instance()
return s_logger;
}

void Logger::redirect_std()
{
_override_cout.reset(new intercept_streambuf(this, LOGGER_INFO));
_original_cout = std::cout.rdbuf(_override_cout.get());

_override_cerr.reset(new intercept_streambuf(this, LOGGER_ERROR));
_original_cerr = std::cerr.rdbuf(_override_cerr.get());
}

void Logger::debug_stream(PrintToStreamFunction print)
{
if (level > LOGGER_DEBUG) return;
Expand Down Expand Up @@ -161,33 +223,33 @@ StdLogger::StdLogger()

void StdLogger::flush()
{
std::cout.flush();
std::cerr.flush();
fflush(stdout);
fflush(stderr);
}

void StdLogger::debug_implementation(const std::string_view& message)
{
std::cout << debugPrefix << message << std::endl;
fprintf(stdout, "%s%.*s\n", debugPrefix.c_str(), static_cast<int>(message.length()), message.data());
}

void StdLogger::info_implementation(const std::string_view& message)
{
std::cout << infoPrefix << message << std::endl;
fprintf(stdout, "%s%.*s\n", infoPrefix.c_str(), static_cast<int>(message.length()), message.data());
}

void StdLogger::warn_implementation(const std::string_view& message)
{
std::cerr << warnPrefix << message << std::endl;
fprintf(stderr, "%s%.*s\n", warnPrefix.c_str(), static_cast<int>(message.length()), message.data());
}

void StdLogger::error_implementation(const std::string_view& message)
{
std::cerr << errorPrefix << message << std::endl;
fprintf(stderr, "%s%.*s\n", errorPrefix.c_str(), static_cast<int>(message.length()), message.data());
}

void StdLogger::fatal_implementation(const std::string_view& message)
{
std::cerr << fatalPrefix << message << std::endl;
fprintf(stderr, "%s%.*s\n", fatalPrefix.c_str(), static_cast<int>(message.length()), message.data());
throw vsg::Exception{std::string(message)};
}

Expand All @@ -201,8 +263,8 @@ ThreadLogger::ThreadLogger()

void ThreadLogger::flush()
{
std::cout.flush();
std::cerr.flush();
fflush(stdout);
fflush(stderr);
}

void ThreadLogger::setThreadPrefix(std::thread::id id, const std::string& str)
Expand All @@ -211,47 +273,46 @@ void ThreadLogger::setThreadPrefix(std::thread::id id, const std::string& str)
_threadPrefixes[id] = str;
}

void ThreadLogger::print_id(std::ostream& out, std::thread::id id)
void ThreadLogger::print_id(FILE* out, std::thread::id id)
{
if (auto itr = _threadPrefixes.find(id); itr != _threadPrefixes.end())
{
out << itr->second;
fprintf(out, "%s", itr->second.c_str());
}
else
{
out << "thread::id = " << id << " | ";
fprintf(out, "thread::id = %s | ", itr->second.c_str());
}
}

void ThreadLogger::debug_implementation(const std::string_view& message)
{
print_id(std::cout, std::this_thread::get_id());
std::cout << debugPrefix << message << std::endl;
print_id(stdout, std::this_thread::get_id());
fprintf(stdout, "%s%.*s\n", debugPrefix.c_str(), static_cast<int>(message.length()), message.data());
}

void ThreadLogger::info_implementation(const std::string_view& message)
{
print_id(std::cout, std::this_thread::get_id());
std::cout << infoPrefix << message << std::endl;
print_id(stdout, std::this_thread::get_id());
fprintf(stdout, "%s%.*s\n", infoPrefix.c_str(), static_cast<int>(message.length()), message.data());
}

void ThreadLogger::warn_implementation(const std::string_view& message)
{
print_id(std::cout, std::this_thread::get_id());
std::cerr << warnPrefix << message << std::endl;
print_id(stderr, std::this_thread::get_id());
fprintf(stderr, "%s%.*s\n", warnPrefix.c_str(), static_cast<int>(message.length()), message.data());
}

void ThreadLogger::error_implementation(const std::string_view& message)
{
print_id(std::cout, std::this_thread::get_id());
std::cerr << errorPrefix << message << std::endl;
print_id(stderr, std::this_thread::get_id());
fprintf(stderr, "%s%.*s\n", errorPrefix.c_str(), static_cast<int>(message.length()), message.data());
}

void ThreadLogger::fatal_implementation(const std::string_view& message)
{
print_id(std::cout, std::this_thread::get_id());
std::cerr << fatalPrefix << message << std::endl;
throw vsg::Exception{std::string(message)};
print_id(stderr, std::this_thread::get_id());
fprintf(stderr, "%s%.*s\n", fatalPrefix.c_str(), static_cast<int>(message.length()), message.data());
}

////////////////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -277,5 +338,5 @@ void NullLogger::error_implementation(const std::string_view&)
}
void NullLogger::fatal_implementation(const std::string_view& message)
{
throw vsg::Exception{std::string(message)};
throw Exception{std::string(message)};
}

0 comments on commit c39d2db

Please sign in to comment.