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.
Related
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;
}
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
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.
Is it possible to write a method that takes a stringstream and have it look something like this,
void method(string str)
void printStringStream( StringStream& ss)
{
method(ss.str());
}
And can be called like this
stringstream var;
printStringStream( var << "Text" << intVar << "More text"<<floatvar);
I looked up the << operator and it looks like it returns a ostream& object but I'm probably reading this wrong or just not implementing it right.
Really all I want is a clean way to concatenate stuff together as a string and pass it to a function. The cleanest thing I could find was a stringstream object but that still leaves much to be desired.
Notes:
I can't use much of c++11 answers because I'm running on Visual Studio 2010 (against my will, but still)
I have access to Boost so go nuts with that.
I wouldn't be against a custom method as long as it cleans up this mess.
Edit:
With #Mooing Duck's answer mixed with #PiotrNycz syntax I achieved my goal of written code like this,
try{
//code
}catch(exception e)
{
printStringStream( stringstream() << "An exception has occurred.\n"
<<" Error: " << e.message
<<"\n If this persists please contact "<< contactInfo
<<"\n Sorry for the inconvenience");
}
This is as clean and readable as I could have hoped for.
Hopefully this helps others clean up writing messages.
Ah, took me a minute. Since operator<< is a free function overloaded for all ostream types, it doesn't return a std::stringstream, it returns a std::ostream like you say.
void printStringStream(std::ostream& ss)
Now clearly, general ostreams don't have a .str() member, but they do have a magic way to copy one entire stream to another:
std::cout << ss.rdbuf();
Here's a link to the full code showing that it compiles and runs fine http://ideone.com/DgL5V
EDIT
If you really need a string in the function, I can think of a few solutions:
First, do the streaming seperately:
stringstream var;
var << "Text" << intVar << "More text"<<floatvar;
printStringStream(var);
Second: copy the stream to a string (possible performance issue)
void printStringStream( ostream& t)
{
std::stringstream ss;
ss << t.rdbuf();
method(ss.str());
}
Third: make the other function take a stream too
Make your wrapper over std::stringstream. In this new class you can define whatever operator << you need:
class SSB {
public:
operator std::stringstream& () { return ss; }
template <class T>
SSB& operator << (const T& v) { ss << v; return *this; }
template <class T>
SSB& operator << (const T* v) { ss << v; return *this; }
SSB& operator << (std::ostream& (*v)(std::ostream&)) { ss << v; return *this; }
// Be aware - I am not sure I cover all <<'s
private:
std::stringstream ss;
};
void print(std::stringstream& ss)
{
std::cout << ss.str() << std::endl;
}
int main() {
SSB ssb;
print (ssb << "Hello" << " world in " << 2012 << std::endl);
print (SSB() << "Hello" << " world in " << 2012 << std::endl);
}
For ease of writing objects that can be inserted into a stream, all these classes overload operator<< on ostream&. (Operator overloading can be used by subclasses, if no closer match exists.) These operator<< overloads all return ostream&.
What you can do is make the function take an ostream& and dynamic_cast<> it to stringstream&. If the wrong type is passed in, bad_cast is thrown.
void printStringStream(ostream& os) {
stringstream &ss = dynamic_cast<stringstream&>(os);
cout << ss.str();
}
Note: static_cast<> can be used, it will be faster, but not so bug proof in the case you passed something that is not a stringstream.
Since you know you've got a stringstream, just cast the return value:
stringstream var;
printStringStream(static_cast<stringstream&>(var << whatever));
Just to add to the mix: Personally, I would create a stream which calls whatever function I need to call upon destruction:
#include <sstream>
#include <iostream>
void someFunction(std::string const& value)
{
std::cout << "someFunction(" << value << ")\n";
}
void method(std::string const& value)
{
std::cout << "method(" << value << ")\n";
}
class FunctionStream
: private virtual std::stringbuf
, public std::ostream
{
public:
FunctionStream()
: std::ostream(this)
, d_function(&method)
{
}
FunctionStream(void (*function)(std::string const&))
: std::ostream(this)
, d_function(function)
{
}
~FunctionStream()
{
this->d_function(this->str());
}
private:
void (*d_function)(std::string const&);
};
int main(int ac, char* av[])
{
FunctionStream() << "Hello, world: " << ac;
FunctionStream(&someFunction) << "Goodbye, world: " << ac;
}
It is worth noting that the first object sent to the temporary has to be of a specific set of types, namely one of those, the class std::ostream knows about: Normally, the shift operator takes an std::ostream& as first argument but a temporary cannot be bound to this type. However, there are a number of member operators which, being a member, don't need to bind to a reference! If you want to use a user defined type first, you need to extract a reference temporary which can be done by using one of the member input operators.
I have a class in c++ in order to write log files for an application of mine. I have already built the class and it works, it is something like this:
class Logger {
std::string _filename;
public:
void print(std::string tobeprinted);
}
Well, it is intuitive that, in order to print a line in the log file, for an object of Logger, it is simply necessary to do the following:
Logger mylogger("myfile.log");
mylogger.print(std::string("This is a log line"));
Well. Using a method approach is not the same as using a much better pattern like << is.
I would like to do the following:
Logger mylogger("myfile.log");
mylogger << "This is a log line";
That's all. I suppose I must overload the << operator... But overloading using this signature (the classic one):
ostream& operator<<(ostream& output, const MyObj& o);
But I do not have a ostream...
So, should I do as follows?
Logger& operator<<(Logger& output, const std::string& o);
Is this the right way?
Thanks
class Log
{
public:
enum Level { Debug, Error, Info };
static ostream& GetStream() { return cout; }
static bool IsLevelActive(Level l) { return true; }
};
#ifndef NO_LOG
#define LOG_ERROR(M) do { if (Log::IsLevelActive(Log::Error)) (Log::GetStream() << "ERR: " << M << "\n"); } while (false)
#define LOG_INFO(M) do { if (Log::IsLevelActive(Log::Info)) (Log::GetStream() << "INF: " << M << "\n"); } while (false)
#define LOG_WARNING(M) do { if (Log::IsLevelActive(Log::Warning)) (Log::GetStream() << "WRN: " << M << "\n"); } while (false)
#else
#define LOG_ERROR(M)
#define LOG_INFO(M)
#define LOG_WARNING(M)
#endif
struct MyObject {
int a, b;
};
ostream& operator<<(ostream& ostr, const MyObject& obj) {
ostr << "(a=" << obj.a << ", b=" << obj.b << ")";
return ostr;
}
void test() {
int v1 = 42;
int v2 = 43;
LOG_INFO("value1=" << v1 << ", value2=" << v2);
MyObject o = {1, 2};
LOG_INFO("obj=" << o);
}
Why not simply make Logger a sub-class of either std::ostream or std::ostringstream? Then all that functionality will already be implemented.
Yes, this is the right way. But you'll have to add << operator overloads for every data type that you need to log.
You don't really want to create whole new streams, as you then need to redefine all the stream operators. You'd only do that if you want to change entirely how data gets converted into character data. (Ick).
What I've found best for this is to create a class that will keep track of a stream and send its contents to a destination of my choice (the logger) when destructed. That, combined with a smattering of macros, gives you what you are looking for: stream syntax for logging.
Boost actually has some classes that help with this. Look at iostreams.