preCICE v3.2.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
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
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%";
168
170{
171 boost::algorithm::to_lower(key);
172 if (key == "type") {
173 boost::algorithm::to_lower(value);
174 type = value;
175 }
176 if (key == "output")
177 output = value;
178 if (key == "filter")
179 filter = value;
180 if (key == "format")
181 format = value;
182}
183
185{
186 boost::algorithm::to_lower(key);
187 return key == "output" || key == "filter" || key == "format" || key == "type";
188}
189
191{
192 this->enabled = enabled;
193}
194
195void setupLogging(LoggingConfiguration configs, bool enabled)
196{
197 if (getGlobalLoggingConfig().locked)
198 return;
199
200 namespace bl = boost::log;
201 bl::register_formatter_factory("TimeStamp", boost::make_shared<timestamp_formatter_factory>());
202 bl::register_formatter_factory("ColorizedSeverity", boost::make_shared<colorized_severity_formatter_factory>());
203 bl::register_formatter_factory("Severity", boost::make_shared<severity_formatter_factory>());
204 bl::register_simple_filter_factory<bl::trivial::severity_level, char>("Severity");
205
206 // Possible, longer output format. Currently unused.
207 auto fmtStream =
208 bl::expressions::stream
209 << "(" << bl::expressions::attr<int>("Rank") << ") "
210 << bl::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%H:%M:%S") << " "
211 << bl::expressions::attr<std::string>("File") << ":"
212 << bl::expressions::attr<int>("Line")
213 << " [" << bl::expressions::attr<std::string>("Module") << "] in "
214 << bl::expressions::attr<std::string>("Function") << ": "
215 << bl::expressions::message;
216
217 // Remove active preCICE sinks
218 using sink_ptr = typename boost::shared_ptr<boost::log::sinks::sink>;
219
220 static std::vector<sink_ptr> activeSinks;
221 for (auto &sink : activeSinks) {
222 boost::log::core::get()->remove_sink(sink);
223 sink->flush();
224 sink.reset();
225 }
226 activeSinks.clear();
227
228 // If logging sinks are disabled, then we need to disable the default sink.
229 // We do this by adding a NullSink.
230 // We need to exit after the sink removal as the default sink exists before
231 // the log configuration is parsed.
232 if (auto noconfigs = std::none_of(configs.begin(), configs.end(), [](const auto &config) { return config.enabled; });
233 !enabled && noconfigs) {
234 auto sink = boost::make_shared<NullSink>();
235 boost::log::core::get()->add_sink(sink);
236 activeSinks.emplace_back(std::move(sink));
237 return;
238 }
239
240 // Add the default config in case no sinks are configured
241 if (configs.empty()) {
242 configs.emplace_back();
243 }
244
245 // Create new sinks
246 for (const auto &config : configs) {
247 if (!config.enabled) {
248 continue;
249 }
250
251 // Setup backend of sink
252 boost::shared_ptr<StreamBackend> backend;
253 if (config.type == "file")
254 backend = boost::make_shared<StreamBackend>(boost::shared_ptr<std::ostream>(new std::ofstream(config.output)));
255 if (config.type == "stream") {
256 if (config.output == "stdout")
257 backend = boost::make_shared<StreamBackend>(boost::shared_ptr<std::ostream>(&std::cout, boost::null_deleter()));
258 if (config.output == "stderr")
259 backend = boost::make_shared<StreamBackend>(boost::shared_ptr<std::ostream>(&std::cerr, boost::null_deleter()));
260 }
261 PRECICE_ASSERT(backend != nullptr, "The logging backend was not initialized properly. Check your log config.");
262 backend->auto_flush(true);
263
264 // Setup sink
265 auto sink = boost::make_shared<boost::log::sinks::synchronous_sink<StreamBackend>>(backend);
266 sink->set_formatter(boost::log::parse_formatter(config.format));
267
268 if (config.filter.empty()) {
269 sink->set_filter(boost::log::expressions::attr<bool>("preCICE") == true);
270 } else {
271 // We extend the filter here to filter all log entries not originating from preCICE.
272 sink->set_filter(boost::log::parse_filter("%preCICE% & ( " + config.filter + " )"));
273 }
274
275 boost::log::core::get()->add_sink(sink);
276 activeSinks.emplace_back(std::move(sink));
277 }
278}
279
280void setupLogging(std::string const &logConfigFile)
281{
282 setupLogging(readLogConfFile(logConfigFile));
283}
284
285void setMPIRank(int const rank)
286{
288}
289
290void setParticipant(std::string const &participant)
291{
292 getGlobalLoggingConfig().participant = participant;
293}
294
296{
297 static GlobalLoggingConfig instance;
298 return instance;
299}
300
302{
304}
305
306} // namespace precice::logging
std::string name
T none_of(T... args)
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
T begin(T... args)
A custom sink that does nothing. It is used to disable the default sink of boost.Log.
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
A simple backends that outputs the message to a stream.
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
T clear(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T exists(T... args)
T exit(T... args)
T flush(T... args)
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)
LoggingConfiguration readLogConfFile(std::string const &filename)
Reads a log file, returns a logging configuration.
GlobalLoggingConfig & getGlobalLoggingConfig()
Returns the global logging configuration.
STL namespace.
T push_back(T... args)
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.
T substr(T... args)