Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 andreas 1
/*
21 andreas 2
 * Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
2 andreas 3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
#include <iostream>
19
#include <fstream>
20
#include <sstream>
21
#include <ios>
22
#include <time.h>
14 andreas 23
#include <mutex>
289 andreas 24
#include <thread>
306 andreas 25
//#ifdef __APPLE__
26
//#include <unistd.h>
27
//#include <sys/syscall.h>
28
//#endif
14 andreas 29
 
386 andreas 30
#include <QMessageBox>
31
#include <QTimer>
32
 
2 andreas 33
#include "terror.h"
34
#include "tconfig.h"
35
 
406 andreas 36
#if __cplusplus < 201402L
37
#   error "This module requires at least C++ 14 standard!"
38
#else
39
#   if __cplusplus < 201703L
40
#       include <experimental/filesystem>
41
namespace fs = std::experimental::filesystem;
42
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
43
#   else
44
#       include <filesystem>
45
#       ifdef __ANDROID__
46
namespace fs = std::__fs::filesystem;
47
#       else
48
namespace fs = std::filesystem;
49
#       endif
50
#   endif
51
#endif
235 andreas 52
 
23 andreas 53
#if LOGPATH == LPATH_SYSLOG || defined(__ANDROID__)
22 andreas 54
#   ifdef __ANDROID__
55
#       include <android/log.h>
56
#   else
57
#       include <syslog.h>
386 andreas 58
#       ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
59
#           include "ios/QASettings.h"
60
#       endif
22 andreas 61
#   endif
62
#endif
63
 
242 andreas 64
#define LOGBUFFER_SIZE      4096
65
 
2 andreas 66
using std::string;
292 andreas 67
using std::mutex;
68
 
14 andreas 69
std::mutex message_mutex;
305 andreas 70
std::mutex _macro_mutex;
2 andreas 71
 
242 andreas 72
bool TError::mHaveError{false};
73
terrtype_t TError::mErrType{TERRNONE};
74
TStreamError *TError::mCurrent{nullptr};
2 andreas 75
std::string TError::msError;
315 andreas 76
#ifdef __ANDROID__
77
threadID_t TError::mThreadID;
78
#else
306 andreas 79
threadID_t TError::mThreadID{0};
315 andreas 80
#endif
2 andreas 81
 
242 andreas 82
int TStreamError::mIndent{1};
83
std::ostream *TStreamError::mStream{nullptr};
84
std::filebuf TStreamError::mOfStream;
85
char *TStreamError::mBuffer{nullptr};
2 andreas 86
std::string TStreamError::mLogfile;
242 andreas 87
bool TStreamError::mInitialized{false};
88
unsigned int TStreamError::mLogLevel{HLOG_PROTOCOL};
250 andreas 89
unsigned int TStreamError::mLogLevelOld{HLOG_NONE};
90
bool TStreamError::haveTemporaryLogLevel{false};
385 andreas 91
#ifdef __ANDROID__
92
bool TStreamError::mLogFileEnabled{false};
93
#else
94
bool TStreamError::mLogFileEnabled{true};
95
#endif
2 andreas 96
 
306 andreas 97
string _threadIDtoStr(threadID_t tid)
304 andreas 98
{
99
    std::stringstream s;
100
    s << std::hex << std::setw(8) << std::setfill('0') << tid;
101
    return s.str();
102
}
103
 
306 andreas 104
threadID_t _getThreadID()
105
{
106
#ifdef __APPLE__
107
//    threadID_t tid;
108
//    return pthread_threadid_np(NULL, &tid);
109
    return pthread_mach_thread_np(pthread_self());
110
//    return syscall(SYS_thread_selfid);
111
#else
112
    return std::this_thread::get_id();
113
#endif
114
}
115
 
305 andreas 116
void _lock()
117
{
118
    _macro_mutex.lock();
119
}
120
 
121
void _unlock()
122
{
123
    _macro_mutex.unlock();
124
}
125
 
23 andreas 126
#if LOGPATH == LPATH_SYSLOG || defined(__ANDROID__)
127
class androidbuf : public std::streambuf
128
{
129
    public:
142 andreas 130
        enum { bufsize = 1024 };
23 andreas 131
        androidbuf() { this->setp(buffer, buffer + bufsize - 1); }
132
 
133
    private:
134
        int overflow(int c)
135
        {
136
            if (c == traits_type::eof())
137
            {
138
                *this->pptr() = traits_type::to_char_type(c);
139
                this->sbumpc();
140
            }
141
 
142
            return this->sync()? traits_type::eof(): traits_type::not_eof(c);
143
        }
144
 
145
        int sync()
146
        {
147
            int rc = 0;
148
 
149
            if (this->pbase() != this->pptr())
150
            {
151
                char writebuf[bufsize+1];
152
                memcpy(writebuf, this->pbase(), this->pptr() - this->pbase());
153
                writebuf[this->pptr() - this->pbase()] = '\0';
154
                int eType;
155
#ifdef __ANDROID__
156
                switch(TError::getErrorType())
157
                {
158
                    case TERRINFO:      eType = ANDROID_LOG_INFO; break;
159
                    case TERRWARNING:   eType = ANDROID_LOG_WARN; break;
160
                    case TERRERROR:     eType = ANDROID_LOG_ERROR; break;
161
                    case TERRTRACE:     eType = ANDROID_LOG_VERBOSE; break;
162
                    case TERRDEBUG:     eType = ANDROID_LOG_DEBUG; break;
26 andreas 163
                    case TERRNONE:      eType = ANDROID_LOG_INFO; break;
23 andreas 164
                }
165
 
166
                rc = __android_log_print(eType, "tpanel", "%s", writebuf) > 0;
167
#else
386 andreas 168
#ifdef Q_OS_IOS
169
                QASettings::writeLog(TError::getErrorType(), writebuf);
170
#else
23 andreas 171
                switch(TError::getErrorType())
172
                {
173
                    case TERRINFO:      eType = LOG_INFO; break;
386 andreas 174
                    case TERRWARNING:   eType = LOG_WARNING; break;
175
                    case TERRERROR:     eType = LOG_ERR; break;
23 andreas 176
                    case TERRTRACE:     eType = LOG_INFO; break;
177
                    case TERRDEBUG:     eType = LOG_DEBUG; break;
178
                    case TERRNONE:      eType = LOG_INFO; break;
179
                }
180
 
386 andreas 181
                syslog(eType, "(tpanel) %s", writebuf);
26 andreas 182
                rc = 1;
386 andreas 183
#endif  // Q_OS_IOS
184
#endif  // __ANDROID__
23 andreas 185
                this->setp(buffer, buffer + bufsize - 1);
186
            }
22 andreas 187
 
23 andreas 188
            return rc;
189
        }
190
 
191
        char buffer[bufsize];
192
};
193
#endif
194
 
2 andreas 195
TStreamError::TStreamError(const string& logFile, const std::string& logLevel)
196
{
116 andreas 197
    if (!TConfig::isInitialized())
198
        return;
199
 
14 andreas 200
    if (!logFile.empty())
23 andreas 201
        mLogfile = logFile;
14 andreas 202
    else if (!TConfig::getLogFile().empty())
23 andreas 203
        mLogfile = TConfig::getLogFile();
2 andreas 204
 
14 andreas 205
    if (!logLevel.empty())
206
        setLogLevel(logLevel);
207
    else if (!TConfig::getLogLevel().empty())
208
        setLogLevel(TConfig::getLogFile());
2 andreas 209
 
14 andreas 210
    _init();
2 andreas 211
}
212
 
213
TStreamError::~TStreamError()
214
{
242 andreas 215
    if (mOfStream.is_open())
216
        mOfStream.close();
243 andreas 217
 
242 andreas 218
    if (mStream)
14 andreas 219
    {
220
        delete mStream;
221
        mStream = nullptr;
222
    }
242 andreas 223
 
224
    if (mBuffer)
225
        delete mBuffer;
226
 
227
    mInitialized = false;
2 andreas 228
}
229
 
23 andreas 230
void TStreamError::setLogFile(const std::string &lf)
231
{
239 andreas 232
#ifdef Q_OS_IOS
233
    if (!lf.empty())
234
    {
243 andreas 235
        if ((!mLogfile.empty() && mLogfile != lf) || mLogfile.empty())
239 andreas 236
            mLogfile = lf;
237
    }
238
 
239
    if (!mInitialized)
240
        _init();
241
#else
383 andreas 242
#ifndef __ANDROID__
116 andreas 243
    if (mInitialized && mLogfile.compare(lf) == 0)
23 andreas 244
        return;
383 andreas 245
#endif
23 andreas 246
    mLogfile = lf;
247
    mInitialized = false;
248
    _init();
239 andreas 249
#endif
23 andreas 250
}
251
 
2 andreas 252
void TStreamError::setLogLevel(const std::string& slv)
253
{
14 andreas 254
    size_t pos = slv.find("|");
255
    size_t start = 0;
256
    string lv;
257
    mLogLevel = 0;
2 andreas 258
 
14 andreas 259
    while (pos != string::npos)
260
    {
261
        lv = slv.substr(start, pos - start);
262
        start = pos + 1;
263
        mLogLevel |= _getLevel(lv);
264
        pos = slv.find("|", start);
265
    }
2 andreas 266
 
14 andreas 267
    mLogLevel |= _getLevel(slv.substr(start));
260 andreas 268
#ifdef __ANDROID__
269
    __android_log_print(ANDROID_LOG_INFO, "tpanel", "TStreamError::setLogLevel: New loglevel: %s", slv.c_str());
270
#else
261 andreas 271
    if (TError::Current()->getStream())
272
        *TError::Current()->getStream() << TError::append(HLOG_INFO) << "New loglevel: " << slv << std::endl;
273
    else
274
        std::cout << TError::append(HLOG_INFO) << "New loglevel: " << slv << std::endl;
260 andreas 275
#endif
2 andreas 276
}
277
 
278
bool TStreamError::checkFilter(terrtype_t err)
279
{
116 andreas 280
    if (!TConfig::isInitialized())
281
        return false;
282
 
23 andreas 283
    if (err == TERRINFO && (mLogLevel & HLOG_INFO) != 0)
14 andreas 284
        return true;
23 andreas 285
    else if (err == TERRWARNING && (mLogLevel & HLOG_WARNING) != 0)
14 andreas 286
        return true;
23 andreas 287
    else if (err == TERRERROR && (mLogLevel & HLOG_ERROR) != 0)
14 andreas 288
        return true;
23 andreas 289
    else if (err == TERRTRACE && (mLogLevel & HLOG_TRACE) != 0)
14 andreas 290
        return true;
23 andreas 291
    else if (err == TERRDEBUG && (mLogLevel & HLOG_DEBUG) != 0)
14 andreas 292
        return true;
2 andreas 293
 
14 andreas 294
    return false;
2 andreas 295
}
296
 
369 andreas 297
bool TStreamError::checkFilter(unsigned int lv)
2 andreas 298
{
116 andreas 299
    if (!TConfig::isInitialized())
300
        return false;
301
 
369 andreas 302
    if ((mLogLevel & HLOG_INFO) != 0 &&
303
        (mLogLevel & HLOG_WARNING) != 0 &&
304
        (mLogLevel & HLOG_ERROR) != 0 &&
305
        lv == HLOG_PROTOCOL)
14 andreas 306
        return true;
2 andreas 307
 
369 andreas 308
    if ((mLogLevel & lv) != 0 && lv != HLOG_PROTOCOL)
309
        return true;
310
 
14 andreas 311
    return false;
2 andreas 312
}
313
 
314
unsigned int TStreamError::_getLevel(const std::string& slv)
315
{
14 andreas 316
    if (slv.compare(SLOG_NONE) == 0)
23 andreas 317
        return HLOG_NONE;
2 andreas 318
 
14 andreas 319
    if (slv.compare(SLOG_INFO) == 0)
23 andreas 320
        return HLOG_INFO;
2 andreas 321
 
14 andreas 322
    if (slv.compare(SLOG_WARNING) == 0)
23 andreas 323
        return HLOG_WARNING;
2 andreas 324
 
14 andreas 325
    if (slv.compare(SLOG_ERROR) == 0)
23 andreas 326
        return HLOG_ERROR;
2 andreas 327
 
14 andreas 328
    if (slv.compare(SLOG_TRACE) == 0)
23 andreas 329
        return HLOG_TRACE;
2 andreas 330
 
14 andreas 331
    if (slv.compare(SLOG_DEBUG) == 0)
23 andreas 332
        return HLOG_DEBUG;
2 andreas 333
 
14 andreas 334
    if (slv.compare(SLOG_PROTOCOL) == 0)
23 andreas 335
        return HLOG_PROTOCOL;
2 andreas 336
 
14 andreas 337
    if (slv.compare(SLOG_ALL) == 0)
23 andreas 338
        return HLOG_ALL;
2 andreas 339
 
23 andreas 340
    return HLOG_NONE;
2 andreas 341
}
342
 
242 andreas 343
void TStreamError::_init(bool reinit)
2 andreas 344
{
116 andreas 345
    if (!TConfig::isInitialized() || mInitialized)
14 andreas 346
        return;
2 andreas 347
 
14 andreas 348
    mInitialized = true;
23 andreas 349
 
383 andreas 350
#ifdef __ANDROID__
385 andreas 351
    __android_log_print(ANDROID_LOG_DEBUG, "tpanel", "TStreamError::_init: Logfile is %s", (mLogFileEnabled ? "ENABLED" : "DISABLED"));
383 andreas 352
#endif
392 andreas 353
 
22 andreas 354
#if LOGPATH == LPATH_FILE
385 andreas 355
    if (mLogFileEnabled && !mLogfile.empty())
14 andreas 356
    {
23 andreas 357
        try
358
        {
359
#ifndef __ANDROID__
242 andreas 360
            if (mOfStream.is_open())
361
                mOfStream.close();
362
 
23 andreas 363
            if (mStream && mStream != &std::cout)
364
                delete mStream;
243 andreas 365
 
242 andreas 366
            if (!mBuffer)
367
            {
368
                mBuffer = new char[LOGBUFFER_SIZE];
369
                mOfStream.pubsetbuf(mBuffer, LOGBUFFER_SIZE);
370
            }
116 andreas 371
#if __cplusplus < 201402L
242 andreas 372
            mOfStream.open(mLogfile.c_str(), std::ios::out | std::ios::ate);
240 andreas 373
#else   // __cplusplus < 201402L
242 andreas 374
            mOfStream.open(mLogfile, std::ios::out | std::ios::ate);
240 andreas 375
#endif  //__cplusplus < 201402L
242 andreas 376
            mStream = new std::ostream(&mOfStream);
377
 
378
            if (!isStreamValid())
379
            {
380
                if (mOfStream.is_open())
381
                    mOfStream.close();
382
 
23 andreas 383
                mStream = &std::cout;
242 andreas 384
            }
240 andreas 385
#else   //__ANDROID__
43 andreas 386
            char *HOME = getenv("HOME");
387
            bool bigLog = false;
388
            uint logLevel = _getLevel(TConfig::getLogLevel());
389
 
390
            if ((logLevel & HLOG_TRACE) || (logLevel & HLOG_DEBUG))
391
                bigLog = true;
392
 
393
            if (HOME && !bigLog && mLogfile.find(HOME) == string::npos)
394
            {
251 andreas 395
                if (mOfStream.is_open())
396
                    mOfStream.close();
397
 
43 andreas 398
                if (mStream && mStream != &std::cout)
399
                    delete mStream;
400
 
406 andreas 401
                __android_log_print(ANDROID_LOG_DEBUG, "tpanel", "TStreamError::_init: Opening logfile: \"%s\"", mLogfile.c_str());
43 andreas 402
 
406 andreas 403
                if (!mOfStream.open(mLogfile, std::ios::out | std::ios::trunc))
43 andreas 404
                {
406 andreas 405
                    __android_log_print(ANDROID_LOG_ERROR, "tpanel", "TStreamError::_init: Could not open logfile!");
406
                    std::cout.rdbuf(new androidbuf);
43 andreas 407
                    mStream = &std::cout;
406 andreas 408
                    mLogFileEnabled = false;
43 andreas 409
                }
406 andreas 410
                else
411
                {
412
                    mStream = new std::ostream(&mOfStream);
413
 
414
                    if (!isStreamValid())
415
                    {
416
                        delete mStream;
417
 
418
                        if (mOfStream.is_open())
419
                            mOfStream.close();
420
 
421
                        std::cout.rdbuf(new androidbuf);
422
                        mStream = &std::cout;
423
                        mLogFileEnabled = false;
424
                    }
425
                }
43 andreas 426
            }
427
            else
428
            {
429
                std::cout.rdbuf(new androidbuf);
430
                mStream = &std::cout;
431
            }
23 andreas 432
#endif  // __ANDROID__
433
        }
434
        catch (std::exception& e)
435
        {
436
#ifdef __ANDROID__
260 andreas 437
            __android_log_print(ANDROID_LOG_ERROR, "tpanel", "TStreamError::_init: %s", e.what());
240 andreas 438
#else   // __ANDROID__
23 andreas 439
            std::cerr << "ERROR: " << e.what() << std::endl;
440
#endif  // __ANDROID__
441
            mStream = &std::cout;
442
        }
14 andreas 443
    }
242 andreas 444
    else if (!isStreamValid())
23 andreas 445
    {
446
#ifdef __ANDROID__
447
        std::cout.rdbuf(new androidbuf);
240 andreas 448
#endif  // __ANDROID__
14 andreas 449
        mStream = &std::cout;
306 andreas 450
#if defined(QT_DEBUG) || defined(DEBUG)
260 andreas 451
#ifdef __ANDROID__
452
        __android_log_print(ANDROID_LOG_DEBUG, "tpanel", "TStreamError::_init: Stream wurde auf std::cout gesetzt.");
453
#else
239 andreas 454
        std::cout << "DEBUG: Stream wurde auf std::cout gesetzt." << std::endl;
306 andreas 455
#endif  // __ANDROID__
260 andreas 456
#endif
23 andreas 457
    }
116 andreas 458
#else  // LOGPATH == LPATH_FILE
459
    if (!mStream)
460
    {
392 andreas 461
#if defined(__ANDROID__) || defined(__IOS_AVAILABLE)
116 andreas 462
        std::cout.rdbuf(new androidbuf);
463
#endif
464
        mStream = &std::cout;
465
    }
23 andreas 466
#endif  // LOGPATH == LPATH_FILE
467
 
242 andreas 468
    if (reinit)
469
        return;
470
 
260 andreas 471
    if (mLogLevel > 0)
14 andreas 472
        *mStream << "Logfile started at " << getTime() << std::endl;
2 andreas 473
 
153 andreas 474
    *mStream << TConfig::getProgName() << " version " << VERSION_STRING() << std::endl;
260 andreas 475
    *mStream << "(C) Copyright by Andreas Theofilu <andreas@theosys.at>\n" << std::endl;
2 andreas 476
 
260 andreas 477
    if (mLogLevel > 0)
478
    {
479
        if (TConfig::isLongFormat())
304 andreas 480
            *mStream << "Timestamp           Type LNr., File name           , ThreadID Message" << std::endl;
260 andreas 481
        else
316 andreas 482
            *mStream << "Type LNr., ThreadID Message" << std::endl;
260 andreas 483
 
484
        *mStream << "-----------------------------------------------------------------" << std::endl << std::flush;
485
    }
14 andreas 486
    else
260 andreas 487
        *mStream << std::flush;
14 andreas 488
}
116 andreas 489
 
23 andreas 490
std::ostream *TStreamError::resetFlags(std::ostream *os)
22 andreas 491
{
238 andreas 492
    if (!isStreamValid(*os))
493
        return os;
494
 
23 andreas 495
    *os << std::resetiosflags(std::ios::boolalpha) <<
496
           std::resetiosflags(std::ios::showbase) <<
497
           std::resetiosflags(std::ios::showpoint) <<
498
           std::resetiosflags(std::ios::showpos) <<
499
           std::resetiosflags(std::ios::skipws) <<
500
           std::resetiosflags(std::ios::unitbuf) <<
501
           std::resetiosflags(std::ios::uppercase) <<
502
           std::resetiosflags(std::ios::dec) <<
503
           std::resetiosflags(std::ios::hex) <<
504
           std::resetiosflags(std::ios::oct) <<
505
           std::resetiosflags(std::ios::fixed) <<
506
           std::resetiosflags(std::ios::scientific) <<
507
           std::resetiosflags(std::ios::internal) <<
508
           std::resetiosflags(std::ios::left) <<
509
           std::resetiosflags(std::ios::right) <<
510
           std::setfill(' ');
511
    return os;
22 andreas 512
}
513
 
247 andreas 514
void TStreamError::resetFlags()
515
{
292 andreas 516
    std::lock_guard<mutex> guard(message_mutex);
247 andreas 517
    resetFlags(TError::Current()->getStream());
518
}
519
 
2 andreas 520
void TStreamError::decIndent()
521
{
14 andreas 522
    if (mIndent > 0)
523
        mIndent--;
2 andreas 524
}
525
 
526
string TStreamError::getTime()
527
{
14 andreas 528
    time_t rawtime;
529
    struct tm * timeinfo;
530
    char buffer[80];
2 andreas 531
 
240 andreas 532
    rawtime = time(nullptr);
14 andreas 533
    timeinfo = localtime(&rawtime);
2 andreas 534
 
240 andreas 535
    if (!timeinfo)
536
    {
260 andreas 537
#ifdef __ANDROID__
538
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "TStreamError::getTime: Couldn't get the local time!");
539
#else
240 andreas 540
        std::cerr << "ERROR: Couldn't get the local time!" << std::endl;
260 andreas 541
#endif
240 andreas 542
        return string();
543
    }
544
 
14 andreas 545
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
546
    string str(buffer);
547
    return str;
2 andreas 548
}
549
 
242 andreas 550
std::ostream *TStreamError::getStream()
551
{
552
    try
553
    {
554
        if (!isStreamValid())
555
        {
260 andreas 556
#ifdef __ANDROID__
557
            __android_log_print(ANDROID_LOG_ERROR, "tpanel", "TStreamError::getStream: Internal stream is invalid!");
558
#else
242 andreas 559
            std::cerr << "ERROR: Internal stream is invalid!" << std::endl;
260 andreas 560
#endif
242 andreas 561
            mInitialized = false;
562
            _init();
260 andreas 563
#ifdef __ANDROID__
564
            __android_log_print(ANDROID_LOG_INFO, "tpanel", "TStreamError::getStream: Reinitialized stream.");
565
#else
242 andreas 566
            std::cerr << "INFO: Reinitialized stream." << std::endl;
260 andreas 567
#endif
242 andreas 568
 
569
            if (!isStreamValid())
570
            {
260 andreas 571
#ifdef __ANDROID__
572
                __android_log_print(ANDROID_LOG_ERROR, "tpanel", "TStreamError::getStream: Reinitializing of stream failed!");
573
#else
242 andreas 574
                std::cerr << "ERROR: Reinitializing of stream failed! Using \"std::cout\" to write log messages." << std::endl;
260 andreas 575
#endif
242 andreas 576
                return &std::cout;
577
            }
578
        }
579
 
580
        return mStream;
581
    }
582
    catch (std::exception& e)
583
    {
260 andreas 584
#ifdef __ANDROID__
585
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "TStreamError::getStream: Error retrieving the current stream!");
586
#else
242 andreas 587
        std::cerr << "ERROR: Error retrieving the current stream! Using \"std::cout\" instead." << std::endl;
260 andreas 588
#endif
242 andreas 589
    }
590
 
591
    return &std::cout;
592
}
593
 
2 andreas 594
std::ostream& indent(std::ostream& os)
595
{
238 andreas 596
    if (!TStreamError::isStreamValid(os))
14 andreas 597
        return os;
2 andreas 598
 
14 andreas 599
    if (TStreamError::getIndent() > 0)
600
        os << std::setw(TStreamError::getIndent()) << " ";
2 andreas 601
 
14 andreas 602
    return os;
2 andreas 603
}
604
 
238 andreas 605
bool TStreamError::isStreamValid()
606
{
607
    if (!mStream)
406 andreas 608
    {
609
#ifdef __ANDROID__
610
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "TStreamError::isStreamValid: Stream is nullptr!");
611
#else
612
        std::cerr << "ERROR: TStreamError::isStreamValid: Stream is nullptr!" << std::endl;
613
#endif
238 andreas 614
        return false;
406 andreas 615
    }
238 andreas 616
 
242 andreas 617
    if (mStream->rdstate() & std::ostream::failbit)
406 andreas 618
    {
619
#ifdef __ANDROID__
620
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "TStreamError::isStreamValid: Stream has failbit set!");
621
#else
622
        std::cerr << "ERROR: TStreamError::isStreamValid: Stream has failbit set!" << std::endl;
623
#endif
238 andreas 624
        return false;
406 andreas 625
    }
238 andreas 626
 
242 andreas 627
    if (mStream->rdstate() & std::ostream::badbit)
406 andreas 628
    {
629
#ifdef __ANDROID__
630
        __android_log_print(ANDROID_LOG_ERROR, "tpanel", "TStreamError::isStreamValid: Stream has badbit set!");
631
#else
632
        std::cerr << "ERROR: TStreamError::isStreamValid: Stream has badbit set!" << std::endl;
633
#endif
238 andreas 634
        return false;
406 andreas 635
    }
238 andreas 636
 
637
    return true;
638
}
639
 
640
bool TStreamError::isStreamValid(std::ostream& os)
641
{
242 andreas 642
    if (os.rdstate() & std::ostream::failbit)
238 andreas 643
        return false;
644
 
242 andreas 645
    if (os.rdstate() & std::ostream::badbit)
238 andreas 646
        return false;
647
 
648
    return true;
649
}
650
 
250 andreas 651
void TStreamError::startTemporaryLogLevel(unsigned int l)
652
{
653
    if (haveTemporaryLogLevel)
654
        return;
655
 
656
    mLogLevelOld = mLogLevel;
657
    mLogLevel |= l;
658
    haveTemporaryLogLevel = true;
659
}
660
 
661
void TStreamError::endTemporaryLogLevel()
662
{
260 andreas 663
    if (!haveTemporaryLogLevel)
664
        return;
665
 
250 andreas 666
    mLogLevel = mLogLevelOld;
667
    haveTemporaryLogLevel = false;
668
}
669
 
2 andreas 670
/********************************************************************/
671
 
14 andreas 672
std::mutex tracer_mutex;
673
 
398 andreas 674
TTracer::TTracer(const std::string& msg, int line, const char *file, threadID_t tid)
306 andreas 675
    : mThreadID(tid)
2 andreas 676
{
116 andreas 677
    if (!TConfig::isInitialized() || !TStreamError::checkFilter(HLOG_TRACE))
14 andreas 678
        return;
2 andreas 679
 
292 andreas 680
    std::lock_guard<mutex> guard(tracer_mutex);
116 andreas 681
 
14 andreas 682
    mFile = file;
683
    size_t pos = mFile.find_last_of("/");
2 andreas 684
 
14 andreas 685
    if (pos != string::npos)
686
        mFile = mFile.substr(pos + 1);
2 andreas 687
 
23 andreas 688
    TError::setErrorType(TERRTRACE);
292 andreas 689
    std::lock_guard<mutex> guardm(message_mutex);
242 andreas 690
 
14 andreas 691
    if (!TConfig::isLongFormat())
316 andreas 692
        *TError::Current()->getStream() << "TRC " << std::setw(5) << std::right << line << ", " << _threadIDtoStr(mThreadID) << " " << indent << "{entry " << msg << std::endl;
14 andreas 693
    else
304 andreas 694
        *TError::Current()->getStream() << TStreamError::getTime() <<  " TRC " << std::setw(5) << std::right << line << ", " << std::setw(20) << std::left << mFile << ", " << _threadIDtoStr(mThreadID) << " " << indent << "{entry " << msg << std::endl;
2 andreas 695
 
14 andreas 696
    TError::Current()->incIndent();
697
    mHeadMsg = msg;
698
    mLine = line;
35 andreas 699
 
700
    if (TConfig::getProfiling())
701
        mTimePoint = std::chrono::steady_clock::now();
2 andreas 702
}
703
 
704
TTracer::~TTracer()
705
{
116 andreas 706
    if (!TConfig::isInitialized() || !TStreamError::checkFilter(HLOG_TRACE))
14 andreas 707
        return;
2 andreas 708
 
292 andreas 709
    std::lock_guard<mutex> guard(tracer_mutex);
23 andreas 710
    TError::setErrorType(TERRTRACE);
14 andreas 711
    TError::Current()->decIndent();
35 andreas 712
    string nanosecs;
713
 
714
    if (TConfig::getProfiling())
715
    {
716
        std::chrono::steady_clock::time_point endPoint = std::chrono::steady_clock::now();
717
        std::chrono::nanoseconds difftime = endPoint - mTimePoint;
718
        std::chrono::seconds secs = std::chrono::duration_cast<std::chrono::seconds>(difftime);
719
        std::chrono::milliseconds msecs = std::chrono::duration_cast<std::chrono::milliseconds>(difftime) - std::chrono::duration_cast<std::chrono::seconds>(secs);
720
        std::stringstream s;
721
        s << std::chrono::duration_cast<std::chrono::nanoseconds> (difftime).count() << "[ns]" << " --> " << std::chrono::duration_cast<std::chrono::seconds>(secs).count() << "s " << std::chrono::duration_cast<std::chrono::milliseconds>(msecs).count() << "ms";
722
        nanosecs = s.str();
723
    }
724
 
292 andreas 725
    std::lock_guard<mutex> guardm(message_mutex);
386 andreas 726
 
35 andreas 727
    if (TConfig::getProfiling())
728
    {
729
        if (!TConfig::isLongFormat())
316 andreas 730
            *TError::Current()->getStream() << "TRC      , " << _threadIDtoStr(mThreadID) << " " << indent << "}exit " << mHeadMsg << " Elapsed time: " << nanosecs << std::endl;
35 andreas 731
        else
304 andreas 732
            *TError::Current()->getStream() << TStreamError::getTime() << " TRC      , " << std::setw(20) << std::left << mFile << ", " << _threadIDtoStr(mThreadID) << " " << indent << "}exit " << mHeadMsg << " Elapsed time: " << nanosecs << std::endl;
35 andreas 733
    }
14 andreas 734
    else
35 andreas 735
    {
736
        if (!TConfig::isLongFormat())
316 andreas 737
            *TError::Current()->getStream() << "TRC      , " << _threadIDtoStr(mThreadID) << " " << indent << "}exit " << mHeadMsg << std::endl;
35 andreas 738
        else
304 andreas 739
            *TError::Current()->getStream() << TStreamError::getTime() << " TRC      , " << std::setw(20) << std::left << mFile << ", " << _threadIDtoStr(mThreadID) << " " << indent << "}exit " << mHeadMsg << std::endl;
35 andreas 740
    }
14 andreas 741
 
742
    mHeadMsg.clear();
2 andreas 743
}
744
 
745
/********************************************************************/
746
 
747
TError::~TError()
748
{
14 andreas 749
    if (mCurrent)
750
    {
751
        delete mCurrent;
752
        mCurrent = nullptr;
753
    }
2 andreas 754
}
14 andreas 755
 
2 andreas 756
TStreamError* TError::Current()
757
{
14 andreas 758
    if (!mCurrent)
759
        mCurrent = new TStreamError(TConfig::getLogFile(), TConfig::getLogLevel());
2 andreas 760
 
14 andreas 761
    return mCurrent;
2 andreas 762
}
763
 
306 andreas 764
TStreamError *TError::Current(threadID_t tid)
304 andreas 765
{
766
    mThreadID = tid;
767
    return Current();
768
}
769
 
5 andreas 770
void TError::logHex(char* str, size_t size)
771
{
772
    if (!str || !size)
773
        return;
774
 
116 andreas 775
    if (!Current())
290 andreas 776
        return;
289 andreas 777
 
5 andreas 778
    // Print out the message
779
    std::ostream *stream = mCurrent->getStream();
21 andreas 780
    *stream << strToHex(str, size, 16, true, 12) << std::endl;
23 andreas 781
    *stream << mCurrent->resetFlags(stream);
21 andreas 782
}
5 andreas 783
 
21 andreas 784
string TError::toHex(int num, int width)
785
{
786
    string ret;
787
    std::stringstream stream;
788
    stream << std::setfill ('0') << std::setw(width) << std::hex << num;
789
    ret = stream.str();
790
    return ret;
791
}
792
 
793
string TError::strToHex(const char *str, size_t size, int width, bool format, int indent)
794
{
795
    int len = 0, pos = 0, old = 0;
796
    int w = (format) ? 1 : width;
797
    string out, left, right;
798
    string ind;
799
 
800
    if (indent > 0)
801
    {
802
        for (int j = 0; j < indent; j++)
803
            ind.append(" ");
804
    }
805
 
5 andreas 806
    for (size_t i = 0; i < size; i++)
807
    {
21 andreas 808
        if (len >= w)
809
        {
810
            left.append(" ");
811
            len = 0;
812
        }
5 andreas 813
 
21 andreas 814
        if (format && i > 0 && (pos % width) == 0)
5 andreas 815
        {
21 andreas 816
            out += ind + toHex(old, 4) + ": " + left + " | " + right + "\n";
817
            left.clear();
818
            right.clear();
819
            old = pos;
5 andreas 820
        }
21 andreas 821
 
822
        int c = *(str+i) & 0x000000ff;
823
        left.append(toHex(c, 2));
824
 
825
        if (format)
826
        {
827
            if (std::isprint(c))
828
                right.push_back(c);
829
            else
830
                right.push_back('.');
831
        }
832
 
833
        len++;
834
        pos++;
5 andreas 835
    }
836
 
21 andreas 837
    if (!format)
838
        return left;
839
    else if (pos > 0)
840
    {
841
        if ((pos % width) != 0)
842
        {
843
            for (int i = 0; i < (width - (pos % width)); i++)
844
                left.append("   ");
845
        }
846
 
847
        out += ind + toHex(old, 4)+": "+left + "  | " + right;
848
    }
849
 
850
    return out;
5 andreas 851
}
852
 
2 andreas 853
void TError::setErrorMsg(const std::string& msg)
854
{
14 andreas 855
    if (msg.empty())
856
        return;
2 andreas 857
 
14 andreas 858
    msError = msg;
859
    mHaveError = true;
860
    mErrType = TERRERROR;
2 andreas 861
}
862
 
863
void TError::setErrorMsg(terrtype_t t, const std::string& msg)
864
{
14 andreas 865
    if (msg.empty())
866
        return;
2 andreas 867
 
14 andreas 868
    msError = msg;
869
    mHaveError = true;
870
    mErrType = t;
2 andreas 871
}
872
 
873
std::ostream & TError::append(int lv, std::ostream& os)
874
{
14 andreas 875
    Current();
2 andreas 876
 
242 andreas 877
    if (!TConfig::isInitialized() && (lv == HLOG_ERROR || lv == HLOG_WARNING))
878
    {
879
        std::cerr << append(lv);
880
        return std::cerr;
881
    }
882
 
883
    return os << append(lv);
884
}
885
 
886
std::string TError::append(int lv)
887
{
292 andreas 888
    std::lock_guard<mutex> guard(message_mutex);
242 andreas 889
    std::string prefix, out;
890
 
14 andreas 891
    switch (lv)
892
    {
94 andreas 893
        case HLOG_PROTOCOL: prefix = "PRT    ++, "; mErrType = TERRINFO; break;
23 andreas 894
        case HLOG_INFO:     prefix = "INF    >>, "; mErrType = TERRINFO; break;
895
        case HLOG_WARNING:  prefix = "WRN    !!, "; mErrType = TERRWARNING; break;
896
        case HLOG_ERROR:    prefix = "ERR *****, "; mErrType = TERRERROR; break;
897
        case HLOG_TRACE:    prefix = "TRC      , "; mErrType = TERRTRACE; break;
898
        case HLOG_DEBUG:    prefix = "DBG    --, "; mErrType = TERRDEBUG; break;
14 andreas 899
 
900
        default:
22 andreas 901
            prefix = "           ";
23 andreas 902
            mErrType = TERRNONE;
14 andreas 903
    }
904
 
242 andreas 905
    if (!TConfig::isLongFormat())
316 andreas 906
        out = prefix + _threadIDtoStr(mThreadID) + " ";
242 andreas 907
    else
116 andreas 908
    {
242 andreas 909
        std::stringstream s;
304 andreas 910
        s << TStreamError::getTime() << " " << prefix << std::setw(20) << " " << ", " << _threadIDtoStr(mThreadID) << " ";
242 andreas 911
        out = s.str();
116 andreas 912
    }
913
 
242 andreas 914
    return out;
2 andreas 915
}
22 andreas 916
 
917
void TError::displayMessage(const std::string& msg)
918
{
919
    QMessageBox m;
920
    m.setText(msg.c_str());
921
 
922
    int cnt = 10;
923
 
924
    QTimer cntDown;
925
    QObject::connect(&cntDown, &QTimer::timeout, [&m, &cnt, &cntDown]()->void
926
        {
927
            if (--cnt < 0)
928
            {
929
                cntDown.stop();
930
                m.close();
931
            }
932
        });
933
 
239 andreas 934
    cntDown.start(1000);
935
    m.exec();
22 andreas 936
}