#ifndef logging_h
#define logging_h
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable:4996 4717)
#define fileno _fileno
#define isatty _isatty
#define lseek _lseek
#ifdef _ftime
#define ftime _ftime
#endif
#endif
#ifdef unix
#include <sys/types.h>
#include <unistd.h>
#else
#include <io.h>
#include <comdef.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/timeb.h>
#include <time.h>
#include <sstream>
#include <string>
#include <iostream>
template <class _LOGGER> class Logger {
public:
Logger() : fatal_(false) {}
enum LogLevel {
logFATAL = 0, logERROR, logWARN, logINFO, logDEBUG, logTRACE };
~Logger() {
os_ << std::endl, _LOGGER::Log(os_.str(), fatal_);
if (fatal_) {
exit(EXIT_FAILURE);
}
}
static void Level(const std::string& level) {
Level() = ToLogLevel(level);
}
static LogLevel& Level() {
static LogLevel level = GetLogLevelEnv();
return level;
}
std::ostringstream& Stream(LogLevel level) {
static char severity[] = { 'F', 'E', 'W', 'I', 'D', 'T' };
os_ << severity[level] << ' ' << Time();
if (level == logFATAL)
fatal_ = true, os_ << L"FATAL ";
return os_;
}
static std::string Time() {
struct timeb tb; ftime(&tb);
char time[26];
size_t length = strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S:",
localtime(reinterpret_cast<const time_t*>(&tb.time)));
sprintf(time + length, "%03u ", tb.millitm);
return time;
}
private:
static LogLevel ToLogLevel(const std::string& level) {
if (level == "ERROR") {
return logERROR;
} else if (level == "WARN" ) {
return logWARN;
} else if (level == "INFO" ) {
return logINFO;
} else if (level == "DEBUG") {
return logDEBUG;
} else if (level == "TRACE") {
return logTRACE;
} else {
return logFATAL;
}
}
static LogLevel GetLogLevelEnv() {
char* tmp = getenv("SELENIUM_LOG_LEVEL");
return tmp ? ToLogLevel(std::string(tmp)) : logFATAL;
}
std::ostringstream os_;
bool fatal_;
};
class LOG : public Logger<LOG> {
public:
static void File(const std::string& name, const char* openMode = "w") {
const std::string& file = Name(name);
if (file == "stdout") {
LOG::File() = stdout;
} else if (file == "stderr") {
LOG::File() = stderr;
} else {
LOG::File() = fopen(file.c_str(), openMode);
}
}
static void Limit(off_t size) {
LOG::Limit() = size;
}
private:
static std::string& Name(const std::string& name) {
static std::string file_name = "stdout";
if (!name.empty())
file_name.assign(name);
return file_name;
}
static FILE*& File() {
static FILE* file = stdout;
return file;
}
static off_t& Limit() {
static off_t size_limit = 0;
return size_limit;
}
static void Log(const std::string& str, bool fatal) {
if (fatal) Limit() = 0;
FILE* output = File();
if (output) {
fwrite(str.data(), sizeof(char), str.size(), output);
fflush(output);
if (Limit() && !isatty(fileno(output))) {
if (lseek(fileno(output), 0, SEEK_END) > Limit()) {
fclose(output), File("");
}
}
}
if (fatal && !isatty(fileno(output))) {
fputs(str.c_str(), stderr);
}
}
friend class Logger<LOG>;
};
#ifdef _WIN32
#pragma warning(pop)
#endif
#define LOG(LEVEL) \
if (LOG::log ## LEVEL > LOG::Level()) ; \
else LOG().Stream(LOG::log ## LEVEL) << __FILE__ << "(" << __LINE__ << ") "
#ifdef _WIN32
#define LOGHR(LEVEL,HR) LOG( ## LEVEL) << HR << " [" << (_bstr_t(_com_error((DWORD) HR).ErrorMessage())) << "]: "
#define LOGERR(LEVEL) LOG( ## LEVEL) << " [Windows Error " << (::GetLastError()) << "]: "
#define LOGWSTRING(STR) _bstr_t( (## STR).c_str())
#endif
#endif