Coverage Report

Created: 2026-05-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/oflog/libsrc/patlay.cc
Line
Count
Source
1
// Module:  Log4CPLUS
2
// File:    patternlayout.cxx
3
// Created: 6/2001
4
// Author:  Tad E. Smith
5
//
6
//
7
// Copyright 2001-2010 Tad E. Smith
8
//
9
// Licensed under the Apache License, Version 2.0 (the "License");
10
// you may not use this file except in compliance with the License.
11
// You may obtain a copy of the License at
12
//
13
//     http://www.apache.org/licenses/LICENSE-2.0
14
//
15
// Unless required by applicable law or agreed to in writing, software
16
// distributed under the License is distributed on an "AS IS" BASIS,
17
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
// See the License for the specific language governing permissions and
19
// limitations under the License.
20
21
#include "dcmtk/oflog/layout.h"
22
#include "dcmtk/oflog/logmacro.h"
23
#include "dcmtk/oflog/helpers/loglog.h"
24
#include "dcmtk/oflog/helpers/timehelp.h"
25
#include "dcmtk/oflog/helpers/strhelp.h"
26
#include "dcmtk/oflog/helpers/socket.h"
27
#include "dcmtk/oflog/helpers/property.h"
28
#include "dcmtk/oflog/spi/logevent.h"
29
#include "dcmtk/oflog/internal/internal.h"
30
#include "dcmtk/oflog/internal/env.h"
31
#include <cstdlib>
32
33
34
namespace
35
{
36
37
38
static
39
dcmtk::log4cplus::tstring
40
get_basename (const dcmtk::log4cplus::tstring& filename)
41
0
{
42
#if defined(_WIN32)
43
    dcmtk::log4cplus::tchar const dir_sep(DCMTK_LOG4CPLUS_TEXT('\\'));
44
#else
45
0
    dcmtk::log4cplus::tchar const dir_sep(DCMTK_LOG4CPLUS_TEXT('/'));
46
0
#endif
47
48
0
    dcmtk::log4cplus::tstring::size_type pos = filename.rfind(dir_sep);
49
0
    if (pos != OFString_npos)
50
0
        return filename.substr(pos+1);
51
0
    else
52
0
        return filename;
53
0
}
54
55
56
} // namespace
57
58
59
namespace dcmtk
60
{
61
namespace log4cplus
62
{
63
64
static tchar const ESCAPE_CHAR = DCMTK_LOG4CPLUS_TEXT('%');
65
66
extern void formatRelativeTimestamp (log4cplus::tostream & output,
67
    log4cplus::spi::InternalLoggingEvent const & event);
68
69
70
namespace pattern
71
{
72
73
74
/**
75
 * This is used by PatternConverter class to inform them how to format
76
 * their output.
77
 */
78
struct FormattingInfo {
79
    int minLen;
80
    size_t maxLen;
81
    bool leftAlign;
82
    FormattingInfo()
83
        : minLen(), maxLen(), leftAlign()
84
6
    { reset(); }
85
86
    void reset();
87
    void dump(helpers::LogLog&);
88
};
89
90
91
92
/**
93
 * This is the base class of all "Converter" classes that format a
94
 * field of InternalLoggingEvent objects.  In fact, the PatternLayout
95
 * class simply uses an array of PatternConverter objects to format
96
 * and append a logging event.
97
 */
98
class PatternConverter
99
{
100
public:
101
    explicit PatternConverter(const FormattingInfo& info);
102
0
    virtual ~PatternConverter() {}
103
    void formatAndAppend(tostream& output,
104
        const spi::InternalLoggingEvent& event);
105
106
    virtual void convert(tstring & result,
107
        const spi::InternalLoggingEvent& event) = 0;
108
109
private:
110
    int minLen;
111
    size_t maxLen;
112
    bool leftAlign;
113
};
114
115
116
typedef OFVector<pattern::PatternConverter*> PatternConverterList;
117
118
119
/**
120
 * This PatternConverter returns a constant string.
121
 */
122
class LiteralPatternConverter : public PatternConverter
123
{
124
public:
125
    LiteralPatternConverter(const tstring& str);
126
    virtual void convert(tstring & result,
127
        const spi::InternalLoggingEvent&)
128
0
    {
129
0
        result = str;
130
0
    }
131
132
private:
133
    tstring str;
134
};
135
136
137
/**
138
 * This PatternConverter is used to format most of the "simple" fields
139
 * found in the InternalLoggingEvent object.
140
 */
141
class BasicPatternConverter
142
    : public PatternConverter
143
{
144
public:
145
    enum Type { THREAD_CONVERTER,
146
                THREAD2_CONVERTER,
147
                PROCESS_CONVERTER,
148
                LOGLEVEL_CONVERTER,
149
                LOGLEVEL_PREFIX_CONVERTER,
150
                NDC_CONVERTER,
151
                MESSAGE_CONVERTER,
152
                NEWLINE_CONVERTER,
153
                BASENAME_CONVERTER,
154
                FILE_CONVERTER,
155
                LINE_CONVERTER,
156
                FULL_LOCATION_CONVERTER,
157
                FUNCTION_CONVERTER };
158
    BasicPatternConverter(const FormattingInfo& info, Type type);
159
    virtual void convert(tstring & result,
160
        const spi::InternalLoggingEvent& event);
161
162
private:
163
  // Disable copy
164
    BasicPatternConverter(const BasicPatternConverter&);
165
    BasicPatternConverter& operator=(BasicPatternConverter&);
166
167
    LogLevelManager& llmCache;
168
    Type type;
169
};
170
171
172
173
/**
174
 * This PatternConverter is used to format the Logger field found in
175
 * the InternalLoggingEvent object.
176
 */
177
class LoggerPatternConverter : public PatternConverter {
178
public:
179
    LoggerPatternConverter(const FormattingInfo& info, int precision);
180
    virtual void convert(tstring & result,
181
        const spi::InternalLoggingEvent& event);
182
183
private:
184
    int precision;
185
};
186
187
188
189
/**
190
 * This PatternConverter is used to format the timestamp field found in
191
 * the InternalLoggingEvent object.  It will be formatted according to
192
 * the specified "pattern".
193
 */
194
class DatePatternConverter : public PatternConverter {
195
public:
196
    DatePatternConverter(const FormattingInfo& info,
197
                         const tstring& pattern,
198
                         bool use_gmtime);
199
    virtual void convert(tstring & result,
200
        const spi::InternalLoggingEvent& event);
201
202
private:
203
    bool use_gmtime;
204
    tstring format;
205
};
206
207
208
//! This pattern is used to format milliseconds since process start.
209
class RelativeTimestampConverter: public PatternConverter {
210
public:
211
    RelativeTimestampConverter(const FormattingInfo& info);
212
    virtual void convert(tstring & result,
213
        const spi::InternalLoggingEvent& event);
214
};
215
216
217
/**
218
 * This PatternConverter is used to format the hostname field.
219
 */
220
class HostnamePatternConverter : public PatternConverter {
221
public:
222
    HostnamePatternConverter(const FormattingInfo& info, bool fqdn);
223
    virtual void convert(tstring & result,
224
        const spi::InternalLoggingEvent& event);
225
226
private:
227
    tstring hostname_;
228
};
229
230
231
/**
232
 * This PatternConverter is used to format the MDC field found in
233
 * the InternalLoggingEvent object, optionally limited to
234
 * \c k Mapped diagnostic context key.
235
 */
236
class MDCPatternConverter
237
    : public PatternConverter
238
{
239
public:
240
    MDCPatternConverter(const FormattingInfo& info, tstring const & k);
241
    virtual void convert(tstring & result,
242
        const spi::InternalLoggingEvent& event);
243
244
private:
245
    tstring key;
246
};
247
248
249
/**
250
 * This PatternConverter is used to format the NDC field found in
251
 * the InternalLoggingEvent object, optionally limited to
252
 * \c precision levels (using space to separate levels).
253
 */
254
class NDCPatternConverter : public PatternConverter {
255
public:
256
    NDCPatternConverter(const FormattingInfo& info, int precision);
257
    virtual void convert(tstring & result,
258
        const spi::InternalLoggingEvent& event);
259
260
private:
261
    int precision;
262
};
263
264
265
266
/**
267
 * This class parses a "pattern" string into an array of
268
 * PatternConverter objects.
269
 * <p>
270
 * @see PatternLayout for the formatting of the "pattern" string.
271
 */
272
class PatternParser
273
{
274
public:
275
    PatternParser(const tstring& pattern, unsigned ndcMaxDepth);
276
    OFVector<PatternConverter*> parse();
277
278
private:
279
  // Types
280
    enum ParserState { LITERAL_STATE,
281
                       CONVERTER_STATE,
282
                       DOT_STATE,
283
                       MIN_STATE,
284
                       MAX_STATE };
285
286
  // Methods
287
    tstring extractOption();
288
    int extractPrecisionOption();
289
    void finalizeConverter(tchar c);
290
291
  // Data
292
    tstring pattern;
293
    FormattingInfo formattingInfo;
294
    OFVector<PatternConverter*> list;
295
    ParserState state;
296
    tstring::size_type pos;
297
    tstring currentLiteral;
298
    unsigned ndcMaxDepth;
299
};
300
301
302
////////////////////////////////////////////////
303
// FormattingInfo methods:
304
////////////////////////////////////////////////
305
306
void
307
24
FormattingInfo::reset() {
308
24
    minLen = -1;
309
24
    maxLen = 0x7FFFFFFF;
310
24
    leftAlign = false;
311
24
}
312
313
314
void
315
0
FormattingInfo::dump(helpers::LogLog& loglog) {
316
0
    tostringstream buf;
317
0
    buf << DCMTK_LOG4CPLUS_TEXT("min=") << minLen
318
0
        << DCMTK_LOG4CPLUS_TEXT(", max=") << maxLen
319
0
        << DCMTK_LOG4CPLUS_TEXT(", leftAlign=") << STD_NAMESPACE boolalpha << leftAlign;
320
0
    loglog.debug(OFString(buf.str().c_str(), buf.str().length()));
321
0
}
322
323
324
325
326
////////////////////////////////////////////////
327
// PatternConverter methods:
328
////////////////////////////////////////////////
329
330
PatternConverter::PatternConverter(const FormattingInfo& i)
331
12
    : minLen(i.minLen)
332
12
    , maxLen(i.maxLen)
333
12
    , leftAlign(i.leftAlign)
334
12
{
335
12
}
336
337
338
339
void
340
PatternConverter::formatAndAppend(
341
    tostream& output, const spi::InternalLoggingEvent& event)
342
0
{
343
0
    tstring & s = internal::get_ptd ()->faa_str;
344
0
    convert (s, event);
345
0
    size_t len = s.length();
346
347
0
    if (len > maxLen)
348
0
        output << s.substr(len - maxLen);
349
0
    else if (OFstatic_cast(int, len) < minLen)
350
0
    {
351
/*
352
        STD_NAMESPACE ios_base::fmtflags const original_flags = output.flags ();
353
        tchar const fill = output.fill (DCMTK_LOG4CPLUS_TEXT(' '));
354
        output.setf (leftAlign ? STD_NAMESPACE ios_base::left : STD_NAMESPACE ios_base::right,
355
            STD_NAMESPACE ios_base::adjustfield);
356
        output.width (minLen);
357
        output << s;
358
        output.fill (fill);
359
        output.flags (original_flags);
360
*/
361
        // use implementation from log4cplus 1.0.x since the above code does not work correctly
362
0
        if(leftAlign) {
363
0
            output << s;
364
0
            output << tstring(minLen - len, DCMTK_LOG4CPLUS_TEXT(' '));
365
0
        }
366
0
        else {
367
0
            output << tstring(minLen - len, DCMTK_LOG4CPLUS_TEXT(' '));
368
0
            output << s;
369
0
        }
370
0
    }
371
0
    else
372
0
        output << s;
373
0
}
374
375
376
377
////////////////////////////////////////////////
378
// LiteralPatternConverter methods:
379
////////////////////////////////////////////////
380
381
LiteralPatternConverter::LiteralPatternConverter(
382
    const tstring& str_)
383
3
    : PatternConverter(FormattingInfo())
384
3
    , str(str_)
385
3
{
386
3
}
387
388
389
390
////////////////////////////////////////////////
391
// BasicPatternConverter methods:
392
////////////////////////////////////////////////
393
394
BasicPatternConverter::BasicPatternConverter(
395
    const FormattingInfo& info, Type type_)
396
9
    : PatternConverter(info)
397
9
    , llmCache(getLogLevelManager())
398
9
    , type(type_)
399
9
{
400
9
}
401
402
403
404
void
405
BasicPatternConverter::convert(tstring & result,
406
    const spi::InternalLoggingEvent& event)
407
0
{
408
0
    switch(type)
409
0
    {
410
0
    case LOGLEVEL_CONVERTER:
411
0
        result = llmCache.toString(event.getLogLevel());
412
0
        return;
413
414
0
    case LOGLEVEL_PREFIX_CONVERTER:
415
0
        result = llmCache.toString(event.getLogLevel()).substr(0, 1);
416
0
        return;
417
418
0
    case BASENAME_CONVERTER:
419
0
        result = get_basename(event.getFile());
420
0
        return;
421
422
0
    case PROCESS_CONVERTER:
423
0
        helpers::convertIntegerToString(result, internal::get_process_id ());
424
0
        return;
425
426
0
    case NDC_CONVERTER:
427
0
        result = event.getNDC();
428
0
        return;
429
430
0
    case MESSAGE_CONVERTER:
431
0
        result = event.getMessage();
432
0
        return;
433
434
0
    case NEWLINE_CONVERTER:
435
0
        result = DCMTK_LOG4CPLUS_TEXT("\n");
436
0
        return;
437
438
0
    case FILE_CONVERTER:
439
0
        result = event.getFile();
440
0
        return;
441
442
0
    case THREAD_CONVERTER:
443
0
        result = event.getThread();
444
0
        return;
445
446
0
    case THREAD2_CONVERTER:
447
0
        result = event.getThread2();
448
0
        return;
449
450
0
    case LINE_CONVERTER:
451
0
        {
452
0
            if(event.getLine() != -1)
453
0
                helpers::convertIntegerToString(result, event.getLine());
454
0
            else
455
0
                result.clear ();
456
0
            return;
457
0
        }
458
459
0
    case FULL_LOCATION_CONVERTER:
460
0
        {
461
0
            tstring const & file = event.getFile();
462
0
            if (! file.empty ())
463
0
            {
464
0
                result = file;
465
0
                result += DCMTK_LOG4CPLUS_TEXT(":");
466
0
                result += helpers::convertIntegerToString(event.getLine());
467
0
            }
468
0
            else
469
0
                result = DCMTK_LOG4CPLUS_TEXT(":");
470
0
            return;
471
0
        }
472
473
0
    case FUNCTION_CONVERTER:
474
0
        result = event.getFunction ();
475
0
        return;
476
0
    }
477
478
0
    result = DCMTK_LOG4CPLUS_TEXT("INTERNAL LOG4CPLUS ERROR");
479
0
}
480
481
482
483
////////////////////////////////////////////////
484
// LoggerPatternConverter methods:
485
////////////////////////////////////////////////
486
487
LoggerPatternConverter::LoggerPatternConverter(
488
    const FormattingInfo& info, int prec)
489
0
    : PatternConverter(info)
490
0
    , precision(prec)
491
0
{
492
0
}
493
494
495
496
void
497
LoggerPatternConverter::convert(tstring & result,
498
    const spi::InternalLoggingEvent& event)
499
0
{
500
0
    const tstring& name = event.getLoggerName();
501
0
    if (precision <= 0) {
502
0
        result = name;
503
0
    }
504
0
    else {
505
0
        size_t len = name.length();
506
507
        // We subtract 1 from 'len' when assigning to 'end' to avoid out of
508
        // bounds exception in return r.substring(end+1, len). This can happen
509
        // if precision is 1 and the logger name ends with a dot.
510
0
        tstring::size_type end = len - 1;
511
0
        for (int i = precision; i > 0; --i)
512
0
        {
513
0
            end = name.rfind(DCMTK_LOG4CPLUS_TEXT('.'), end - 1);
514
0
            if(end == OFString_npos) {
515
0
                result = name;
516
0
                return;
517
0
            }
518
0
        }
519
0
        result = name.substr(end + 1);
520
0
    }
521
0
}
522
523
524
525
////////////////////////////////////////////////
526
// DatePatternConverter methods:
527
////////////////////////////////////////////////
528
529
530
DatePatternConverter::DatePatternConverter(
531
    const FormattingInfo& info, const tstring& pattern,
532
    bool use_gmtime_)
533
0
    : PatternConverter(info)
534
0
    , use_gmtime(use_gmtime_)
535
0
    , format(pattern)
536
0
{
537
0
}
538
539
540
541
void
542
DatePatternConverter::convert(tstring & result,
543
    const spi::InternalLoggingEvent& event)
544
0
{
545
0
    result = event.getTimestamp().getFormattedTime(format, use_gmtime);
546
0
}
547
548
549
//
550
//
551
//
552
553
RelativeTimestampConverter::RelativeTimestampConverter (FormattingInfo const & info)
554
0
    : PatternConverter (info)
555
0
{ }
556
557
558
void
559
RelativeTimestampConverter::convert (tstring & result,
560
    spi::InternalLoggingEvent const & event)
561
0
{
562
0
    tostringstream & oss = internal::get_ptd ()->layout_oss;
563
0
    detail::clear_tostringstream (oss);
564
0
    formatRelativeTimestamp (oss, event);
565
    // oss.str ().swap (result);
566
0
    result = OFString(oss.str().c_str(), oss.str().length());
567
0
}
568
569
570
////////////////////////////////////////////////
571
// HostnamePatternConverter methods:
572
////////////////////////////////////////////////
573
574
HostnamePatternConverter::HostnamePatternConverter (
575
    const FormattingInfo& info, bool fqdn)
576
0
    : PatternConverter(info)
577
0
    , hostname_ (helpers::getHostname (fqdn))
578
0
{ }
579
580
581
void
582
HostnamePatternConverter::convert (
583
    tstring & result, const spi::InternalLoggingEvent&)
584
0
{
585
0
    result = hostname_;
586
0
}
587
588
589
590
////////////////////////////////////////////////
591
// MDCPatternConverter methods:
592
////////////////////////////////////////////////
593
594
log4cplus::pattern::MDCPatternConverter::MDCPatternConverter (
595
    const FormattingInfo& info, tstring const & k)
596
0
    : PatternConverter(info)
597
0
    , key (k)
598
0
{ }
599
600
601
void
602
log4cplus::pattern::MDCPatternConverter::convert (tstring & result,
603
    const spi::InternalLoggingEvent& event)
604
0
{
605
0
    result = event.getMDC (key);
606
0
}
607
608
609
////////////////////////////////////////////////
610
// NDCPatternConverter methods:
611
////////////////////////////////////////////////
612
613
log4cplus::pattern::NDCPatternConverter::NDCPatternConverter (
614
    const FormattingInfo& info, int precision_)
615
0
    : PatternConverter(info)
616
0
    , precision(precision_)
617
0
{ }
618
619
620
void
621
log4cplus::pattern::NDCPatternConverter::convert (tstring & result,
622
    const spi::InternalLoggingEvent& event)
623
0
{
624
0
    const log4cplus::tstring& text = event.getNDC();
625
0
    if (precision <= 0)
626
0
        result = text;
627
0
    else
628
0
    {
629
0
        tstring::size_type p = text.find(DCMTK_LOG4CPLUS_TEXT(' '));
630
0
        for (int i = 1; i < precision && p != OFString_npos; ++i)
631
0
            p = text.find(DCMTK_LOG4CPLUS_TEXT(' '), p + 1);
632
633
0
        result = text.substr(0, p);
634
0
    }
635
0
}
636
637
638
639
////////////////////////////////////////////////
640
// PatternParser methods:
641
////////////////////////////////////////////////
642
643
PatternParser::PatternParser(
644
    const tstring& pattern_, unsigned ndcMaxDepth_)
645
3
    : pattern(pattern_)
646
3
    , formattingInfo()
647
3
    , list()
648
3
    , state(LITERAL_STATE)
649
3
    , pos(0)
650
3
    , currentLiteral()
651
3
    , ndcMaxDepth (ndcMaxDepth_)
652
3
{
653
3
}
654
655
656
657
tstring
658
PatternParser::extractOption()
659
0
{
660
0
    if (   (pos < pattern.length())
661
0
        && (pattern[pos] == DCMTK_LOG4CPLUS_TEXT('{')))
662
0
    {
663
0
        tstring::size_type end = pattern.find_first_of(DCMTK_LOG4CPLUS_TEXT('}'), pos);
664
0
        if (end != OFString_npos) {
665
0
            tstring r = pattern.substr(pos + 1, end - pos - 1);
666
0
            pos = end + 1;
667
0
            return r;
668
0
        }
669
0
        else {
670
0
            log4cplus::tostringstream buf;
671
0
            buf << DCMTK_LOG4CPLUS_TEXT("No matching '}' found in conversion pattern string \"")
672
0
                << pattern
673
0
                << DCMTK_LOG4CPLUS_TEXT("\"");
674
0
            helpers::getLogLog().error(OFString(buf.str().c_str(), buf.str().length()));
675
0
            pos = pattern.length();
676
0
        }
677
0
    }
678
679
0
    return DCMTK_LOG4CPLUS_TEXT("");
680
0
}
681
682
683
int
684
PatternParser::extractPrecisionOption()
685
0
{
686
0
    tstring opt = extractOption();
687
0
    int r = 0;
688
0
    if (! opt.empty ())
689
0
        r = atoi(DCMTK_LOG4CPLUS_TSTRING_TO_STRING(opt).c_str());
690
691
0
    return r;
692
0
}
693
694
695
696
PatternConverterList
697
PatternParser::parse()
698
3
{
699
3
    tchar c;
700
3
    pos = 0;
701
27
    while(pos < pattern.length()) {
702
24
        c = pattern[pos++];
703
24
        switch (state) {
704
15
        case LITERAL_STATE :
705
            // In literal state, the last char is always a literal.
706
15
            if(pos == pattern.length()) {
707
0
                currentLiteral += c;
708
0
                continue;
709
0
            }
710
15
            if(c == ESCAPE_CHAR) {
711
                // peek at the next char.
712
9
                switch (pattern[pos]) {
713
0
                case ESCAPE_CHAR:
714
0
                    currentLiteral += c;
715
0
                    pos++; // move pointer
716
0
                    break;
717
9
                default:
718
9
                    if(! currentLiteral.empty ()) {
719
3
                        list.push_back
720
3
                             (new LiteralPatternConverter(currentLiteral));
721
                        //getLogLog().debug("Parsed LITERAL converter: \""
722
                        //                  +currentLiteral+"\".");
723
3
                    }
724
9
                    currentLiteral.resize(0);
725
9
                    currentLiteral += c; // append %
726
9
                    state = CONVERTER_STATE;
727
9
                    formattingInfo.reset();
728
9
                }
729
9
            }
730
6
            else {
731
6
                currentLiteral += c;
732
6
            }
733
15
            break;
734
735
15
        case CONVERTER_STATE:
736
9
            currentLiteral += c;
737
9
            switch (c) {
738
0
            case DCMTK_LOG4CPLUS_TEXT('-'):
739
0
                formattingInfo.leftAlign = true;
740
0
                break;
741
0
            case DCMTK_LOG4CPLUS_TEXT('.'):
742
0
                state = DOT_STATE;
743
0
                break;
744
9
            default:
745
9
                if(c >= DCMTK_LOG4CPLUS_TEXT('0') && c <= DCMTK_LOG4CPLUS_TEXT('9')) {
746
0
                    formattingInfo.minLen = c - DCMTK_LOG4CPLUS_TEXT('0');
747
0
                    state = MIN_STATE;
748
0
                }
749
9
                else {
750
9
                    finalizeConverter(c);
751
9
                }
752
9
            } // switch
753
9
            break;
754
755
9
        case MIN_STATE:
756
0
            currentLiteral += c;
757
0
            if (c >= DCMTK_LOG4CPLUS_TEXT('0') && c <= DCMTK_LOG4CPLUS_TEXT('9')) {
758
0
                formattingInfo.minLen = formattingInfo.minLen * 10 + (c - DCMTK_LOG4CPLUS_TEXT('0'));
759
0
            }
760
0
            else if(c == DCMTK_LOG4CPLUS_TEXT('.')) {
761
0
                state = DOT_STATE;
762
0
            }
763
0
            else {
764
0
                finalizeConverter(c);
765
0
            }
766
0
            break;
767
768
0
        case DOT_STATE:
769
0
            currentLiteral += c;
770
0
            if(c >= DCMTK_LOG4CPLUS_TEXT('0') && c <= DCMTK_LOG4CPLUS_TEXT('9')) {
771
0
                formattingInfo.maxLen = c - DCMTK_LOG4CPLUS_TEXT('0');
772
0
                state = MAX_STATE;
773
0
            }
774
0
            else {
775
0
                tostringstream buf;
776
0
                buf << DCMTK_LOG4CPLUS_TEXT("Error occurred in position ")
777
0
                    << pos
778
0
                    << DCMTK_LOG4CPLUS_TEXT(".\n Was expecting digit, instead got char \"")
779
0
                    << c
780
0
                    << DCMTK_LOG4CPLUS_TEXT("\".");
781
0
                helpers::getLogLog().error(OFString(buf.str().c_str(), buf.str().length()));
782
0
                state = LITERAL_STATE;
783
0
            }
784
0
            break;
785
786
0
         case MAX_STATE:
787
0
            currentLiteral += c;
788
0
            if (c >= DCMTK_LOG4CPLUS_TEXT('0') && c <= DCMTK_LOG4CPLUS_TEXT('9'))
789
0
                formattingInfo.maxLen = formattingInfo.maxLen * 10 + (c - DCMTK_LOG4CPLUS_TEXT('0'));
790
0
            else {
791
0
                finalizeConverter(c);
792
0
                state = LITERAL_STATE;
793
0
            }
794
0
            break;
795
24
        } // end switch
796
24
    } // end while
797
798
3
    if(! currentLiteral.empty ()) {
799
0
        list.push_back(new LiteralPatternConverter(currentLiteral));
800
      //getLogLog().debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
801
0
    }
802
803
3
    return list;
804
3
}
805
806
807
808
void
809
PatternParser::finalizeConverter(tchar c)
810
9
{
811
9
    PatternConverter* pc = 0;
812
9
    switch (c) {
813
0
        case DCMTK_LOG4CPLUS_TEXT('b'):
814
0
            pc = new BasicPatternConverter
815
0
                          (formattingInfo,
816
0
                           BasicPatternConverter::BASENAME_CONVERTER);
817
            //getLogLog().debug("BASENAME converter.");
818
            //formattingInfo.dump(getLogLog());
819
0
            break;
820
821
0
        case DCMTK_LOG4CPLUS_TEXT('c'):
822
0
            pc = new LoggerPatternConverter(formattingInfo,
823
0
                                            extractPrecisionOption());
824
            //getLogLog().debug( DCMTK_LOG4CPLUS_TEXT("LOGGER converter.") );
825
            //formattingInfo.dump(getLogLog());
826
0
            break;
827
828
0
        case DCMTK_LOG4CPLUS_TEXT('d'):
829
0
        case DCMTK_LOG4CPLUS_TEXT('D'):
830
0
            {
831
0
                tstring dOpt = extractOption();
832
0
                if(dOpt.empty ()) {
833
0
                    dOpt = DCMTK_LOG4CPLUS_TEXT("%Y-%m-%d %H:%M:%S");
834
0
                }
835
0
                bool use_gmtime = c == DCMTK_LOG4CPLUS_TEXT('d');
836
0
                pc = new DatePatternConverter(formattingInfo, dOpt, use_gmtime);
837
                //if(use_gmtime) {
838
                //    getLogLog().debug("GMT DATE converter.");
839
                //}
840
                //else {
841
                //    getLogLog().debug("LOCAL DATE converter.");
842
                //}
843
                //formattingInfo.dump(getLogLog());
844
0
            }
845
0
            break;
846
847
0
        case DCMTK_LOG4CPLUS_TEXT('F'):
848
0
            pc = new BasicPatternConverter
849
0
                          (formattingInfo,
850
0
                           BasicPatternConverter::FILE_CONVERTER);
851
            //getLogLog().debug("FILE NAME converter.");
852
            //formattingInfo.dump(getLogLog());
853
0
            break;
854
855
0
        case DCMTK_LOG4CPLUS_TEXT('h'):
856
0
        case DCMTK_LOG4CPLUS_TEXT('H'):
857
0
            {
858
0
                bool fqdn = (c == DCMTK_LOG4CPLUS_TEXT('H'));
859
0
                pc = new HostnamePatternConverter(formattingInfo, fqdn);
860
                // getLogLog().debug( DCMTK_LOG4CPLUS_TEXT("HOSTNAME converter.") );
861
                // formattingInfo.dump(getLogLog());
862
0
            }
863
0
            break;
864
865
0
        case DCMTK_LOG4CPLUS_TEXT('i'):
866
0
            pc = new BasicPatternConverter
867
0
                          (formattingInfo,
868
0
                           BasicPatternConverter::PROCESS_CONVERTER);
869
            //getLogLog().debug("PROCESS_CONVERTER converter.");
870
            //formattingInfo.dump(getLogLog());
871
0
            break;
872
873
0
        case DCMTK_LOG4CPLUS_TEXT('l'):
874
0
            pc = new BasicPatternConverter
875
0
                          (formattingInfo,
876
0
                           BasicPatternConverter::FULL_LOCATION_CONVERTER);
877
            //getLogLog().debug("FULL LOCATION converter.");
878
            //formattingInfo.dump(getLogLog());
879
0
            break;
880
881
0
        case DCMTK_LOG4CPLUS_TEXT('L'):
882
0
            pc = new BasicPatternConverter
883
0
                          (formattingInfo,
884
0
                           BasicPatternConverter::LINE_CONVERTER);
885
            //getLogLog().debug("LINE NUMBER converter.");
886
            //formattingInfo.dump(getLogLog());
887
0
            break;
888
889
3
        case DCMTK_LOG4CPLUS_TEXT('m'):
890
3
            pc = new BasicPatternConverter
891
3
                          (formattingInfo,
892
3
                           BasicPatternConverter::MESSAGE_CONVERTER);
893
            //getLogLog().debug("MESSAGE converter.");
894
            //formattingInfo.dump(getLogLog());
895
3
            break;
896
897
0
        case DCMTK_LOG4CPLUS_TEXT('M'):
898
0
            pc = new BasicPatternConverter (
899
0
                formattingInfo, BasicPatternConverter::FUNCTION_CONVERTER);
900
            //getLogLog().debug("METHOD (function name) converter.");
901
            //formattingInfo.dump(getLogLog());
902
0
            break;
903
904
3
        case DCMTK_LOG4CPLUS_TEXT('n'):
905
3
            pc = new BasicPatternConverter
906
3
                          (formattingInfo,
907
3
                           BasicPatternConverter::NEWLINE_CONVERTER);
908
            //getLogLog().debug("MESSAGE converter.");
909
            //formattingInfo.dump(getLogLog());
910
3
            break;
911
912
0
        case DCMTK_LOG4CPLUS_TEXT('p'):
913
0
            pc = new BasicPatternConverter
914
0
                          (formattingInfo,
915
0
                           BasicPatternConverter::LOGLEVEL_CONVERTER);
916
            //getLogLog().debug("LOGLEVEL converter.");
917
            //formattingInfo.dump(getLogLog());
918
0
            break;
919
920
3
        case DCMTK_LOG4CPLUS_TEXT('P'):
921
3
            pc = new BasicPatternConverter
922
3
                          (formattingInfo,
923
3
                           BasicPatternConverter::LOGLEVEL_PREFIX_CONVERTER);
924
            //getLogLog().debug("LOGLEVEL converter.");
925
            //formattingInfo.dump(getLogLog());
926
3
            break;
927
928
0
        case DCMTK_LOG4CPLUS_TEXT('r'):
929
0
            pc = new RelativeTimestampConverter (formattingInfo);
930
            //getLogLog().debug("RELATIVE converter.");
931
            //formattingInfo.dump(getLogLog());
932
0
            break;
933
934
0
        case DCMTK_LOG4CPLUS_TEXT('t'):
935
0
            pc = new BasicPatternConverter
936
0
                          (formattingInfo,
937
0
                           BasicPatternConverter::THREAD_CONVERTER);
938
            //getLogLog().debug("THREAD converter.");
939
            //formattingInfo.dump(getLogLog());
940
0
            break;
941
942
0
        case DCMTK_LOG4CPLUS_TEXT('T'):
943
0
            pc = new BasicPatternConverter
944
0
                          (formattingInfo,
945
0
                           BasicPatternConverter::THREAD2_CONVERTER);
946
            //getLogLog().debug("THREAD2 converter.");
947
            //formattingInfo.dump(getLogLog());
948
0
            break;
949
950
0
        case DCMTK_LOG4CPLUS_TEXT('x'):
951
0
            pc = new NDCPatternConverter (formattingInfo, ndcMaxDepth);
952
            //getLogLog().debug("NDC converter.");
953
0
            break;
954
955
0
        case DCMTK_LOG4CPLUS_TEXT('X'):
956
0
            pc = new MDCPatternConverter (formattingInfo, extractOption ());
957
            //getLogLog().debug("MDC converter.");
958
0
            break;
959
960
0
        default:
961
0
            tostringstream buf;
962
0
            buf << DCMTK_LOG4CPLUS_TEXT("Unexpected char [")
963
0
                << c
964
0
                << DCMTK_LOG4CPLUS_TEXT("] at position ")
965
0
                << pos
966
0
                << DCMTK_LOG4CPLUS_TEXT(" in conversion pattern.");
967
0
            helpers::getLogLog().error(OFString(buf.str().c_str(), buf.str().length()));
968
0
            pc = new LiteralPatternConverter(currentLiteral);
969
9
    }
970
971
9
    list.push_back(pc);
972
9
    currentLiteral.resize(0);
973
9
    state = LITERAL_STATE;
974
9
    formattingInfo.reset();
975
9
}
976
977
978
} // namespace pattern
979
980
981
typedef pattern::PatternConverterList PatternConverterList;
982
983
984
////////////////////////////////////////////////
985
// PatternLayout methods:
986
////////////////////////////////////////////////
987
988
PatternLayout::PatternLayout(const tstring& pattern_, bool formatEachLine_)
989
3
    : pattern()
990
    , formatEachLine()
991
3
    , parsedPattern()
992
3
{
993
3
    init(pattern_, formatEachLine_, 0);
994
3
}
995
996
997
PatternLayout::PatternLayout(const helpers::Properties& properties)
998
0
    : pattern()
999
    , formatEachLine()
1000
0
    , parsedPattern()
1001
0
{
1002
0
    unsigned ndcMaxDepth = 0;
1003
0
    bool formatEachLine_ = true;
1004
0
    properties.getUInt (ndcMaxDepth, DCMTK_LOG4CPLUS_TEXT ("NDCMaxDepth"));
1005
0
    properties.getBool(formatEachLine_, DCMTK_LOG4CPLUS_TEXT("FormatEachLine"));
1006
1007
0
    bool hasPattern = properties.exists( DCMTK_LOG4CPLUS_TEXT("Pattern") );
1008
0
    bool hasConversionPattern = properties.exists( DCMTK_LOG4CPLUS_TEXT("ConversionPattern") );
1009
1010
0
    if(hasPattern) {
1011
0
        helpers::getLogLog().warn(
1012
0
            DCMTK_LOG4CPLUS_TEXT("PatternLayout- the \"Pattern\" property has been")
1013
0
            DCMTK_LOG4CPLUS_TEXT(" deprecated.  Use \"ConversionPattern\" instead."));
1014
0
    }
1015
1016
0
    if(hasConversionPattern) {
1017
0
        init(properties.getProperty( DCMTK_LOG4CPLUS_TEXT("ConversionPattern") ), formatEachLine_,
1018
0
            ndcMaxDepth);
1019
0
    }
1020
0
    else if(hasPattern) {
1021
0
        init(properties.getProperty( DCMTK_LOG4CPLUS_TEXT("Pattern") ), formatEachLine_, ndcMaxDepth);
1022
0
    }
1023
0
    else {
1024
0
        helpers::getLogLog().error(
1025
0
            DCMTK_LOG4CPLUS_TEXT ("ConversionPattern not specified in properties"),
1026
0
            true);
1027
0
    }
1028
1029
0
}
1030
1031
1032
void
1033
PatternLayout::init(const tstring& pattern_, bool formatEachLine_, unsigned ndcMaxDepth)
1034
3
{
1035
3
    pattern = pattern_;
1036
3
    formatEachLine = formatEachLine_;
1037
3
    parsedPattern = pattern::PatternParser(pattern, ndcMaxDepth).parse();
1038
1039
    // Let's validate that our parser didn't give us any NULLs.  If it did,
1040
    // we will convert them to a valid PatternConverter that does nothing so
1041
    // at least we don't core.
1042
3
    for(PatternConverterList::iterator it=parsedPattern.begin();
1043
15
        it!=parsedPattern.end();
1044
12
        ++it)
1045
12
    {
1046
12
        if( (*it) == 0 ) {
1047
0
            helpers::getLogLog().error(
1048
0
                DCMTK_LOG4CPLUS_TEXT("Parsed Pattern created a NULL PatternConverter"));
1049
0
            (*it) = new pattern::LiteralPatternConverter( DCMTK_LOG4CPLUS_TEXT("") );
1050
0
        }
1051
12
    }
1052
3
    if(parsedPattern.empty ()) {
1053
0
        helpers::getLogLog().warn(
1054
0
            DCMTK_LOG4CPLUS_TEXT("PatternLayout pattern is empty.  Using default..."));
1055
0
        parsedPattern.push_back (
1056
0
            new pattern::BasicPatternConverter(pattern::FormattingInfo(),
1057
0
            pattern::BasicPatternConverter::MESSAGE_CONVERTER));
1058
0
    }
1059
3
}
1060
1061
1062
1063
PatternLayout::~PatternLayout()
1064
0
{
1065
0
    for(PatternConverterList::iterator it=parsedPattern.begin();
1066
0
        it!=parsedPattern.end();
1067
0
        ++it)
1068
0
    {
1069
0
        delete (*it);
1070
0
    }
1071
0
}
1072
1073
1074
1075
void
1076
PatternLayout::formatAndAppend(tostream& output,
1077
                               const spi::InternalLoggingEvent& event)
1078
0
{
1079
0
    if (formatEachLine && event.getMessage().find('\n') != OFString_npos)
1080
0
    {
1081
0
        size_t pos = 0;
1082
0
        size_t last_pos = 0;
1083
1084
0
        while (pos != OFString_npos)
1085
0
        {
1086
0
            pos = event.getMessage().find('\n', last_pos);
1087
1088
            // Create a substring from just this single line
1089
0
            tstring tmp_message(event.getMessage().substr(last_pos, (pos == OFString_npos) ? pos : pos - last_pos));
1090
1091
            // Then create a temporary InternalLoggingEvent for this one line
1092
0
            spi::InternalLoggingEvent tmp_event(event.getLoggerName(), event.getLogLevel(),
1093
0
                event.getNDC(), event.getMDCCopy(), tmp_message, event.getThread(),
1094
0
                event.getTimestamp(), event.getFile(), event.getLine());
1095
0
            tmp_event.setFunction(event.getFunction());
1096
1097
            // And finally, log this single line
1098
0
            formatAndAppend(output, tmp_event);
1099
1100
            // Skip the "\n"
1101
0
            last_pos = pos + 1;
1102
0
        }
1103
0
    }
1104
0
    else
1105
0
    {
1106
0
        for(PatternConverterList::iterator it=parsedPattern.begin();
1107
0
            it!=parsedPattern.end();
1108
0
            ++it)
1109
0
        {
1110
0
            (*it)->formatAndAppend(output, event);
1111
0
        }
1112
0
    }
1113
0
}
1114
1115
1116
} // namespace log4cplus
1117
} // end namespace dcmtk