hydrogen 1.2.3
Logger.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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, see https://www.gnu.org/licenses
20 *
21 */
22
23#include "core/Logger.h"
25
26#include <cstdio>
27#include <chrono>
28#include <thread>
29#include <QtCore/QDir>
30
31#ifdef WIN32
32#include <windows.h>
33#endif
34
35namespace H2Core {
36
37unsigned Logger::__bit_msk = 0;
38Logger* Logger::__instance=nullptr;
39const char* Logger::__levels[] = { "None", "Error", "Warning", "Info", "Debug", "Constructors", "Locks" };
40thread_local QString *Logger::pCrashContext = nullptr;
41
42pthread_t loggerThread;
43
44void* loggerThread_func( void* param ) {
45 if ( param == nullptr ) return nullptr;
46 Logger* logger = ( Logger* )param;
47#ifdef WIN32
48# ifdef H2CORE_HAVE_DEBUG
49 ::AllocConsole();
50// ::SetConsoleTitle( "Hydrogen debug log" );
51 freopen( "CONOUT$", "wt", stdout );
52# endif
53#endif
54 FILE* log_file = nullptr;
55 if ( logger->__use_file ) {
56 log_file = fopen( logger->m_sLogFilePath.toLocal8Bit().data(), "w" );
57 if ( ! log_file ) {
58 fprintf( stderr, "%s",
59 QString( "Error: can't open log file [%1] for writing...\n" )
60 .arg( logger->m_sLogFilePath ).toLocal8Bit().data() );
61 }
62 }
63 Logger::queue_t* queue = &logger->__msg_queue;
64 Logger::queue_t::iterator it, last;
65
66 while ( logger->__running ) {
67 pthread_mutex_lock( &logger->__mutex );
68 pthread_cond_wait( &logger->__messages_available, &logger->__mutex );
69 pthread_mutex_unlock( &logger->__mutex );
70 if( !queue->empty() ) {
71 for( it = last = queue->begin() ; it != queue->end() ; ++it ) {
72 last = it;
73 if ( logger->m_bUseStdout ) {
74 fprintf( stdout, "%s", it->toLocal8Bit().data() );
75 fflush( stdout );
76 }
77 if( log_file ) {
78 fprintf( log_file, "%s", it->toLocal8Bit().data() );
79 fflush( log_file );
80 }
81 }
82 // remove all in front of last
83 pthread_mutex_lock( &logger->__mutex );
84 queue->erase( queue->begin(), last );
85 queue->pop_front();
86 pthread_mutex_unlock( &logger->__mutex );
87 }
88 }
89 if ( log_file ) {
90 fprintf( log_file, "Stop logger" );
91 fclose( log_file );
92 }
93#ifdef WIN32
94 ::FreeConsole();
95#endif
96
97 fflush( stdout );
98 pthread_exit( nullptr );
99 return nullptr;
100}
101
102Logger* Logger::bootstrap( unsigned msk, const QString& sLogFilePath, bool bUseStdout ) {
104 return Logger::create_instance( sLogFilePath, bUseStdout );
105}
106
107Logger* Logger::create_instance( const QString& sLogFilePath, bool bUseStdout ) {
108 if ( __instance == nullptr ) __instance = new Logger( sLogFilePath, bUseStdout );
109 return __instance;
110}
111
112Logger::Logger( const QString& sLogFilePath, bool bUseStdout ) :
113 __use_file( true ),
114 __running( true ),
115 m_sLogFilePath( sLogFilePath ),
116 m_bUseStdout( bUseStdout ) {
117 __instance = this;
118
119 // Sanity checks.
120 QFileInfo fiLogFile( m_sLogFilePath );
121 QFileInfo fiParentFolder( fiLogFile.absolutePath() );
122 if ( ( fiLogFile.exists() && ! fiLogFile.isWritable() ) ||
123 ( ! fiLogFile.exists() && ! fiParentFolder.isWritable() ) ) {
124 m_sLogFilePath = "";
125 }
126
127 if ( m_sLogFilePath.isEmpty() ) {
129 }
130
131 pthread_attr_t attr;
132 pthread_attr_init( &attr );
133 pthread_mutex_init( &__mutex, nullptr );
134 pthread_cond_init( &__messages_available, nullptr );
135 pthread_create( &loggerThread, &attr, loggerThread_func, this );
136}
137
139 __running = false;
140 pthread_cond_broadcast ( &__messages_available );
141 pthread_join( loggerThread, nullptr );
142}
143
144void Logger::log( unsigned level, const QString& class_name, const char* func_name, const QString& msg ) {
145
146 if( level == None ){
147 return;
148 }
149
150 const char* prefix[] = { "", "(E) ", "(W) ", "(I) ", "(D) ", "(C)", "(L) " };
151#ifdef WIN32
152 const char* color[] = { "", "", "", "", "", "", "" };
153#else
154 const char* color[] = { "", "\033[31m", "\033[36m", "\033[32m", "\033[35m", "\033[35;1m", "\033[35;1m" };
155#endif // WIN32
156
157 int i;
158 switch( level ) {
159 case Error:
160 i = 1;
161 break;
162 case Warning:
163 i = 2;
164 break;
165 case Info:
166 i = 3;
167 break;
168 case Debug:
169 i = 4;
170 break;
171 case Constructors:
172 i = 5;
173 break;
174 case Locks:
175 i = 6;
176 break;
177 default:
178 i = 0;
179 break;
180 }
181
182 QString tmp = QString( "%1%2%3::%4 %5\033[0m\n" )
183 .arg( color[i] )
184 .arg( prefix[i] )
185 .arg( class_name )
186 .arg( func_name )
187 .arg( msg );
188
189 pthread_mutex_lock( &__mutex );
190 __msg_queue.push_back( tmp );
191 pthread_mutex_unlock( &__mutex );
192 pthread_cond_broadcast( &__messages_available );
193}
194
195void Logger::flush() const {
196
197 int nTimeout = 100;
198 for ( int ii = 0; ii < nTimeout; ++ii ) {
199 if ( __msg_queue.empty() ) {
200 break;
201 }
202
203 std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
204 }
205 return;
206}
207
208unsigned Logger::parse_log_level( const char* level ) {
209 unsigned log_level = Logger::None;
210 if( 0 == strncasecmp( level, __levels[0], strlen( __levels[0] ) ) ) {
211 log_level = Logger::None;
212 } else if ( 0 == strncasecmp( level, __levels[1], strlen( __levels[1] ) ) ) {
213 log_level = Logger::Error;
214 } else if ( 0 == strncasecmp( level, __levels[2], strlen( __levels[2] ) ) ) {
215 log_level = Logger::Error | Logger::Warning;
216 } else if ( 0 == strncasecmp( level, __levels[3], strlen( __levels[3] ) ) ) {
218 } else if ( 0 == strncasecmp( level, __levels[4], strlen( __levels[4] ) ) ) {
220 } else if ( 0 == strncasecmp( level, __levels[5], strlen( __levels[5] ) ) ) {
222 } else if ( 0 == strncasecmp( level, __levels[6], strlen( __levels[6] ) ) ) {
224 } else {
225#ifdef HAVE_SSCANF
226 int val = sscanf( level,"%x",&log_level );
227 if( val != 1 ) {
228 log_level = Logger::Error;
229 }
230#else
231 log_level = hextoi( level, -1 );
232 if( log_level==-1 ) {
233 log_level = Logger::Error;
234 }
235#endif
236 }
237 return log_level;
238}
239
240#ifndef HAVE_SSCANF
241int Logger::hextoi( const char* str, long len ) {
242 long pos = 0;
243 char c = 0;
244 int v = 0;
245 int res = 0;
246 bool leading_zero = false;
247
248 while( 1 ) {
249 if( ( len!=-1 ) && ( pos>=len ) ) {
250 break;
251 }
252 c = str[pos];
253 if( c==0 ) {
254 break;
255 } else if( c=='x' || c=='X' ) {
256 if ( ( pos==1 ) && leading_zero ) {
257 assert( res == 0 );
258 pos++;
259 continue;
260 } else {
261 return -1;
262 }
263 } else if( c>='a' ) {
264 v = c-'a'+10;
265 } else if( c>='A' ) {
266 v = c-'A'+10;
267 } else if( c>='0' ) {
268 if ( ( c=='0' ) && ( pos==0 ) ) {
269 leading_zero = true;
270 }
271 v = c-'0';
272 } else {
273 return -1;
274 }
275 if( v>15 ) {
276 return -1;
277 }
278 //assert( v == (v & 0xF) );
279 res = ( res << 4 ) | v;
280 assert( ( res & 0xF ) == ( v & 0xF ) );
281 pos++;
282 }
283 return res;
284}
285#endif // HAVE_SSCANF
286
287
290 Logger::pCrashContext = pContext;
291 pThisContext = nullptr;
292}
293
295 pSavedContext = Logger::pCrashContext;
296 // Copy context string
297 pThisContext = new QString( sContext );
298 Logger::pCrashContext = pThisContext;
299}
300
302 Logger::pCrashContext = pSavedContext;
303 if ( pThisContext ) {
304 delete pThisContext;
305 }
306}
307
308};
309
310/* vim: set softtabstop=4 noexpandtab: */
static QString log_file_path()
returns the full path (including filename) of the logfile
CrashContext(QString *pContext)
Definition Logger.cpp:288
Class for writing logs to the console.
Definition Logger.h:42
~Logger()
destructor
Definition Logger.cpp:138
static Logger * create_instance(const QString &sLogFilePath=QString(), bool bUseStdout=true)
If __instance equals 0, a new H2Core::Logger singleton will be created and stored in it.
Definition Logger.cpp:107
pthread_cond_t __messages_available
Definition Logger.h:163
bool __use_file
write log to file if set to true
Definition Logger.h:157
void log(unsigned level, const QString &class_name, const char *func_name, const QString &msg)
the log function
Definition Logger.cpp:144
std::list< QString > queue_t
message queue type
Definition Logger.h:56
pthread_mutex_t __mutex
lock for adding or removing elements only
Definition Logger.h:159
void flush() const
Waits till the logger thread poped all remaining messages from __msg_queue.
Definition Logger.cpp:195
bool __running
set to true when the logger thread is running
Definition Logger.h:158
static Logger * __instance
Object holding the current H2Core::Logger singleton.
Definition Logger.h:156
Logger(const QString &sLogFilePath=QString(), bool bUseStdout=true)
constructor
Definition Logger.cpp:112
static int hextoi(const char *str, long len)
convert an hex string to an integer.
Definition Logger.cpp:241
bool m_bUseStdout
Definition Logger.h:165
static Logger * bootstrap(unsigned msk, const QString &sLogFilePath=QString(), bool bUseStdout=true)
create the logger instance if not exists, set the log level and return the instance
Definition Logger.cpp:102
queue_t __msg_queue
the message queue
Definition Logger.h:160
static thread_local QString * pCrashContext
Definition Logger.h:167
static const char * __levels[]
levels strings
Definition Logger.h:162
QString m_sLogFilePath
Definition Logger.h:164
static void set_bit_mask(unsigned msk)
set the bitmask
Definition Logger.h:88
static unsigned parse_log_level(const char *lvl)
parse a log level string and return the corresponding bit mask
Definition Logger.cpp:208
static unsigned __bit_msk
the bitmask of log_level_t
Definition Logger.h:161
friend void * loggerThread_func(void *param)
needed for being able to access logger internal
Definition Logger.cpp:44
pthread_t loggerThread
Definition Logger.cpp:42
void * loggerThread_func(void *param)
Definition Logger.cpp:44