preCICE
Loading...
Searching...
No Matches
LogConfiguration.cpp
Go to the documentation of this file.
2#include "utils/assertion.hpp"
3
4#include <algorithm>
5#include <filesystem>
6#include <fstream>
7#include <iostream>
8#include <map>
9#include <string>
10#include <utility>
11
12#include <boost/core/null_deleter.hpp>
13#include <boost/log/attributes/mutable_constant.hpp>
14#include <boost/log/core.hpp>
15#include <boost/log/expressions.hpp>
16#include <boost/log/sinks/sink.hpp>
17#include <boost/log/sinks/sync_frontend.hpp>
18#include <boost/log/sinks/text_ostream_backend.hpp>
19#include <boost/log/support/date_time.hpp>
20#include <boost/log/trivial.hpp>
21#include <boost/log/utility/setup/filter_parser.hpp>
22#include <boost/log/utility/setup/formatter_parser.hpp>
23#include <boost/program_options.hpp>
24
25namespace precice::logging {
26
28class timestamp_formatter_factory : public boost::log::basic_formatter_factory<char, boost::posix_time::ptime> {
29public:
30 formatter_type create_formatter(boost::log::attribute_name const &name, args_map const &args) override
31 {
32 namespace expr = boost::log::expressions;
33 args_map::const_iterator it = args.find("format");
34 if (it != args.end())
35 return expr::stream << expr::format_date_time<boost::posix_time::ptime>(expr::attr<boost::posix_time::ptime>(name), it->second);
36 else
37 return expr::stream << expr::attr<boost::posix_time::ptime>(name);
38 }
39};
40
42class colorized_severity_formatter_factory : public boost::log::formatter_factory<char> {
43public:
44 formatter_type create_formatter(boost::log::attribute_name const &name, args_map const &args) override
45 {
46 namespace expr = boost::log::expressions;
47 auto severity = expr::attr<boost::log::trivial::severity_level>("Severity");
48
49 return expr::stream
50 << expr::if_(severity == boost::log::trivial::severity_level::error)
51 [expr::stream << "\033[31m" // red
52 << "ERROR: "]
53 << expr::if_(severity == boost::log::trivial::severity_level::warning)
54 [expr::stream << "\033[36m" // cyan
55 << "WARNING: "]
56 << "\033[0m";
57 }
58};
59
61class severity_formatter_factory : public boost::log::formatter_factory<char> {
62public:
63 formatter_type create_formatter(boost::log::attribute_name const &name, args_map const &args) override
64 {
65 namespace expr = boost::log::expressions;
66 auto severity = expr::attr<boost::log::trivial::severity_level>("Severity");
67
68 return expr::stream
69 << expr::if_(severity == boost::log::trivial::severity_level::error)
70 [expr::stream
71 << "ERROR: "]
72 << expr::if_(severity == boost::log::trivial::severity_level::warning)
73 [expr::stream
74 << "WARNING: "];
75 }
76};
77
79class NullSink final : public boost::log::sinks::sink {
80public:
82 : boost::log::sinks::sink(false) {}
83
84 bool will_consume(boost::log::attribute_value_set const &) override
85 {
86 return false;
87 }
88
89 void consume(boost::log::record_view const &) override {}
90
91 bool try_consume(boost::log::record_view const &) override
92 {
93 return false;
94 }
95
96 void flush() override {}
97
98 bool is_cross_thread() const noexcept
99 {
100 return false;
101 }
102};
103
105
110class StreamBackend final : public boost::log::sinks::text_ostream_backend {
111private:
112 boost::shared_ptr<std::ostream> _ostream;
113
114public:
115 explicit StreamBackend(boost::shared_ptr<std::ostream> ostream)
116 : _ostream(std::move(ostream)) {}
117
118 void consume(boost::log::record_view const &rec, string_type const &formatted_record)
119 {
120 *_ostream << formatted_record << '\n'
121 << std::flush;
122 }
123};
124
126LoggingConfiguration readLogConfFile(std::string const &filename)
127{
128 if (!std::filesystem::exists(filename)) {
129 return {};
130 }
131
132 namespace po = boost::program_options;
133 po::options_description desc;
134 std::ifstream ifs(filename);
135
136 std::map<std::string, BackendConfiguration> configs;
137 try {
138 po::parsed_options parsed = parse_config_file(ifs, desc, true);
139 for (auto const &opt : parsed.options) {
140 std::string section = opt.string_key.substr(0, opt.string_key.find('.'));
141 std::string key = opt.string_key.substr(opt.string_key.find('.') + 1);
143 PRECICE_ASSERT(!opt.value.empty());
144 configs[section].setOption(key, opt.value[0]);
145 } else {
146 std::cerr << "WARNING: section [" << section << "] in configuration file \"" << filename << "\" contains invalid key \"" << key << "\"\n";
147 }
148 }
149 } catch (po::error &e) {
150 std::cout << "ERROR reading logging configuration: " << e.what() << "\n\n";
151 std::exit(-1);
152 }
153
155
156 for (auto const &c : configs)
157 if (c.second.enabled)
158 retVal.push_back(c.second);
159
160 return retVal;
161}
162
163// Default values for filter and format. They are also used from config/LogConfiguration.cpp
164const std::string BackendConfiguration::default_filter = "(%Severity% > debug) and not ((%Severity% = info) and (%Rank% != 0))";
165const std::string BackendConfiguration::default_formatter = "(%Rank%) %TimeStamp(format=\"%H:%M:%S\")% [%Module%]:%Line% in %Function%: %ColorizedSeverity%%Message%";
166const std::string BackendConfiguration::default_type = "stream";
167const std::string BackendConfiguration::default_output = "stdout";
168
169static boost::log::formatter createDefaultFormatter()
170{
171 namespace expr = boost::log::expressions;
172 namespace trivial = boost::log::trivial;
173 auto severity = expr::attr<trivial::severity_level>("Severity");
174 return expr::stream
175 << "("
176 << expr::attr<int>("Rank")
177 << ") "
178 << expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%H:%M:%S")
179 << " ["
180 << expr::attr<std::string>("Module")
181 << "]:"
182 << expr::attr<int>("Line")
183 << " in "
184 << expr::attr<std::string>("Function")
185 << ": "
186 << expr::if_(severity == trivial::error)[expr::stream << "\033[31mERROR: "]
187 << expr::if_(severity == trivial::warning)[expr::stream << "\033[36mWARNING: ]"]
188 << "\033[0m"
189 << expr::smessage;
190}
191
192static boost::log::filter createDefaultFilter()
193{
194 namespace expr = boost::log::expressions;
195 namespace trivial = boost::log::trivial;
196 auto severity = trivial::severity;
197 auto rank = expr::attr<int>("Rank");
198 return expr::has_attr("preCICE") && expr::attr<bool>("preCICE") && (severity > trivial::debug) && !((severity == trivial::info) && (rank != 0));
199}
200
201void BackendConfiguration::setOption(std::string key, std::string value)
202{
203 boost::algorithm::to_lower(key);
204 if (key == "type") {
205 boost::algorithm::to_lower(value);
206 type = value;
207 }
208 if (key == "output")
209 output = value;
210 if (key == "filter")
211 filter = value;
212 if (key == "format")
213 format = value;
214}
215
217{
218 boost::algorithm::to_lower(key);
219 return key == "output" || key == "filter" || key == "format" || key == "type";
220}
221
223{
224 this->enabled = enabled;
225}
226
227void setupLogging(LoggingConfiguration configs, bool enabled)
228{
229 if (getGlobalLoggingConfig().locked)
230 return;
231
232 namespace bl = boost::log;
233 bl::register_formatter_factory("TimeStamp", boost::make_shared<timestamp_formatter_factory>());
234 bl::register_formatter_factory("ColorizedSeverity", boost::make_shared<colorized_severity_formatter_factory>());
235 bl::register_formatter_factory("Severity", boost::make_shared<severity_formatter_factory>());
236 bl::register_simple_filter_factory<bl::trivial::severity_level, char>("Severity");
237
238 // Possible, longer output format. Currently unused.
239 auto fmtStream =
240 bl::expressions::stream
241 << "(" << bl::expressions::attr<int>("Rank") << ") "
242 << bl::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%H:%M:%S") << " "
243 << bl::expressions::attr<std::string>("File") << ":"
244 << bl::expressions::attr<int>("Line")
245 << " [" << bl::expressions::attr<std::string>("Module") << "] in "
246 << bl::expressions::attr<std::string>("Function") << ": "
247 << bl::expressions::message;
248
249 // Remove active preCICE sinks
250 using sink_ptr = typename boost::shared_ptr<boost::log::sinks::sink>;
251
252 static std::vector<sink_ptr> activeSinks;
253 for (auto &sink : activeSinks) {
254 boost::log::core::get()->remove_sink(sink);
255 sink->flush();
256 sink.reset();
257 }
258 activeSinks.clear();
259
260 // If logging sinks are disabled, then we need to disable the default sink.
261 // We do this by adding a NullSink.
262 // We need to exit after the sink removal as the default sink exists before
263 // the log configuration is parsed.
264 if (auto noconfigs = std::none_of(configs.begin(), configs.end(), [](const auto &config) { return config.enabled; });
265 !enabled && noconfigs) {
266 auto sink = boost::make_shared<NullSink>();
267 boost::log::core::get()->add_sink(sink);
268 activeSinks.emplace_back(std::move(sink));
269 return;
270 }
271
272 // Add the default config in case no sinks are configured
273 if (configs.empty()) {
274 configs.emplace_back();
275 }
276
277 // Create new sinks
278 for (const auto &config : configs) {
279 if (!config.enabled) {
280 continue;
281 }
282
283 // Setup backend of sink
284 boost::shared_ptr<StreamBackend> backend;
285 if (config.type == "file")
286 backend = boost::make_shared<StreamBackend>(boost::shared_ptr<std::ostream>(new std::ofstream(config.output)));
287 if (config.type == "stream") {
288 if (config.output == "stdout")
289 backend = boost::make_shared<StreamBackend>(boost::shared_ptr<std::ostream>(&std::cout, boost::null_deleter()));
290 if (config.output == "stderr")
291 backend = boost::make_shared<StreamBackend>(boost::shared_ptr<std::ostream>(&std::cerr, boost::null_deleter()));
292 }
293 PRECICE_ASSERT(backend != nullptr, "The logging backend was not initialized properly. Check your log config.");
294 backend->auto_flush(true);
295
296 // Setup sink
297 auto sink = boost::make_shared<boost::log::sinks::synchronous_sink<StreamBackend>>(backend);
298
299 // Set formatter
301 sink->set_formatter(createDefaultFormatter());
302 } else {
303 sink->set_formatter(boost::log::parse_formatter(config.format));
304 }
305
306 // Set filter
307 if (config.filter.empty()) {
308 sink->set_filter(boost::log::expressions::attr<bool>("preCICE") == true);
309 } else if (config.filter == BackendConfiguration::default_filter) {
310 sink->set_filter(createDefaultFilter());
311 } else {
312 // We extend the filter here to filter all log entries not originating from preCICE.
313 sink->set_filter(boost::log::parse_filter("%preCICE% & ( " + config.filter + " )"));
314 }
315
316 boost::log::core::get()->add_sink(sink);
317 activeSinks.emplace_back(std::move(sink));
318 }
319}
320
321void setupLogging(std::string const &logConfigFile)
322{
323 setupLogging(readLogConfFile(logConfigFile));
324}
325
326void setMPIRank(int const rank)
327{
329}
330
331void setParticipant(std::string const &participant)
332{
333 getGlobalLoggingConfig().participant = participant;
334}
335
337{
338 static GlobalLoggingConfig instance;
339 return instance;
340}
341
343{
345}
346
347} // namespace precice::logging
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
void consume(boost::log::record_view const &) override
bool is_cross_thread() const noexcept
bool will_consume(boost::log::attribute_value_set const &) override
bool try_consume(boost::log::record_view const &) override
boost::shared_ptr< std::ostream > _ostream
StreamBackend(boost::shared_ptr< std::ostream > ostream)
void consume(boost::log::record_view const &rec, string_type const &formatted_record)
A custom formatter that handles the colorized Severity formatting.
formatter_type create_formatter(boost::log::attribute_name const &name, args_map const &args) override
A custom formatter that handles non-colorized Severity formatting.
formatter_type create_formatter(boost::log::attribute_name const &name, args_map const &args) override
A custom formatter that handles the TimeStamp format string.
formatter_type create_formatter(boost::log::attribute_name const &name, args_map const &args) override
contains the logging framework.
void setupLogging(LoggingConfiguration configs, bool enabled)
Configures the logging from a LoggingConfiguration.
void setMPIRank(int const rank)
void setParticipant(std::string const &participant)
static boost::log::filter createDefaultFilter()
LoggingConfiguration readLogConfFile(std::string const &filename)
Reads a log file, returns a logging configuration.
std::vector< BackendConfiguration > LoggingConfiguration
Holds the configuration of the logging system.
GlobalLoggingConfig & getGlobalLoggingConfig()
Returns the global logging configuration.
static boost::log::formatter createDefaultFormatter()
STL namespace.
void setEnabled(bool enabled)
Sets weather the sink is enabled or disabled.
void setOption(std::string key, std::string value)
Sets on option, overwrites default values.
static bool isValidOption(std::string key)
Checks if an option is usable.
Holds global logging data in a central place.