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>
24
 
2 andreas 25
#include "terror.h"
26
#include "tconfig.h"
27
 
22 andreas 28
#if defined(__linux__) || defined(Q_OS_ANDROID)
29
#include <QMessageBox>
30
#include <QTimer>
31
#endif
23 andreas 32
#if LOGPATH == LPATH_SYSLOG || defined(__ANDROID__)
22 andreas 33
#   ifdef __ANDROID__
34
#       include <android/log.h>
35
#   else
36
#       include <syslog.h>
37
#   endif
38
#endif
39
 
2 andreas 40
using std::string;
14 andreas 41
std::mutex message_mutex;
2 andreas 42
 
43
bool TError::mHaveError = false;
44
terrtype_t TError::mErrType = TERRNONE;
45
TStreamError *TError::mCurrent = nullptr;
46
std::string TError::msError;
47
 
48
int TStreamError::mIndent = 1;
49
std::ostream *TStreamError::mStream = nullptr;
50
std::string TStreamError::mLogfile;
51
bool TStreamError::mInitialized = false;
23 andreas 52
unsigned int TStreamError::mLogLevel = HLOG_PROTOCOL;
2 andreas 53
 
23 andreas 54
#if LOGPATH == LPATH_SYSLOG || defined(__ANDROID__)
55
class androidbuf : public std::streambuf
56
{
57
    public:
58
        enum { bufsize = 512 };
59
        androidbuf() { this->setp(buffer, buffer + bufsize - 1); }
60
 
61
    private:
62
        int overflow(int c)
63
        {
64
            if (c == traits_type::eof())
65
            {
66
                *this->pptr() = traits_type::to_char_type(c);
67
                this->sbumpc();
68
            }
69
 
70
            return this->sync()? traits_type::eof(): traits_type::not_eof(c);
71
        }
72
 
73
        int sync()
74
        {
75
            int rc = 0;
76
 
77
            if (this->pbase() != this->pptr())
78
            {
79
                char writebuf[bufsize+1];
80
                memcpy(writebuf, this->pbase(), this->pptr() - this->pbase());
81
                writebuf[this->pptr() - this->pbase()] = '\0';
82
                int eType;
83
#ifdef __ANDROID__
84
                switch(TError::getErrorType())
85
                {
86
                    case TERRINFO:      eType = ANDROID_LOG_INFO; break;
87
                    case TERRWARNING:   eType = ANDROID_LOG_WARN; break;
88
                    case TERRERROR:     eType = ANDROID_LOG_ERROR; break;
89
                    case TERRTRACE:     eType = ANDROID_LOG_VERBOSE; break;
90
                    case TERRDEBUG:     eType = ANDROID_LOG_DEBUG; break;
26 andreas 91
                    case TERRNONE:      eType = ANDROID_LOG_INFO; break;
23 andreas 92
                }
93
 
94
                rc = __android_log_print(eType, "tpanel", "%s", writebuf) > 0;
95
#else
96
                switch(TError::getErrorType())
97
                {
98
                    case TERRINFO:      eType = LOG_INFO; break;
99
                    case TERRWARNING:   eType = LOG_WARN; break;
100
                    case TERRERROR:     eType = LOG_ERROR; break;
101
                    case TERRTRACE:     eType = LOG_INFO; break;
102
                    case TERRDEBUG:     eType = LOG_DEBUG; break;
103
                    case TERRNONE:      eType = LOG_INFO; break;
104
                }
105
 
106
                syslog(eType, writebuf);
26 andreas 107
                rc = 1;
22 andreas 108
#endif
23 andreas 109
                this->setp(buffer, buffer + bufsize - 1);
110
            }
22 andreas 111
 
23 andreas 112
            return rc;
113
        }
114
 
115
        char buffer[bufsize];
116
};
117
#endif
118
 
2 andreas 119
TStreamError::TStreamError(const string& logFile, const std::string& logLevel)
120
{
14 andreas 121
    if (!logFile.empty())
23 andreas 122
        mLogfile = logFile;
14 andreas 123
    else if (!TConfig::getLogFile().empty())
23 andreas 124
        mLogfile = TConfig::getLogFile();
2 andreas 125
 
14 andreas 126
    if (!logLevel.empty())
127
        setLogLevel(logLevel);
128
    else if (!TConfig::getLogLevel().empty())
129
        setLogLevel(TConfig::getLogFile());
2 andreas 130
 
14 andreas 131
    _init();
2 andreas 132
}
133
 
134
TStreamError::~TStreamError()
135
{
14 andreas 136
    if (mStream && mStream != &std::cout)
137
    {
138
        delete mStream;
139
        mStream = nullptr;
140
        mInitialized = false;
141
    }
2 andreas 142
}
143
 
23 andreas 144
void TStreamError::setLogFile(const std::string &lf)
145
{
146
    if (mLogfile.compare(lf) == 0)
147
        return;
148
 
149
    mLogfile = lf;
150
    mInitialized = false;
151
    _init();
152
}
153
 
2 andreas 154
void TStreamError::setLogLevel(const std::string& slv)
155
{
14 andreas 156
    size_t pos = slv.find("|");
157
    size_t start = 0;
158
    string lv;
159
    mLogLevel = 0;
2 andreas 160
 
14 andreas 161
    while (pos != string::npos)
162
    {
163
        lv = slv.substr(start, pos - start);
164
        start = pos + 1;
165
        mLogLevel |= _getLevel(lv);
166
        pos = slv.find("|", start);
167
    }
2 andreas 168
 
14 andreas 169
    mLogLevel |= _getLevel(slv.substr(start));
2 andreas 170
}
171
 
172
bool TStreamError::checkFilter(terrtype_t err)
173
{
23 andreas 174
    if (err == TERRINFO && (mLogLevel & HLOG_INFO) != 0)
14 andreas 175
        return true;
23 andreas 176
    else if (err == TERRWARNING && (mLogLevel & HLOG_WARNING) != 0)
14 andreas 177
        return true;
23 andreas 178
    else if (err == TERRERROR && (mLogLevel & HLOG_ERROR) != 0)
14 andreas 179
        return true;
23 andreas 180
    else if (err == TERRTRACE && (mLogLevel & HLOG_TRACE) != 0)
14 andreas 181
        return true;
23 andreas 182
    else if (err == TERRDEBUG && (mLogLevel & HLOG_DEBUG) != 0)
14 andreas 183
        return true;
2 andreas 184
 
14 andreas 185
    return false;
2 andreas 186
}
187
 
188
bool TStreamError::checkFilter(int lv)
189
{
14 andreas 190
    if ((mLogLevel & lv) != 0)
191
        return true;
2 andreas 192
 
14 andreas 193
    return false;
2 andreas 194
}
195
 
196
unsigned int TStreamError::_getLevel(const std::string& slv)
197
{
14 andreas 198
    if (slv.compare(SLOG_NONE) == 0)
23 andreas 199
        return HLOG_NONE;
2 andreas 200
 
14 andreas 201
    if (slv.compare(SLOG_INFO) == 0)
23 andreas 202
        return HLOG_INFO;
2 andreas 203
 
14 andreas 204
    if (slv.compare(SLOG_WARNING) == 0)
23 andreas 205
        return HLOG_WARNING;
2 andreas 206
 
14 andreas 207
    if (slv.compare(SLOG_ERROR) == 0)
23 andreas 208
        return HLOG_ERROR;
2 andreas 209
 
14 andreas 210
    if (slv.compare(SLOG_TRACE) == 0)
23 andreas 211
        return HLOG_TRACE;
2 andreas 212
 
14 andreas 213
    if (slv.compare(SLOG_DEBUG) == 0)
23 andreas 214
        return HLOG_DEBUG;
2 andreas 215
 
14 andreas 216
    if (slv.compare(SLOG_PROTOCOL) == 0)
23 andreas 217
        return HLOG_PROTOCOL;
2 andreas 218
 
14 andreas 219
    if (slv.compare(SLOG_ALL) == 0)
23 andreas 220
        return HLOG_ALL;
2 andreas 221
 
23 andreas 222
    return HLOG_NONE;
2 andreas 223
}
224
 
225
void TStreamError::_init()
226
{
14 andreas 227
    if (mInitialized)
228
        return;
2 andreas 229
 
14 andreas 230
    mInitialized = true;
23 andreas 231
 
22 andreas 232
#if LOGPATH == LPATH_FILE
14 andreas 233
    if (!mLogfile.empty())
234
    {
23 andreas 235
        try
236
        {
237
#ifndef __ANDROID__
238
            if (mStream && mStream != &std::cout)
239
                delete mStream;
2 andreas 240
 
23 andreas 241
            mStream = new std::ofstream(mLogfile.c_str(), std::ios::out | std::ios::ate);
2 andreas 242
 
23 andreas 243
            if (!mStream || mStream->fail())
244
                mStream = &std::cout;
245
#else
246
            std::cout.rdbuf(new androidbuf);
14 andreas 247
            mStream = &std::cout;
23 andreas 248
#endif  // __ANDROID__
249
        }
250
        catch (std::exception& e)
251
        {
252
#ifdef __ANDROID__
253
            __android_log_print(ANDROID_LOG_ERROR, "tpanel", "ERROR: %s", e.what());
254
#else
255
            std::cerr << "ERROR: " << e.what() << std::endl;
256
#endif  // __ANDROID__
257
            mStream = &std::cout;
258
        }
14 andreas 259
    }
260
    else
23 andreas 261
    {
262
#ifdef __ANDROID__
263
        std::cout.rdbuf(new androidbuf);
22 andreas 264
#endif
14 andreas 265
        mStream = &std::cout;
23 andreas 266
    }
267
#else
268
    std::cout.rdbuf(new androidbuf);
269
    mStream = &std::cout;
270
#endif  // LOGPATH == LPATH_FILE
271
 
14 andreas 272
    if (!TConfig::isLongFormat())
273
        *mStream << "Logfile started at " << getTime() << std::endl;
2 andreas 274
 
14 andreas 275
    *mStream << TConfig::getProgName() << " version " << V_MAJOR << "." << V_MINOR << "." << V_PATCH << std::endl;
31 andreas 276
    *mStream << "(C) Copyright by Andreas Theofilu <andreas@theosys.at>" << std::endl << " " << std::endl;
2 andreas 277
 
14 andreas 278
    if (TConfig::isLongFormat())
279
        *mStream << "Timestamp           Type LNr., File name           , Message" << std::endl;
280
    else
281
        *mStream << "Type LNr., Message" << std::endl;
2 andreas 282
 
23 andreas 283
    *mStream << "-----------------------------------------------------------------" << std::endl << std::flush;
14 andreas 284
}
2 andreas 285
 
286
void TStreamError::logMsg(std::ostream& str)
287
{
5 andreas 288
    _init();
2 andreas 289
 
5 andreas 290
    if (!mStream || str.fail())
291
        return;
2 andreas 292
 
5 andreas 293
    // Save the old format settings in case they were manipulated
23 andreas 294
//    std::ios oldstate(nullptr);
295
//#ifdef __ANDROID__
296
//    oldstate.copyfmt(*mStream);
297
//#else
298
//    oldstate.copyfmt(std::cout);
299
//#endif
5 andreas 300
    // Print out the message
301
    std::stringstream s;
302
    s << str.rdbuf() << std::ends;
303
    *mStream << s.str() << std::flush;
26 andreas 304
    resetFlags(mStream);
23 andreas 305
//    mStream->copyfmt(oldstate);     // Restore old format (reset)
2 andreas 306
}
307
 
23 andreas 308
std::ostream *TStreamError::resetFlags(std::ostream *os)
22 andreas 309
{
23 andreas 310
    *os << std::resetiosflags(std::ios::boolalpha) <<
311
           std::resetiosflags(std::ios::showbase) <<
312
           std::resetiosflags(std::ios::showpoint) <<
313
           std::resetiosflags(std::ios::showpos) <<
314
           std::resetiosflags(std::ios::skipws) <<
315
           std::resetiosflags(std::ios::unitbuf) <<
316
           std::resetiosflags(std::ios::uppercase) <<
317
           std::resetiosflags(std::ios::dec) <<
318
           std::resetiosflags(std::ios::hex) <<
319
           std::resetiosflags(std::ios::oct) <<
320
           std::resetiosflags(std::ios::fixed) <<
321
           std::resetiosflags(std::ios::scientific) <<
322
           std::resetiosflags(std::ios::internal) <<
323
           std::resetiosflags(std::ios::left) <<
324
           std::resetiosflags(std::ios::right) <<
325
           std::setfill(' ');
326
    return os;
22 andreas 327
}
328
 
2 andreas 329
void TStreamError::decIndent()
330
{
14 andreas 331
    if (mIndent > 0)
332
        mIndent--;
2 andreas 333
}
334
 
335
string TStreamError::getTime()
336
{
14 andreas 337
    time_t rawtime;
338
    struct tm * timeinfo;
339
    char buffer[80];
2 andreas 340
 
14 andreas 341
    time(&rawtime);
342
    timeinfo = localtime(&rawtime);
2 andreas 343
 
14 andreas 344
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
345
    string str(buffer);
346
    return str;
2 andreas 347
}
348
 
349
std::ostream& indent(std::ostream& os)
350
{
14 andreas 351
    if (os.fail())
352
        return os;
2 andreas 353
 
14 andreas 354
    if (TStreamError::getIndent() > 0)
355
        os << std::setw(TStreamError::getIndent()) << " ";
2 andreas 356
 
14 andreas 357
    return os;
2 andreas 358
}
359
 
360
/********************************************************************/
361
 
14 andreas 362
std::mutex tracer_mutex;
363
 
2 andreas 364
TTracer::TTracer(const std::string& msg, int line, char *file)
365
{
23 andreas 366
    if (!TStreamError::checkFilter(HLOG_TRACE))
14 andreas 367
        return;
2 andreas 368
 
14 andreas 369
    tracer_mutex.lock();
370
    mFile = file;
371
    size_t pos = mFile.find_last_of("/");
2 andreas 372
 
14 andreas 373
    if (pos != string::npos)
374
        mFile = mFile.substr(pos + 1);
2 andreas 375
 
23 andreas 376
    TError::setErrorType(TERRTRACE);
22 andreas 377
#if LOGPATH == LPATH_FILE
14 andreas 378
    if (!TConfig::isLongFormat())
379
        TError::Current()->logMsg(*TStreamError::getStream() << "TRC " << std::setw(5) << std::right << line << ", " << indent << "{entry " << msg << std::endl);
380
    else
381
        TError::Current()->logMsg(*TStreamError::getStream() << TStreamError::getTime() <<  " TRC " << std::setw(5) << std::right << line << ", " << std::setw(20) << std::left << mFile << ", " << indent << "{entry " << msg << std::endl);
22 andreas 382
#else
383
    std::stringstream s;
2 andreas 384
 
22 andreas 385
    if (!TConfig::isLongFormat())
386
        s  << "TRC " << std::setw(5) << std::right << line << ", " << &indents << "{entry " << msg << std::endl;
387
    else
388
        s << TStreamError::getTime() <<  " TRC " << std::setw(5) << std::right << line << ", " << std::setw(20) << std::left << mFile << ", " << &indents << "{entry " << msg << std::endl;
389
 
390
    TError::Current()->logMsg(s);
391
#endif
14 andreas 392
    TError::Current()->incIndent();
393
    mHeadMsg = msg;
394
    mLine = line;
395
    tracer_mutex.unlock();
2 andreas 396
}
397
 
398
TTracer::~TTracer()
399
{
23 andreas 400
    if (!TStreamError::checkFilter(HLOG_TRACE))
14 andreas 401
        return;
2 andreas 402
 
14 andreas 403
    tracer_mutex.lock();
23 andreas 404
    TError::setErrorType(TERRTRACE);
14 andreas 405
    TError::Current()->decIndent();
22 andreas 406
#if LOGPATH == LPATH_FILE
14 andreas 407
    if (!TConfig::isLongFormat())
408
        TError::Current()->logMsg(*TStreamError::getStream() << "TRC      , " << indent << "}exit " << mHeadMsg << std::endl);
409
    else
410
        TError::Current()->logMsg(*TStreamError::getStream() << TStreamError::getTime() << " TRC      , " << std::setw(20) << std::left << mFile << ", " << indent << "}exit " << mHeadMsg << std::endl);
22 andreas 411
#else
412
    std::stringstream s;
14 andreas 413
 
22 andreas 414
    if (!TConfig::isLongFormat())
415
        s << "TRC      , " << &indents << "}exit " << mHeadMsg << std::endl;
416
    else
417
        s << TStreamError::getTime() << " TRC      , " << std::setw(20) << std::left << mFile << ", " << &indents << "}exit " << mHeadMsg << std::endl;
418
 
419
    TError::Current()->logMsg(s);
420
#endif
14 andreas 421
    mHeadMsg.clear();
422
    tracer_mutex.unlock();
2 andreas 423
}
424
 
425
/********************************************************************/
426
 
427
TError::~TError()
428
{
14 andreas 429
    if (mCurrent)
430
    {
431
        delete mCurrent;
432
        mCurrent = nullptr;
433
    }
2 andreas 434
}
435
 
14 andreas 436
void TError::lock()
437
{
438
    message_mutex.lock();
439
}
440
 
441
void TError::unlock()
442
{
443
    message_mutex.unlock();
444
}
445
 
2 andreas 446
TStreamError* TError::Current()
447
{
14 andreas 448
    if (!mCurrent)
449
        mCurrent = new TStreamError(TConfig::getLogFile(), TConfig::getLogLevel());
2 andreas 450
 
14 andreas 451
    return mCurrent;
2 andreas 452
}
453
 
5 andreas 454
void TError::logHex(char* str, size_t size)
455
{
456
    if (!str || !size)
457
        return;
458
 
21 andreas 459
    message_mutex.lock();
5 andreas 460
    Current();
461
    // Save the old format settings in case they were manipulated
23 andreas 462
//    std::ios oldstate(nullptr);
463
//    oldstate.copyfmt(std::cout);
5 andreas 464
    // Print out the message
465
    std::ostream *stream = mCurrent->getStream();
21 andreas 466
    *stream << strToHex(str, size, 16, true, 12) << std::endl;
23 andreas 467
    *stream << mCurrent->resetFlags(stream);
468
//    stream->copyfmt(oldstate);     // Restore old format (reset)
21 andreas 469
    message_mutex.unlock();
470
}
5 andreas 471
 
21 andreas 472
string TError::toHex(int num, int width)
473
{
474
    string ret;
475
    std::stringstream stream;
476
    stream << std::setfill ('0') << std::setw(width) << std::hex << num;
477
    ret = stream.str();
478
    return ret;
479
}
480
 
481
string TError::strToHex(const char *str, size_t size, int width, bool format, int indent)
482
{
483
    int len = 0, pos = 0, old = 0;
484
    int w = (format) ? 1 : width;
485
    string out, left, right;
486
    string ind;
487
 
488
    if (indent > 0)
489
    {
490
        for (int j = 0; j < indent; j++)
491
            ind.append(" ");
492
    }
493
 
5 andreas 494
    for (size_t i = 0; i < size; i++)
495
    {
21 andreas 496
        if (len >= w)
497
        {
498
            left.append(" ");
499
            len = 0;
500
        }
5 andreas 501
 
21 andreas 502
        if (format && i > 0 && (pos % width) == 0)
5 andreas 503
        {
21 andreas 504
            out += ind + toHex(old, 4) + ": " + left + " | " + right + "\n";
505
            left.clear();
506
            right.clear();
507
            old = pos;
5 andreas 508
        }
21 andreas 509
 
510
        int c = *(str+i) & 0x000000ff;
511
        left.append(toHex(c, 2));
512
 
513
        if (format)
514
        {
515
            if (std::isprint(c))
516
                right.push_back(c);
517
            else
518
                right.push_back('.');
519
        }
520
 
521
        len++;
522
        pos++;
5 andreas 523
    }
524
 
21 andreas 525
    if (!format)
526
        return left;
527
    else if (pos > 0)
528
    {
529
        if ((pos % width) != 0)
530
        {
531
            for (int i = 0; i < (width - (pos % width)); i++)
532
                left.append("   ");
533
        }
534
 
535
        out += ind + toHex(old, 4)+": "+left + "  | " + right;
536
    }
537
 
538
    return out;
5 andreas 539
}
540
 
2 andreas 541
void TError::setErrorMsg(const std::string& msg)
542
{
14 andreas 543
    if (msg.empty())
544
        return;
2 andreas 545
 
14 andreas 546
    msError = msg;
547
    mHaveError = true;
548
    mErrType = TERRERROR;
2 andreas 549
}
550
 
551
void TError::setErrorMsg(terrtype_t t, const std::string& msg)
552
{
14 andreas 553
    if (msg.empty())
554
        return;
2 andreas 555
 
14 andreas 556
    msError = msg;
557
    mHaveError = true;
558
    mErrType = t;
2 andreas 559
}
560
 
561
std::ostream & TError::append(int lv, std::ostream& os)
562
{
14 andreas 563
    std::string prefix;
564
    Current();
2 andreas 565
 
14 andreas 566
    switch (lv)
567
    {
23 andreas 568
        case HLOG_INFO:     prefix = "INF    >>, "; mErrType = TERRINFO; break;
569
        case HLOG_WARNING:  prefix = "WRN    !!, "; mErrType = TERRWARNING; break;
570
        case HLOG_ERROR:    prefix = "ERR *****, "; mErrType = TERRERROR; break;
571
        case HLOG_TRACE:    prefix = "TRC      , "; mErrType = TERRTRACE; break;
572
        case HLOG_DEBUG:    prefix = "DBG    --, "; mErrType = TERRDEBUG; break;
14 andreas 573
 
574
        default:
22 andreas 575
            prefix = "           ";
23 andreas 576
            mErrType = TERRNONE;
14 andreas 577
    }
578
 
579
    if (!TConfig::isLongFormat())
580
        return os << prefix;
581
    else
582
        return os << TStreamError::getTime() << " " << prefix << std::setw(20) << " " << ", ";
2 andreas 583
}
22 andreas 584
 
585
#if defined(__linux__) || defined(Q_OS_ANDROID)
586
void TError::displayMessage(const std::string& msg)
587
{
588
    QMessageBox m;
589
    m.setText(msg.c_str());
590
 
591
    int cnt = 10;
592
 
593
    QTimer cntDown;
594
    QObject::connect(&cntDown, &QTimer::timeout, [&m, &cnt, &cntDown]()->void
595
        {
596
            if (--cnt < 0)
597
            {
598
                cntDown.stop();
599
                m.close();
600
            }
601
        });
602
 
603
        cntDown.start(1000);
604
        m.exec();
605
}
606
#endif