1 // Stream based logging mechanism.
3 // Written by Bernie Bright, 1998
5 // Copyright (C) 1998 Bernie Bright - bbright@c031.aone.net.au
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.
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.
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.
23 #include "logstream.hxx"
29 #include <boost/foreach.hpp>
31 #include <simgear/sg_inlines.h>
32 #include <simgear/threads/SGThread.hxx>
33 #include <simgear/threads/SGQueue.hxx>
34 #include <simgear/threads/SGGuard.hxx>
36 #include <simgear/misc/sg_path.hxx>
43 const char* debugClassToString(sgDebugClass 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";
74 //////////////////////////////////////////////////////////////////////////////
76 class FileLogCallback : public simgear::LogCallback
79 FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
80 m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc),
86 virtual void operator()(sgDebugClass c, sgDebugPriority p,
87 const char* file, int line, const std::string& message)
89 if ((c & m_class) == 0 || p < m_priority) return;
90 m_file << debugClassToString(c) << ":" << (int) p
91 << ":" << file << ":" << line << ":" << message << std::endl;
96 sgDebugPriority m_priority;
99 class StderrLogCallback : public simgear::LogCallback
102 StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
107 AllocConsole(); // but only if we want a console
108 freopen("conin$", "r", stdin);
109 freopen("conout$", "w", stdout);
110 freopen("conout$", "w", stderr);
114 void setLogLevels( sgDebugClass c, sgDebugPriority p )
120 virtual void operator()(sgDebugClass c, sgDebugPriority p,
121 const char* file, int line, const std::string& aMessage)
123 if ((c & m_class) == 0 || p < m_priority) return;
125 // if running under MSVC, we could use OutputDebugString here
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());
133 sgDebugClass m_class;
134 sgDebugPriority m_priority;
137 class LogStreamPrivate : public SGThread
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.
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),
155 sgDebugClass debugClass;
156 sgDebugPriority debugPriority;
165 PauseThread(LogStreamPrivate* parent) : m_parent(parent)
167 m_wasRunning = m_parent->stop();
173 m_parent->startLog();
177 LogStreamPrivate* m_parent;
183 m_logPriority(SG_ALERT),
186 m_stderrCallback = new StderrLogCallback(m_logClass, m_logPriority);
187 addCallback(m_stderrCallback);
191 SGBlockingQueue<LogEntry> m_entries;
193 typedef std::vector<simgear::LogCallback*> CallbackVec;
194 CallbackVec m_callbacks;
196 sgDebugClass m_logClass;
197 sgDebugPriority m_logPriority;
199 StderrLogCallback* m_stderrCallback;
203 SGGuard<SGMutex> g(m_lock);
204 if (m_isRunning) return;
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")) {
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);
224 } // of main thread loop
229 SGGuard<SGMutex> g(m_lock);
234 // log a special marker value, which will cause the thread to wakeup,
236 log(SG_NONE, SG_ALERT, "done", -1, "");
243 void addCallback(simgear::LogCallback* cb)
245 PauseThread pause(this);
246 m_callbacks.push_back(cb);
249 void removeCallback(simgear::LogCallback* cb)
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);
258 void setLogLevels( sgDebugClass c, sgDebugPriority p )
260 PauseThread pause(this);
263 m_stderrCallback->setLogLevels(c, p);
266 bool would_log( sgDebugClass c, sgDebugPriority p ) const
268 if (p >= SG_INFO) return true;
269 return ((c & m_logClass) != 0 && p >= m_logPriority);
272 void log( sgDebugClass c, sgDebugPriority p,
273 const char* fileName, int line, const std::string& msg)
275 LogEntry entry(c, p, fileName, line, msg);
276 m_entries.push(entry);
280 /////////////////////////////////////////////////////////////////////////////
282 static logstream* global_logstream = NULL;
283 static LogStreamPrivate* global_privateLogstream = NULL;
285 logstream::logstream()
287 global_privateLogstream = new LogStreamPrivate;
288 global_privateLogstream->startLog();
292 logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
294 global_privateLogstream->setLogLevels(c, p);
298 logstream::addCallback(simgear::LogCallback* cb)
300 global_privateLogstream->addCallback(cb);
304 logstream::removeCallback(simgear::LogCallback* cb)
306 global_privateLogstream->removeCallback(cb);
310 logstream::log( sgDebugClass c, sgDebugPriority p,
311 const char* fileName, int line, const std::string& msg)
313 global_privateLogstream->log(c, p, fileName, line, msg);
317 logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
319 return global_privateLogstream->would_log(c,p);
323 logstream::get_log_classes() const
325 return global_privateLogstream->m_logClass;
329 logstream::get_log_priority() const
331 return global_privateLogstream->m_logPriority;
335 logstream::set_log_priority( sgDebugPriority p)
337 global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
341 logstream::set_log_classes( sgDebugClass c)
343 global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
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;
357 logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
359 global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p));