]> git.mxchange.org Git - simgear.git/blob - simgear/debug/logstream.cxx
Canvas: fix element mouse hit detection with OSG 3.3.2.
[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 <simgear_config.h>
24
25 #include "logstream.hxx"
26
27 #include <iostream>
28 #include <fstream>
29 #include <sstream>
30 #include <algorithm>
31
32 #include <boost/foreach.hpp>
33
34 #include <simgear/sg_inlines.h>
35 #include <simgear/threads/SGThread.hxx>
36 #include <simgear/threads/SGQueue.hxx>
37 #include <simgear/threads/SGGuard.hxx>
38
39 #include <simgear/misc/sg_path.hxx>
40
41 #ifdef SG_WINDOWS
42 // for AllocConsole, OutputDebugString
43     #include "windows.h"
44 #endif
45
46 const char* debugClassToString(sgDebugClass c)
47 {
48     switch (c) {
49     case SG_NONE:       return "none";
50     case SG_TERRAIN:    return "terrain";
51     case SG_ASTRO:      return "astro";
52     case SG_FLIGHT:     return "flight";
53     case SG_INPUT:      return "input";
54     case SG_GL:         return "opengl";
55     case SG_VIEW:       return "view";
56     case SG_COCKPIT:    return "cockpit";
57     case SG_GENERAL:    return "general";
58     case SG_MATH:       return "math";
59     case SG_EVENT:      return "event";
60     case SG_AIRCRAFT:   return "aircraft";
61     case SG_AUTOPILOT:  return "autopilot";
62     case SG_IO:         return "io";
63     case SG_CLIPPER:    return "clipper";
64     case SG_NETWORK:    return "network";
65     case SG_ATC:        return "atc";
66     case SG_NASAL:      return "nasal";
67     case SG_INSTR:      return "instruments";
68     case SG_SYSTEMS:    return "systems";
69     case SG_AI:         return "ai";
70     case SG_ENVIRONMENT:return "environment";
71     case SG_SOUND:      return "sound";
72     case SG_NAVAID:     return "navaid";
73     case SG_GUI:        return "gui";
74     default:            return "unknown";
75     }
76 }
77
78 //////////////////////////////////////////////////////////////////////////////
79
80 namespace simgear
81 {
82
83 LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) :
84         m_class(c),
85         m_priority(p)
86 {
87 }
88
89 bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
90 {
91          return ((c & m_class) != 0 && p >= m_priority);
92 }
93
94 void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
95 {
96         m_priority = p;
97         m_class = c;
98 }
99
100 } // of namespace simgear
101
102 //////////////////////////////////////////////////////////////////////////////
103
104 class FileLogCallback : public simgear::LogCallback
105 {
106 public:
107     FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
108             simgear::LogCallback(c, p),
109         m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc)
110     {
111     }
112     
113     virtual void operator()(sgDebugClass c, sgDebugPriority p, 
114         const char* file, int line, const std::string& message)
115     {
116         if (!shouldLog(c, p)) return;
117         m_file << debugClassToString(c) << ":" << (int) p
118             << ":" << file << ":" << line << ":" << message << std::endl;
119     }
120 private:
121     std::ofstream m_file;   
122 };
123    
124 class StderrLogCallback : public simgear::LogCallback
125 {
126 public:
127     StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
128                 simgear::LogCallback(c, p)
129     {
130 #ifdef SG_WINDOWS
131         AllocConsole(); // but only if we want a console
132         freopen("conin$", "r", stdin);
133         freopen("conout$", "w", stdout);
134         freopen("conout$", "w", stderr);
135 #endif
136     }
137     
138     virtual void operator()(sgDebugClass c, sgDebugPriority p, 
139         const char* file, int line, const std::string& aMessage)
140     {
141         if (!shouldLog(c, p)) return;
142         
143         fprintf(stderr, "%s\n", aMessage.c_str());
144         //fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
145         //    file, line, aMessage.c_str());
146         fflush(stderr);
147     }
148 };
149
150
151 #ifdef SG_WINDOWS
152
153 class WinDebugLogCallback : public simgear::LogCallback
154 {
155 public:
156     WinDebugLogCallback(sgDebugClass c, sgDebugPriority p) :
157                 simgear::LogCallback(c, p)
158     {
159     }
160     
161     virtual void operator()(sgDebugClass c, sgDebugPriority p, 
162         const char* file, int line, const std::string& aMessage)
163     {
164         if (!shouldLog(c, p)) return;
165         
166         std::ostringstream os;
167                 os << debugClassToString(c) << ":" << aMessage << std::endl;
168                 OutputDebugStringA(os.str().c_str());
169     }
170 };
171
172 #endif
173
174 class LogStreamPrivate : public SGThread
175 {
176 private:
177     /**
178      * storage of a single log entry. Note this is not used for a persistent
179      * store, but rather for short term buffering between the submitting
180      * and output threads.
181      */
182     class LogEntry
183     {
184     public:
185         LogEntry(sgDebugClass c, sgDebugPriority p,
186             const char* f, int l, const std::string& msg) :
187             debugClass(c), debugPriority(p), file(f), line(l),
188                 message(msg)
189         {   
190         }
191         
192         sgDebugClass debugClass;
193         sgDebugPriority debugPriority;
194         const char* file;
195         int line;
196         std::string message;
197     };
198     
199     class PauseThread
200     {
201     public:
202         PauseThread(LogStreamPrivate* parent) : m_parent(parent)
203         {
204             m_wasRunning = m_parent->stop();
205         }
206         
207         ~PauseThread()
208         {
209             if (m_wasRunning) {
210                 m_parent->startLog();
211             }
212         }
213     private:
214         LogStreamPrivate* m_parent;
215         bool m_wasRunning;
216     };
217 public:
218     LogStreamPrivate() :
219         m_logClass(SG_ALL), 
220         m_logPriority(SG_ALERT),
221         m_isRunning(false),
222                 m_consoleRequested(false)
223     { 
224
225 #if !defined(SG_WINDOWS)
226         m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
227                 m_consoleCallbacks.push_back(m_callbacks.back());
228                 m_consoleRequested = true;
229 #endif
230
231 #if defined (SG_WINDOWS) && !defined(NDEBUG)
232                 m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
233                 m_consoleCallbacks.push_back(m_callbacks.back());
234 #endif
235     }
236                     
237     SGMutex m_lock;
238     SGBlockingQueue<LogEntry> m_entries;
239     
240     typedef std::vector<simgear::LogCallback*> CallbackVec;
241     CallbackVec m_callbacks;    
242     /// subset of callbacks which correspond to stdout / console,
243         /// and hence should dynamically reflect console logging settings
244         CallbackVec m_consoleCallbacks;
245
246     sgDebugClass m_logClass;
247     sgDebugPriority m_logPriority;
248     bool m_isRunning;
249         bool m_consoleRequested;
250     
251     void startLog()
252     {
253         SGGuard<SGMutex> g(m_lock);
254         if (m_isRunning) return;
255         m_isRunning = true;
256         start();
257     }
258     
259     virtual void run()
260     {
261         while (1) {
262             LogEntry entry(m_entries.pop());
263             // special marker entry detected, terminate the thread since we are
264             // making a configuration change or quitting the app
265             if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
266                 return;
267             }
268             
269             // submit to each installed callback in turn
270             BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
271                 (*cb)(entry.debugClass, entry.debugPriority,
272                     entry.file, entry.line, entry.message);
273             }           
274         } // of main thread loop
275     }
276     
277     bool stop()
278     {
279         SGGuard<SGMutex> g(m_lock);
280         if (!m_isRunning) {
281             return false;
282         }
283         
284         // log a special marker value, which will cause the thread to wakeup,
285         // and then exit
286         log(SG_NONE, SG_ALERT, "done", -1, "");
287         join();
288         
289         m_isRunning = false;
290         return true;
291     }
292     
293     void addCallback(simgear::LogCallback* cb)
294     {
295         PauseThread pause(this);
296         m_callbacks.push_back(cb);
297     }
298     
299     void removeCallback(simgear::LogCallback* cb)
300     {
301         PauseThread pause(this);
302         CallbackVec::iterator it = std::find(m_callbacks.begin(), m_callbacks.end(), cb);
303         if (it != m_callbacks.end()) {
304             m_callbacks.erase(it);
305         }
306     }
307     
308     void setLogLevels( sgDebugClass c, sgDebugPriority p )
309     {
310         PauseThread pause(this);
311         m_logPriority = p;
312         m_logClass = c;
313                 BOOST_FOREACH(simgear::LogCallback* cb, m_consoleCallbacks) {
314                 cb->setLogLevels(c, p);
315                 }
316     }
317     
318     bool would_log( sgDebugClass c, sgDebugPriority p ) const
319     {
320         if (p >= SG_INFO) return true;
321         return ((c & m_logClass) != 0 && p >= m_logPriority);
322     }
323     
324     void log( sgDebugClass c, sgDebugPriority p,
325             const char* fileName, int line, const std::string& msg)
326     {
327         LogEntry entry(c, p, fileName, line, msg);
328         m_entries.push(entry);
329     }
330
331         void requestConsole()
332         {
333                 PauseThread pause(this);
334                 if (m_consoleRequested) {
335                         return;
336                 }
337
338                 m_consoleRequested = true;
339                 m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
340                 m_consoleCallbacks.push_back(m_callbacks.back());
341         }
342 };
343
344 /////////////////////////////////////////////////////////////////////////////
345
346 static logstream* global_logstream = NULL;
347 static LogStreamPrivate* global_privateLogstream = NULL;
348
349 logstream::logstream()
350 {
351     global_privateLogstream = new LogStreamPrivate;
352     global_privateLogstream->startLog();
353 }
354
355 void
356 logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
357 {
358     global_privateLogstream->setLogLevels(c, p);
359 }
360
361 void
362 logstream::addCallback(simgear::LogCallback* cb)
363 {   
364     global_privateLogstream->addCallback(cb);
365 }
366
367 void
368 logstream::removeCallback(simgear::LogCallback* cb)
369 {   
370     global_privateLogstream->removeCallback(cb);
371 }
372
373 void
374 logstream::log( sgDebugClass c, sgDebugPriority p,
375         const char* fileName, int line, const std::string& msg)
376 {
377     global_privateLogstream->log(c, p, fileName, line, msg);
378 }
379
380 bool
381 logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
382 {
383     return global_privateLogstream->would_log(c,p);
384 }
385
386 sgDebugClass
387 logstream::get_log_classes() const
388 {
389     return global_privateLogstream->m_logClass;
390 }
391     
392 sgDebugPriority
393 logstream::get_log_priority() const
394 {
395     return global_privateLogstream->m_logPriority;
396 }
397
398 void
399 logstream::set_log_priority( sgDebugPriority p)
400 {
401     global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
402 }
403     
404 void
405 logstream::set_log_classes( sgDebugClass c)
406 {
407     global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
408 }
409
410 logstream&
411 sglog()
412 {
413     // Force initialization of cerr.
414     static std::ios_base::Init initializer;
415     
416     // http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
417     // in the absence of portable memory barrier ops in Simgear,
418     // let's keep this correct & safe
419     static SGMutex m;
420     SGGuard<SGMutex> g(m);
421     
422     if( !global_logstream )
423         global_logstream = new logstream();
424     return *global_logstream;
425 }
426
427 void
428 logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
429 {
430     global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p));
431 }
432
433 namespace simgear
434 {
435
436 void requestConsole()
437 {
438         sglog(); // force creation
439         global_privateLogstream->requestConsole();
440 }
441
442 } // of namespace simgear