Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Improved logging to a file by rewriting the log according to the right debug level. Also, failure to open the file is now being handled.

  • Property svn:eol-style set to native
File size: 10.6 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("consoleLog")
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#ifdef ORXONOX_RELEASE
218        const OutputLevel::Value defaultLevelConsole = OutputLevel::Error;
219        const OutputLevel::Value defaultLevelLogFile = OutputLevel::Info;
220#else
221        const OutputLevel::Value defaultLevelConsole = OutputLevel::Info;
222        const OutputLevel::Value defaultLevelLogFile = OutputLevel::Debug;
223#endif
224
225        this->logFile_ = new LogFileWriter();
226        // Use default level until we get the configValue from the Core
227        this->logFile_->softDebugLevel_ = defaultLevelLogFile;
228        this->registerOutputListener(this->logFile_);
229
230        this->consoleWriter_ = new ConsoleWriter();
231        this->consoleWriter_->softDebugLevel_ = defaultLevelConsole;
232        this->registerOutputListener(this->consoleWriter_);
233
234        this->memoryBuffer_ = new MemoryLogWriter();
235        // Write everything, e.g. use hardDebugLevel
236        this->memoryBuffer_->softDebugLevel_ = hardDebugLevel;
237        this->registerOutputListener(this->memoryBuffer_);
238    }
239
240    //! Destroys the LogFileWriter and the MemoryLogWriter
241    OutputHandler::~OutputHandler()
242    {
243        delete this->logFile_;
244        delete this->consoleWriter_;
245        delete this->memoryBuffer_; // Might already be NULL
246    }
247
248    OutputHandler& OutputHandler::getInstance()
249    {
250        static OutputHandler orxout;
251        return orxout;
252    }
253
254    void OutputHandler::registerOutputListener(OutputListener* listener)
255    {
256        for (std::vector<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
257        {
258            if ((*it)->name_ == listener->name_)
259            {
260                COUT(2) << "OutputHandler, Warning: Trying to register two listeners with the same name!" << std::endl;
261                return;
262            }
263        }
264        this->listeners_.push_back(listener);
265        this->updateGlobalDebugLevel();
266    }
267
268    void OutputHandler::unregisterOutputListener(OutputListener* listener)
269    {
270        for (std::vector<OutputListener*>::iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
271        {
272            if ((*it)->name_ == listener->name_)
273            {
274                this->listeners_.erase(it);
275                break;
276            }
277        }
278        this->updateGlobalDebugLevel();
279    }
280
281    void OutputHandler::setLogPath(const std::string& path)
282    {
283        this->logFile_->setLogPath(path);
284        this->rewriteLogFile();
285    }
286
287    void OutputHandler::rewriteLogFile()
288    {
289        logFile_->clearFile();
290
291        if (logFile_->outputStream_ == NULL)
292            return;
293
294        for (OutputVector::const_iterator it = this->getOutput().begin(); it != this->getOutput().end(); ++it)
295        {
296            if (it->first <= logFile_->softDebugLevel_)
297                (*logFile_->outputStream_) << it->second;
298        }
299        logFile_->outputStream_->flush();
300    }
301
302    void OutputHandler::disableCout()
303    {
304        this->unregisterOutputListener(this->consoleWriter_);
305    }
306
307    void OutputHandler::enableCout()
308    {
309        this->registerOutputListener(this->consoleWriter_);
310    }
311
312    void OutputHandler::disableMemoryLog()
313    {
314        this->unregisterOutputListener(this->memoryBuffer_);
315        // Only clear the buffer so we can still reference the vector
316        this->memoryBuffer_->output_.clear();
317    }
318
319    const OutputHandler::OutputVector& OutputHandler::getOutput() const
320    {
321        return this->memoryBuffer_->output_;
322    }
323
324    int OutputHandler::getSoftDebugLevel(const std::string& name) const
325    {
326        for (std::vector<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
327        {
328            if ((*it)->name_ == name)
329                return (*it)->softDebugLevel_;
330        }
331        return -1;
332    }
333
334    void OutputHandler::setSoftDebugLevel(const std::string& name, int level)
335    {
336        for (std::vector<OutputListener*>::const_iterator it = this->listeners_.begin(); it != this->listeners_.end(); ++it)
337        {
338            if ((*it)->name_ == name)
339                (*it)->softDebugLevel_ = level;
340        }
341        this->updateGlobalDebugLevel();
342    }
343
344    void OutputHandler::updateGlobalDebugLevel()
345    {
346        int globalSoftDebugLevel = -1;
347        std::vector<OutputListener*>::const_iterator it = this->listeners_.begin();
348        for (; it != this->listeners_.end(); ++it)
349            globalSoftDebugLevel = std::max(globalSoftDebugLevel, (*it)->softDebugLevel_);
350
351        OutputHandler::softDebugLevel_s = globalSoftDebugLevel;
352    }
353}
Note: See TracBrowser for help on using the repository browser.