Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8848 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
Line 
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:
25 *      Reto Grieder
26 *
27 */
28
29/**
30@file
31@brief
32    Definition of classes related to output (logging).
33*/
34
35#include "OutputHandler.h"
36
37#include <algorithm>
38#include <ctime>
39#include <cstdlib>
40#include <fstream>
41#include <iostream>
42#include <sstream>
43
44#include "Debug.h"
45
46namespace orxonox
47{
48    //! How the log file shall be named on the filesystem
49    const std::string logFileBaseName_g = "orxonox.log";
50
51    /////////////////////////
52    ///// LogFileWriter /////
53    /////////////////////////
54    /**
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.
62    */
63    class LogFileWriter : public OutputListener
64    {
65    public:
66        /**
67        @brief
68            Gets temporary log path and starts the log file
69        */
70        LogFileWriter()
71            : OutputListener("LogFile")
72        {
73            // Get path for a temporary file
74#ifdef ORXONOX_PLATFORM_WINDOWS
75            char* pTempDir = getenv("TEMP");
76            this->logFilename_ = std::string(pTempDir) + '/' + logFileBaseName_g;
77#else
78            this->logFilename_ = std::string("/tmp/") + logFileBaseName_g;
79#endif
80
81            // Get current time
82            time_t rawtime;
83            struct tm* timeinfo;
84            time(&rawtime);
85            timeinfo = localtime(&rawtime);
86
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            }
93        }
94
95        //! Closes the log file
96        ~LogFileWriter()
97        {
98            if (this->logFile_.is_open())
99            {
100                this->logFile_ << "Closed log" << std::endl;
101                this->logFile_.close();
102            }
103        }
104
105        //! Changes the log path
106        void setLogPath(const std::string& path)
107        {
108            if (this->logFile_.is_open())
109                this->logFile_.close();
110
111            // Open the new file
112            this->logFilename_ = path + logFileBaseName_g;
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        {
129            this->logFile_.open(this->logFilename_.c_str(), std::fstream::out);
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            }
138        }
139
140        std::ofstream logFile_;     //!< File handle for the log file
141        std::string   logFilename_; //!< Filename of the log file
142    };
143
144
145    /////////////////////////
146    ///// ConsoleWriter /////
147    /////////////////////////
148    /**
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.
153    */
154    class ConsoleWriter : public OutputListener
155    {
156    public:
157        //! Only assigns the output stream with std::cout
158        ConsoleWriter()
159            : OutputListener("Console")
160        {
161            this->outputStream_ = &std::cout;
162        }
163    };
164
165
166    ///////////////////////////
167    ///// MemoryLogWriter /////
168    ///////////////////////////
169    /**
170    @brief
171        OutputListener that writes all the output piece by piece to an array
172        associated with the corresponding output level.
173        Used as buffer until all output devices have been initialised.
174    @note
175        At some point, OutputHandler::disableMemoryLog() has to be called in
176        order to avoid large memory footprints of this class.
177    */
178    class MemoryLogWriter : public OutputListener
179    {
180    public:
181        friend class OutputHandler;
182
183        MemoryLogWriter()
184            : OutputListener("memoryLog")
185        {
186            this->outputStream_ = &this->buffer_;
187        }
188
189        //! Push the just written output to the internal array
190        void outputChanged(int level)
191        {
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            }
199            this->buffer_.clear();
200        }
201
202    private:
203        std::ostringstream          buffer_; //!< Stream object used to process the output
204        OutputHandler::OutputVector output_; //!< Vector containing ALL output
205    };
206
207
208    /////////////////////////
209    ///// OutputHandler /////
210    /////////////////////////
211    int OutputHandler::softDebugLevel_s = hardDebugLevel;
212
213    //! Creates the LogFileWriter and the MemoryLogWriter
214    OutputHandler::OutputHandler()
215        : outputLevel_(OutputLevel::Verbose)
216    {
217        // Note: These levels only concern startup before orxonox.ini is read.
218#ifdef ORXONOX_RELEASE
219        const OutputLevel::Value initialLevelConsole = OutputLevel::Error;
220#else
221        const OutputLevel::Value initialLevelConsole = OutputLevel::Info;
222#endif
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;
227
228        this->logFile_ = new LogFileWriter();
229        // Use default level until we get the configValue from the Core
230        this->logFile_->softDebugLevel_ = intialLevelLogFile;
231        this->registerOutputListener(this->logFile_);
232
233        this->consoleWriter_ = new ConsoleWriter();
234        this->consoleWriter_->softDebugLevel_ = initialLevelConsole;
235        this->registerOutputListener(this->consoleWriter_);
236
237        this->memoryBuffer_ = new MemoryLogWriter();
238        // Write everything, e.g. use hardDebugLevel
239        this->memoryBuffer_->softDebugLevel_ = hardDebugLevel;
240        this->registerOutputListener(this->memoryBuffer_);
241    }
242
243    //! Destroys the LogFileWriter and the MemoryLogWriter
244    OutputHandler::~OutputHandler()
245    {
246        delete this->logFile_;
247        delete this->consoleWriter_;
248        delete this->memoryBuffer_; // Might already be NULL
249    }
250
251    OutputHandler& OutputHandler::getInstance()
252    {
253        static OutputHandler orxout;
254        return orxout;
255    }
256
257    void OutputHandler::registerOutputListener(OutputListener* listener)
258    {
259        for (std::vector<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
260        {
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            }
266        }
267        this->listeners_.push_back(listener);
268        this->updateGlobalDebugLevel();
269    }
270
271    void OutputHandler::unregisterOutputListener(OutputListener* listener)
272    {
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();
282    }
283
284    void OutputHandler::setLogPath(const std::string& path)
285    {
286        this->logFile_->setLogPath(path);
287        this->rewriteLogFile();
288    }
289
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
305    void OutputHandler::disableCout()
306    {
307        this->unregisterOutputListener(this->consoleWriter_);
308    }
309
310    void OutputHandler::enableCout()
311    {
312        this->registerOutputListener(this->consoleWriter_);
313    }
314
315    void OutputHandler::disableMemoryLog()
316    {
317        this->unregisterOutputListener(this->memoryBuffer_);
318        // Only clear the buffer so we can still reference the vector
319        this->memoryBuffer_->output_.clear();
320    }
321
322    const OutputHandler::OutputVector& OutputHandler::getOutput() const
323    {
324        return this->memoryBuffer_->output_;
325    }
326
327    int OutputHandler::getSoftDebugLevel(const std::string& name) const
328    {
329        for (std::vector<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
330        {
331            if ((*it)->name_ == name)
332                return (*it)->softDebugLevel_;
333        }
334        return -1;
335    }
336
337    void OutputHandler::setSoftDebugLevel(const std::string& name, int level)
338    {
339        for (std::vector<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
340        {
341            if ((*it)->name_ == name)
342                (*it)->softDebugLevel_ = level;
343        }
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
354        OutputHandler::softDebugLevel_s = globalSoftDebugLevel;
355    }
356}
Note: See TracBrowser for help on using the repository browser.