I've a working logger class, which outputs some text into a richtextbox (Win32, C++).
Problem is, i always end up using it like this:
stringstream ss;
ss << someInt << someString;
debugLogger.log(ss.str());
instead, it would be much more convenient to use it like a stream as in:
debugLogger << someInt << someString;
Is there a better way than forwarding everything to an internal stringstream instance? If'd do this, when would i need to flush?
You need to implement operator << appropriately for your class. The general pattern looks like this:
template <typename T>
logger& operator <<(logger& log, T const& value) {
log.your_stringstream << value;
return log;
}
Notice that this deals with (non-const) references since the operation modifies your logger. Also notice that you need to return the log parameter in order for chaining to work:
log << 1 << 2 << endl;
// is the same as:
((log << 1) << 2) << endl;
If the innermost operation didn't return the current log instance, all other operations would either fail at compile-time (wrong method signature) or would be swallowed at run-time.
Overloading the insertion operator<< is not the way to go. You will have to add overloads for all the endl or any other user defined functions.
The way to go is to define your own streambuf, and to bind it into a stream. Then, you just have to use the stream.
Here are a few simple examples:
Logging In C++ by Petru Marginean, DDJ Sept 05th 2007
Rutger E.W. van Beusekom's logstream class, check also the .hpp alongside with this file
As Luc Hermitte noted, there is "Logging In C++" article which describes very neat approach to solve this problem. In a nutshell, given you have a function like the following:
void LogFunction(const std::string& str) {
// write to socket, file, console, e.t.c
std::cout << str << std::endl;
}
it is possible to write a wrapper to use it in std::cout like way:
#include <sstream>
#include <functional>
#define LOG(loggingFuntion) \
Log(loggingFuntion).GetStream()
class Log {
using LogFunctionType = std::function<void(const std::string&)>;
public:
explicit Log(LogFunctionType logFunction) : m_logFunction(std::move(logFunction)) { }
std::ostringstream& GetStream() { return m_stringStream; }
~Log() { m_logFunction(m_stringStream.str()); }
private:
std::ostringstream m_stringStream;
LogFunctionType m_logFunction;
};
int main() {
LOG(LogFunction) << "some string " << 5 << " smth";
}
(online demo)
Also, there is very nice solution provided by Stewart.
An elegant solution that also solves the flushing issues is the following:
#include <string>
#include <memory>
#include <sstream>
#include <iostream>
class Logger
{
using Stream = std::ostringstream;
using Buffer_p = std::unique_ptr<Stream, std::function<void(Stream*)>>;
public:
void log(const std::string& cmd) {
std::cout << "INFO: " << cmd << std::endl;
}
Buffer_p log() {
return Buffer_p(new Stream, [&](Stream* st) {
log(st->str());
});
}
};
#define LOG(instance) *(instance.log())
int main()
{
Logger logger;
LOG(logger) << "e.g. Log a number: " << 3;
return 0;
}
In the Logger class, override the << operator.
Click Here to know how to implement the << operator.
You can also avoid the logging statements inside the code
using Aspect Oriented programming.
Related
Why would we want to do this:
#include <iostream>
void print(std::ostream& os) {
os << "Hi";
}
int main() {
print(std::cout);
return 0;
}
instead of this:
#include <iostream>
void print() {
std::cout << "Hi";
}
int main() {
print();
return 0;
}
Is there some certain advantage or functionality that is obtained only with the first version?
Yes, the first version is significantly better. Like already mentioned in the comments, it allows you to use any kind of std::ostream, not just std::cout. Some of the most important consequences of this architectural choice are:
You can use your function to print the required data to standard output, a file, a custom class written by your colleagues (e.g. database adapter, logger).
It is possible to test your void print function. E.g.:
TEST(MyFunctionShould, printHello)
{
std::string expectedResult("Hello");
std::ostringstream oss;
print(oss);
ASSERT_EQ(expectedResult, oss.str());
}
I have some trivial logging:
BOOST_LOG_TRIVIAL(trace) << make_trace_record();
Now make_trace_record is a somewhat expensive function to call (don't ask why, it's complicated). I want to call it only if the log currently passes filtering. How can I do that? I don't see a way to call the severity filter explicitly.
Boost.Log filters beforehand; therefore, make_trace_record() will not be called if the severity is not high enough.
In order to set the severity filter for the trivial logger, call:
boost::log::core::get()->set_filter(
boost::log::trivial::severity >= boost::log::trivial::...
);
For instance, the following example outputs 1, showing that expensive() is only called once:
Live On Coliru
#include <iostream>
#include <boost/log/expressions.hpp>
#include <boost/log/trivial.hpp>
int count = 0;
int expensive()
{
return ++count;
}
int main()
{
boost::log::core::get()->set_filter(
boost::log::trivial::severity >= boost::log::trivial::warning
);
BOOST_LOG_TRIVIAL(error) << expensive();
BOOST_LOG_TRIVIAL(info) << expensive();
std::cout << count << '\n';
return 0;
}
Prints:
[2018-05-21 14:33:47.327507] [0x00007eff37aa1740] [error] 1
1
For those wondering how it works, take a look to: How does the "lazy evaluation" of Boost Log's trivial loggers work?
Acorn's answer correctly points out that Boost.Log macros already implement conditional execution of the streaming expression. This behavior is documented in the Tutorial.
I will add that you can generate log records manually, avoiding the macros. An example is given here:
logging::record rec = lg.open_record();
if (rec)
{
logging::record_ostream strm(rec);
strm << "Hello, World!";
strm.flush();
lg.push_record(boost::move(rec));
}
This can be useful if log message formatting is complicated and doesn't fit easily in a streaming expression.
I would do this with an intermediate class who's ostream operator lazily calls your function.
Something like this:
#include <type_traits>
#include <utility>
#include <ostream>
#include <iostream>
namespace detail
{
// an ostreamable object that will stream out the result of a unary function object call
template<class F>
struct lazy_generator
{
void write(std::ostream& os) const
{
os << generator_();
}
friend std::ostream& operator<<(std::ostream& os, lazy_generator const& tr)
{
tr.write(os);
return os;
}
F generator_;
};
}
// construct a lazy_generator
template<class F>
auto lazy_trace(F&& f)
{
return detail::lazy_generator<std::decay_t<F>>({std::forward<F>(f)});
}
// test
int main()
{
extern std::string make_trace_record();
// function pointer
std::clog << lazy_trace(&make_trace_record);
// function object
std::clog << lazy_trace([](){ return make_trace_record(); });
}
I have a strange issue where ostringstream is empty even though I insert an output to it.
Here is my code:
//logger.h
#include <string>
#include <iostream>
#include <sstream>
using std::string;
class BasicLogger {
public:
BasicLogger(const string name);
BasicLogger(const BasicLogger& basicLogger);
~BasicLogger();
template<class T>
BasicLogger& operator<<(const T &msg){
std::cout << "msg is: " << msg << std::endl;
mBuf << msg;
std::cout << "mBuf is: " << mBuf.str() << std::endl;
return *this;
}
private:
string mName;
std::ostringstream mBuf;
};
class Logger {
public:
Logger();
~Logger();
BasicLogger info();
BasicLogger error();
private:
BasicLogger mInfoLogger;
BasicLogger mErrorLogger;
};
//logger.cpp
#include "logger.h"
BasicLogger::BasicLogger(const string name):
mName(name) { }
BasicLogger::BasicLogger(const BasicLogger& otherLogger) {
this->mName = otherLogger.mName;
this->mBuf << otherLogger.mBuf.rdbuf();
}
BasicLogger::~BasicLogger() { }
Logger::Logger():
mInfoLogger("[INFO]"),
mErrorLogger("[ERROR]") {}
Logger::~Logger() {};
BasicLogger Logger::info() {
return mInfoLogger;
}
BasicLogger Logger::error() {
return mErrorLogger;
}
//main.cpp
#include "logger.h"
int main() {
Logger logger;
logger.info() << "Hellooo";
}
The output is
msg is: Hellooo
mBuf is:
While #BoPersson already gave you the solution, I would like to explain what happened here, and why your output is empty, even though you are adding to ostringstream on the line just before you display its content.
I think when you attempted to return mInfoLogger by value, the compiler complained about not being able to return it because copy constructor was deleted. The reason it was deleted because ostringstream member you have is non-copyable.
So you provided custom copy constructor and attempted to copy the stream this way:
this->mBuf << otherLogger.mBuf.rdbuf();
Only this does not copy anything as your rdbuf is empty and instead sets failbit on
The failbit
The streambuf overload of basic_ostream::operator<< if the function
inserts no characters.
http://en.cppreference.com/w/cpp/io/ios_base/iostate
If you were to check your message insertion like this:
if (!(mBuf << msg))
std::cout << "Not Inserted" << std::endl;
You would see Not Inserted message printed. There are many ways to make it print the desired message. For example making sure your rdbuf buffer is not empty or by resetting the failbit before you reuse the mBuf, something like mBuf.clear(); or simply by returning the reference to mInfoLogger (and mErrorLogger). Then you can safely get rid of your copy constructor.
I would like to write code like this:
std::string s = ??? << "asdf" << 123;
I am not sure what to place for the ??? to make it work. I can write a helper class:
#include <string>
#include <sstream>
#include <iostream>
struct Stringify {
std::stringstream o;
template <typename T>
Stringify& operator<<(const T& t){
o << t;
return *this;
}
operator std::string(){ return o.str(); }
};
int main(){
std::string s;
s = Stringify() << " test " << 123 << " asd";
std::cout << s << std::endl;
}
...but I am not able to do it wihtout the helper class. I tried different things, the nicest error message i got from this:
s = (std::stringstream() << "test" << 123 << " asd").str();
-> error: ‘class std::basic_ostream<char>’ has no member named ‘str’
I also tried more, but it just resulted in more complicated error messages.
Is it possible to do what I want without having to write a helper class? If not, is there any good reason why it isnt possible only with std stuff?
Something like this:
std::string s = static_cast<std::ostringstream&>(std::ostringstream() << 7 << 10 << 12).str();
I want to make a Logger that can be used like std::cout, but I want to log some extra data like date, time, __LINE__, __func__, and __FILE__ which should be saved to the file automatically.
Example
ToolLogger log;
log << "some data" << std::endl;
Expected output
[14.11.2015 21:10:12.344 (main.cpp) (main,14): some data
Inadequate solution
To do this I have to put macros like __LINE__ direct in the line where I call my logger, otherwise the macros won't work correct. I found that I can replace std::endl with my macro that will do this black magic like this:
#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl \
((ToolLogger::fileName = __FILENAME__).empty() ? "" : "") \
<< ((ToolLogger::line = __LINE__) ? "" : "") \
<< ((ToolLogger::function = __func__).empty() ? "" : "") \
<< std::endl
The macro logendl uses static variables from my ToolLogger class to save the values of __LINE__, __func__ and __FILE__ needed later. So actually using the logger will looks like this:
ToolLogger log;
log << "some data" << logendl;
In the class i have to overload the operator<< to get this to work, and I need two of them. One for taking the normal values like std::string or int, and the other to take the std::endl manipulator. Here is the most important things from my class:
class ToolLogger
{
public:
// standard operator<< //
template<typename T>
ToolLogger& operator<< (const T& str)
{
out << str;
return *this;
}
// operator<< for taking the std::endl manipulator //
typedef std::basic_ostream<char, std::char_traits<char> > CoutType;
typedef CoutType& (*StandardEndLine)(CoutType&);
ToolLogger& operator<<(StandardEndLine manip)
{
// save fileName, line and function to the file //
// and all what is already in stringstream //
// clear stringstream //
return *this;
}
static string fileName;
static int line;
static string function;
private:
ofstream file;
std::stringstream out;
};
string ToolLogger::fileName;
int ToolLogger::line;
string ToolLogger::function;
Problem
The problem in this solution is that I can use my logger in two ways:
log << "some data" << logendl; // correct //
log << "some data" << std::endl; // compiles -> wrong /
So actually I need to remove the operator<< from my class that takes std::endl manipulator, and solve it other way, but how to do it? I was thinking about changing std::endl in logendl macro to other custom manipulator, and then this custom manipulator will do the work that is actually doing the operator<<, but I have no idea how to do it. I'm looking for other solution, any suggestions?
Here's what I do. It kind of skirts your question. That is, is does away with having to define an endl. What I do is separate out a Logger class (which just takes strings and outputs then to wherever you need them to go) from a LogMessage class which builds a message.
The benefits are:
Each class, on it's own, is pretty simple.
Very simple macros. I don't define the macro below but it's easy enough to do.
No need to define an endl. The message ends at the semicolon when the LogMessage class destructs
Let me know what you think:
#include <iostream>
#include <sstream>
#include <string>
// logger class
// this is not complete, it exists just to illustrate the LogIt function
class Logger
{
public:
void LogIt(const std::string & s)
{
std::cout << s << std::endl;
}
};
// builds a logging message; outputs it in the destructor
class LogMessage
{
public:
// constructor
// takes identifying info of message. You can add log level if needed
LogMessage(const char * file, const char * function, int line)
{
os << file << ": " << function << '(' << line << ") ";
}
// output operator
template<typename T>
LogMessage & operator<<(const T & t)
{
os << t;
return *this;
}
// output message to Logger
~LogMessage()
{
Logger logger; // get logger here (perhaps it's a singleton?)
logger.LogIt(os.str());
}
private:
std::ostringstream os;
};
int main()
{
// example usage
// typically this is invoked via a simple macro to reduce typing of the LogMessage constructor
LogMessage(__FILE__, __func__, __LINE__) << "this is an int " << 5;
}
You might have a LoggerAt class with a LoggerAt(const char*filename, int lineno) constructor (perhaps a subclass of std::ostringstream, etc...), then define
#define LOG(Out) do {LoggerAt(__FILE__,__LINE__) \
<< Out << std::endl; }while(0)
In some of my C++ projects I have coded:
void mom_inform_at(const char*fil, int lin, std::ostringstream& out)
{ out.flush();
std::clog << fil << ":" << lin
<< " INFORM: " << out.str() << std::endl ;
}
#define MOM_INFORM_AT(Fil,Lin,Output) do { \
std::ostringstream out_##Lin; \
out_##Lin << mom_outlog << Output ; \
mom_inform_at(Fil,Lin,out_##Lin); \
} while(0)
#define MOM_INFORM_AT_BIS(Fil,Lin,Output) \
MOM_INFORM_AT(Fil,Lin,Output)
#define MOM_INFORM(Out) \
MOM_INFORM_AT_BIS(__FILE__,__LINE__,Out)
And using something like MOM_INFORM("x=" << " point:" << pt); where you could imagine the usual Point pt; example with appropriate std::ostream& operator << (std::ostream&out, const Point&point) function.
Notice that to use conveniently __FILE__ and __LINE__ you'll better use macros.
I have solved my own problem. Other answers posted here may be better than main, but I wanted to use logger in a simple way just like in C++ std::cout is used. Also my solution may not be optimal and may lead to other problems, but it meets my requirements.
I have added a custom std::ostream
class CustomOstream : public std::ostream
{
public:
static CustomOstream& endl( CustomOstream& out )
{
return out;
}
};
and changed macro to use the endl function from CustomOstream
#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl \
((ToolLogger::fileName = __FILENAME__).empty() ? "" : "") \
<< ((ToolLogger::line = __LINE__) ? "" : "") \
<< ((ToolLogger::function = __func__).empty() ? "" : "") \
<< ToolLogger::CustomOstream::endl
Also the operator<< from the main class has been changed
ToolLogger& operator<< (CustomOstream& (*f)(CustomOstream&))
{
// do something //
return *this;
}
Now the logger can be used just like I wanted
log << "some data" << logendl; // correct //
log << "some data" << std::endl; // won't compile -> correct //