Program Listing for File log_stream.h
↰ Return to documentation for file (framework/logging/log_stream.h)
// SPDX-FileCopyrightText: 2024 The OpenSn Authors <https://open-sn.github.io/opensn/>
// SPDX-License-Identifier: MIT
#pragma once
#include <iostream>
#include <sstream>
#include <string_view>
#include <utility>
#include "stringstream_color.h"
namespace opensn
{
inline int
NoWrapStreamIndex()
{
static const int index = std::ios_base::xalloc();
return index;
}
inline std::ostream&
no_wrap(std::ostream& stream)
{
stream.iword(NoWrapStreamIndex()) = 1;
return stream;
}
/// Log stream for adding header information to a string stream.
class LogStream : public std::stringstream
{
public:
struct Header
{
std::string prefix;
std::string label;
std::string color;
};
LogStream(std::ostream* output_stream,
Header header,
bool dummy_flag = false,
bool use_color = false)
: log_stream_(output_stream),
header_(std::move(header)),
dummy_(dummy_flag),
use_color_(use_color)
{
}
LogStream(std::ostream* output_stream, std::string header, bool dummy_flag = false)
: LogStream(output_stream, Header{std::move(header), "", ""}, dummy_flag, false)
{
}
LogStream(const LogStream&) = delete;
LogStream& operator=(const LogStream&) = delete;
~LogStream() noexcept override
{
if (dummy_)
return;
try
{
std::string content = this->str();
if (content.empty())
return;
std::istringstream iss(content);
std::string line;
std::string oline;
std::string reset_str = use_color_ ? StringStreamColor(StringStreamColorCode::RESET) : "";
const bool wrap_lines = this->iword(NoWrapStreamIndex()) == 0;
while (std::getline(iss, line))
{
if (wrap_lines)
AppendWrappedLine(oline, line, reset_str);
else
AppendUnwrappedLine(oline, line, reset_str);
}
if (!oline.empty())
*log_stream_ << oline << std::flush;
}
catch (...) // NOLINT(bugprone-empty-catch)
{
// No exceptions escape the destructor...
}
}
private:
static constexpr std::size_t LINE_WRAP_WIDTH = 150;
static constexpr std::size_t CONTINUATION_INDENT = 4;
static std::size_t FindWrapPosition(std::string_view text, std::size_t max_width)
{
if (text.size() <= max_width)
return std::string_view::npos;
const auto search_limit = std::min(max_width, text.size() - 1);
for (std::size_t i = search_limit; i > 0; --i)
if (text[i] == ' ')
return i;
for (std::size_t i = max_width + 1; i < text.size(); ++i)
if (text[i] == ' ')
return i;
return std::string_view::npos;
}
static std::string_view TrimLeadingSpaces(std::string_view text)
{
while (not text.empty() and text.front() == ' ')
text.remove_prefix(1);
return text;
}
void AppendUnwrappedLine(std::string& output,
std::string_view line,
const std::string& reset_str) const
{
output += header_.prefix;
output += header_.color;
output += header_.label;
output += line;
output += reset_str;
output += "\n";
}
void
AppendWrappedLine(std::string& output, std::string_view line, const std::string& reset_str) const
{
bool first_piece = true;
bool line_remaining = true;
while (line_remaining)
{
const auto header_width = first_piece ? header_.prefix.size() + header_.label.size()
: header_.prefix.size() + CONTINUATION_INDENT;
const auto content_width =
header_width < LINE_WRAP_WIDTH ? LINE_WRAP_WIDTH - header_width : LINE_WRAP_WIDTH;
const auto wrap_pos = FindWrapPosition(line, content_width);
const auto piece = wrap_pos == std::string_view::npos ? line : line.substr(0, wrap_pos);
output += header_.prefix;
output += header_.color;
if (first_piece)
output += header_.label;
else
output += std::string(CONTINUATION_INDENT, ' ');
output += piece;
output += reset_str;
output += "\n";
first_piece = false;
line_remaining = wrap_pos != std::string_view::npos;
if (line_remaining)
line = TrimLeadingSpaces(line.substr(wrap_pos));
}
}
std::ostream* log_stream_;
Header header_;
const bool dummy_;
bool use_color_;
};
struct DummyStream : public std::ostream
{
struct DummyStreamBuffer : std::streambuf
{
int overflow(int c) override { return c; };
} buffer;
DummyStream() : std::ostream(&buffer) {}
~DummyStream() override = default;
};
} // namespace opensn