Coverage Report

Created: 2026-05-16 07:13

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