LDMX Software
Logger.cxx
1#include "Framework/Logger.h"
2
3// STL
4#include <fstream>
5#include <iostream>
6#include <ostream>
7#include <string>
8#include <unordered_map>
9#include <vector>
10
11// Boost
12#include <boost/core/null_deleter.hpp> //to avoid deleting std::cout
13#include <boost/log/attributes/value_extraction.hpp>
14#include <boost/log/core/core.hpp>
15#include <boost/log/expressions/message.hpp>
16#include <boost/log/sinks/sync_frontend.hpp>
17#include <boost/log/sinks/text_ostream_backend.hpp>
18#include <boost/log/utility/formatting_ostream.hpp>
19#include <boost/log/utility/setup/common_attributes.hpp> //for loading commont attributes
20#include <boost/smart_ptr/make_shared_object.hpp>
21#include <boost/smart_ptr/shared_ptr.hpp>
22
23#include "Framework/Configure/Parameters.h"
24
25namespace framework {
26
27namespace logging {
28
29namespace sinks = boost::log::sinks;
30
40level convertLevel(int iLvl) {
41 if (iLvl < -1)
42 iLvl = -1;
43 else if (iLvl > 4)
44 iLvl = 4;
45 return level(iLvl);
46}
47
48logger makeLogger(const std::string& name) {
49 logger lg(log::keywords::channel = name); // already has severity built in
50 return boost::move(lg);
51}
52
53template <typename T>
54const T& safeExtract(log::attribute_value attr) {
55 static const T empty_value = {};
56 auto attr_val = log::extract<T>(attr);
57 if (attr_val) {
58 return *attr_val;
59 }
60 return empty_value;
61}
62
71class Filter {
72 level fallback_level_;
73 std::unordered_map<std::string, level> custom_levels_;
74
75 public:
76 Filter(level fallback, std::unordered_map<std::string, level> custom)
77 : fallback_level_{fallback}, custom_levels_{custom} {}
78 Filter(level fallback) : Filter(fallback, {}) {}
79 bool operator()(log::attribute_value_set const& attrs) {
80 auto it = custom_levels_.find(safeExtract<std::string>(attrs["Channel"]));
81 if (it != custom_levels_.end()) {
82 return safeExtract<level>(attrs["Severity"]) >= it->second;
83 }
84 return safeExtract<level>(attrs["Severity"]) >= fallback_level_;
85 }
86};
87
88void open(const framework::config::Parameters& p) {
89 // some helpful types
90 typedef sinks::text_ostream_backend ourSinkBack_t;
91 typedef sinks::synchronous_sink<ourSinkBack_t> ourSinkFront_t;
92
93 level file_level{convertLevel(p.get<int>("file_level", 0))};
94 std::string file_path{p.get<std::string>("file_path", "")};
95
96 level term_level{convertLevel(p.get<int>("term_level", 4))};
97 std::vector<framework::config::Parameters> empty{};
98 const auto& log_rules{
99 p.get<std::vector<framework::config::Parameters>>("log_rules", empty)};
100 std::unordered_map<std::string, level> custom_levels;
101 for (const auto& log_rule : log_rules) {
102 custom_levels[log_rule.get<std::string>("name")] =
103 convertLevel(log_rule.get<int>("level"));
104 }
105
106 // allow our logs to access common attributes, the ones availabe are
107 // "LineID" : counter increments for each record being made (terminal or
108 // file) "TimeStamp" : time the log message was created "ProcessID" : machine
109 // ID for the process that is running "ThreadID" : machine ID for the thread
110 // the message is in
111 log::add_common_attributes();
112
113 // get the core logging service
114 boost::shared_ptr<log::core> core = log::core::get();
115
116 // file sink is optional
117 // don't even make it if no filePath is provided
118 if (not file_path.empty()) {
119 boost::shared_ptr<ourSinkBack_t> file_back =
120 boost::make_shared<ourSinkBack_t>();
121 file_back->add_stream(boost::make_shared<std::ofstream>(file_path));
122
123 boost::shared_ptr<ourSinkFront_t> file_sink =
124 boost::make_shared<ourSinkFront_t>(file_back);
125
126 // this is where the logging level is set
127 file_sink->set_filter(Filter(file_level, custom_levels));
128 file_sink->set_formatter(
129 [](const log::record_view& view, log::formatting_ostream& os) {
130 Formatter::get()(view, os);
131 });
132 core->add_sink(file_sink);
133 } // file set to pass something
134
135 // terminal sink is always created
136 boost::shared_ptr<ourSinkBack_t> term_back =
137 boost::make_shared<ourSinkBack_t>();
138 term_back->add_stream(boost::shared_ptr<std::ostream>(
139 &std::cout, // point this stream to std::cout
140 boost::null_deleter() // don't let boost delete std::cout
141 ));
142 // flushes message to screen **after each message**
143 term_back->auto_flush(true);
144
145 boost::shared_ptr<ourSinkFront_t> term_sink =
146 boost::make_shared<ourSinkFront_t>(term_back);
147
148 // translate integer level to enum
149 term_sink->set_filter(Filter(term_level, custom_levels));
150 // need to wrap formatter in lambda to enforce singleton formatter
151 term_sink->set_formatter(
152 [](const log::record_view& view, log::formatting_ostream& os) {
153 Formatter::get()(view, os);
154 });
155 core->add_sink(term_sink);
156
157 return;
158
159} // open
160
161void close() {
162 // prevents crashes on some systems when logging to a file
163 log::core::get()->remove_all_sinks();
164
165 return;
166}
167
169 static Formatter the_formatter;
170 return the_formatter;
171}
172
173void Formatter::set(int n) { Formatter::get().event_number_ = n; }
174
175void Formatter::operator()(const log::record_view& view,
176 log::formatting_ostream& os) {
177 os << "[ " << log::extract<std::string>("Channel", view) << " ] "
178 << event_number_ << " ";
183 const level msg_level{safeExtract<level>(view["Severity"])};
184 switch (msg_level) {
185 case level::trace:
186 os << "trace";
187 break;
188 case level::debug:
189 os << "debug";
190 break;
191 case level::info:
192 os << " info";
193 break;
194 case level::warn:
195 os << " warn";
196 break;
197 case level::error:
198 os << "error";
199 break;
200 case level::fatal:
201 os << "fatal";
202 break;
203 default:
204 // this should never happen since the loggers created
205 // with makeLogger use the level enum and thus check
206 // at compile time that the given level is an good option.
207 // That being said, we leave this in case someone creates
208 // their own logger circumventing makeLogger for whatever
209 // reason and changes the enum defining the severity.
210 os << "?????";
211 break;
212 }
213 os << ": " << view[log::expressions::smessage];
214}
215
216} // namespace logging
217
218} // namespace framework
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
Our filter implementation aligning with Boost.Log.
Definition Logger.cxx:71
Our logging formatter.
Definition Logger.h:90
void operator()(const log::record_view &view, log::formatting_ostream &os)
format the passed record view into the output stream
Definition Logger.cxx:175
static Formatter & get()
get reference to the current single Formatter
Definition Logger.cxx:168
static void set(int n)
set the event number in the current Formatter
Definition Logger.cxx:173
All classes in the ldmx-sw project use this namespace.