forked from nafur/l3pp
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathformatter.h
158 lines (134 loc) · 4.46 KB
/
formatter.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* @file formatter.h
* @author Harold Bruintjes <[email protected]>
*
* Define the Formatter class, used to format output of a sink
*/
#pragma once
#include <string>
#include <tuple>
namespace l3pp {
/**
* Formats a log messages. This is a base class that simply print the message
* with the log level prefix, see derived classes such as TemplatedFormatter
* for more interesting data.
*/
class Formatter {
friend class Logger;
static void initialize();
virtual std::string format(EntryContext const& context, std::string const& msg) const;
public:
virtual ~Formatter() {}
std::string operator()(EntryContext const& context, std::string const& msg) {
return format(context, msg);
}
};
typedef std::shared_ptr<Formatter> FormatterPtr;
/**
* Possible fields for FieldStr instance
*/
enum class Field {
/// Name of the file (everything following the last path separator)
FileName,
/// Full path of the file
FilePath,
/// Line number
Line,
/// Name of function currently executed
Function,
/// Name of the logger
LoggerName,
/// Message to be logged
Message,
/// Level of the log entry
LogLevel,
/// Number of milliseconds since the logger was initialized
WallTime,
};
/**
* Controls justification of formatted log fields.
*/
enum class Justification {
/// Left align field
LEFT,
/// Right align field
RIGHT
};
/**
* Formatter for log entry fields, with the exception of time stamp formatting
* (see TimeStr for that). The Field template argument determines which field
* is printed, see logging::Field.
* The other template arguments control the alignment of the output string.
*/
template<Field field, int Width = 0, Justification j = Justification::RIGHT, char Fill = ' '>
class FieldStr {
public:
void stream(std::ostream& os, EntryContext const& context, std::string const& msg) const;
};
/**
* Formatter for log time stamps. The constructor expects a single string
* argument which is a formatter for the time stamp. For the specification of
* this format string see the documentation for std::put_time . You can use for
* example "%c" or "%T".
* The template arguments control the alignment of the output string.
*/
class TimeStr {
std::string formatStr;
public:
TimeStr(char const* format) : formatStr(format) {
}
TimeStr(std::string const& format) : formatStr(format) {
}
void stream(std::ostream& os, EntryContext const& context, std::string const&) const;
};
/**
* Formatter which formats the output based on the (templated) arguments given.
* The arguments can be anything that implements the stream operator <<, but
* more interestingly also the various FormatField subclasses. These classes
* can output the various fields associated with a log entry.
*/
template<typename ... Formatters>
class TemplateFormatter : public Formatter {
std::tuple<Formatters...> formatters;
template <int N>
typename std::enable_if<N < (sizeof...(Formatters))>::type
formatTuple(EntryContext const& context, std::string const& msg, std::ostream& os) const {
formatElement(std::get<N>(formatters), os, context, msg);
formatTuple<N+1>(context, msg, os);
}
template <int N>
typename std::enable_if<(N >= sizeof...(Formatters))>::type
formatTuple(EntryContext const&, std::string const&, std::ostream&) const {
}
template<Field field, int Width, Justification j, char Fill>
void formatElement(FieldStr<field, Width, j, Fill> const& t, std::ostream& stream, EntryContext const& context, std::string const& msg) const {
t.stream(stream, context, msg);
}
void formatElement(TimeStr const& t, std::ostream& stream, EntryContext const& context, std::string const& msg) const {
t.stream(stream, context, msg);
}
template<typename T>
void formatElement(T const& t, std::ostream& stream, EntryContext const&, std::string const&) const {
stream << t;
}
public:
TemplateFormatter(Formatters ... formatters) :
formatters(std::forward<Formatters>(formatters)...)
{
}
std::string format(EntryContext const& context, std::string const& msg) const override;
};
/**
* Helper function to create a TemplateFormatter. Simply call with some
* formatable arguments, e.g.,
* @code{.cpp}
* logging::makeTemplateFormatter(
* logging::FieldStr<logging::Field::LogLevel>(), " - ",
* logging::FieldStr<logging::Field::Message>(), "\n");
* @endcode
*/
template<typename ... Formatters>
FormatterPtr makeTemplateFormatter(Formatters&& ... formatters) {
return std::make_shared<TemplateFormatter<Formatters...>>(std::forward<Formatters>(formatters)...);
}
}