]> git.mxchange.org Git - simgear.git/blob - simgear/debug/logstream.cxx
Ensure individual log-level setting works.
[simgear.git] / simgear / debug / logstream.cxx
1 // Stream based logging mechanism.
2 //
3 // Written by Bernie Bright, 1998
4 //
5 // Copyright (C) 1998  Bernie Bright - bbright@c031.aone.net.au
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Library General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
11 //
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // Library General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #include "logstream.hxx"
24
25 #include <iostream>
26 #include <fstream>
27 #include <algorithm>
28
29 #include <boost/foreach.hpp>
30
31 #include <simgear/sg_inlines.h>
32 #include <simgear/threads/SGThread.hxx>
33 #include <simgear/threads/SGQueue.hxx>
34 #include <simgear/threads/SGGuard.hxx>
35
36 #include <simgear/misc/sg_path.hxx>
37
38 #ifdef _WIN32
39 // for AllocConsole
40     #include "windows.h"
41 #endif
42
43 const char* debugClassToString(sgDebugClass c)
44 {
45     switch (c) {
46     case SG_NONE:       return "none";
47     case SG_TERRAIN:    return "terrain";
48     case SG_ASTRO:      return "astro";
49     case SG_FLIGHT:     return "flight";
50     case SG_INPUT:      return "input";
51     case SG_GL:         return "opengl";
52     case SG_VIEW:       return "view";
53     case SG_COCKPIT:    return "cockpit";
54     case SG_GENERAL:    return "general";
55     case SG_MATH:       return "math";
56     case SG_EVENT:      return "event";
57     case SG_AIRCRAFT:   return "aircraft";
58     case SG_AUTOPILOT:  return "autopilot";
59     case SG_IO:         return "io";
60     case SG_CLIPPER:    return "clipper";
61     case SG_NETWORK:    return "network";
62     case SG_ATC:        return "atc";
63     case SG_NASAL:      return "nasal";
64     case SG_INSTR:      return "instruments";
65     case SG_SYSTEMS:    return "systems";
66     case SG_AI:         return "ai";
67     case SG_ENVIRONMENT:return "environment";
68     case SG_SOUND:      return "sound";
69     case SG_NAVAID:     return "navaid";
70     default:            return "unknown";
71     }
72 }
73
74 //////////////////////////////////////////////////////////////////////////////
75
76 class FileLogCallback : public simgear::LogCallback
77 {
78 public:
79     FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
80         m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc),
81         m_class(c),
82         m_priority(p)
83     {
84     }
85     
86     virtual void operator()(sgDebugClass c, sgDebugPriority p, 
87         const char* file, int line, const std::string& message)
88     {
89         if ((c & m_class) == 0 || p < m_priority) return;
90         m_file << debugClassToString(c) << ":" << (int) p
91             << ":" << file << ":" << line << ":" << message << std::endl;
92     }
93 private:
94     std::ofstream m_file;   
95     sgDebugClass m_class;
96     sgDebugPriority m_priority;
97 };
98    
99 class StderrLogCallback : public simgear::LogCallback
100 {
101 public:
102     StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
103         m_class(c),
104             m_priority(p)
105     {
106 #ifdef _WIN32
107         AllocConsole(); // but only if we want a console
108         freopen("conin$", "r", stdin);
109         freopen("conout$", "w", stdout);
110         freopen("conout$", "w", stderr);
111 #endif
112     }
113     
114     void setLogLevels( sgDebugClass c, sgDebugPriority p )
115     {
116         m_priority = p;
117         m_class = c;
118     }
119     
120     virtual void operator()(sgDebugClass c, sgDebugPriority p, 
121         const char* file, int line, const std::string& aMessage)
122     {
123         if ((c & m_class) == 0 || p < m_priority) return;
124         
125         // if running under MSVC, we could use OutputDebugString here
126         
127         fprintf(stderr, "%s\n", aMessage.c_str());
128         //fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
129         //    file, line, aMessage.c_str());
130         fflush(stderr);
131     }
132 private:
133     sgDebugClass m_class;
134     sgDebugPriority m_priority;
135 };
136
137 class LogStreamPrivate : public SGThread
138 {
139 private:
140     /**
141      * storage of a single log entry. Note this is not used for a persistent
142      * store, but rather for short term buffering between the submitting
143      * and output threads.
144      */
145     class LogEntry
146     {
147     public:
148         LogEntry(sgDebugClass c, sgDebugPriority p,
149             const char* f, int l, const std::string& msg) :
150             debugClass(c), debugPriority(p), file(f), line(l),
151                 message(msg)
152         {   
153         }
154         
155         sgDebugClass debugClass;
156         sgDebugPriority debugPriority;
157         const char* file;
158         int line;
159         std::string message;
160     };
161     
162     class PauseThread
163     {
164     public:
165         PauseThread(LogStreamPrivate* parent) : m_parent(parent)
166         {
167             m_wasRunning = m_parent->stop();
168         }
169         
170         ~PauseThread()
171         {
172             if (m_wasRunning) {
173                 m_parent->startLog();
174             }
175         }
176     private:
177         LogStreamPrivate* m_parent;
178         bool m_wasRunning;
179     };
180 public:
181     LogStreamPrivate() :
182         m_logClass(SG_ALL), 
183         m_logPriority(SG_ALERT),
184         m_isRunning(false)
185     { 
186         m_stderrCallback = new StderrLogCallback(m_logClass, m_logPriority);
187         addCallback(m_stderrCallback);
188     }
189                     
190     SGMutex m_lock;
191     SGBlockingQueue<LogEntry> m_entries;
192     
193     typedef std::vector<simgear::LogCallback*> CallbackVec;
194     CallbackVec m_callbacks;    
195     
196     sgDebugClass m_logClass;
197     sgDebugPriority m_logPriority;
198     bool m_isRunning;
199     StderrLogCallback* m_stderrCallback;
200     
201     void startLog()
202     {
203         SGGuard<SGMutex> g(m_lock);
204         if (m_isRunning) return;
205         m_isRunning = true;
206         start();
207     }
208     
209     virtual void run()
210     {
211         while (1) {
212             LogEntry entry(m_entries.pop());
213             // special marker entry detected, terminate the thread since we are
214             // making a configuration change or quitting the app
215             if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
216                 return;
217             }
218             
219             // submit to each installed callback in turn
220             BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
221                 (*cb)(entry.debugClass, entry.debugPriority,
222                     entry.file, entry.line, entry.message);
223             }           
224         } // of main thread loop
225     }
226     
227     bool stop()
228     {
229         SGGuard<SGMutex> g(m_lock);
230         if (!m_isRunning) {
231             return false;
232         }
233         
234         // log a special marker value, which will cause the thread to wakeup,
235         // and then exit
236         log(SG_NONE, SG_ALERT, "done", -1, "");
237         join();
238         
239         m_isRunning = false;
240         return true;
241     }
242     
243     void addCallback(simgear::LogCallback* cb)
244     {
245         PauseThread pause(this);
246         m_callbacks.push_back(cb);
247     }
248     
249     void removeCallback(simgear::LogCallback* cb)
250     {
251         PauseThread pause(this);
252         CallbackVec::iterator it = std::find(m_callbacks.begin(), m_callbacks.end(), cb);
253         if (it != m_callbacks.end()) {
254             m_callbacks.erase(it);
255         }
256     }
257     
258     void setLogLevels( sgDebugClass c, sgDebugPriority p )
259     {
260         PauseThread pause(this);
261         m_logPriority = p;
262         m_logClass = c;
263         m_stderrCallback->setLogLevels(c, p);
264     }
265     
266     bool would_log( sgDebugClass c, sgDebugPriority p ) const
267     {
268         if (p >= SG_INFO) return true;
269         return ((c & m_logClass) != 0 && p >= m_logPriority);
270     }
271     
272     void log( sgDebugClass c, sgDebugPriority p,
273             const char* fileName, int line, const std::string& msg)
274     {
275         LogEntry entry(c, p, fileName, line, msg);
276         m_entries.push(entry);
277     }
278 };
279
280 /////////////////////////////////////////////////////////////////////////////
281
282 static logstream* global_logstream = NULL;
283 static LogStreamPrivate* global_privateLogstream = NULL;
284
285 logstream::logstream()
286 {
287     global_privateLogstream = new LogStreamPrivate;
288     global_privateLogstream->startLog();
289 }
290
291 void
292 logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
293 {
294     global_privateLogstream->setLogLevels(c, p);
295 }
296
297 void
298 logstream::addCallback(simgear::LogCallback* cb)
299 {   
300     global_privateLogstream->addCallback(cb);
301 }
302
303 void
304 logstream::removeCallback(simgear::LogCallback* cb)
305 {   
306     global_privateLogstream->removeCallback(cb);
307 }
308
309 void
310 logstream::log( sgDebugClass c, sgDebugPriority p,
311         const char* fileName, int line, const std::string& msg)
312 {
313     global_privateLogstream->log(c, p, fileName, line, msg);
314 }
315
316 bool
317 logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
318 {
319     return global_privateLogstream->would_log(c,p);
320 }
321
322 sgDebugClass
323 logstream::get_log_classes() const
324 {
325     return global_privateLogstream->m_logClass;
326 }
327     
328 sgDebugPriority
329 logstream::get_log_priority() const
330 {
331     return global_privateLogstream->m_logPriority;
332 }
333
334 void
335 logstream::set_log_priority( sgDebugPriority p)
336 {
337     global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
338 }
339     
340 void
341 logstream::set_log_classes( sgDebugClass c)
342 {
343     global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
344 }
345
346 logstream&
347 sglog()
348 {
349     // Force initialization of cerr.
350     static std::ios_base::Init initializer;
351     if( !global_logstream )
352         global_logstream = new logstream();
353     return *global_logstream;
354 }
355
356 void
357 logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
358 {
359     global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p));
360 }
361