Eine Logfile Klasse zur freien Verfügung

Präsentation und Organisation von eigenen Projekten
Antworten
Benutzeravatar
Bebu
Beiträge: 562
Registriert: Mi Okt 21, 2009 6:19 pm
Wohnort: In der Nähe von Salzburg - Bin aber kein Österreicher!

Eine Logfile Klasse zur freien Verfügung

Beitrag von Bebu » So Jan 17, 2010 7:14 pm

Im Rahmen meines Listengenerators bin ich gedanklich auf ein Problem gestoßen. Wie verhindere ich, das ich auf ungenaue Ferndiagnosen zurückgreifen muss, wenn beim Benutzer ein Fehler auftritt? Die Lösung heißt für mich Logfiles. Damit die Logfiles zum einen nicht aus allen Nähten platzen, auch bei einem Programmfehler ordentlich geschlossen werden und ich mich nicht um das Erstellen von Verzeichnissen kümmern wollte, ist der nachfolgende Code entstanden. Wird die vorgegebene Dateigröße überschritten, dann löscht die Klasse selbstständig die erste Hälfte der Logdatei. Auf diese Weise bleiben die aktuellen Logs erhalten. Um diesen Code nutzen zu können, sind drei kompilierte Boostlibrarys nötig: libboost_date_time, libboost_filesystem und libboost_system. Diese müssen zum Programm gelinkt werden, sonst klappts nicht. Meine Klasse greift dabei über Boost auf verschiedene Betriebssystemfunktionen zu, die fehlschlagen können. Es ist also ratsam diese Klasse in einem Try-Catch Block zu instanzieren, wenn ihr nicht wollt, das euer Programm komplett abschmirt :evil: Der Aufruf der Add-Funktion sollte allerdings relativ sicher sein, solange man Schreibrechte im Zielverzeichnis hat und das instanzieren problemlos funktioniert hat. Ich bin für jede Kritik und Anregungen offen, schließlich soll mein Code besser werden :ugeek:

Ich wäre auch einem Windows und Mac-Benutzer verbunden, der das ganze mal unter seinem OS testet, mein Ziel war dabei ja auch plattformunabhängig zu schreiben. Leider fehlen mir im Moment die geeigneten Testmittel :mrgreen:

So jetzt der Code:

Header:

Code: Alles auswählen

/******************************************************************************
This ist a Logfile Class für C++. Log every text you want to a file.

Copyright (C) 2009 by Bernhard Buchwinkler

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, see http://www.gnu.org/licenses/.
******************************************************************************/
#ifndef LOGFILE_HPP_INCLUDED
#define LOGFILE_HPP_INCLUDED

#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem.hpp>
#include <string>
#include <stdexcept>
#include <deque>

/*
to use this code you need the boost library(www.boost.org)
the following boost-librarys must be linked to your programm:
-libboost_date_time
-libboost_file_system
-libboost_system

ATTENTION: This class uses boost systemfunctions, which can
fail and throw an error. Make sure to catch exceptions!
*/

enum size
{
    B=1,        //BYTE
    KB=1000,    //KILOBYTE
    MB=1000000  //MEGABYTE
};

/*
      using this class:
logfile log(path/to/logfile,50,KB);
log.add("This Text to the log");
*/
class logfile
{
    boost::filesystem::path path,path_tmp;

    int size, max_size;
    boost::filesystem::ofstream logstream;

public:
    logfile(std::string path_c, int max_size_c,int size_factor_c) :
            path(path_c),max_size(max_size_c*size_factor_c)
    {
        check_path();
        logstream.open(path, std::ios_base::out | std::ios_base::app);
        if(!logstream) throw std::runtime_error("could not open logfile");
        check_size();
    }

    ~logfile()
    {
        logstream.close();
    }

    void add(const std::string &input);

private:
    void check_path();
    void check_size();
    void del_half_log();
};
#endif // LOGFILE_HPP_INCLUDED
Und die Quellcodedatei:

Code: Alles auswählen

/******************************************************************************
This ist a Logfile Class für C++. Log every text you want to a file.

Copyright (C) 2009 by Bernhard Buchwinkler

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, see http://www.gnu.org/licenses/.
******************************************************************************/
#include "logfile.hpp"

//after checking size of logfile add a string to the log
void logfile::add(const std::string &input)
{
    check_size();
    logfile::logstream << input << std::endl;
}
/*
this function checks if the path is existing and creates it,
if not existing.
*/
void logfile::check_path()
{
    std::string path_str;
    std::string dir;
    boost::filesystem::path directory;

    path_str = logfile::path.file_string();
    dir = path_str;

    //add .tmp if no filextension exists for use n del_half_log()
    if (logfile::path.extension()=="")
    {
        path_str = path_str + ".tmp";
        logfile::path_tmp = path_str;
    }
    //change a existing fileextension to .tmp for use in del_half_log()
    else
    {
        path_str.erase(path_str.rfind(logfile::path.extension()));
        path_str = path_str + ".tmp";
        logfile::path_tmp = path_str;
    }

    //create a path object that includes only the path but not the file
    dir = dir.erase(dir.rfind(path.extension()));
    dir = dir.erase(dir.rfind(path.stem()));

    directory = dir;

    boost::filesystem::file_status status =
                        boost::filesystem::status(directory);

    //create directory if not exists or something else than a directory
    if (boost::filesystem::exists(status) == false ||
       (boost::filesystem::is_directory(status) == false))
       {
           boost::filesystem::create_directories(directory);
       }
}

//check size of logfile and resize if to big
void logfile::check_size()
{
    logfile::size = boost::filesystem::file_size(logfile::path);
    if (logfile::size > logfile::max_size) del_half_log();
}

//this function is used to delete the half of the logfile
void logfile::del_half_log()
{
    int filelines;
    typedef std::deque<std::string> deque_;
    deque_ tmp;
    std::string line;

    //open sourcefile and temporary file to copy
    boost::filesystem::ifstream input;
    boost::filesystem::ofstream output;

    input.open(path,std::ios_base::in);
    output.open(path_tmp,std::ios_base::out);

    if (!input)
    {
        input.close();
        output.close();
        throw std::runtime_error("Could not open logfile to read");
    }
    if (!output)
    {
        input.close();
        output.close();
        throw std::runtime_error("Could not open tmp logfile to write");
    }

    //read file into deque container.
    while(!input.eof())
    {
        getline(input,line);
        tmp.push_back(line);
    }
    //get size of container and erase the first half
    filelines = tmp.size();
    tmp.erase(tmp.begin(),tmp.begin()+(filelines/2));

    //write container to temporary file; Last fileline without newline
    for (deque_::const_iterator iter = tmp.begin(); iter != tmp.end(); iter++)
    {
        if (iter != tmp.end()-1)
        {
            output << *iter << std::endl;
        }
        else
        {
            output << *iter;
        }
    }
    output.close();
    input.close();
    //remove sourcefile and rename temporary file to sourcefile
    boost::filesystem::remove(logfile::path);
    boost::filesystem::rename(path_tmp,path);
    //calling check_size to make sure the filesize is correct now.
    //if not, del_half_log will be called again.
    check_size();
}

Kleines Anwendungsbeispiel:

Code: Alles auswählen

logfile log("log/log.txt,50,KB);
log.add("Ich bin eine Zeile in der Logdatei");
Wer immer nach dem Unerreichbaren jagt, der wird irgendwann auf die Schnauze fallen!

Antworten