I wrote a logger class for my program but I want to force the client to use my class in a specific way. Here is my Logger
class:
#ifndef __LOGGER_HPP
#define __LOGGER_HPP
#include <Uncopyable.hpp>
#include <sstream>
enum LoggerLevel
{
ERROR,
WARNING,
INFO,
DEBUG,
ALL
};
class Logger : private Uncopyable
{
private:
static LoggerLevel reportingLevel;
static std::ostringstream os;
static bool hide;
static std::string file;
LoggerLevel messageLevel;
std::string className;
public:
static void setVerbosity(LoggerLevel level);
static void setLogFile(const std::string &file);
static void hideOutput();
static bool outputHidden();
static std::ostringstream &getStream();
Logger(const std::string &className);
~Logger();
std::ostringstream &debug();
std::ostringstream &info();
std::ostringstream &warning();
std::ostringstream &error();
};
#endif // __LOGGER_HPP
the implementation:
#include <Logger.hpp>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <ctime>
using namespace std;
LoggerLevel Logger::reportingLevel = LoggerLevel::ALL;
ostringstream Logger::os;
string Logger::file;
bool Logger::hide = false;
Logger::Logger(const string &className) :
className(className)
{
os.str("");
}
Logger::~Logger()
{
os << endl;
if (this->messageLevel <= reportingLevel)
{
if (!hide)
{
if (this->messageLevel == LoggerLevel::ERROR)
{
cerr << os.str() << flush;
}
else
{
cout << os.str() << flush;
}
}
if (!file.empty())
{
time_t now = time(nullptr);
string time(ctime(&now));
ofstream log(file, ios::in | ios::app);
log << time.substr(0, time.length() - 1) << " " << os.str() << flush;
log.close();
}
}
}
void Logger::setVerbosity(LoggerLevel level)
{
Logger::reportingLevel = level;
}
void Logger::setLogFile(const string &file)
{
Logger::file = file;
}
void Logger::hideOutput()
{
Logger::hide = true;
}
bool Logger::outputHidden()
{
return hide;
}
ostringstream &Logger::getStream()
{
return os;
}
ostringstream &Logger::debug()
{
os << "[DEBUG] " << this->className << ": ";
this->messageLevel = LoggerLevel::DEBUG;
return os;
}
ostringstream &Logger::info()
{
os << "[INFO] " << this->className << ": ";
this->messageLevel = LoggerLevel::INFO;
return os;
}
ostringstream &Logger::warning()
{
os << "[WARNING] " << this->className << ": ";
this->messageLevel = LoggerLevel::WARNING;
return os;
}
ostringstream &Logger::error()
{
os << "[ERROR] " << this->className << ": ";
this->messageLevel = LoggerLevel::ERROR;
return os;
}
As you can see, I want to force the client to use the class like this:
Logger("MyCLass").debug() << "This is a debug message.";
So I'd like to statically avoid this kind of use:
Logger myLogger("MyClass");
myLogger.debug() << "This is a debug message.";
myLogger.info() << "This is an information.";
Is there any solution to do that? Thanks.
Yes, there is a solution, thanks to C++11. You want to rvalue-reference-qualify each of your logging functions:
std::ostringstream &debug() &&;
// etc.
In otherwords, the &&
at the end of the function declaration means that debug()
and friends can be called only on temporary Logger
objects:
Logger("MyClass").debug() << "foo"; // fine
Logger log("MyClass");
log.debug() << "bar"; // compile-time error!