Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/util/OutputHandler.cc @ 8739

Last change on this file since 8739 was 8729, checked in by rgrieder, 13 years ago

Merged unity_build branch back to trunk.

Features:

  • Implemented fully automatic build units to speed up compilation if requested
  • Added DOUT macro for quick debug output
  • Activated text colouring in the POSIX IOConsole
  • DeclareToluaInterface is not necessary anymore

Improvements:

  • Output levels now change appropriately when switch back and forth from dev mode
  • Log level for the file output is now also correct during startup
  • Removed some header file dependencies in core and tools to speed up compilation

no more file for command line options

  • Improved util::tribool by adapting some concepts from boost::tribool

Regressions:

  • It is not possible anymore to specify command line arguments in an extra file because we've got config values for that purpose.
  • 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()
[8729]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
[8729]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        {
[8729]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        {
[8729]108            if (this->logFile_.is_open())
109                this->logFile_.close();
110
111            // Open the new file
[6105]112            this->logFilename_ = path + logFileBaseName_g;
[8729]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);
[8729]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()
[8729]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.
[8729]173        Used as buffer until all output devices have been initialised.
[6105]174    @note
[8729]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
[8729]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:
[8729]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    /////////////////////////
[8729]211    int OutputHandler::softDebugLevel_s = hardDebugLevel;
[6105]212
213    //! Creates the LogFileWriter and the MemoryLogWriter
214    OutputHandler::OutputHandler()
215        : outputLevel_(OutputLevel::Verbose)
216    {
[8729]217        // Note: These levels only concern startup before orxonox.ini is read.
[6105]218#ifdef ORXONOX_RELEASE
[8729]219        const OutputLevel::Value initialLevelConsole = OutputLevel::Error;
[6105]220#else
[8729]221        const OutputLevel::Value initialLevelConsole = OutputLevel::Info;
[6105]222#endif
[8729]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
[8729]230        this->logFile_->softDebugLevel_ = intialLevelLogFile;
[6105]231        this->registerOutputListener(this->logFile_);
232
233        this->consoleWriter_ = new ConsoleWriter();
[8729]234        this->consoleWriter_->softDebugLevel_ = initialLevelConsole;
[6105]235        this->registerOutputListener(this->consoleWriter_);
236
[8729]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_;
[8729]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    {
[8729]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);
[8729]268        this->updateGlobalDebugLevel();
[1505]269    }
270
[6105]271    void OutputHandler::unregisterOutputListener(OutputListener* listener)
272    {
[8729]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);
[8729]287        this->rewriteLogFile();
[2710]288    }
289
[8729]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
[8729]315    void OutputHandler::disableMemoryLog()
[6105]316    {
[8729]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
[8729]322    const OutputHandler::OutputVector& OutputHandler::getOutput() const
[1505]323    {
[8729]324        return this->memoryBuffer_->output_;
[1505]325    }
326
[6105]327    int OutputHandler::getSoftDebugLevel(const std::string& name) const
[1505]328    {
[8729]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    {
[8729]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        }
[8729]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.