У меня есть функция CloseLogFile
он вызывается для закрытия файла журнала после того, как регистратор записал в него 10000 строк. У меня есть строки для сохранения в deque
типа std::string
, Вот шапка
#pragma once
#include <deque>
#include <string>
#include <fstream>
#include <map>
#include <iostream>
#include <pthread.h>
#include <time.h>
#define MAX_LINES 1000
#define MESSAGES_PER_WRITE 100
class AtlLogger
{
friend class Driver;
friend class OrderManagementSystem;
public:
static AtlLogger* Instance();
void Log(const std::string line, const std::string prefix);
void DeleteInstance();
void WriteToFile();
private:
AtlLogger();
//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 AtlLogger* 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;
};
Вот файл .cpp:
#include "AtlLogger.h"
AtlLogger* AtlLogger::instance = NULL;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
using std::cout;
using std::endl;
/*
* @construct
* @param
* @description creates a logger to record system information
*/
AtlLogger::AtlLogger()
{
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
*/
AtlLogger* AtlLogger::Instance()
{
if(instance == NULL)
{
instance = new AtlLogger;
}
return instance;
}
/*
* @param
* @return
* @description deletes the logger after closing all IO
*/
void AtlLogger::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 AtlLogger::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 AtlLogger::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 AtlLogger::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 AtlLogger::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 AtlLogger::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;
cout << "1" << endl;
cout << prefix << endl;
info->name = new std::string("logs/" + prefix + ".log_" + timestamp);
cout << "2" << endl;
cout << "3" << endl;
cout << info->name->c_str() << endl;
info->ofs.open(info->name->c_str());
cout << "4" << endl;
info->lines = 0;
cout << "5" << endl;
file_map[prefix] = info;
cout << "Creating New Log File: " << timestamp << endl;
}
/*
* @param
* @return
* @description closes the current log file
*/
void AtlLogger::CloseLogFile(const std::string& prefix)
{
cout << "Attempting to Close File!" << endl;
cout << prefix << endl;
cout << "Is Open?: " << file_map[prefix]->ofs.is_open() << endl;
cout << "good?: " << file_map[prefix]->ofs.good() << endl;
cout << "eof?: " << file_map[prefix]->ofs.eof() << endl;
cout << "fail?: " << file_map[prefix]->ofs.fail() << endl;
cout << "bad?: " << file_map[prefix]->ofs.bad() << endl;
cout << "name? " << *file_map[prefix]->name << endl;
cout << "lines? " << file_map[prefix]->lines << endl;
//cout << "rdbuf: " << file_map[prefix]->ofs.rdbuf() << endl;
cout << "rdbuf open?: " << file_map[prefix]->ofs.rdbuf()->is_open() << endl;
file_map[prefix]->ofs.close();
cout << "closed stream" << endl;
delete file_map[prefix];
cout << "deleted memory" << endl;
file_map.erase(prefix);
cout << "Close File End!"<< endl;
}
Иногда в моей программе возникают сбои, и я не могу понять, почему это так. Он работает много раз, и в конечном итоге вызывает сбой, иногда при первом вызове, иногда после многих вызовов.
Вот мой след от GDB:
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 0x000000336fe69cad in std::basic_ofstream<char, std::char_traits<char> >::close() () from /usr/lib64/libstdc++.so.6
5 0x00000000004c2a25 in AtlLogger::CloseLogFile() ()
6 0x00000000004c2ef1 in AtlLogger::WriteToFile() ()
7 0x0000000000482270 in Driver::launchLog (this=0x7fffffffe86f) at driver.cpp:672
8 0x000000000048228f in launchLogThread (ptr=0x7fffffffe86f) at driver.cpp:654
9 0x0000003d8840673d in start_thread () from /lib64/libpthread.so.0
10 0x0000003d878d3d1d in clone () from /lib64/libc.so.6
Вот вывод консоли прямо перед ошибкой сегмента:
Attempting to Close File!
test
Is Open?: 1
good?: 1
eof?: 0
fail?: 0
bad?: 0
name? logs/test.log_09132012_095549
lines? 1001
rdbuf open?: 1
Может кто-нибудь показать мне, где это может пойти не так? (И на заметку, почему в GDB я вижу номера строк для некоторых частей трассы, а не для других?)
Функция регистрации может использоваться со следующими:
Logger::Instance()->Log("Log This", "File");
Это любопытная и необычная архитектура.
Существует (по-видимому) глобальная переменная files[]
содержащий предварительно открытый файл.
CloseLogFile()
использования files["default"]
а затем закрывает его. Что будет, если CloseLogFile()
снова закрыт? Функция не проверяет, закрыта ли она уже. И если другая логика обращается files["default"]
Будет ли он проверяться на то, что он открыт, прежде чем предположить, что он открыт?
Было бы более «нормально» (спроектировано так, как ожидает большинство программистов) либо сделать файл журнала объектом, который является автономным, то есть не зависит от функций, не являющихся членами, для его закрытия, либо создать содержимое массива не указатели, а объекты, на которые указывают, так что удаление массива вызывает автоматическую очистку.
Из стека трассировки выглядит, как будто вы входите в поток. Условие, которое вы видите, может быть состоянием гонки ofstream *, который либо удаляется, либо становится недействительным в главном потоке к тому времени, когда вы вводите CloseLogFile () в вашем потоке регистратора.
Вам может потребоваться синхронизировать (одновременные?) Вызовы с CloseLogFile () и любой логикой очистки, которая есть в основном потоке.
Если вы опубликуете ваш код main (), я смогу дать вам точное исправление / предложения