Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/unity_build/src/libraries/util/OutputHandler.cc @ 8740

Last change on this file since 8740 was 8518, checked in by rgrieder, 14 years ago

Sorted out log level handling on startup and transferred its control back to Core.

  • Property svn:eol-style set to native
File size: 10.8 KB
RevLine 
[1505]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
[6105]25 *      Reto Grieder
[1505]26 *
27 */
28
29/**
[6105]30@file
31@brief
32    Definition of classes related to output (logging).
[1505]33*/
34
35#include "OutputHandler.h"
36
[6105]37#include <algorithm>
[2710]38#include <ctime>
39#include <cstdlib>
[6105]40#include <fstream>
[6417]41#include <iostream>
[6105]42#include <sstream>
[2710]43
[6105]44#include "Debug.h"
45
[1505]46namespace orxonox
47{
[6105]48    //! How the log file shall be named on the filesystem
49    const std::string logFileBaseName_g = "orxonox.log";
50
51    /////////////////////////
52    ///// LogFileWriter /////
53    /////////////////////////
[1505]54    /**
[6105]55    @brief
56        Writes the output to the log file.
57    @note
58        As long as the correct log path is not yet known (for pre main code), the
59        LogFileWriter will write to a temporary file in /temp (Unix) or %TEMP% (Windows).
60        As soon as you set the correct path setLogPath the content of the temporary file
61        is read and put into the new file as well.
[1505]62    */
[6105]63    class LogFileWriter : public OutputListener
[1505]64    {
[6105]65    public:
66        /**
67        @brief
68            Gets temporary log path and starts the log file
69        */
70        LogFileWriter()
[8517]71            : OutputListener("LogFile")
[6105]72        {
73            // Get path for a temporary file
[2710]74#ifdef ORXONOX_PLATFORM_WINDOWS
[6105]75            char* pTempDir = getenv("TEMP");
[6417]76            this->logFilename_ = std::string(pTempDir) + '/' + logFileBaseName_g;
[2710]77#else
[6105]78            this->logFilename_ = std::string("/tmp/") + logFileBaseName_g;
[2710]79#endif
80
[6105]81            // Get current time
82            time_t rawtime;
83            struct tm* timeinfo;
84            time(&rawtime);
85            timeinfo = localtime(&rawtime);
[2087]86
[8517]87            this->openFile();
88            if (this->logFile_.is_open())
89            {
90                this->logFile_ << "Started log on " << asctime(timeinfo) << std::endl;
91                this->logFile_.flush();
92            }
[6105]93        }
[1505]94
[6105]95        //! Closes the log file
96        ~LogFileWriter()
97        {
[8517]98            if (this->logFile_.is_open())
99            {
100                this->logFile_ << "Closed log" << std::endl;
101                this->logFile_.close();
102            }
[6105]103        }
104
105        //! Changes the log path
106        void setLogPath(const std::string& path)
107        {
[8517]108            if (this->logFile_.is_open())
109                this->logFile_.close();
110
111            // Open the new file
[6105]112            this->logFilename_ = path + logFileBaseName_g;
[8517]113            this->openFile();
114        }
115
116        //! Erases the log file
117        void clearFile()
118        {
119            if (this->logFile_.is_open())
120            {
121                this->logFile_.close();
122                this->openFile();
123            }
124        }
125
126    private:
127        void openFile()
128        {
[6105]129            this->logFile_.open(this->logFilename_.c_str(), std::fstream::out);
[8517]130
131            if (this->logFile_.is_open())
132                this->outputStream_ = &this->logFile_;
133            else
134            {
135                COUT(2) << "Warning: Failed to open log file. File logging disabled." << std::endl;
136                this->outputStream_ = NULL;
137            }
[6105]138        }
139
[7401]140        std::ofstream logFile_;     //!< File handle for the log file
141        std::string   logFilename_; //!< Filename of the log file
[6105]142    };
143
144
145    /////////////////////////
146    ///// ConsoleWriter /////
147    /////////////////////////
[1505]148    /**
[6105]149    @brief
150        Writes the output to std::cout.
151    @note
152        This listener will usually be disable once an actual shell with console is instantiated.
[1505]153    */
[6105]154    class ConsoleWriter : public OutputListener
[1505]155    {
[6105]156    public:
157        //! Only assigns the output stream with std::cout
158        ConsoleWriter()
[8518]159            : OutputListener("Console")
[6105]160        {
161            this->outputStream_ = &std::cout;
162        }
163    };
[1505]164
[6105]165
166    ///////////////////////////
167    ///// MemoryLogWriter /////
168    ///////////////////////////
[1505]169    /**
[6105]170    @brief
171        OutputListener that writes all the output piece by piece to an array
172        associated with the corresponding output level.
[8515]173        Used as buffer until all output devices have been initialised.
[6105]174    @note
[8515]175        At some point, OutputHandler::disableMemoryLog() has to be called in
176        order to avoid large memory footprints of this class.
[1505]177    */
[6105]178    class MemoryLogWriter : public OutputListener
[1505]179    {
[6105]180    public:
181        friend class OutputHandler;
182
183        MemoryLogWriter()
184            : OutputListener("memoryLog")
185        {
186            this->outputStream_ = &this->buffer_;
187        }
188
[8515]189        //! Push the just written output to the internal array
[6105]190        void outputChanged(int level)
191        {
[6417]192            if (!this->buffer_.str().empty())
193            {
194                // Read ostringstream and store it
195                this->output_.push_back(std::make_pair(level, this->buffer_.str()));
196                // Clear content and flags
197                this->buffer_.str(std::string());
198            }
[6105]199            this->buffer_.clear();
200        }
201
202    private:
[8515]203        std::ostringstream          buffer_; //!< Stream object used to process the output
204        OutputHandler::OutputVector output_; //!< Vector containing ALL output
[6105]205    };
206
207
208    /////////////////////////
209    ///// OutputHandler /////
210    /////////////////////////
[8517]211    int OutputHandler::softDebugLevel_s = hardDebugLevel;
[6105]212
213    //! Creates the LogFileWriter and the MemoryLogWriter
214    OutputHandler::OutputHandler()
215        : outputLevel_(OutputLevel::Verbose)
216    {
[8518]217        // Note: These levels only concern startup before orxonox.ini is read.
[6105]218#ifdef ORXONOX_RELEASE
[8518]219        const OutputLevel::Value initialLevelConsole = OutputLevel::Error;
[6105]220#else
[8518]221        const OutputLevel::Value initialLevelConsole = OutputLevel::Info;
[6105]222#endif
[8518]223        // Use high log level because we rewrite the log file anyway with the
224        // correct level. But if Orxonox were to crash before that, we might be
225        // grateful to have a high debug level, esp. for releases.
226        const OutputLevel::Value intialLevelLogFile = OutputLevel::Debug;
[6105]227
228        this->logFile_ = new LogFileWriter();
229        // Use default level until we get the configValue from the Core
[8518]230        this->logFile_->softDebugLevel_ = intialLevelLogFile;
[6105]231        this->registerOutputListener(this->logFile_);
232
233        this->consoleWriter_ = new ConsoleWriter();
[8518]234        this->consoleWriter_->softDebugLevel_ = initialLevelConsole;
[6105]235        this->registerOutputListener(this->consoleWriter_);
236
[8515]237        this->memoryBuffer_ = new MemoryLogWriter();
238        // Write everything, e.g. use hardDebugLevel
239        this->memoryBuffer_->softDebugLevel_ = hardDebugLevel;
240        this->registerOutputListener(this->memoryBuffer_);
[1505]241    }
242
[6105]243    //! Destroys the LogFileWriter and the MemoryLogWriter
244    OutputHandler::~OutputHandler()
[1586]245    {
[6105]246        delete this->logFile_;
[6417]247        delete this->consoleWriter_;
[8515]248        delete this->memoryBuffer_; // Might already be NULL
[1586]249    }
250
[6105]251    OutputHandler& OutputHandler::getInstance()
[1505]252    {
[6105]253        static OutputHandler orxout;
254        return orxout;
[1505]255    }
256
[6105]257    void OutputHandler::registerOutputListener(OutputListener* listener)
[1505]258    {
[8516]259        for (std::vector<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
[2662]260        {
[6105]261            if ((*it)->name_ == listener->name_)
262            {
263                COUT(2) << "OutputHandler, Warning: Trying to register two listeners with the same name!" << std::endl;
264                return;
265            }
[2662]266        }
[6105]267        this->listeners_.push_back(listener);
[8516]268        this->updateGlobalDebugLevel();
[1505]269    }
270
[6105]271    void OutputHandler::unregisterOutputListener(OutputListener* listener)
272    {
[8516]273        for (std::vector<OutputListener*>::iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
274        {
275            if ((*it)->name_ == listener->name_)
276            {
277                this->listeners_.erase(it);
278                break;
279            }
280        }
281        this->updateGlobalDebugLevel();
[6105]282    }
283
[2710]284    void OutputHandler::setLogPath(const std::string& path)
285    {
[6105]286        this->logFile_->setLogPath(path);
[8517]287        this->rewriteLogFile();
[2710]288    }
289
[8517]290    void OutputHandler::rewriteLogFile()
291    {
292        logFile_->clearFile();
293
294        if (logFile_->outputStream_ == NULL)
295            return;
296
297        for (OutputVector::const_iterator it = this->getOutput().begin(); it != this->getOutput().end(); ++it)
298        {
299            if (it->first <= logFile_->softDebugLevel_)
300                (*logFile_->outputStream_) << it->second;
301        }
302        logFile_->outputStream_->flush();
303    }
304
[6105]305    void OutputHandler::disableCout()
[1505]306    {
[6105]307        this->unregisterOutputListener(this->consoleWriter_);
308    }
[1505]309
[6105]310    void OutputHandler::enableCout()
311    {
312        this->registerOutputListener(this->consoleWriter_);
313    }
[1505]314
[8515]315    void OutputHandler::disableMemoryLog()
[6105]316    {
[8515]317        this->unregisterOutputListener(this->memoryBuffer_);
318        // Only clear the buffer so we can still reference the vector
319        this->memoryBuffer_->output_.clear();
[1505]320    }
321
[8515]322    const OutputHandler::OutputVector& OutputHandler::getOutput() const
[1505]323    {
[8515]324        return this->memoryBuffer_->output_;
[1505]325    }
326
[6105]327    int OutputHandler::getSoftDebugLevel(const std::string& name) const
[1505]328    {
[8516]329        for (std::vector<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
[1505]330        {
[6105]331            if ((*it)->name_ == name)
332                return (*it)->softDebugLevel_;
[1505]333        }
[6105]334        return -1;
[1505]335    }
336
[6105]337    void OutputHandler::setSoftDebugLevel(const std::string& name, int level)
[1505]338    {
[8516]339        for (std::vector<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
[1505]340        {
[6105]341            if ((*it)->name_ == name)
342                (*it)->softDebugLevel_ = level;
[1505]343        }
[8516]344        this->updateGlobalDebugLevel();
345    }
346
347    void OutputHandler::updateGlobalDebugLevel()
348    {
349        int globalSoftDebugLevel = -1;
350        std::vector<OutputListener*>::const_iterator it = this->listeners_.begin();
351        for (; it != this->listeners_.end(); ++it)
352            globalSoftDebugLevel = std::max(globalSoftDebugLevel, (*it)->softDebugLevel_);
353
[6105]354        OutputHandler::softDebugLevel_s = globalSoftDebugLevel;
[1505]355    }
356}
Note: See TracBrowser for help on using the repository browser.