Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/libraries/util/OutputHandler.cc @ 8775

Last change on this file since 8775 was 8775, checked in by landauf, 13 years ago

fixed problem with static initialization in old implementation of the log file

  • Property svn:eol-style set to native
File size: 10.9 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    /////////////////////////
49    ///// LogFileWriter /////
50    /////////////////////////
51    /**
52    @brief
53        Writes the output to the log file.
54    @note
55        As long as the correct log path is not yet known (for pre main code), the
56        LogFileWriter will write to a temporary file in /temp (Unix) or %TEMP% (Windows).
57        As soon as you set the correct path setLogPath the content of the temporary file
58        is read and put into the new file as well.
59    */
60    class LogFileWriter : public OutputListener
61    {
62    public:
63        /**
64        @brief
65            Gets temporary log path and starts the log file
66        */
67        LogFileWriter()
68            : OutputListener("LogFile")
69        {
70            this->logFileBaseName_ = "orxonox.log";
71
72            // Get path for a temporary file
73#ifdef ORXONOX_PLATFORM_WINDOWS
74            char* pTempDir = getenv("TEMP");
75            this->logFilename_ = std::string(pTempDir) + '/' + this->logFileBaseName_;
76#else
77            this->logFilename_ = std::string("/tmp/") + this->logFileBaseName_;
78#endif
79
80            // Get current time
81            time_t rawtime;
82            struct tm* timeinfo;
83            time(&rawtime);
84            timeinfo = localtime(&rawtime);
85
86            this->openFile();
87            if (this->logFile_.is_open())
88            {
89                this->logFile_ << "Started log on " << asctime(timeinfo) << std::endl;
90                this->logFile_.flush();
91            }
92        }
93
94        //! Closes the log file
95        ~LogFileWriter()
96        {
97            if (this->logFile_.is_open())
98            {
99                this->logFile_ << "Closed log" << std::endl;
100                this->logFile_.close();
101            }
102        }
103
104        //! Changes the log path
105        void setLogPath(const std::string& path)
106        {
107            if (this->logFile_.is_open())
108                this->logFile_.close();
109
110            // Open the new file
111            this->logFilename_ = path + this->logFileBaseName_;
112            this->openFile();
113        }
114
115        //! Erases the log file
116        void clearFile()
117        {
118            if (this->logFile_.is_open())
119            {
120                this->logFile_.close();
121                this->openFile();
122            }
123        }
124
125    private:
126        void openFile()
127        {
128            this->logFile_.open(this->logFilename_.c_str(), std::fstream::out);
129
130            if (this->logFile_.is_open())
131                this->outputStream_ = &this->logFile_;
132            else
133            {
134                COUT(2) << "Warning: Failed to open log file. File logging disabled." << std::endl;
135                this->outputStream_ = NULL;
136            }
137        }
138
139        std::ofstream logFile_;         //!< File handle for the log file
140        std::string   logFilename_;     //!< Filename of the log file
141        std::string   logFileBaseName_; //!< How the log file shall be named on the filesystem
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.