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