Coverage Report

Created: 2025-12-05 06:25

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