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.

Maybe file_map[prefix]; doesn't exist or points to an invalid memory location?

Also are you sure you don't have to do: file_map[prefix].ofs.close().. Then delete?

Edited 4 Years Ago by triumphost

This is the problem:

void Logger::CloseLogFile(const std::string& prefix)
    {
        delete file_map[prefix];
    }

You delete the entry in file_map, but do not remove it from the map, so it is still in use, but not valid - crashing the next time something tries to access it.

Maybe file_map[prefix]; doesn't exist or points to an invalid memory location?

If you take a look, on lines 207 and 208 in Logger.cpp I immediately call OpenLogFile, and due to the mutex the file_map[prefix] has to exist.

Also are you sure you don't have to do: file_map[prefix].ofs.close().. Then delete?

close() of the ofstream is called in the destructor of ofs.

Edited 4 Years Ago by sidvb1: formatting

You delete the entry in file_map, but do not remove it from the map, so it is still in use, but not valid - crashing the next time something tries to access it.

If you take a look, on lines 207 and 208 in Logger.cpp I immediately call OpenLogFile, and due to the mutex the file_map[prefix] has to exist.

If you take a look, on lines 207 and 208 in Logger.cpp I immediately call OpenLogFile, and due to the mutex the file_map[prefix] has to exist.

Don't think that's what he meant?

Try:

void Logger::CloseLogFile(const std::string& prefix)
{
    std::map<std::string, FileInfo*>::iterator it = file_map.find(prefix);
    delete file_map[prefix];
    file_map.erase(it);
}
This article has been dead for over six months. Start a new discussion instead.