Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Oct 27, 2009, 2:47:14 PM (15 years ago)
Author:
rgrieder
Message:

Changed Output concept a little bit to allow for more general use.
Every output (log) target has to be implemented as OutputListener. There is already a LogFileWriter and a MemoryLogWriter (stores ALL the log in a vector and provides iterators).
The OutputListener has a unique and constant name, a stream pointer and a soft debug level (that can only be changed via OutputHandler::setSoftDebugLevel(name, level)).
This concept doesn't require the OutputBuffer anymore, so I deleted it.

The adjustments in the Shell are just preliminary for this commit.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • code/branches/console/src/libraries/util/OutputHandler.cc

    r5983 r5994  
    2323 *      Fabian 'x3n' Landau
    2424 *   Co-authors:
    25  *      ...
     25 *      Reto Grieder
    2626 *
    2727 */
    2828
    2929/**
    30     @file
    31     @brief Implementation of the OutputHandler class.
     30@file
     31@brief
     32    Definition of classes related to output (logging).
    3233*/
    3334
    3435#include "OutputHandler.h"
    3536
     37#include <algorithm>
    3638#include <ctime>
    3739#include <cstdlib>
     40#include <fstream>
     41#include <sstream>
     42
     43#include "Debug.h"
    3844
    3945namespace orxonox
    4046{
     47    //! How the log file shall be named on the filesystem
     48    const std::string logFileBaseName_g = "orxonox.log";
     49
     50    /////////////////////////
     51    ///// LogFileWriter /////
     52    /////////////////////////
    4153    /**
    42         @brief Constructor: Opens the logfile and writes the first line.
    43         @param logfilename The name of the logfile
     54    @brief
     55        Writes the output to the log file.
     56    @note
     57        As long as the correct log path is not yet known (for pre main code), the
     58        LogFileWriter will write to a temporary file in /temp (Unix) or %TEMP% (Windows).
     59        As soon as you set the correct path setLogPath the content of the temporary file
     60        is read and put into the new file as well.
    4461    */
     62    class LogFileWriter : public OutputListener
     63    {
     64    public:
     65        //! Gets temporary log path and starts the log file
     66        LogFileWriter()
     67            : OutputListener(OutputHandler::logFileOutputListenerName_s)
     68        {
     69            // Get path for a temporary file
     70#ifdef ORXONOX_PLATFORM_WINDOWS
     71            char* pTempDir = getenv("TEMP");
     72            this->logFilename_ = std::string(pTempDir) + "/" + logFileBaseName_g;
     73#else
     74            this->logFilename_ = std::string("/tmp/") + logFileBaseName_g;
     75#endif
     76
     77            // Get current time
     78            time_t rawtime;
     79            struct tm* timeinfo;
     80            time(&rawtime);
     81            timeinfo = localtime(&rawtime);
     82
     83            this->logFile_.open(this->logFilename_.c_str(), std::fstream::out);
     84            this->logFile_ << "Started log on " << asctime(timeinfo) << std::endl;
     85            this->logFile_.flush();
     86
     87            this->outputStream_ = &this->logFile_;
     88            // Use default level until we get the configValue from the Core
     89            OutputHandler::getInstance().setSoftDebugLevel(this->getOutputListenerName(), OutputLevel::Debug);
     90            OutputHandler::getInstance().registerOutputListener(this);
     91        }
     92
     93        //! Closes the log file
     94        ~LogFileWriter()
     95        {
     96            this->logFile_ << "Closed log" << std::endl;
     97            this->logFile_.close();
     98        }
     99
     100        //! Changes the log path
     101        void setLogPath(const std::string& path)
     102        {
     103            this->logFile_.close();
     104            // Read old file into a buffer
     105            std::ifstream old(this->logFilename_.c_str());
     106            this->logFilename_ = path + logFileBaseName_g;
     107            // Open the new file and feed it the content of the old one
     108            this->logFile_.open(this->logFilename_.c_str(), std::fstream::out);
     109            this->logFile_ << old.rdbuf();
     110            this->logFile_.flush();
     111            old.close();
     112        }
     113
     114    private:
     115        std::ofstream logFile_;     //! File handle for the log file
     116        std::string   logFilename_; //! Filename of the log file
     117    };
     118
     119
     120    ///////////////////////////
     121    ///// MemoryLogWriter /////
     122    ///////////////////////////
     123    /**
     124    @brief
     125        OutputListener that writes all the output piece by piece to an array
     126        associated with the corresponding output level.
     127    @note
     128        Only output below or equal to the current soft debug level is written
     129        to minimise huge arrays for the normal run.
     130    */
     131    class MemoryLogWriter : public OutputListener
     132    {
     133    public:
     134        friend class OutputHandler;
     135
     136        //! Sets the right soft debug level and registers itself
     137        MemoryLogWriter()
     138            : OutputListener("memoryLog")
     139        {
     140            this->outputStream_ = &this->buffer_;
     141            // We capture as much input as the listener with the highest level
     142            OutputHandler::getInstance().setSoftDebugLevel(this->getOutputListenerName(), OutputHandler::getSoftDebugLevel());
     143            OutputHandler::getInstance().registerOutputListener(this);
     144        }
     145
     146        //! Pushed the just written output to the internal array
     147        void outputChanged()
     148        {
     149            // Read ostringstream and store it
     150            this->output_.push_back(std::make_pair(OutputHandler::getInstance().getOutputLevel(), this->buffer_.str()));
     151            // Clear content and flags
     152            this->buffer_.str(std::string());
     153            this->buffer_.clear();
     154        }
     155
     156    private:
     157        std::ostringstream                        buffer_; //! Stream object used to process the output
     158        std::vector<std::pair<int, std::string> > output_; //! Vector containing ALL output
     159    };
     160
     161
     162    /////////////////////////
     163    ///// OutputHandler /////
     164    /////////////////////////
     165    const std::string OutputHandler::logFileOutputListenerName_s = "logFile";
     166          int         OutputHandler::softDebugLevel_s = hardDebugLevel;
     167
     168    //! Creates the LogFileWriter and the MemoryLogWriter
    45169    OutputHandler::OutputHandler()
    46     {
    47 #ifdef ORXONOX_PLATFORM_WINDOWS
    48         char* pTempDir = getenv("TEMP");
    49         this->logfilename_ = std::string(pTempDir) + "/orxonox.log";
    50 #else
    51         this->logfilename_ = "/tmp/orxonox.log";
    52 #endif
    53 #ifdef NDEBUG
    54         this->softDebugLevel_[LD_All] = this->softDebugLevel_[LD_Logfile] = 2;
    55         this->softDebugLevel_[LD_Console] = this->softDebugLevel_[LD_Shell] = 1;
    56 #else
    57         this->softDebugLevel_[LD_All] = this->softDebugLevel_[LD_Logfile] = 3;
    58         this->softDebugLevel_[LD_Console] = this->softDebugLevel_[LD_Shell] = 2;
    59 #endif
    60 
    61         this->outputBuffer_ = &this->fallbackBuffer_;
    62         this->logfile_.open(this->logfilename_.c_str(), std::fstream::out);
    63 
    64         time_t rawtime;
    65         struct tm* timeinfo;
    66         time(&rawtime);
    67         timeinfo = localtime(&rawtime);
    68 
    69         this->logfile_ << "Started log on " << asctime(timeinfo) << std::endl;
    70         this->logfile_.flush();
    71     }
    72 
    73     /**
    74         @brief Destructor: Writes the last line to the logfile and closes it.
    75     */
     170        : outputLevel_(OutputLevel::Verbose)
     171    {
     172        this->logFile_ = new LogFileWriter();
     173        this->output_  = new MemoryLogWriter();
     174    }
     175
     176    //! Destroys the LogFileWriter and the MemoryLogWriter
    76177    OutputHandler::~OutputHandler()
    77178    {
    78         this->logfile_ << "Closed log" << std::endl;
    79         this->logfile_.close();
    80     }
    81 
    82     /**
    83         @brief Returns a reference to the only existing instance of the OutputHandler class.
    84         @return The instance
    85     */
    86     OutputHandler& OutputHandler::getOutStream()
     179        delete this->logFile_;
     180        delete this->output_;
     181    }
     182
     183    OutputHandler& OutputHandler::getInstance()
    87184    {
    88185        static OutputHandler orxout;
     
    90187    }
    91188
    92     /**
    93         @brief Sets the soft debug level for a given output device.
    94         @param device The output device
    95         @param level The debug level
    96     */
    97     void OutputHandler::setSoftDebugLevel(OutputHandler::OutputDevice device, int level)
    98     {
    99         OutputHandler::getOutStream().softDebugLevel_[static_cast<unsigned int>(device)] = level;
    100     }
    101 
    102     /**
    103         @brief Returns the soft debug level for a given output device.
    104         @param device The output device
    105         @return The debug level
    106     */
    107     int OutputHandler::getSoftDebugLevel(OutputHandler::OutputDevice device)
    108     {
    109         return OutputHandler::getOutStream().softDebugLevel_[static_cast<unsigned int>(device)];
    110     }
    111 
    112     /**
    113         @brief Sets the OutputBuffer, representing the third output stream.
    114         @param buffer The OutputBuffer
    115     */
    116     void OutputHandler::setOutputBuffer(OutputBuffer* buffer)
    117     {
    118         if (buffer == NULL)
    119             this->outputBuffer_ = &this->fallbackBuffer_;
    120         else
    121         {
    122             buffer->getStream() >> this->outputBuffer_->getStream().rdbuf();
    123             this->outputBuffer_ = buffer;
    124         }
    125     }
    126 
    127     /**
    128         @brief Sets the path where to create orxonox.log
    129         @param Path string with trailing slash
    130     */
     189    void OutputHandler::registerOutputListener(OutputListener* listener)
     190    {
     191        for (std::list<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
     192        {
     193            if ((*it)->name_ == listener->name_)
     194            {
     195                COUT(2) << "OutputHandler, Warning: Trying to register two listeners with the same name!" << std::endl;
     196                return;
     197            }
     198        }
     199        this->listeners_.push_back(listener);
     200    }
     201
     202    void OutputHandler::unregisterOutputListener(OutputListener* listener)
     203    {
     204        this->listeners_.remove(listener);
     205    }
     206
    131207    void OutputHandler::setLogPath(const std::string& path)
    132208    {
    133         OutputHandler::getOutStream().logfile_.close();
    134         // store old content
    135         std::ifstream old;
    136         old.open(OutputHandler::getOutStream().logfilename_.c_str());
    137         OutputHandler::getOutStream().logfilename_ = path + "orxonox.log";
    138         OutputHandler::getOutStream().logfile_.open(OutputHandler::getOutStream().logfilename_.c_str(), std::fstream::out);
    139         OutputHandler::getOutStream().logfile_ << old.rdbuf();
    140         old.close();
    141         OutputHandler::getOutStream().logfile_.flush();
    142     }
    143 
    144     /**
    145         @brief Overloaded << operator, redirects the output to the console and the logfile.
    146         @param sb The streambuffer that should be shown in the console
    147         @return A reference to the OutputHandler itself
    148     */
    149     OutputHandler& OutputHandler::operator<<(std::streambuf* sb)
    150     {
    151         //if (getSoftDebugLevel(OutputHandler::LD_Console) >= this->outputLevel_)
    152         //    std::cout << sb;
    153 
    154         if (getSoftDebugLevel(OutputHandler::LD_Logfile) >= this->outputLevel_)
    155         {
    156             this->logfile_ << sb;
    157             this->logfile_.flush();
    158         }
    159 
    160         if (OutputHandler::getSoftDebugLevel(OutputHandler::LD_Shell) >= this->outputLevel_)
    161             (*this->outputBuffer_) << sb;
    162 
    163         return *this;
    164     }
    165 
    166     /**
    167         @brief Overloaded << operator, redirects the output to the console, the logfile and the ingame shell.
    168         @param manipulator A function, manipulating the outstream.
    169         @return A reference to the OutputHandler itself
    170     */
    171     OutputHandler& OutputHandler::operator<<(std::ostream& (*manipulator)(std::ostream&))
    172     {
    173         //if (getSoftDebugLevel(OutputHandler::LD_Console) >= this->outputLevel_)
    174         //    manipulator(std::cout);
    175 
    176         if (getSoftDebugLevel(OutputHandler::LD_Logfile) >= this->outputLevel_)
    177         {
    178             manipulator(this->logfile_);
    179             this->logfile_.flush();
    180         }
    181 
    182         if (OutputHandler::getSoftDebugLevel(OutputHandler::LD_Shell) >= this->outputLevel_)
    183             (*this->outputBuffer_) << manipulator;
    184 
    185         return *this;
    186     }
    187 
    188     /**
    189         @brief Overloaded << operator, redirects the output to the console, the logfile and the ingame shell.
    190         @param manipulator A function, manipulating the outstream.
    191         @return A reference to the OutputHandler itself
    192     */
    193     OutputHandler& OutputHandler::operator<<(std::ios& (*manipulator)(std::ios&))
    194     {
    195         //if (getSoftDebugLevel(OutputHandler::LD_Console) >= this->outputLevel_)
    196         //    manipulator(std::cout);
    197 
    198         if (getSoftDebugLevel(OutputHandler::LD_Logfile) >= this->outputLevel_)
    199         {
    200             manipulator(this->logfile_);
    201             this->logfile_.flush();
    202         }
    203 
    204         if (OutputHandler::getSoftDebugLevel(OutputHandler::LD_Shell) >= this->outputLevel_)
    205             (*this->outputBuffer_) << manipulator;
    206 
    207         return *this;
    208     }
    209 
    210     /**
    211         @brief Overloaded << operator, redirects the output to the console, the logfile and the ingame shell.
    212         @param manipulator A function, manipulating the outstream.
    213         @return A reference to the OutputHandler itself
    214     */
    215     OutputHandler& OutputHandler::operator<<(std::ios_base& (*manipulator)(std::ios_base&))
    216     {
    217         //if (getSoftDebugLevel(OutputHandler::LD_Console) >= this->outputLevel_)
    218         //    manipulator(std::cout);
    219 
    220         if (getSoftDebugLevel(OutputHandler::LD_Logfile) >= this->outputLevel_)
    221         {
    222             manipulator(this->logfile_);
    223             this->logfile_.flush();
    224         }
    225 
    226         if (OutputHandler::getSoftDebugLevel(OutputHandler::LD_Shell) >= this->outputLevel_)
    227             (*this->outputBuffer_) << manipulator;
    228 
    229         return *this;
     209        this->logFile_->setLogPath(path);
     210    }
     211
     212    OutputHandler::OutputVectorIterator OutputHandler::getOutputVectorBegin() const
     213    {
     214        return this->output_->output_.begin();
     215    }
     216
     217    OutputHandler::OutputVectorIterator OutputHandler::getOutputVectorEnd() const
     218    {
     219        return this->output_->output_.end();
     220    }
     221
     222    int OutputHandler::getSoftDebugLevel(const std::string& name) const
     223    {
     224        for (std::list<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
     225        {
     226            if ((*it)->name_ == name)
     227                return (*it)->softDebugLevel_;
     228        }
     229        return -1;
     230    }
     231
     232    void OutputHandler::setSoftDebugLevel(const std::string& name, int level)
     233    {
     234        int globalSoftDebugLevel = -1;
     235        for (std::list<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
     236        {
     237            if ((*it)->name_ == name)
     238                (*it)->softDebugLevel_ = level;
     239            if ((*it)->softDebugLevel_ > globalSoftDebugLevel)
     240                globalSoftDebugLevel = (*it)->softDebugLevel_;
     241        }
     242        // Update global soft debug level
     243        OutputHandler::softDebugLevel_s = globalSoftDebugLevel;
    230244    }
    231245}
Note: See TracChangeset for help on using the changeset viewer.