How to write iostream-like interface to logging library? - c++

I would like to write a convinient interface to my very simple logging library. Take two following pieces of code. The first one is what I do now, the second one is my idea for an intuitive interface:
std::ostringstream stream;
stream<<"Some text "<<and_variables<<" formated using standard string stream"
logger.log(stream.str()); //then passed to the logger
And
logger.convinient_log()<<"Same text "<<with_variables<<" but passed directly";
My thought-design process behind that idea is to return some kind of temporary stringstream-like object from logger.convinient_log() function. That object on destruction (I hope it happens at the end of the line or in a similar, convinient place) would collect string from itself and call an actual logger.log(). The point is I want to process it whole, not term-by-term, so that log() can add eg. prefix and sufix to whole line of text.
I'm very well avare that it might be straight impossible or impossible without some heavy magic. If that's the case, what would be an almost-as-convinient way to do that and how to implement it? I bet on passing some special variable that would force collect-call-logger.log() operation.
If you don't know an exact answer, resources on the topic (eg. extending stringstream) would be also welcome.

This is how Boost.Log works, for example. The basic idea is simple:
struct log
{
log() {
uncaught = std::uncaught_exceptions();
}
~log() {
if (uncaught >= std::uncaught_exceptions()) {
std::cout << "prefix: " << stream.str() << " suffix\n";
}
}
std::stringstream stream;
int uncaught;
};
template <typename T>
log& operator<<(log& record, T&& t) {
record.stream << std::forward<T>(t);
return record;
}
template <typename T>
log& operator<<(log&& record, T&& t) {
return record << std::forward<T>(t);
}
// Usage:
log() << "Hello world! " << 42;
std::uncaught_exceptions() is used to avoid logging an incomplete message if an exception is thrown in the middle.

Here's a class I've togeather a while ago. It sounds like what you're looking for is this. I was able to achieve it without any daunting inheriting of ostreams, stream_buf or anything else. You can write to files, console, sockets, or whatever you want whenever a flush is caught.
It doesn't work with ostream_iterators but handles all of the io_manip functions well.
Usage:
Logger log;
int age = 32;
log << "Hello, I am " << age << " years old" << std::endl;
log << "That's " << std::setbase(16) << age << " years in hex" << std::endl;
log(Logger::ERROR) << "Now I'm logging an error" << std::endl;
log << "However, after a flush/endl, the error will revert to INFO" << std::end;
Implementation
#include <iostream>
#include <sstream>
#include <string>
class Logger
{
public:
typedef std::ostream& (*ManipFn)(std::ostream&);
typedef std::ios_base& (*FlagsFn)(std::ios_base&);
enum LogLevel
{
INFO,
WARN,
ERROR
};
Logger() : m_logLevel(INFO) {}
template<class T> // int, double, strings, etc
Logger& operator<<(const T& output)
{
m_stream << output;
return *this;
}
Logger& operator<<(ManipFn manip) /// endl, flush, setw, setfill, etc.
{
manip(m_stream);
if (manip == static_cast<ManipFn>(std::flush)
|| manip == static_cast<ManipFn>(std::endl ) )
this->flush();
return *this;
}
Logger& operator<<(FlagsFn manip) /// setiosflags, resetiosflags
{
manip(m_stream);
return *this;
}
Logger& operator()(LogLevel e)
{
m_logLevel = e;
return *this;
}
void flush()
{
/*
m_stream.str() has your full message here.
Good place to prepend time, log-level.
Send to console, file, socket, or whatever you like here.
*/
m_logLevel = INFO;
m_stream.str( std::string() );
m_stream.clear();
}
private:
std::stringstream m_stream;
int m_logLevel;
};

Create a custom class derived from std::basic_streambuf to write to your logger, eg:
class LoggerBuf : public std::stringbuf
{
private:
Logger logger;
public:
LoggerBuf(params) : std::stringbuf(), logger(params) {
...
}
virtual int sync() {
int ret = std::stringbuf::sync();
logger.log(str());
return ret;
}
};
And then you can instantiate a std::basic_ostream object giving it a pointer to a LoggerBuf object, eg:
LoggerBuf buff(params);
std::ostream stream(&buf);
stream << "Some text " << and_variables << " formated using standard string stream";
stream << std::flush; // only if you need to log before the destructor is called
Alternatively, derive a custom class from std::basic_ostream to wrap your LoggerBuf class, eg:
class logger_ostream : public std::ostream
{
private:
LoggerBuf buff;
public:
logger_ostream(params) : std:ostream(), buff(params)
{
init(&buff);
}
};
std::logger_ostream logger(params);
logger << "Some text " << and_variables << " formated using standard string stream";
logger << std::flush; // only if you need to log before the destructor is called

Related

How can I inject a newline when a std::ostream object is std::flush'ed?

How can I minimally wrap a std::ofstream so that any call to sync (std::flush) gets turned into a call to std::endl.
(everything below is an answer to the question "why would you do that?" and is not relevant to the above question)
I have an application which contains a custom std::ostream implementation (which wraps around zmq), let's call it zmq_stream.
This zmq_stream also internally implements a custom streambuffer, and works very nicely.
One of the motivations of this design is that in my unit tests, I can simply swap out the zmq_stream in my unit tests with a regular old std::ostringstream and test the results, completely hiding out the network layer.
One of the features of the application itself is to redirect application output to a file or stdout via a command line flag.
Once again, using a basic switch at startup, I can pass either zmq_stream, std::ofstream or plain old std::cout to my main loop.
This is accomplished something like this:
std::ostream* out;
switch(cond)
{
case 1:
out = &std::cout;
break;
case 2:
out = new ofstream("file");
break;
case 3:
out = new zmq_stream(method, endpoints);
break;
}
main_loop(out);
The usage of zmq_stream is as follows:
zmq_stream out(method, endpoints);
out << "do something " << 42 << " or other";
out << "and another" << std::flush; // <- full constructed buffer is sent over the network
Note: it is by design that I use std::flush and not std::endl when flushing to the network. I do not wish to append a newline to all of my network transmissions. As such, all network outputs by the application use std::flush and not std::endl and changing this is not the correct answer.
Question: while for testing purposes, everything works as expected, for the std::cout and std::ofstream options, I'd like to be able to inject a newline when std::flush is called, so that my network transmissions can be separated into newlines on stdout. This would allow me to pipe them onto the standard posix suite of tooling...
Without this injection, there is no way to determine (aside from timing) where the network boundaries are.
I'm looking for the most minimal possible override here so that I don't have to write out a bunch of new classes. Ideally, overriding the sync() virtual method so that it calls overflow('\n'); and then calls base sync().
However, this is proving to be much more challenging than I expected, so I'm wondering if I'm doing it wrong.
The initial solution I wrote doesn't work because it is a compile time solution and I can't switch on it without re-implementing inheritance (so that the main_loop function gets a base class as a parameter).
Instead, here's the answer to original problem implemented in what appears to be the most minimal way possible.
#include <iostream>
#include <fstream>
#include <iomanip>
typedef std::ostringstream zmq_stream;
template <class T>
class newline_injector_streambuf: public std::basic_streambuf<T> {
public:
using int_type = typename std::basic_streambuf<T>::int_type;
newline_injector_streambuf(std::basic_streambuf<T>& dest) : sink(dest) {}
protected:
virtual int_type sync() override { overflow('\n'); return sink.pubsync(); }
virtual int_type overflow(int_type c) override { return sink.sputc(c); }
std::basic_streambuf<T>& sink;
};
template <class T>
struct newline_injector_stream : public std::basic_ostream<typename T::char_type> {
newline_injector_streambuf<typename T::char_type> buf;
newline_injector_stream(T* file) : buf(*file->rdbuf())
{
std::basic_ostream<typename T::char_type>::rdbuf(&buf);
}
};
void test(std::ostream& out)
{
out << std::setfill('x') << std::setw(10) << "" << std::flush;
out << "Hello, world!" << std::flush << "asdf" << std::endl;
}
int main() {
newline_injector_stream out1(&std::cout);
newline_injector_stream out2(new std::ofstream("test.output", std::ios::out | std::ios::ate));
test(std::cout);
test(out1);
test(out2);
return 0;
}
This is the solution that was devised thanks to #463035818-is-not-a-number's initial answer:
struct zmq_stream;
template<typename T>
struct output_device {
T& output;
output_device(T& backing_stream) : output(backing_stream){}
template <typename U>
output_device& operator<<(const U& u)
{
output << u;
return *this;
}
output_device& operator<<(std::ostream& (*f)(std::ostream&)) {
if constexpr (!std::is_same<T, zmq_stream>::value)
if (f == &std::flush<std::ostream::char_type,std::ostream::traits_type>)
output << "\n";
output << f;
return *this;
}
};
// Usage:
int main()
{
/* prelude code */
auto out1 = output_device(std::cout);
auto out2 = output_device(std::ofstream("filename"));
auto out3 = output_device(zmq_stream(method, endpoints));
out1 << "foo" << std::flush << "bar"; // results in foo\nbar
out2 << "foo" << std::flush << "bar"; // results in foo\nbar
out3 << "foo" << std::flush << "bar"; // results in foobar
}
template <class T>
class separator: public std::basic_streambuf<T> {
public:
using int_type = typename std::basic_streambuf<T>::int_type;
using char_type = typename std::basic_streambuf<T>::char_type;
using traits_type = typename std::basic_streambuf<T>::traits_type;
separator(std::basic_streambuf<T> *dest) : sink(dest) {}
protected:
virtual int_type sync() override {
if (nullptr == sink)
return 1;
//std::basic_streambuf<T>::overflow('\n');
overflow('\n');
return sink->pubsync();
}
virtual int_type overflow(int_type c) override {
if (sink == nullptr)
return 1;
return sink->sputc(c);
}
std::basic_streambuf<T> *sink = nullptr;
};
template <class T> struct newline_injector_stream : public std::basic_ostream<T> {
separator<T> buf;
public:
newline_injector_stream(std::basic_ostream<T> &file) : buf(file.rdbuf())
{
std::basic_ostream<T>::rdbuf(&buf);
}
};
The main_loop function signature now goes from taking a std::ostream* type to a output_device& type.

Is it possible to create a C++ class that mimics the std::cout syntax by chaining overloaded insertion operators the same way that easylogging++ does?

The easylogging++ code defines a macro that makes it very simple to use:
LOG(logLevel) << "This mimics std::cout syntax. " << 1 << " + " << 1 << " = " << 2;
I want to make a wrapper class for easylogging++. I can easily create a function with two parameters to wrap the above line. However, is it possible to mimic this syntax in a wrapper class? For example:
Logger logger;
logger(logLevel) << "Line " << 1 << " of log text.";
I know I can easily overload the insertion operator, but that still leaves me with having to write another function to set the log level each time.
UPDATE:
Thanks to Starl1ght's answer I was able to get this working. I figured I would share in case anyone else has a similar need.
I created two overloads. One was for () and the other for <<.
Logger &operator()(logLevelT logLevel) {
mLogLevel = logLevel;
return *this;
}
template <typename T>
Logger &operator<<(T const &value) {
LOG(mLogLevel) << value;
return *this;
}
UPDATE 2:
I wanted to update this post again to give my reasoning and show my final solution.
My reasoning is that my project is a demonstration of abstraction. I'm trying to demonstrate that logging libraries (and many other things) can be abstracted away from the core functionality of your software. This also makes the software components modular. This way I can swap out the easylogging++ library without loosing the syntax because it is implemented in the module interface.
My last update didn't mention how I overcame the obstacle of insertion chaining, so I wanted to post an example to show how I did it. The following code is a simplified example of how to achieve std::cout like syntax for a class.
#include <iostream> // For cout
#include <string> // For strings
#include <sstream> // For ostringstream
enum logLevelT {
INFO_LEVEL,
WARNING_LEVEL,
ERROR_LEVEL,
FATAL_LEVEL
};
class Logger {
private:
std::string logName;
public:
Logger(std::string nameOfLog, std::string pathToLogFile) {
logName = nameOfLog;
//TODO Configure your logging library and instantiate
// an instance if applicable.
}
~Logger(){}
// LogInputStream is instantiated as a temporary object. It is used
// to build the log entry stream. It writes the completed stream
// in the destructor as the object goes out of scope automatically.
struct LogInputStream {
LogInputStream(logLevelT logLevel, std::string nameOfLog) {
currentLogLevel = logLevel;
currentLogName = nameOfLog;
}
// Copy Constructor
LogInputStream(LogInputStream &lis) {
currentLogLevel = lis.currentLogLevel;
currentLogName = lis.currentLogName;
logEntryStream.str(lis.logEntryStream.str());
}
// Destructor that writes the log entry stream to the log as the
// LogInputStream object goes out of scope.
~LogInputStream() {
std::cout << "Logger: " << currentLogName
<< " Level: " << currentLogLevel
<< " logEntryStream = " << logEntryStream.str()
<< std::endl;
//TODO Make a log call to your logging library. You have your log level
// and a completed log entry stream.
}
// Overloaded insertion operator that adds the given parameter
// to the log entry stream.
template <typename T>
LogInputStream &operator<<(T const &value) {
logEntryStream << value;
return *this;
}
std::string currentLogName;
logLevelT currentLogLevel;
std::ostringstream logEntryStream;
};
// Overloaded function call operator for providing the log level
Logger::LogInputStream operator()(logLevelT logLevel) {
LogInputStream logInputStream(logLevel, logName);
return logInputStream;
}
// Overloaded insertion operator that is used if the overloaded
// function call operator is not used.
template <typename T>
Logger::LogInputStream operator<<(T const &value) {
LogInputStream logInputStream(INFO_LEVEL, logName);
logInputStream << value;
return logInputStream;
}
};
int main(int argc, char *argv[]) {
Logger logger1 = Logger("Logger1", "/path/to/log.log");
Logger logger2 = Logger("Logger2", "/path/to/log.log");
logger1(INFO_LEVEL) << "This is the " << 1 << "st test";
logger2(ERROR_LEVEL) << "This is the " << 2 << "nd test";
logger2 << "This is the " << 3 << "rd test";
return 0;
}
I feel like I could have done a better job with the naming and the comments but I am pressed for time. I'm definitely open to any comments or critiques.
You must overload operator() so, it will set internal log level and return *this as type Logger&, so, overloaded operator<< will work on returned reference with necessary log-level set.
Something like this:
Logger& Logger::operator()(LogLevel level) {
// set internal log level
return *this;
}

Overloading operator<< in C++ with a prefix

I'm working on a logger class in C++ that has the following syntax:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
And it prints:
a string 1 6.2
This is what my class looks like:
class Logger
{
private:
unique_ptr<ofstream> m_pOutStream;
public:
Logger(std::string sFile) : m_pOutStream(new ofstream(sFile, std::ios::app))
{}
template<typename T>
Logger& operator<< (const T& data)
{
*m_pOutStream << data;
return *this;
}
};
It works fine, but I would also like to add a prefix to every line (e.g. a timestamp).
So when I write:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
I want something like this to be displayed:
11:59:12 a string 1 6.2
I have thought of a couple of solutions:
1.Keep every input stored in a list/stream and use an extra function to print and then clear the list/stream:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
log.logd(); // <- this prints and then clears the internal stream/list.
2.Keep every input stored in a list/stream and print everything after a "new line" character is detected. And clear the internal stream/list after that.
Both of these solutions are nice but I'd prefer to use them only as a last resort.
Is there any other/better way to achieve what I want?
Derive a class from std::stringbuf, say LoggerStringBuf, and give it a reference to your output std::ofstream in its constructor. Override the virtual std::stringbuf::sync() method to retrieve a std::string from the base std::stringbuf::str() method and prefix it with a timestamp when writing it to the std::ofstream. This way you generate a new timestamp every time your LoggerStringBuf object is flushed to the std::ofstream for any reason, whether explicitly by std::endl or std::flush, or implicitly by its destructor.
Then have your Logger class derive from std::ostream and initialize it with a LoggerStringBuf object. Then you can stream input values to your Logger and they will be cached in your LoggerStringBuf object until flushed to the std::ofstream. At which time you can prepend timestamps as needed.
For example:
class LoggerStringBuf : public std::stringbuf
{
private:
std::ostream &m_OutStream;
protected:
virtual int sync()
{
int ret = std::stringbuf::sync();
std::string s = str();
str("");
// note sure if the string includes non-flushing
// line breaks. If needed, you can use std::getline()
// to break up the string into multiple lines and
// write a timestamp for each line...
//
m_OutStream << "[timestamp] " << s << std::endl;
return ret;
};
public:
LoggerStringBuf(std::ostream &OutStream)
: std::stringbuf(std::ios_base::out), m_OutStream(OutStream)
{
}
~LoggerStringBuf()
{
sync();
}
};
class Logger : public std::ostream
{
private:
std::ofstream m_OutStream;
LoggerStringBuf m_Buf;
public:
Logger(const std::string &sFile)
: std::ostream(0), m_OutStream(sFile, std::ios::app), m_Buf(m_OutStream)
{
init(&m_Buf);
}
template<typename T>
std::ostream& operator<< (const T& data)
{
return static_cast<std::ostream&>(*this) << data;
}
};
You need to introduce an additional wrapper class for Logger that knows whether the line is starting or being appended.
class Appender
{
Appender(Logger& logger) : os_(os) { }
Appender& operator <<(const T& x) { os_ << x; return *this; }
};
class Logger
{
Appender operator <<(const T& x) { os_ << timestamp() << x; return Appender(os_); }
};
The actual code will be more complicated, but try implementing the following logic.
Add a member_variable bool last_char_was_newline, and use it in the code like this:
template<typename T>
Logger& operator<< (const T& data)
{
if (last_char_was_newline) {
*m_pOutStream << current_time_string();
last_char_was_newline = false;
}
*m_pOutStream << data;
if (last_char(data) == '\n') {
last_char_was_newline = true;
}
return *this;
}
To be more general, you should scan data for embedded newlines, and put the time after each of them as well.
The above pseudo-code is glossing over the tricky part. Since data can be any type, last_char(data) (and the more general scanning of the output for embedded newlines) is non-trivial. A general way to implement it might be to write data to a std::stringstream. Then you can scan this string for newlines, and finally output the string to *m_pOutStream.

how to find out if output stream chain is ended?

What I am trying to achieve?
How can I find if a stream chain is ended? Look at the function below (all these functions are inside a LogRouter class in this question):
template<typename First, typename... Rest>
void log(const LogLevel &level_, First first_, Rest... rest_) {
sstream << first_ << " ";
log(level_, rest_...);
}
void log(const LogLevel &level_) {
for(auto &route : routes)
route->stream() << sstream.str() << std::endl;
sstream.clear();
sstream.str("");
}
I want to achieve the exact same functionality in the above but using streams. So, when I reach an end of a stream it needs to send the final data to the routes and instead of using
router.log(LogLevel::Alert, "test stream", 15);
I want to be able to use
router.log(LogLevel::Alert) << "test stream " << 15;
What I have tried:
std::ostream operator overloading does not accept packed variables.
going through every single passed value one by one. Like below:
struct LogEnd {};
static LogEnd end() { return LogEnd; }
template<typename T> LogRouter &operator<<(const T &value) {
sstream << value;
return *this;
}
LogRouter &log(const LogLevel &level_) {
currentLogLevel = level_; //had to add another variable
return *this;
}
void operator<<(const LogEnd &end) {
for(auto &route : routes)
route.stream() << sstream.str() << std::endl;
currentLogLevel = LogLevel::None;
}
This gives me what I want syntax wise but I need to call the additional LogRouter::end() at the end of every:
router.log(LogLevel::Alert) << "test stream " << 15 << LogRouter::end();
I have a syntax for std::endl also but it would be best if I can call it without anything in the end.
Question
Is there a way to know an end of a stream chain. Something similar to what you can do when using recursive variadic template function.
You could put the interesting logic into the stream's destructor. Obviously, I would also properly deal with stream rather than cooking up something which somewhat looks like a stream but isn't really a stream:
#include <iostream>
#include <sstream>
#include <string>
class logstream
: private virtual std::stringbuf
, public std::ostream {
std::string level;
public:
logstream(std::string l)
: std::ostream(this)
, level(l) {
}
logstream(logstream&& other)
: std::stringbuf(std::move(other))
, std::ostream(std::move(other))
, level(std::move(other.level)) {
this->rdbuf(0);
}
~logstream() {
std::cout << "do something interesting here("
<< this->level<< ", " << this->str() << ")\n";
}
};
logstream trace() {
return logstream("trace");
}
int main()
{
trace() << "hello, world";
}
The stream buffer used (std::stringbuf in this case but it could also be a custom stream buffer) is made a base class to have it constructed before the std::ostream. In principle it is meant to be a data member but data members are constructed after the base classes. Thus, it is made a private base class instead.
It turns out that std::ostream has a virtual base class (std::ios) which would cause the std::ostream to still be constructed before the std::stringbuf if normal inheritance where used for std::stringbuf. Using virtual inheritance and making the std::stringbuf the first base class makes sure it really is constructed first.

How to create a type-tag for template specialization

I have a custom logging class that supports iostream-syntax via a templated operator <<:
template< class T >
MyLoggingClass & operator <<(MyLoggingClass &, const T &) {
// do stuff
}
I also have a specialized version of this operator that is supposed to be called when a log-message is complete:
template< >
MyLoggingClass & operator <<(MyLoggingClass &, consts EndOfMessageType &){
// build the message and process it
}
EndOfMessageType is defined like this:
class EndOfMessageType {};
const EndOfMessageType eom = EndOfMessageType( );
The global constant eom is defined so that users can use it just like std::endl at the end of their log-messages. My question is, are there any pitfalls to this solution, or is there some established pattern to do this?
Thanks in advance!
std::endl is a function, not an object, and operator<< is overloaded for accepting a pointer to a function taking and returning a reference to ostream. This overload just calls the function and passes *this.
#include <iostream>
int main()
{
std::cout << "Let's end this line now";
std::endl(std::cout); //this is the result of cout << endl, or cout << &endl ;)
}
Just an alternative to consider.
By the way, I don't think there is any need to specialize the operator: a normal overload does just as well, if not better.
I think your solution is acceptable. If you wanted to do it differently, you could create a class Message, that would be used instead of the your MyLoggingClass and provided automatic termination.
{
Message m;
m << "Line: " << l; // or m << line(l)
m << "Message: foo"; // or m << message("foo");
log << m; // this would automatically format the message
}
I have done it this way, like some other people did. Have a function Error / Log / Warning / etc that could look like this
DiagnosticBuilder Error( ErrType type, string msg, int line );
This will return a temporary builder object, whose class is basically defined like
struct DiagnosticBuilder {
DiagnosticBuilder(std::string const& format)
:m_emit(true), m_format(format)
{ }
DiagnosticBuilder(DiagnosticBuilder const& other)
:m_emit(other.m_emit), m_format(other.m_format), m_args(other.m_args) {
other.m_emit = false;
}
~DiagnosticBuilder() {
if(m_emit) {
/* iterate over m_format, and print the next arg
everytime you hit '%' */
}
}
DiagnosticBuilder &operator<<(string const& s) {
m_args.push_back(s);
return *this;
}
DiagnosticBuilder &operator<<(int n) {
std::ostringstream oss; oss << n;
m_args.push_back(oss.str());
return *this;
}
// ...
private:
mutable bool m_emit;
std::string m_format;
std::vector<std::string> m_args;
};
So if you are building a log message in a loop, be it so
DiagnosticBuilder b(Error("The data is: %"));
/* do some loop */
b << result;
As soon as the builder's destructor is called automatically, the message is emitted. Mostly you would use it anonymously
Error("Hello %, my name is %") << "dear" << "litb";