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