วันศุกร์ที่ 5 พฤศจิกายน พ.ศ. 2553

xlogger - logging information to a log file

xlogger is used to log any information to a log file. It can log by setting level log. See test.cpp





////////////////////////////////////////////////////////////////////////////////
// xlogger.h
// Author: Seree Rakwong
// Date: 5-NOV-10
// Purpose: Log a file
//
#ifndef xlogger_h
#define xlogger_h

#include <fstream>
#include <map>
#include <string>
#include <pthread.h>

const char DDMMYY_HHMMSS[] = "%d/%m/%y %H:%M:%S";
const char DMY_HMS[]       = "%d%m%y_%H%M%S";

class xlogger : public std::ofstream {
private:
  std::map<int, std::string> level2name_map_;
  std::map<std::string, int> name2level_map_;
  int                        cur_level_;
  std::string                filename_;
  std::string                datetime_format_;
  unsigned long              log_size_;
  pthread_mutex_t            write_mutex_;

public:
  xlogger();
  xlogger(const char *filename);
  virtual ~xlogger();

public:
  void open(const char *filename);
  void open(const std::string& filename);
  int  write(int level, const char *format, ...);
  void set_level(int level, const std::string& lv_name);
  const int current_level() const;
  int& current_level();
  const std::string& datetime_format() const;
  std::string& datetime_format();
  const unsigned long log_size() const;
  unsigned long& log_size();

protected:
  std::string current_time(const char *format = DDMMYY_HHMMSS);
};

#endif // xlogger_h


/////////////////////////////////////////////////////////////////////////
// xlogger.cpp
// Author: Seree Rakwong
// Date: 5-NOV-10
// Purpose: Log a file
//
#include "xlogger.h"
#include <cstdarg>
#include <cstdio>
#include <ctime>

#ifdef DEBUG
#include <iostream>
using std::cout;
using std::endl;
#endif

using std::ofstream;
using std::string;
using std::map;

extern int errno;

xlogger::xlogger()
: filename_(),
  cur_level_(0),
  datetime_format_(DDMMYY_HHMMSS),
  log_size_(1048576) {
  pthread_mutex_init(&write_mutex_, 0);
}

xlogger::xlogger(const char *filename)
: ofstream(filename, ios_base::out|ios_base::app),
  filename_(filename),
  cur_level_(0),
  datetime_format_(DDMMYY_HHMMSS),
  log_size_(1048576) {
  pthread_mutex_init(&write_mutex_, 0);
}

xlogger::~xlogger() {
  pthread_mutex_destroy(&write_mutex_);
  close();
}

void xlogger::open(const char *filename) {
  ofstream::open(filename, ios_base::out|ios_base::app);
  filename_ = filename;
}

void xlogger::open(const string& filename) {
  ofstream::open(filename.c_str(), ios_base::out|ios_base::app);
  filename_ = filename;
}

void xlogger::set_level(int level, const string& name) {
  level2name_map_[level] = name;
  name2level_map_[name]  = level;
}

const int xlogger::current_level() const {
  return cur_level_;
}

int& xlogger::current_level() {
  return cur_level_;
}

const string& xlogger::datetime_format() const {
  return datetime_format_;
}

string& xlogger::datetime_format() {
  return datetime_format_;
}

string xlogger::current_time(const char *format) {
  char buffer[80];
  time_t raw_time;
  struct tm *time_info_ptr = 0;

  time(&raw_time);
  time_info_ptr = localtime(&raw_time);

  string fmt(format);
  if(fmt.length() > 0) {
    strftime(buffer, 80, fmt.c_str(), time_info_ptr);
  }
  else {
    strftime(buffer, 80, datetime_format_.c_str(), time_info_ptr);
  }

  string tmp(buffer);
  return tmp;
}

int xlogger::write(int level, const char *format, ...) {
  if(level > cur_level_) return 0;

  char buffer[1024];
  va_list args;
  va_start(args, format);
  int rc = vsprintf(buffer, format, args);
  va_end(args);

  // return failed if it's negative number
  if(rc < 0) return rc;

  // get level name
  map<int, string>::iterator it = level2name_map_.find(level);
  // write the buffer to output stream
  string str(current_time(DDMMYY_HHMMSS));
  if(it != level2name_map_.end()) {
    str += " [";
    str += it->second;
    str += "] ";
  }
  str += buffer;
  str += "\n"; // end-of-line

  // write
  pthread_mutex_lock(&write_mutex_);
  size_t len = tellp();
  if((unsigned long)(len + str.length()) >= log_size_) {
    // rotate the log file
    string cur_time = current_time(DMY_HMS);

    // close
    close();
    // rename file
    string new_filename(filename_);
    new_filename += ".";
    new_filename += cur_time;
    int rc = rename(filename_.c_str(), new_filename.c_str());
    if(rc == -1) {
      return rc;
    }

    // reopen
    open(filename_);
  }
  ofstream::write(str.c_str(), str.length());
  pthread_mutex_unlock(&write_mutex_);

  return rc;
}

const unsigned long xlogger::log_size() const {
  return log_size_;
}

unsigned long& xlogger::log_size() {
  return log_size_;
}

/////////////////////////////////////////////////////////////////////////
// test.cpp
// Author: Seree Rakwong
// Date: 5-NOV-10
// Purpose: Log a file
#include "xlogger.h"
#include <iostream>
#include <string>

using namespace std;

class mylog : public xlogger {
public:
  mylog() { init(); }
  mylog(const char *filename) : xlogger(filename) { init(); }
  //mylog(const string& filename) : xlogger(filename) { init(); }

public:
enum log_level_t_ {
  System,
  Protocol,
  Data,
  Debug
} log_level_t;


private:
  void init() {
    set_level(System,   "S");
    set_level(Protocol, "P");
    set_level(Data,     "D");
    set_level(Debug,    "G");
    //current_level() = Debug;
  }
};

int main(int argc, char **argv) {
  if(argc < 2) {
    cerr << "USAGE: " << argv[0] << " S|P|D|G" << endl;
    cerr << "S - System level"   << endl;
    cerr << "P - Protocol level" << endl;
    cerr << "D - Data level"     << endl;
    cerr << "G - Debug level"    << endl;
    return 1;
  }

  mylog logger("./meo.log");
  switch(argv[1][0]) {
    case 'P': logger.current_level() = mylog::Protocol; break;
    case 'D': logger.current_level() = mylog::Data; break;
    case 'G': logger.current_level() = mylog::Debug; break;
    case 'S':
    default:logger.current_level() = mylog::System; break;
  }

  cout << "log_size: [" << logger.log_size() << "]" << endl;

  for(int i=0; i<1024; i++) {
    logger.write(mylog::System,   "{%04d::main} - this is a System level",   __LINE__);
    logger.write(mylog::Protocol, "{%04d::main} - this is a Protocol level", __LINE__);
    logger.write(mylog::Data,     "{%04d::main} - this is a Data level",     __LINE__);
    logger.write(mylog::Debug,    "{%04d::main} - this is a Debug level",    __LINE__);
  }
  return 0;
}