Flow 1.0.1
Flow project: Full implementation reference.
verbosity_config.cpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
20#include <boost/algorithm/string.hpp>
21#include <boost/lexical_cast.hpp>
22
23namespace flow::log
24{
25
26// Static initializations.
27
31
32// Implementations.
33
35{
36 using std::string;
37 using std::make_pair;
38
39 // As promised:
40 m_component_sev_pairs.push_back(make_pair<string, Sev>(string(), Sev(Config::S_MOST_VERBOSE_SEV_DEFAULT)));
41 assert(m_component_sev_pairs.size() == 1);
42}
43
44bool Verbosity_config::parse(std::istream& is)
45{
47 using boost::algorithm::split;
48 using boost::algorithm::is_any_of;
49 using boost::algorithm::to_upper_copy;
50 using boost::lexical_cast;
51 using boost::bad_lexical_cast;
52 using std::vector;
53 using std::string;
54 using std::make_pair;
55 using std::locale;
56
57 string tokens_str;
58 is >> tokens_str; // As promised read up to (not including) first space.
59
60 Component_sev_pair_seq result_pairs;
61
62 /* Everything below follows directly from the doc header of component_sev_pairs().
63 * Keeping comments light but do ensure it all matches when maintaining this code. */
64
65 if (!tokens_str.empty()) // Degenerate case.
66 {
67 vector<string> tokens;
68 split(tokens, tokens_str, is_any_of(string(1, S_TOKEN_SEPARATOR)));
69 result_pairs.reserve(tokens.size()); // Little optimization.
70
71 const auto is_pair_sep = is_any_of(string(1, S_PAIR_SEPARATOR));
72 for (const auto& token : tokens)
73 {
74 if (token.empty()) // Trying to split "" may still produce non-empty leaf_tokens... just eliminate corner case.
75 {
76 m_last_result_message = ostream_op_string("A pair token is empty in [", tokens_str, "].");
77 return false;
78 }
79 // else
80
81 vector<string> leaf_tokens;
82 split(leaf_tokens, token, is_pair_sep);
83
84 if (leaf_tokens.empty() || (leaf_tokens.size() > 2)) // The former shouldn't happen really. @todo assert() it?
85 {
86 m_last_result_message = ostream_op_string("Pair token [", token,
87 "] in [", tokens_str, "] must contain 1-2 `", S_PAIR_SEPARATOR,
88 "`-separated leaf tokens: `<component>",
89 S_PAIR_SEPARATOR, "<sev>` or `",
91 "<sev>` or `", S_PAIR_SEPARATOR, "<sev> or just `<sev>`.");
92 return false;
93 }
94 // else
95
96 if (leaf_tokens.size() == 1) // "sev" treated as-if ":sev".
97 {
98 leaf_tokens.insert(leaf_tokens.begin(), "");
99 }
100 else
101 {
102 if (leaf_tokens[0] == S_ALL_COMPONENT_NAME_ALIAS) // "ALL:sev" treated as-if ":sev".
103 {
104 leaf_tokens[0].clear();
105 }
106 }
107
108 /* Parse `sev` in "name:sev" from string to Sev via lexical_cast via its operator<<(is).
109 * Subtlety: Store the component name in normalized (upper-case) form. Then ostream<<() output will be pretty
110 * and match S_ALL_COMPONENT_NAME_ALIAS.
111 * @todo Should reuse Config::normalized_component_name(). Though its action is officially documented as
112 * performing to-upper-case, so this is correct. */
113 assert(leaf_tokens.size() == 2);
114
115 Sev sev;
116 try
117 {
118 /* As of this writing Sev<<istream is quite permissive -- anything matching [A-Za-z_]* is accepted;
119 * but what's not obvious is: Suppose leaf_tokens[1] is [A-Za-z_]+ followed by junk like other characters.
120 * For example, say they wrote WARNING,INFO by mistake. Sev<<istream will read up to and not including the
121 * comma; load WARNING into the Sev. Then lexical_cast<> will throw bad_cast_exception, because
122 * operator<< stopped reading and left stuff in the istream (that's the non-obvious thing). So catch it. */
123 sev = lexical_cast<Sev>(leaf_tokens[1]);
124 }
125 catch (const bad_lexical_cast&)
126 {
127 m_last_result_message = ostream_op_string("Leaf token [", leaf_tokens[1],
128 "] in [", tokens_str, "] must contain a severity composed of "
129 "alphanumerics and underscores but appears to contain other "
130 "characters.");
131 return false;
132 }
133 result_pairs.push_back
134 (make_pair<string, Sev>
135 (string(to_upper_copy(leaf_tokens[0], locale::classic())),
136 std::move(sev)));
137 } // for (token : tokens)
138 } // if (!tokens_str.empty())
139 // else if (tokens_str.empty()) { No problem: we handle result_pairs.empty() just below. }
140
141 // As promised there must be a leading default-verbosity pair.
142 if (result_pairs.empty() || (!result_pairs.front().first.empty()))
143 {
144 result_pairs.insert
145 (result_pairs.begin(),
146 make_pair<string, Sev>(string(), Sev(Config::S_MOST_VERBOSE_SEV_DEFAULT)));
147 }
148
149 // Finalize only if all succeeded only (as promised).
150 m_component_sev_pairs = std::move(result_pairs);
151 // result_pairs is now hosed.
152
153 m_last_result_message.clear();
154 return true;
155} // Verbosity_config::parse()
156
158{
160
161 assert(target_config_ptr);
162 auto& target_config = *target_config_ptr;
163
164 /* Everything below follows directly from the doc header of component_sev_pairs().
165 * Keeping comments light but do ensure it all matches when maintaining this code. */
166
167 // First pair must specify initial default/catch-all verbosity. We never allow ourselves to enter a different state.
168 assert((!m_component_sev_pairs.empty()) && m_component_sev_pairs.front().first.empty());
169 target_config.configure_default_verbosity(m_component_sev_pairs.front().second, true); // Reset!
170
171 // Now handle the rest (if any).
172 for (size_t idx = 1; idx != m_component_sev_pairs.size(); ++idx)
173 {
174 const auto& pair = m_component_sev_pairs[idx];
175 const auto& component_name = pair.first;
176 const auto sev = pair.second;
177
178 if (component_name.empty())
179 {
180 target_config.configure_default_verbosity(sev, false); // Do not reset.
181 }
182 else if (!target_config.configure_component_verbosity_by_name(sev, component_name))
183 {
184 m_last_result_message = ostream_op_string("Component name [", component_name, "] is unknown.");
185 return false;
186 }
187 // else { We set it fine. Keep going. }
188 }
189
190 m_last_result_message.clear();
191 return true;
192} // Verbosity_config::apply_to_config()
193
195{
197}
198
200{
202}
203
204std::istream& operator>>(std::istream& is, Verbosity_config& val)
205{
206 val.parse(is);
207 return is;
208}
209
210std::ostream& operator<<(std::ostream& os, const Verbosity_config& val)
211{
212 const auto& component_sev_pairs = val.component_sev_pairs();
213 for (size_t idx = 0; idx != component_sev_pairs.size(); ++idx)
214 {
215 const auto& pair = component_sev_pairs[idx];
216 const auto& component_name = pair.first;
217
218 os << (component_name.empty() ? Verbosity_config::S_ALL_COMPONENT_NAME_ALIAS : component_name);
220 os << pair.second;
221
222 if (idx != (component_sev_pairs.size() - 1))
223 {
225 }
226 }
227
228 return os;
229}
230
231bool operator==(const Verbosity_config& val1, const Verbosity_config& val2)
232{
233 return val1.component_sev_pairs() == val2.component_sev_pairs();
234}
235
236bool operator!=(const Verbosity_config& val1, const Verbosity_config& val2)
237{
238 return !(operator==(val1, val2));
239}
240
241} // namespace flow::log
Class used to configure the filtering and logging behavior of Loggers; its use in your custom Loggers...
Definition: config.hpp:200
static const Sev S_MOST_VERBOSE_SEV_DEFAULT
Recommended default/catch-all most-verbose-severity value if no specific config is given.
Definition: config.hpp:219
Optional-use structure encapsulating a full set of verbosity config, such that one can parse it from ...
const std::string & last_result_message() const
To be used after parse() or operator<< or apply_to_config(), returns "" on success or a message descr...
bool apply_to_config(Config *target_config)
Applies *this to to the given log::Config.
std::string m_last_result_message
See last_result_message().
Component_sev_pair_seq m_component_sev_pairs
See component_sev_pairs().
static const char S_TOKEN_SEPARATOR
Separates component/severity pairs in a Verbosity_config specifier string.
static const std::string S_ALL_COMPONENT_NAME_ALIAS
String that Verbosity_config::parse() treats as the default/catch-all verbosity's "component" specifi...
Verbosity_config()
Constructor a Verbosity_config that resets all severity config and sets the default/catch-all to Conf...
const Component_sev_pair_seq & component_sev_pairs() const
Read-only access to encapsulated config; specifies the verbosity-setting calls to make on a Config in...
std::vector< std::pair< std::string, Sev > > Component_sev_pair_seq
Short-hand for the configuration capable of being encapsulated by Verbosity_config.
bool parse(std::istream &is)
Deserializes *this from a standard input stream.
static const char S_PAIR_SEPARATOR
Separates component and severity within each pair in a Verbosity_config specifier string.
Flow module providing logging functionality.
std::ostream & operator<<(std::ostream &os, Sev val)
Serializes a log::Sev to a standard output stream.
Definition: log.cpp:249
std::istream & operator>>(std::istream &is, Sev &val)
Deserializes a log::Sev from a standard input stream.
Definition: log.cpp:269
Sev
Enumeration containing one of several message severity levels, ordered from highest to lowest.
Definition: log_fwd.hpp:224
bool operator==(const Verbosity_config &val1, const Verbosity_config &val2)
Checks for exact equality of two Verbosity_config objects.
bool operator!=(const Verbosity_config &val1, const Verbosity_config &val2)
Returns !(val1 == val2).
std::string ostream_op_string(T const &... ostream_args)
Equivalent to ostream_op_to_string() but returns a new string by value instead of writing to the call...
Definition: util.hpp:356