1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2019, assimp team
7
8
9
10All rights reserved.
11
12Redistribution and use of this software in source and binary forms,
13with or without modification, are permitted provided that the following
14conditions are met:
15
16* Redistributions of source code must retain the above
17 copyright notice, this list of conditions and the
18 following disclaimer.
19
20* Redistributions in binary form must reproduce the above
21 copyright notice, this list of conditions and the
22 following disclaimer in the documentation and/or other
23 materials provided with the distribution.
24
25* Neither the name of the assimp team, nor the names of its
26 contributors may be used to endorse or promote products
27 derived from this software without specific prior
28 written permission of the assimp team.
29
30THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41---------------------------------------------------------------------------
42*/
43
44/** @file DefaultLogger.cpp
45 * @brief Implementation of DefaultLogger (and Logger)
46 */
47
48// Default log streams
49#include "Win32DebugLogStream.h"
50#include "StdOStreamLogStream.h"
51#include "FileLogStream.h"
52#include <assimp/StringUtils.h>
53
54#include <assimp/DefaultIOSystem.h>
55#include <assimp/NullLogger.hpp>
56#include <assimp/DefaultLogger.hpp>
57#include <assimp/ai_assert.h>
58#include <iostream>
59#include <stdio.h>
60
61#ifndef ASSIMP_BUILD_SINGLETHREADED
62# include <thread>
63# include <mutex>
64 std::mutex loggerMutex;
65#endif
66
67namespace Assimp {
68
69// ----------------------------------------------------------------------------------
70NullLogger DefaultLogger::s_pNullLogger;
71Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger;
72
73static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging;
74
75// ----------------------------------------------------------------------------------
76// Represents a log-stream + its error severity
77struct LogStreamInfo {
78 unsigned int m_uiErrorSeverity;
79 LogStream *m_pStream;
80
81 // Constructor
82 LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) :
83 m_uiErrorSeverity( uiErrorSev ),
84 m_pStream( pStream ) {
85 // empty
86 }
87
88 // Destructor
89 ~LogStreamInfo() {
90 delete m_pStream;
91 }
92};
93
94// ----------------------------------------------------------------------------------
95// Construct a default log stream
96LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams,
97 const char* name /*= "AssimpLog.txt"*/,
98 IOSystem* io /*= NULL*/)
99{
100 switch (streams)
101 {
102 // This is a platform-specific feature
103 case aiDefaultLogStream_DEBUGGER:
104#ifdef WIN32
105 return new Win32DebugLogStream();
106#else
107 return nullptr;
108#endif
109
110 // Platform-independent default streams
111 case aiDefaultLogStream_STDERR:
112 return new StdOStreamLogStream(std::cerr);
113 case aiDefaultLogStream_STDOUT:
114 return new StdOStreamLogStream(std::cout);
115 case aiDefaultLogStream_FILE:
116 return (name && *name ? new FileLogStream(name,io) : nullptr );
117 default:
118 // We don't know this default log stream, so raise an assertion
119 ai_assert(false);
120
121 };
122
123 // For compilers without dead code path detection
124 return NULL;
125}
126
127// ----------------------------------------------------------------------------------
128// Creates the only singleton instance
129Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
130 LogSeverity severity /*= NORMAL*/,
131 unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/,
132 IOSystem* io /*= NULL*/) {
133 // enter the mutex here to avoid concurrency problems
134#ifndef ASSIMP_BUILD_SINGLETHREADED
135 std::lock_guard<std::mutex> lock(loggerMutex);
136#endif
137
138 if ( m_pLogger && !isNullLogger() ) {
139 delete m_pLogger;
140 }
141
142 m_pLogger = new DefaultLogger( severity );
143
144 // Attach default log streams
145 // Stream the log to the MSVC debugger?
146 if ( defStreams & aiDefaultLogStream_DEBUGGER ) {
147 m_pLogger->attachStream( pStream: LogStream::createDefaultStream( streams: aiDefaultLogStream_DEBUGGER ) );
148 }
149
150 // Stream the log to COUT?
151 if ( defStreams & aiDefaultLogStream_STDOUT ) {
152 m_pLogger->attachStream( pStream: LogStream::createDefaultStream( streams: aiDefaultLogStream_STDOUT ) );
153 }
154
155 // Stream the log to CERR?
156 if ( defStreams & aiDefaultLogStream_STDERR ) {
157 m_pLogger->attachStream( pStream: LogStream::createDefaultStream( streams: aiDefaultLogStream_STDERR ) );
158 }
159
160 // Stream the log to a file
161 if ( defStreams & aiDefaultLogStream_FILE && name && *name ) {
162 m_pLogger->attachStream( pStream: LogStream::createDefaultStream( streams: aiDefaultLogStream_FILE, name, io ) );
163 }
164
165 return m_pLogger;
166}
167
168// ----------------------------------------------------------------------------------
169void Logger::debug(const char* message) {
170
171 // SECURITY FIX: otherwise it's easy to produce overruns since
172 // sometimes importers will include data from the input file
173 // (i.e. node names) in their messages.
174 if (strlen(s: message)>MAX_LOG_MESSAGE_LENGTH) {
175 return;
176 }
177 return OnDebug(message);
178}
179
180// ----------------------------------------------------------------------------------
181void Logger::info(const char* message) {
182
183 // SECURITY FIX: see above
184 if (strlen(s: message)>MAX_LOG_MESSAGE_LENGTH) {
185 return;
186 }
187 return OnInfo(message);
188}
189
190// ----------------------------------------------------------------------------------
191void Logger::warn(const char* message) {
192
193 // SECURITY FIX: see above
194 if (strlen(s: message)>MAX_LOG_MESSAGE_LENGTH) {
195 return;
196 }
197 return OnWarn(essage: message);
198}
199
200// ----------------------------------------------------------------------------------
201void Logger::error(const char* message) {
202 // SECURITY FIX: see above
203 if (strlen(s: message)>MAX_LOG_MESSAGE_LENGTH) {
204 return;
205 }
206 return OnError(message);
207}
208
209// ----------------------------------------------------------------------------------
210void DefaultLogger::set( Logger *logger ) {
211 // enter the mutex here to avoid concurrency problems
212#ifndef ASSIMP_BUILD_SINGLETHREADED
213 std::lock_guard<std::mutex> lock(loggerMutex);
214#endif
215
216 if ( nullptr == logger ) {
217 logger = &s_pNullLogger;
218 }
219 if ( nullptr != m_pLogger && !isNullLogger() ) {
220 delete m_pLogger;
221 }
222
223 DefaultLogger::m_pLogger = logger;
224}
225
226// ----------------------------------------------------------------------------------
227bool DefaultLogger::isNullLogger() {
228 return m_pLogger == &s_pNullLogger;
229}
230
231// ----------------------------------------------------------------------------------
232Logger *DefaultLogger::get() {
233 return m_pLogger;
234}
235
236// ----------------------------------------------------------------------------------
237// Kills the only instance
238void DefaultLogger::kill() {
239 // enter the mutex here to avoid concurrency problems
240#ifndef ASSIMP_BUILD_SINGLETHREADED
241 std::lock_guard<std::mutex> lock(loggerMutex);
242#endif
243
244 if ( m_pLogger == &s_pNullLogger ) {
245 return;
246 }
247 delete m_pLogger;
248 m_pLogger = &s_pNullLogger;
249}
250
251// ----------------------------------------------------------------------------------
252// Debug message
253void DefaultLogger::OnDebug( const char* message ) {
254 if ( m_Severity == Logger::NORMAL ) {
255 return;
256 }
257
258 static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
259 char msg[Size];
260 ai_snprintf(s: msg, maxlen: Size, format: "Debug, T%u: %s", GetThreadID(), message);
261
262 WriteToStreams( message: msg, ErrorSev: Logger::Debugging );
263}
264
265// ----------------------------------------------------------------------------------
266// Logs an info
267void DefaultLogger::OnInfo( const char* message ){
268 static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
269 char msg[Size];
270 ai_snprintf(s: msg, maxlen: Size, format: "Info, T%u: %s", GetThreadID(), message );
271
272 WriteToStreams( message: msg , ErrorSev: Logger::Info );
273}
274
275// ----------------------------------------------------------------------------------
276// Logs a warning
277void DefaultLogger::OnWarn( const char* message ) {
278 static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
279 char msg[Size];
280 ai_snprintf(s: msg, maxlen: Size, format: "Warn, T%u: %s", GetThreadID(), message );
281
282 WriteToStreams( message: msg, ErrorSev: Logger::Warn );
283}
284
285// ----------------------------------------------------------------------------------
286// Logs an error
287void DefaultLogger::OnError( const char* message ) {
288 static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
289 char msg[ Size ];
290 ai_snprintf(s: msg, maxlen: Size, format: "Error, T%u: %s", GetThreadID(), message );
291
292 WriteToStreams( message: msg, ErrorSev: Logger::Err );
293}
294
295// ----------------------------------------------------------------------------------
296// Will attach a new stream
297bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) {
298 if ( nullptr == pStream ) {
299 return false;
300 }
301
302 if (0 == severity) {
303 severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging;
304 }
305
306 for ( StreamIt it = m_StreamArray.begin();
307 it != m_StreamArray.end();
308 ++it )
309 {
310 if ( (*it)->m_pStream == pStream ) {
311 (*it)->m_uiErrorSeverity |= severity;
312 return true;
313 }
314 }
315
316 LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream );
317 m_StreamArray.push_back( x: pInfo );
318 return true;
319}
320
321// ----------------------------------------------------------------------------------
322// Detach a stream
323bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) {
324 if ( nullptr == pStream ) {
325 return false;
326 }
327
328 if (0 == severity) {
329 severity = SeverityAll;
330 }
331
332 bool res( false );
333 for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
334 if ( (*it)->m_pStream == pStream ) {
335 (*it)->m_uiErrorSeverity &= ~severity;
336 if ( (*it)->m_uiErrorSeverity == 0 ) {
337 // don't delete the underlying stream 'cause the caller gains ownership again
338 (**it).m_pStream = nullptr;
339 delete *it;
340 m_StreamArray.erase( position: it );
341 res = true;
342 break;
343 }
344 return true;
345 }
346 }
347 return res;
348}
349
350// ----------------------------------------------------------------------------------
351// Constructor
352DefaultLogger::DefaultLogger(LogSeverity severity)
353 : Logger ( severity )
354 , noRepeatMsg (false)
355 , lastLen( 0 ) {
356 lastMsg[0] = '\0';
357}
358
359// ----------------------------------------------------------------------------------
360// Destructor
361DefaultLogger::~DefaultLogger() {
362 for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
363 // also frees the underlying stream, we are its owner.
364 delete *it;
365 }
366}
367
368// ----------------------------------------------------------------------------------
369// Writes message to stream
370void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) {
371 ai_assert(nullptr != message);
372
373 // Check whether this is a repeated message
374 if (! ::strncmp( s1: message,s2: lastMsg, n: lastLen-1))
375 {
376 if (!noRepeatMsg)
377 {
378 noRepeatMsg = true;
379 message = "Skipping one or more lines with the same contents\n";
380 }
381 else return;
382 }
383 else
384 {
385 // append a new-line character to the message to be printed
386 lastLen = ::strlen(s: message);
387 ::memcpy(dest: lastMsg,src: message,n: lastLen+1);
388 ::strcat(dest: lastMsg+lastLen,src: "\n");
389
390 message = lastMsg;
391 noRepeatMsg = false;
392 ++lastLen;
393 }
394 for ( ConstStreamIt it = m_StreamArray.begin();
395 it != m_StreamArray.end();
396 ++it)
397 {
398 if ( ErrorSev & (*it)->m_uiErrorSeverity )
399 (*it)->m_pStream->write( message);
400 }
401}
402
403// ----------------------------------------------------------------------------------
404// Returns thread id, if not supported only a zero will be returned.
405unsigned int DefaultLogger::GetThreadID()
406{
407 // fixme: we can get this value via std::threads
408 // std::this_thread::get_id().hash() returns a (big) size_t, not sure if this is useful in this case.
409#ifdef WIN32
410 return (unsigned int)::GetCurrentThreadId();
411#else
412 return 0; // not supported
413#endif
414}
415
416// ----------------------------------------------------------------------------------
417
418} // !namespace Assimp
419

source code of qt3d/src/3rdparty/assimp/src/code/Common/DefaultLogger.cpp