Here I have the classes Logger and Driver. Driver takes the singleton class of Logger, and logs the same statement 10000 times. I want to use this logger, so that it runs in the background and only logs every so often. This will force the program to not use too much cpu time to log data, but rather other crucial tasks.
#pragma once
#include <deque>
#include <string>
#include <fstream>
#include <map>
#include <iostream>
#include <pthread.h>
#include <time.h>
#define MAX_LINES 10000
#define MESSAGES_PER_WRITE 101
namespace App
{
class Logger
{
friend class Driver;
friend class OrderManager;
public:
static Logger* Instance();
void Log(const std::string line, const std::string prefix);
void DeleteInstance();
void WriteToFile();
private:
Logger();
//the pointer versions of logging is reserved for internal use
//we don't want a strategy to log with pointers and deal with
//memory management
void Log(const std::string*);
void Log(const std::string*, std::string prefix);
struct LogRequest
{
const std::string* line;
std::string prefix;
};
struct FileInfo
{
std::string name;
std::ofstream ofs;
int lines;
};
static Logger* instance;
void OpenLogFile(const std::string&);
void CloseLogFile(const std::string&);
bool run;
std::deque<LogRequest*> message_queue;
std::map<std::string, FileInfo*> file_map;
};
}
#include "Logger.h"
App::Logger* App::Logger::instance = NULL;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
using std::cout;
using std::endl;
namespace App
{
/*
* @construct
* @param
* @description creates a logger to record system information
*/
Logger::Logger()
{
std::string prefix("Audit");
OpenLogFile(prefix);
run = true;
}
/*
* @return instance: pointer to singleton class
* @description creates an instance of the singleton
* if it does not already exist
*/
Logger* Logger::Instance()
{
if(instance == NULL)
{
instance = new Logger;
}
return instance;
}
/*
* @param
* @return
* @description deletes the logger after closing all IO
*/
void Logger::DeleteInstance()
{
usleep(100000);
pthread_mutex_lock(&mutex);
run = false;
std::map<std::string, FileInfo* >::iterator it;
for (it = file_map.begin(); it != file_map.end(); it++)
{
//TODO ofstream* file = (*file_it).second;
//file->close();
}
pthread_mutex_unlock(&mutex);
delete instance;
instance = NULL;
}
/*
* @param line: string to be logged
* @return
* @description adds a line to the queue of lines that
* will be written to the log
*/
void Logger::Log(const std::string* line)
{
pthread_mutex_lock(&mutex);
LogRequest* request = new LogRequest;
request->line = line;
request->prefix = "Audit";
message_queue.push_back(request);
pthread_mutex_unlock(&mutex);
}
/*
* @param line: string to be logged
* @param name: name of the file to log with
* @return
* @description add the line to the given log file
*/
void Logger::Log(const std::string* line, std::string prefix)
{
pthread_mutex_lock(&mutex);
if (file_map.find(prefix) == file_map.end())
{
OpenLogFile(prefix);
}
LogRequest* request = new LogRequest;
request->line = line;
request->prefix = prefix;
message_queue.push_back(request);
pthread_mutex_unlock(&mutex);
}
/*
* @param line: string to be logged
* @param name: name of the file to log with
* @return
* @description add the line to the given log file
*/
void Logger::Log(const std::string line, std::string prefix)
{
pthread_mutex_lock(&mutex);
if (file_map.find(prefix) == file_map.end())
{
OpenLogFile(prefix);
}
LogRequest* request = new LogRequest;
request->line = new std::string(line);
request->prefix = prefix;
message_queue.push_back(request);
pthread_mutex_unlock(&mutex);
}
/*
* @param
* @return
* @description runs in its own thread, checking whether it needs
* to write log statements periodically
*/
void Logger::WriteToFile()
{
std::map<std::string, FileInfo* >::iterator it;
while(run)
{
char timestamp[16];
time_t now;
time(&now);
struct tm* current = localtime(&now);
sprintf(timestamp, "%02u%02u%04u|%02u%02u%02u|", (current->tm_mon+1),
current->tm_mday,(1900 + current->tm_year), current->tm_hour,
current->tm_min, current->tm_sec);
pthread_mutex_lock(&mutex);
for(it=file_map.begin(); it != file_map.end(); ++it)
{
if(it->second->lines > MAX_LINES)
{
CloseLogFile(it->first);
OpenLogFile(it->first);
}
else
{
int written = 0;
while(!message_queue.empty() && written < MESSAGES_PER_WRITE)
{
LogRequest* request = message_queue.front();
message_queue.pop_front();
std::string line(timestamp, 16);
line.append(*(request->line));
FileInfo* info = file_map[request->prefix];
info->ofs << line << std::endl;
info->lines++;
written++;
//delete request;
}
}
}
pthread_mutex_unlock(&mutex);
usleep(1000);
}
}
/*
* @param
* @return
* @description opens a new file for logging with a timestamp
* as the filename
*/
void Logger::OpenLogFile(const std::string& prefix)
{
//get timestamp to use
char timestamp[15];
time_t now;
time(&now);
struct tm* current = localtime(&now);
sprintf(timestamp, "%02u%02u%04u_%02u%02u%02u", (current->tm_mon+1),
current->tm_mday,(1900 + current->tm_year), current->tm_hour,
current->tm_min, current->tm_sec);
FileInfo* info = new FileInfo;
info->name = "logs/" + prefix + ".log_" + timestamp;
info->ofs.open(info->name.c_str(), std::ios::out);
info->lines = 0;
file_map[prefix] = info;
cout << "Creating New Log File: " << timestamp << endl;
}
/*
* @param
* @return
* @description closes the current log file
*/
void Logger::CloseLogFile(const std::string& prefix)
{
delete file_map[prefix];
}
}
#include <iostream>
#include "Logger.h"
void* launchLogThread(void* ptr);
class Driver
{
public:
Driver();
void launchLog();
void logonListen();
};
Driver::Driver()
{
Logger* logger = Logger::Instance();
pthread_t logThread;
pthread_create(&logThread, NULL, launchLogThread, (void*) this);
pthread_detach(logThread);
for(int i=0; i<100000; ++i)
{
logger->Log("work", "pos");
}
}
void* launchLogThread(void* ptr)
{
Driver* driver = (Driver*) ptr;
driver->launchLog();
}
void Driver::launchLog()
{
Logger* logger = Logger::Instance();
logger->WriteToFile();
}
Occasionally I receive a SEG FAULT when logging.
Here is the gdb output:
#0 0x0000003d8786d1b3 in _IO_un_link_internal () from /lib64/libc.so.6
#1 0x0000003d87860da7 in fclose@@GLIBC_2.2.5 () from /lib64/libc.so.6
#2 0x000000336febb968 in std::__basic_file<char>::close() () from /usr/lib64/libstdc++.so.6
#3 0x000000336fe69c17 in std::basic_filebuf<char, std::char_traits<char> >::close() () from /usr/lib64/libstdc++.so.6
#4 0x000000336fe69da4 in std::basic_filebuf<char, std::char_traits<char> >::~basic_filebuf() () from /usr/lib64/libstdc++.so.6
#5 0x000000336fe6ae3b in std::basic_ofstream<char, std::char_traits<char> >::~basic_ofstream() () from /usr/lib64/libstdc++.so.6
#6 0x00000000004c45b0 in Logger::FileInfo::~FileInfo (this=0x2aaab00327f0, __in_chrg=<value optimized out>) at Logger.h:42
#7 0x00000000004c2bd8 in Logger::CloseLogFile (this=0x2aaab001c460, prefix=...) at Logger.cpp:225
The SEG FAULT is occuring on the closing of the file (AFAIK the ofstream::close() gets called within the destructor). This only happens occasionally, and I have not been able to determine the cause. I have printed out the various bits of the ofstream (eod = 0, good = 1, fail = 0, is_open = 1 etc), so everything seems ok when I am about to delete, but once in a while it crashes. Any help please.