Reputation: 470
Below is the code I have to write every time I want to include the date in my log file at the moment. I'm looking for a way to not have to write my CurrentDateToString()
everytime I want to write something in my file.
ofstream log;
log.open("test.log", ios_base::app);
log << CurrentDateToString() << " | " << "first line" << endl;
log << CurrentDateToString() << " | " << "second line" << endl;
log.close();
And here is my CurrentDateToString()
function:
// convert date to formatted string
string CurrentDateToString()
{
time_t rawtime;
struct tm* timeInfo;
char buffer[80];
time(&rawtime);
timeInfo = localtime(&rawtime);
strftime(buffer, 80, "%Y-%m-%d %I:%M:%S", timeInfo);
string dateString(buffer);
return dateString;
}
The goal here is to be able to write those lines instead of the current lines I'm writing:
log << "first line" << endl;
log << "second line" << endl;
Do I have to write a log class and overload operator<< or is there another way to do it ?
Upvotes: 0
Views: 1887
Reputation: 154035
The way to implement a stream which automagically adds (or removes) characters, e.g., adding a date, is to create a filtering stream buffer. You'd derive a class from std::streambuf
which massages the characters it receives into the necessary form and then forwards them to an underlying stream buffer.
For your use of adding a date at the start of the line you'd simply observe newline characters being received and set a flag need_date
if there is a newline. When a character is being written while need_date
is set, the date is written and the flag is cleared.
Here is how that could look for your date:
#include <streambuf>
class logbuf
: public std::streambuf {
std::streambuf* sbuf;
bool need_date{true};
using traits_type = std::char_traits<char>;
int overflow(int c) override {
if (c != traits_type::eof()) {
if (need_date) {
std::string date{CurrentDateToString()};
this->sbuf->sputn(date.c_str(), date.size());
need_date = false;
}
if (c == '\n') {
need_date = true;
}
return this->sbuf->sputc(c);
}
else {
return traits_type::not_eof(c);
}
}
int sync() override { return this->sbuf->pubsync(); }
public:
logbuf(std::streambuf* sbuf): sbuf(sbuf) {}
};
The virtual
function overflow()
is called every time there is no space in the stream buffer's buffer. Since there is no output buffer set up that happens for each character (performance can be improved by adding an override for xsputn()
and/or adding buffering). The function sync()
is called every time the stream is flushed. Since nothing is buffered, the flush request is simply forwarded to the underlying stream buffer.
Below is a simple demo using this stream buffer. The creation of a suitable underlying stream buffer, e.g., a std::filebuf
, and an std::ostream
can be packaged into a class derived from std::ostream
.
#include <iostream>
int main()
{
logbuf sbuf(std::cout.rdbuf());
std::ostream logout(&sbuf);
logout << "hello\nwordl\n";
logout << "\n";
logout << "goodbye\n";
}
Upvotes: 1