1

在记录器向其写入 10000 行后,我有一个函数CloseLogFile被调用以关闭日志文件。我将要记录的行存储在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");

4

2 回答 2

2

这是一个奇怪而不同寻常的建筑。

(显然)有一个files[]包含预先打开的文件的全局变量。

CloseLogFile()使用files["default"]然后关闭它。如果CloseLogFile()再次关闭会怎样?该函数不检查它是否已经被关闭。如果其他逻辑访问files["default"],它会在假设它打开之前检查它是否打开?

使日志文件成为自包含的对象(即不依赖非成员函数来关闭它)或使数组的内容更“正常”(如大多数程序员所期望的那样)不是指针,而是指向的对象,因此删除数组会导致自动清理。

于 2012-09-12T20:52:31.470 回答
1

从堆栈跟踪看起来你正在登录一个线程。当您在记录器线程中输入 CloseLogFile() 时,您看到的条件可能是 ofstream* 在主线程中被删除或失效的竞争条件。

您可能需要同步(同时?)对 CloseLogFile() 的调用以及主线程中的任何清理逻辑。

如果你发布你的 main() 代码,我可能会给你确切的修复/建议

于 2012-09-12T19:38:30.693 回答