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