logger.h
#pragma once
#include <string>
#include <fstream>
namespace utility {
#define debug(format, ...) \
Logger::instance()->log(Logger::DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define info(format, ...) \
Logger::instance()->log(Logger::INFO, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define warn(format, ...) \
Logger::instance()->log(Logger::WARN, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define error(format, ...) \
Logger::instance()->log(Logger::ERROR, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define fatal(format, ...) \
Logger::instance()->log(Logger::FATAL, __FILE__, __LINE__, format, ##__VA_ARGS__)
class Logger
{
public:
enum Level {
DEBUG = 0,
INFO,
WARN,
ERROR,
FATAL,
LEVEL_COUNT
};
static Logger* instance();
void open(const std::string &filename);
void close();
void log(Level level, const char *file, int line, const char *format, ...);
void max(int bytes);
void level(int level);
private:
Logger();
~Logger();
void rotate();
private:
std::string m_filename;
std::ofstream m_font;
int m_max;
int m_len;
int m_level;
static const char *s_level[LEVEL_COUNT];
static Logger *m_instance;
};
}
logger.cpp
#include "logger.h"
#include <time.h>
#include <stdarg.h>
using namespace utility;
const char* Logger::s_level[LEVEL_COUNT] = {
"DEBUG",
"INFO",
"WARN",
"ERROR",
"FATAL"
};
Logger *Logger::m_instance = nullptr;
Logger::Logger() : m_max(0), m_len(0), m_level(DEBUG) {}
Logger::~Logger() {
close();
}
Logger* Logger::instance() {
if(m_instance == nullptr) {
m_instance = new Logger();
}
return m_instance;
}
void Logger::open(const std::string &filename) {
m_filename = filename;
m_font.open(filename.c_str(), std::ios::app);
if(m_font.fail()) {
throw std::logic_error("open log file failed: " + filename);
}
m_font.seekp(0, std::ios::end);
m_len = m_font.tellp();
}
void Logger::close() {
m_font.close();
}
void Logger::log(Level level, const char *file, int line, const char *format, ...) {
if(level < m_level) {
return;
}
if(m_font.fail()) {
throw std::logic_error("open log file failed: " + m_filename);
}
time_t ticks = time(nullptr);
struct tm* ptm = localtime(&ticks);
char timestamp[32];
memset(timestamp, 0, sizeof(timestamp));
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", ptm);
int len = 0;
const char * fmt = "%s %s %s: %d";
len = snprintf(nullptr, 0, fmt, timestamp, s_level[level], file, line);
if (len > 0) {
char *buffer = new char[len + 1];
snprintf(buffer, len + 1, fmt, timestamp, s_level[level], file, line);
buffer[len] = 0;
m_font << buffer;
delete[] buffer;
m_len += len;
}
va_list arg_ptr;
va_start(arg_ptr, format);
len = vsnprintf(nullptr, 0, format, arg_ptr);
va_end(arg_ptr);
if (len > 0) {
char * content = new char[len + 1];
va_start(arg_ptr, format);
vsnprintf(content, len + 1, format, arg_ptr);
va_end(arg_ptr);
content[len] = 0;
m_font << content;
delete [] content;
m_len += len;
}
m_font << "\n";
m_font.flush();
if(m_max > 0 && m_len >= m_max) {
rotate();
}
}
void Logger::max(int bytes) {
m_max = bytes;
}
void Logger::level(int level) {
m_level = level;
}
void Logger::rotate() {
close();
time_t ticks = time(NULL);
struct tm* ptm = localtime(&ticks);
char timestamp[32];
memset(timestamp, 0, sizeof(timestamp));
strftime(timestamp, sizeof(timestamp), ".%Y-%m-%d_%H-%M-%S", ptm);
std::string filename = m_filename + timestamp;
if (rename(m_filename.c_str(), filename.c_str()) != 0) {
throw std::logic_error("rename log file failed: " + std::string(strerror(errno)));
}
open(m_filename);
}
main.cpp
#include <iostream>
#include <string>
using namespace std;
#include "Logger.h"
using namespace utility;
int main() {
Logger::instance()->open("./main.log");
debug("name=%s age=%d", "jack", 18);
return 0;
}