Date in log file using ofstream - c++

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 ?

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";
}

Related

C/C++ stdout/stderr catch

I try to find a way to catch the standard outputs in a C++ application. This app is quite large, using third parties and may use outputs in both C or C++ ways (assuming there are std::cout calls as well as printf calls)
we can simulate by something like:
void MyAwesomeApp()
{
std::cout << "I am starting" << std::endl;
std::cerr << "Getting a warning!\n";
printf("With old style print\n");
std::cout << "That's all folk!\n";
}
I already tried 2 approachs:
1. std::streambuf
class MyStreambuf : public std::streambuf {
public:
explicit MyStreambuf(const std::string& fileName = "" )
{
if (!fileName.empty())
{
_file1.open(std::filesystem::temp_directory_path() / fileName,std::ios::out);
_file2.open(std::filesystem::current_path() / fileName, std::ios::out);
}
if (_file1.is_open())
{
saveOut = std::cout.rdbuf();
std::cout.rdbuf(this);
saveErr = std::cerr.rdbuf();
std::cerr.rdbuf(this);
}
}
const std::string& GetOutput()const{return saveStr;}
~MyStreambuf() override
{
if (saveOut) std::cout.rdbuf(saveOut);
if (saveErr) std::cerr.rdbuf(saveErr);
_file1.close();
_file2.close();
}
protected:
std::streamsize xsputn(char_type const* s, std::streamsize count) override {
if (_file1.is_open()) _file1.write(s,count);
saveStr.append(s, static_cast<std::size_t>(count));
// .... DO SOME MODIFICATIONS ....
if (_file2.is_open()) _file2.write(s,count);
return count;
}
private:
std::ofstream _file1{};
std::ofstream _file2{};
std::string saveStr{};
std::streambuf* saveOut = nullptr;
std::streambuf* saveErr = nullptr;
};
So I can call, for example:
int main()
{
MyStreambuf outbuf;
MyAwesomeApp();
return !outbuf.GetOutput().empty()
}
I can have the 2 files filled as well as the string, but without the content of the printf call (that remains in the console)
2. dup2
using dup2 I can redirect all the stdout (and stderr) output to a given file. so I get all the contents of std::cout AND printf. but I can Only do that in a file that I have to read (an parse) at the end.
The application may run for a long time And we want to parse outputs during execution not waiting for the end.
Is it possible to have the benefits of the streambuf approach, but capable of catching also the printf outputs?

Check what output is written to standard output

I want to check whether output is written to the standard output (through cout) in a certain part of my program.
What I found is a way to block any output by changing the streambuf of cout. (here:C++: Redirecting STDOUT). That is interesting and first I thought it might help. I found the following code example (source: answer to C++: Redirecting STDOUT)
// Redirect cout.
streambuf* oldCoutStreamBuf = cout.rdbuf();
ostringstream strCout;
cout.rdbuf( strCout.rdbuf() );
// This goes to the string stream.
cout << "Hello, World!" << endl;
// Restore old cout.
cout.rdbuf( oldCoutStreamBuf );
// Will output our Hello World! from above.
cout << strCout.str();
So I can just check whether strCout.str() is a string with 1 or more characters by checking its size.
But what if I do not want to delay the output significantly? So I do not want to save all output in my own ostringstream, wait until I checked whether there was output at the end of my program and then print all the output shortly before my programm ends. I just want to know, whether there was output in a certain part of my program.
Example:
main {
bool wasthereoutput=false;
function1();
function2();
startoutputcheckhere();
function3();
function4();
endoutputcheckhere();
therewasoutput=wasthereoutput();
if therewasoutput{
//do something
}
I don't want to change anything in function3 or function4. I do not want to delay any output generated by those functions until i call endoutputcheckhere() or even wasthereoutput(). I just want to know if there was any output going to std output through cout.
You can create a stream buffer which both collects the written characters for later and also forwards them to another stream buffer, e.g., the one used by std::cout. A simple implementation of this approach could look like this:
class savebuf:
public std::streambuf {
std::streambuf* sbuf;
std::string save;
int overflow(int c) {
if (!traits_type::eq_int_type(c, traits_type::eof()) {
save.push_back(traits_type::to_char_type(c);
return sbuf->sputc(c);
}
else {
return traits_type::not_eof(c);
}
}
int sync() { return sbuf->pubsync(); }
public:
savebuf(std::streambuf* sbuf): sbuf(sbuf) {}
std::string str() const { return save; }
};
int main() {
std::streambuf* coutbuf = std::cout.rdbuf();
savebuf sbuf(coutbuf);
std::cout.rdbuf(&sbuf);
std::cout << "Hello, world\n";
std::cout.rdbuf(coutbuf); // restore the original stream buffer
std::cout << "saved \"" << sbuf.str() << "\"\n";
}
Note that you should restore std::cout's stream buffer as the stream buffer is flushed (i.e. pubsync() is called on it) when std::cout is sort of destroyed.

Discrimination between file and console streams

How to determine weather ostream is a file or a console stream. In the following program I want to print "Hello file!" while writing to a file and "Hello console!" while writing to console. What condition should I specify at line 17?
#include <fstream>
#include<iostream>
#include <string>
using namespace std;
class A{
public:
A(string msg):_str(msg){}
string str()const {return _str;};
private:
string _str;
};
ostream & operator << (ostream & os, const A & a)
{
if (os is ofstream) //this is line 17
os << "Hello file! " << a.str() << endl;
else
os << "Hello console! " << a.str() << endl;
return os;
}
int main()
{
A a("message");
ofstream ofile("test.txt");
if (!ofile)
cerr << "Unable to open file";
else
ofile << a; // "Hello file"
cout << a << endl; // "Hello console"
}
Maybe not pretty, but
std::streambuf const * coutbuf = std::cout.rdbuf();
std::streambuf const * cerrbuf = std::cerr.rdbuf();
ostream & operator << (ostream & os, const A & a)
{
std::streambuf const * osbuf = os.rdbuf();
if ( osbuf == coutbuf || osbuf == cerrbuf )
os << "Hello console! " << a.str() << endl;
else
os << "Hello file! " << a.str() << endl;
return os;
}
We could use &os == &std::cout, but the Standard output might be redirected to file, so I think it is better to use the streambuf object instead. (See this answer for better understanding as to how the redirection works, and why comparing streambuf solves the problem safely! )
You could (ab)use tellp(), which returns -1 if the stream does not have a position:
bool isConsoleStream(ostream const& stream)
{
return stream.tellp() == -1;
}
Of course, there could be other streams that return -1 for this function, so use with caution.
There is no portable means. Under Unix, you can do:
if ( (&os == &std::cout && isatty( STDOUT ))
|| (&os == &std::cerr && isatty( STDERR ))
|| (&os == &std::clog && isatty( STDERR )) ) }
// is a terminal...
}
Under Windows, the isatty becomes _isatty, and I'm not sure
that the macros exist (but I suspect that they do).
Of course, this supposes that you don't do things to confuse it
in your code. Something like:
std::ostream s( std::cout.rdbuf() );
for example, or:
std::cout.rdbuf( &someFileBuf );
Or even:
std::ofstream s( "/dev/tty" ); // (or "CONS" under Windows).
But it's about as close as you can get without the actual fd
from the filebuf.
One is a ofstream and the other is a ostream. Just have two methods.
#include <iostream>
#include <string>
#include <fstream>
class A {
std::string s;
public:
A(const std::string& s) : s(s){}
std::string str() const {return s;}
};
ostream & operator << (std::ostream & os, const A & a)
{
return os << "console: " << a.str() << std::endl;
}
ofstream & operator << (std::ofstream & os, const A & a)
{
return os << "file: " << a.str() << std::endl;
}
int main()
{
A a("hello world");
std::cout << a << endl;
}
This works on Visual Studio 2012
if (typeid(os) == typeid(ofstream)) //this is line 17
But an ostream could be something that isn't an ofstream or the console so you'd have to be careful.
Function to check if a C++ character stream is connected to a terminal/console/tty.
Ideally, we would use the file descriptor under-laying the stream buffer of the C++ stdio stream (cin, cout, cerr or clog).
However, there is no way to retrieve the under-laying file descriptor.
So, we use the fact that at program start-up the stdio stream buffers are connected to the program's standard input and output.
This function only works under the following conditions:
The stream buffers of the start-up C++ stdio streams must not change.
Because the addresses of the stream buffers of the start-up C++ stdio streams are used as identifiers.
For instance by deleting them and then allocating a new stream buffer that has the same address as one of these stream buffers of the start-up C++ stdio streams.
The program's stdio must not change after program start-up.
Because the TTY statuses of the stdio stream buffers are stored at program start-up.
For instance if at start-up the std. out is connected to a terminal and later it is redirected to a pipe or file by something external to the program.
[Instead of storing the TTY statuses at start-up you could retrieve them at run-time, but then you must make sure that your program (and all libraries it uses) does not change the stdio file descriptors (0, 1 and 2). Rember that the stdio stream buffers most likely use other (duplicate) file descriptors.]
Code:
#include <iostream>
extern "C" {
#ifdef _WIN32
# include <io.h> // for: _isatty()
#else
# include <unistd.h> // for: isatty()
#endif
}
// Stdio file descriptors.
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
# define STDOUT_FILENO 1
# define STDERR_FILENO 2
#endif
// Store start-up addresses of C++ stdio stream buffers as identifiers.
// These addresses differ per process and must be statically linked in.
// Assume that the stream buffers at these stored addresses
// are always connected to their underlaying stdio files.
static const streambuf* const StdioBufs[] = {
std::cin.rdbuf(), std::cout.rdbuf(), std::cerr.rdbuf(), std::clog.rdbuf()
};
static const wstreambuf* const StdioWBufs[sizeof(StdioBufs)/sizeof(StdioBufs[0])] = {
std::wcin.rdbuf(), std::wcout.rdbuf(), std::wcerr.rdbuf(), std::wclog.rdbuf()
};
// Store start-up terminal/TTY statuses of C++ stdio stream buffers.
// These statuses differ per process and must be statically linked in.
// Assume that the statuses don't change during the process life-time.
static const bool StdioTtys[sizeof(StdioBufs)/sizeof(StdioBufs[0])] = {
#ifdef _WIN32
_isatty(STDIN_FILENO), _isatty(STDOUT_FILENO), _isatty(STDERR_FILENO), _isatty(STDERR_FILENO)
#else
isatty(STDIN_FILENO), isatty(STDOUT_FILENO), isatty(STDERR_FILENO), isatty(STDERR_FILENO)
#endif
};
// Is a Terminal/Console/TTY connected to the C++ stream?
// Use on C++ stdio chararacter streams: cin, cout, cerr and clog.
bool isTTY(const ios& strm)
{
for(unsigned int i = 0; i < sizeof(StdioBufs)/sizeof(StdioBufs[0]); ++i) {
if(strm.rdbuf() == StdioBufs[i])
return StdioTtys[i];
}
return false;
}
// Is a Terminal/Console/TTY connected to the C++ stream?
// Use on C++ stdio wide-chararacter streams: wcin, wcout, wcerr and wclog.
bool isTTY(const wios& strm)
{
for(unsigned int i = 0; i < sizeof(StdioWBufs)/sizeof(StdioWBufs[0]); ++i) {
if(strm.rdbuf() == StdioWBufs[i])
return StdioTtys[i];
}
return false;
}
Note: I've only tested it on Linux.

Use cout or cerr to output to console after it has been redirected to file

Redirecting cout or cerr to a file is easy enough. I can use this to redirect third party output to a file. However, after I have redirected the third party output to a file, how do I then use cout myself to output to the console?
I'm a great fan of RAII, so I once wrote this small helper class. It will redirect the stream until it goes out of scope, at which point it restores the original buffer. Quite handy. :)
class StreamRedirector {
public:
explicit StreamRedirector(std::ios& stream, std::streambuf* newBuf) :
savedBuf_(stream.rdbuf()), stream_(stream)
{
stream_.rdbuf(newBuf);
}
~StreamRedirector() {
stream_.rdbuf(savedBuf_);
}
private:
std::streambuf* savedBuf_;
std::ios& stream_;
};
Can be used like this:
using namespace std;
cout << "Hello stdout" << endl;
{
ofstream logFile("log.txt");
StreamRedirector redirect(cout, logFile.rdbuf());
cout << "In log file" << endl;
}
cout << "Back to stdout" << endl;
You save the buffer and restore it later:
std::streambuf *buf = std::cout.rdbuf(); //save
// Do other stuff
std::cout.rdbuf(buf); // restore

Checking to see if ofstream is empty?

I've created an ofstream and there is a point in which I need to check if it's empty or has had things streamed into it.
Any ideas how I would go about doing this?
The std::ofstream files don't support this directly. What you can do if this is an important requirement is to create a filtering stream buffer which internally used std::filebuf but also records if there was any output being done. This could look look as simple as this:
struct statusbuf:
std::streambuf {
statusbuf(std::streambuf* buf): buf_(buf), had_output_(false) {}
bool had_output() const { return this->had_output_; }
private:
int overflow(int c) {
if (!traits_type::eq_int_type(c, traits_type::eof())) {
this->had_output_ = true;
}
return this->buf_->overflow(c);
}
std::streambuf* buf_;
bool had_output_;
};
You can initialize an std::ostream with this and query the stream buffer as needed:
std::ofstream out("some file");
statusbuf buf(out.rdbuf());
std::ostream sout(&buf);
std::cout << "had_output: " << buf.had_output() << "\n";
sout << "Hello, world!\n";
std::cout << "had_ouptut: " << buf.had_output() << "\n";
you could use ofstream.rdbuff to get the file buffer and than use streambuf::sgetn to read it. I believe that should work.