/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 |