Enrollment 28.2.0
Loading...
Searching...
No Matches
FormattingLogger.h
Go to the documentation of this file.
1
9
10#pragma once
11
15#include <array>
16#include <charconv>
17#include <cstring>
18#include <functional>
19#include <string>
20#include <type_traits>
21#include <utility>
22
23namespace inno
24{
34 */
35 class FormattingComposer : public LogMessage
36 {
37 public:
45 template<typename... Args>
46 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - false positive
47 explicit FormattingComposer(const char* format, Args&&... arguments)
48 : compose(
49 [this, format, capturedArgs = std::forward_as_tuple(arguments...)]() -> const char*
50 {
51 message.clear();
52 try
53 {
54 std::apply(
55 [this, format](auto&&... args)
56 {
57 LogWriter writer(message);
58 Printf(message, writer, 0, format, std::forward<Args>(args)...);
59 },
60 std::move(capturedArgs));
61 }
62 catch (const std::exception& e)
63 {
64 message += "<exception during logging: ";
65 message += e.what();
66 message += ">";
67 }
68 return message.c_str();
69 })
70 {
71 }
72
76 */
77 const char* Compose() final
78 {
79 return compose();
80 }
81
82 FormattingComposer() = delete;
85 FormattingComposer& operator=(const FormattingComposer&) = delete;
86 FormattingComposer& operator=(FormattingComposer&&) = delete;
87 ~FormattingComposer() = default;
88
89 private:
90 std::string message;
91 std::function<const char*()> compose;
92
93 class LogWriter : public Writer
94 {
95 public:
96 void Write(const char* n, const Serializable& s) final
97 {
98 if (arrayIndex > 0 && objectIndex == 0)
99 {
100 arraySize[arrayIndex - 1]++;
101
102 if (arraySize[arrayIndex - 1] > actualMaxArraySize)
103 {
104 return;
105 }
106
107 isValueInObject[arrayIndex - 1] = true;
108 }
109 objectIndex++;
110 auto name = std::string_view(n);
111 text += name;
112 if (!name.empty())
113 {
114 text += ":";
115 }
116 text += "{";
117 s.WriteTo(*this);
118 objectIndex--;
119 // remove last object member delimiter
120 text.erase(text.end() - 1, text.end());
121 text += "}";
122 if (objectIndex > 0)
123 {
124 text += ObjectValueDelimiter;
125 }
126 if (arrayIndex > 0 && objectIndex == 0)
127 {
128 isValueInObject[arrayIndex - 1] = false;
129 text += ArrayValueDelimiter;
130 }
131 }
132 void WriteRoot(Serializable& /*s*/) final
133 {
134 // not used
135 }
136 void Write(const char* name, const char* value) final
137 {
138 // if we are in array (or in array of array ...) then
139 // append ArrayDelimiter after each element
140 // reduce array size to actualMaxArraySize
141 if (arrayIndex > 0)
142 {
143 // Treat first object in array as one element, regardless how many other object it contains
144 // Treat value outside of object as one element
145 if (!isValueInObject[arrayIndex - 1])
146 {
147 arraySize[arrayIndex - 1]++;
148 }
149 // Do not write more than actualMaxArraySize elements
150 if (arraySize[arrayIndex - 1] <= actualMaxArraySize)
151 {
152 WriteString(name, value);
153 text += ArrayValueDelimiter;
154 }
155 }
156 else
157 {
158 // Value is not array element
159 WriteString(name, value);
160 // if value is part of object, then append ObjectValueDelimiter
161 if (objectIndex > 0)
162 {
163 // add object member delimiter
164 text += ObjectValueDelimiter;
165 }
166 }
167 }
168 void WriteNull(const char* name) final
169 {
170 Write(name, "null");
171 }
172 void Write(const char* name, int value) final
173 {
174 Write(name, std::to_string(value).c_str());
175 }
176 void Write(const char* name, unsigned int value) final
177 {
178 Write(name, std::to_string(value).c_str());
179 }
180 void Write(const char* name, double value) final
181 {
182 Write(name, std::to_string(value).c_str());
183 }
184 void Write(const char* name, int64_t value) final
185 {
186 Write(name, std::to_string(value).c_str());
187 }
188 void Write(const char* name, uint64_t value) final
189 {
190 Write(name, std::to_string(value).c_str());
191 }
192 void Write(const char* name, const std::shared_ptr<Image>& value) final
193 {
194 if (value != nullptr)
195 {
196 const std::string imageDescription =
197 "[w:" + std::to_string(value->Width()) + " h:" + std::to_string(value->Height()) +
198 " d:" + std::to_string(static_cast<unsigned int>(value->Dpi())) + "]";
199 Write(name, imageDescription.c_str());
200 }
201 else
202 {
203 WriteNull(name);
204 }
205 }
206
207 LogWriter(const LogWriter&) = delete;
208 LogWriter(LogWriter&&) = delete;
209 LogWriter& operator=(const LogWriter&) = delete;
210 LogWriter& operator=(LogWriter&&) = delete;
211 LogWriter() = delete;
212 explicit LogWriter(std::string& t)
213 : actualMaxArraySize(MaxArraySize)
214 , text(t)
215 , arrayIndex(0)
216 , objectIndex(0)
217 {
218 }
219 virtual ~LogWriter() = default;
220
221 static const unsigned int MaxArraySize = 10;
222 // The LogWriter is private, helper class for FormattingComposer. The FormattingComposer is responsible to
223 // write to this variable.
224 // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes,misc-non-private-member-variables-in-classes)
225 unsigned int actualMaxArraySize;
226
227 private:
228 // The LogWriter is private, helper class for FormattingComposer. The FormattingComposer is responsible to
229 // keep text reference valid for whole lifetime of LogWriter
230 // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
231 std::string& text;
232 unsigned int arrayIndex;
233 std::vector<unsigned int> arraySize;
234 std::vector<bool> isValueInObject;
235 unsigned int objectIndex;
236 static const char ObjectValueDelimiter = ' ';
237 static const char ArrayValueDelimiter = ',';
238
239 void StartArray(const char* n) final
240 {
241 auto name = std::string_view(n);
242
243 if (not name.empty())
244 {
245 text += name;
246 text += ":";
247 }
248
249 arraySize.insert(arraySize.begin() + arrayIndex, 0);
250 isValueInObject.insert(isValueInObject.begin() + arrayIndex, false);
251 text += "[";
252 arrayIndex++;
253 }
254 void EndArray() final
255 {
256 // remove last ArrayValueDelimiter if any
257 if (text[text.length() - 1] == ',')
258 {
259 text.erase(text.end() - 1, text.end());
260 }
261 arrayIndex--;
262 // if arrays is not fully written, then write also array size
263 if (arraySize[arrayIndex] > actualMaxArraySize)
264 {
265 text += ArrayValueDelimiter;
266 text += "...(" + std::to_string(arraySize[arrayIndex]) + ")";
267 }
268 arraySize[arrayIndex] = 0;
269 // close array
270 text += "]";
271 // if we are still in array (in case of array of array) then array element must be delimited
272 if (arrayIndex > 0)
273 {
274 text += ArrayValueDelimiter;
275 }
276 else
277 {
278 if (objectIndex > 0)
279 {
280 // add object member delimiter
281 text += ObjectValueDelimiter;
282 }
283 }
284 }
285
286 void WriteString(const char* n, const char* v)
287 {
288 auto name = std::string_view(n);
289 auto value = std::string_view(v);
290
291 if (not name.empty())
292 {
293 text += name;
294 text += ":";
295 }
296 text += value;
297 }
298 };
299
300 template<typename T>
301 requires IsIterable<T>
302 void WriteInWriter(Writer& writer, T& value) const
303 {
304 writer.WriteArray("", value);
305 }
306 template<typename T>
307 requires(IsNotIterable<T> && !std::is_scalar_v<T>)
308 void WriteInWriter(Writer& writer, T& value) const
309 {
310 writer.Write(static_cast<const char*>(""), value);
311 }
312
313 // Allow call to writer for exact type that writer supports. To use various type in Formatting logger that can
314 // produce Ambiguous function call, compiler error.
315 template<typename T>
316 requires(std::is_scalar_v<T> &&
317 (std::is_same_v<T, const char*> || std::is_same_v<T, int> || std::is_same_v<T, unsigned int> ||
318 std::is_same_v<T, double> || std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>))
319 void WriteInWriter(Writer& writer, T& value) const
320 {
321 writer.Write("", value);
322 }
323
324 // Ignore call to writer for not exact type to use various type in Formatting logger that can produce Ambiguous
325 // function call, compiler error.
326 template<typename T>
327 requires(std::is_scalar_v<T> &&
328 !(std::is_same_v<T, const char*> || std::is_same_v<T, int> || std::is_same_v<T, unsigned int> ||
329 std::is_same_v<T, double> || std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>))
330 void WriteInWriter(Writer& writer, T& /*value*/) const
331 {
332 writer.Write("", "??");
333 }
334
335 // Even if it can be static, the function is used as overload for Printf function.
336 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
337 void Printf(std::string& text, LogWriter& /*writer*/, int /*ignoreCount*/, const char* format) const
338 {
339 text += format;
340 }
341
342 template<typename T, typename... Args>
343 // The clang-tidy generates false positive for value. The value is used in function body.
344 // NOLINTNEXTLINE(misc-unused-parameters)
345 void Printf(std::string& text,
346 LogWriter& writer,
347 int ignoreCount,
348 const char* format,
349 T&& value,
350 Args&&... args) const
351 {
352 if (ignoreCount > 0)
353 {
354 // Move to next argument. The value will be next argument.
355 Printf(text, writer, ignoreCount - 1, format, args...);
356 return;
357 }
358
359 int stars = 0;
360
361 for (const auto* formatIt = format; *formatIt != '\0'; std::advance(formatIt, 1))
362 {
363 if (*formatIt == '%')
364 {
365 auto sprintfFormatArray = GetFormatForOneValueAndMoveToNext(formatIt);
366 auto sprintfFormat = std::string_view(sprintfFormatArray.data());
367
368 if (!sprintfFormat.empty())
369 {
370 if (IsWriter(sprintfFormat.back()))
371 {
372 auto arrayLength = GetWriterArrayLength(sprintfFormat);
373
374 ComposeInWriter(writer, arrayLength, value);
375 }
376 else if (IsPercentage(sprintfFormat.at(1)))
377 {
378 text += "%";
379 continue;
380 }
381 else
382 {
383 // Add format specifier
384 stars = GetStarsInFormat(sprintfFormat);
385 auto sprintfText =
386 ComposeInPrintf(sprintfFormat, stars, value, std::forward<Args>(args)...);
387 text += sprintfText.data();
388 }
389 }
390 else
391 {
392 // error, too long format specifier, skip the value
393 }
394
395 Printf(text, writer, stars, std::next(formatIt, 1), args...);
396 return;
397 }
398 text += *formatIt;
399 }
400 }
401
402 static constexpr int MaxSprintfFormatSize = 21;
403 static std::array<char, MaxSprintfFormatSize> GetFormatForOneValueAndMoveToNext(const char*& formatIt)
404 {
405 std::advance(formatIt, 1);
406 static constexpr int MaxSprintfFormatLength = MaxSprintfFormatSize - 1;
407 std::array<char, MaxSprintfFormatLength + 1> sprintfFormat = {};
408 sprintfFormat[0] = '%';
409 int i = 1;
410 for (; *formatIt != '\0' && !IsSpecifier(*formatIt); std::advance(formatIt, 1), i++)
411 {
412 if (i < MaxSprintfFormatLength)
413 {
414 sprintfFormat.at(i) = *formatIt;
415 }
416 }
417 // Append format specifier
418 if (*formatIt != '\0' && i <= MaxSprintfFormatLength)
419 {
420 // save specifier
421 sprintfFormat.at(i++) = *formatIt;
422 sprintfFormat.at(i) = '\0';
423 }
424
425 if (i > MaxSprintfFormatLength)
426 {
427 // format specifier is too long
428 // discard the value
429 sprintfFormat.at(0) = '\0';
430 }
431
432 return sprintfFormat;
433 }
434
435 static unsigned int GetWriterArrayLength(std::string_view format)
436 {
437 // check if array element is given, not only format specifier '%v'
438 if (format.length() <= 2)
439 {
440 return LogWriter::MaxArraySize;
441 }
442
443 int i = 0;
444 static constexpr int Base = 10;
445 const char* start = std::next(format.data(), 1);
446 const char* end = std::next(format.data(), static_cast<ptrdiff_t>(format.length() - 1));
447 std::from_chars(start, end, i, Base);
448 return i;
449 }
450
451 template<typename T>
452 void ComposeInWriter(LogWriter& writer, unsigned int arrayLength, T&& value) const
453 {
454 writer.actualMaxArraySize = arrayLength;
455 WriteInWriter(writer, std::forward<T>(value));
456 }
457
458 static int GetStarsInFormat(std::string_view format)
459 {
460 int stars = 0;
461
462 for (const auto c : format)
463 {
464 if (c == '*')
465 {
466 stars++;
467 }
468 }
469 return stars;
470 }
471
472 static constexpr int MaxSprintfTextSize = 101;
473 template<typename T, typename... Args>
474 std::array<char, MaxSprintfTextSize> ComposeInPrintf(
475 std::string_view& format,
476 int stars,
477 T& value,
478 Args&&... args) // NOLINT(cppcoreguidelines-missing-std-forward)
479 // It is unpacked and used as arguments in WriteInSprintf in case of
480 // width/precision is given as variable in variadic parameters
481 // e.g %*d
482 // When std::forward is used, then incorrect WriteInSprintf function is used
483 // and unit tests fails
484 const
485 {
486 static constexpr int MaxSprintfTextLength = MaxSprintfTextSize - 1;
487 auto sprintfText = std::array<char, MaxSprintfTextSize>{};
488 int totalLength = 0;
489
490 // It is not same
491 // NOLINTNEXTLINE(bugprone-branch-clone)
492 if (stars == 0)
493 {
494 totalLength = WriteInSprintf(sprintfText.data(), MaxSprintfTextLength, format.data(), value);
495 }
496 else
497 {
498 // It is false positive of clang-tidy
499 // NOLINTNEXTLINE(readability-suspicious-call-argument)
500 totalLength = WriteInSprintf(sprintfText.data(), MaxSprintfTextLength, format.data(), value, args...);
501 }
502
503 if (totalLength > MaxSprintfTextLength)
504 {
505 sprintfText[MaxSprintfTextLength - 1] = '.';
506 sprintfText[MaxSprintfTextLength - 2] = '.';
507 sprintfText[MaxSprintfTextLength - 3] = '.';
508 sprintfText[MaxSprintfTextLength] = '\0';
509 }
510 else
511 {
512 sprintfText.at(totalLength) = '\0';
513 }
514
515 return sprintfText;
516 }
517
518 template<typename T, typename... Args>
519 requires(std::is_trivially_copyable_v<T>)
520 int WriteInSprintf(char* sprintfText,
521 int sprintfTextSize,
522 const char* sprintfFormat,
523 T& value,
524 Args&&... /*args*/) // NOLINT(cppcoreguidelines-missing-std-forward) false positive
525 const
526 {
527 // We want to use all format specifiers.
528 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
529 return snprintf(sprintfText, sprintfTextSize, sprintfFormat, value);
530 }
531 template<typename W, typename T, typename... Args>
532 requires(std::is_trivially_copyable_v<T> && std::is_integral_v<W>)
533 int WriteInSprintf(char* sprintfText,
534 int sprintfTextSize,
535 const char* sprintfFormat,
536 W width,
537 T& value,
538 Args&&... /*args*/) // NOLINT(cppcoreguidelines-missing-std-forward) false positive
539 const
540 {
541 // We want to use all format specifiers.
542 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
543 return snprintf(sprintfText, sprintfTextSize, sprintfFormat, width, value);
544 }
545 template<typename W, typename P, typename T, typename... Args>
546 requires(std::is_trivially_copyable_v<T> && std::is_integral_v<W> && std::is_integral_v<P>)
547 int WriteInSprintf(char* sprintfText,
548 int sprintfTextSize,
549 const char* sprintfFormat,
550 W width,
551 P precision,
552 T& value,
553 Args&&... /*args*/) // NOLINT(cppcoreguidelines-missing-std-forward) false positive
554 const
555 {
556 // We want to use all format specifiers.
557 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
558 return snprintf(sprintfText, sprintfTextSize, sprintfFormat, width, precision, value);
559 }
560 template<typename T, typename... Args>
561 requires(!std::is_trivially_copyable_v<T>)
562 int WriteInSprintf(char* sprintfText,
563 int /*sprintfTextSize*/,
564 const char* /*sprintfFormat*/,
565 T& /*value*/,
566 Args&&... /*args*/) // NOLINT(cppcoreguidelines-missing-std-forward) false positive
567 const
568 {
569 // ignore non trivial type passed to snprintf
570 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
571 sprintfText[0] = '\0';
572 return 0;
573 }
574
575 static constexpr bool IsSpecifier(char sign)
576 {
577 return sign == 'd' || sign == 'i' || sign == 'u' || sign == 'o' || sign == 'x' || sign == 'X' ||
578 sign == 'f' || sign == 'F' || sign == 'e' || sign == 'E' || sign == 'g' || sign == 'G' ||
579 sign == 'a' || sign == 'A' || sign == 'c' || sign == 's' || sign == 'p' || sign == 'n' ||
580 sign == '%' || sign == 'v';
581 }
582 static constexpr bool IsWriter(char sign)
583 {
584 return sign == 'v';
585 }
586 static constexpr bool IsPercentage(char sign)
587 {
588 return sign == '%';
589 }
590 };
591
600 */
601 class FormattingLogger
602 {
603 public:
611 template<typename... Args>
612 void LogInfo(const char* format, Args&&... args) const
613 {
614 if (log != nullptr)
615 {
616 FormattingComposer composer(format, std::forward<Args>(args)...);
617 log->LogInfo(composer);
618 }
619 }
627 template<typename... Args>
628 void LogDebug(const char* format, Args&&... args) const
629 {
630 if (log != nullptr)
631 {
632 FormattingComposer composer(format, std::forward<Args>(args)...);
633 log->LogDebug(composer);
634 }
635 }
643 template<typename... Args>
644 void LogError(const char* format, Args&&... args) const
645 {
646 if (log != nullptr)
647 {
648 FormattingComposer composer(format, std::forward<Args>(args)...);
649 log->LogError(composer);
650 }
651 }
652
656 */
657 explicit FormattingLogger(std::shared_ptr<Logger> logger)
658 : log(std::move(logger))
659 {
660 }
661
666 */
667 [[nodiscard]] std::shared_ptr<Logger> GetLogger() const
668 {
669 return log;
670 }
671
672 FormattingLogger() = delete;
673 FormattingLogger(const FormattingLogger&) = delete;
675 FormattingLogger& operator=(const FormattingLogger&) = delete;
676 FormattingLogger& operator=(FormattingLogger&&) = delete;
677 ~FormattingLogger() = default;
678
679 private:
680 std::shared_ptr<Logger> log;
681 };
682} // namespace inno
Compose message from variadic arguments.
Definition FormattingLogger.h:35
const char * Compose() final
Compose message from stored references to variadic arguments.
Definition FormattingLogger.h:76
FormattingComposer(const char *format, Args &&... arguments)
Constructs FormattingComposer.
Definition FormattingLogger.h:46
It provides functions with format specifier to compose and log message via provided Logger.
Definition FormattingLogger.h:601
void LogInfo(const char *format, Args &&... args) const
Formats and logs message as info message.
Definition FormattingLogger.h:611
FormattingLogger(std::shared_ptr< Logger > logger)
Constructs FormattingLogger.
Definition FormattingLogger.h:656
void LogDebug(const char *format, Args &&... args) const
Formats and logs message as debug message.
Definition FormattingLogger.h:627
void LogError(const char *format, Args &&... args) const
Formats and logs message as error message.
Definition FormattingLogger.h:643
std::shared_ptr< Logger > GetLogger() const
Get the Logger object.
Definition FormattingLogger.h:666
Interface that MUST be implemented by all classes those want to be serialized.
Definition Writer.h:27
Provides interface for serialization of all classes.
Definition Writer.h:164
virtual void Write(const char *name, const Serializable &s)=0
Serialize serializable.
void WriteArray(const char *name, const std::vector< uint8_t > &value)
Serialize bytes from vector.
Definition Writer.h:233