GCC Code Coverage Report


Directory: ./
File: src/util/log4cpp-extras.cc
Date: 2025-09-01 06:19:01
Exec Total Coverage
Lines: 0 177 0.0%
Functions: 0 36 0.0%
Branches: 0 36 0.0%

Line Branch Exec Source
1 #include "na64util/log4cpp-extras.hh"
2
3 #include <log4cpp/Category.hh>
4 #include <log4cpp/AppendersFactory.hh>
5 #include <log4cpp/OstreamAppender.hh>
6
7 #include <regex>
8 #include <iomanip>
9 #include <cassert>
10
11 #include <memory>
12 #include <ctime>
13 #include <cstring>
14
15 #include <unistd.h>
16
17 #include "na64util/str-fmt.hh"
18
19 namespace na64dp {
20 namespace util {
21
22 StdoutStreamAppender * StdoutStreamAppender::_self = nullptr;
23
24 StdoutStreamAppender *
25 StdoutStreamAppender::self() {
26 return _self;
27 }
28
29 StdoutStreamAppender::StdoutStreamAppender( const std::string & name
30 , int fd )
31 : log4cpp::LayoutAppender(name)
32 , _fd(fd)
33 {
34 *_statusline = '\0';
35 if(_self) {
36 throw std::runtime_error("Duplicating isntantiation of layout appender.");
37 }
38 _self = this;
39 }
40
41 static const char gEraseLine[] = "\033[K" // "erase in line", from cursor to the end of the line
42 , gCurTo0[] = "\033[G" // moves cursor to 1st column
43 ;
44
45 void
46 StdoutStreamAppender::_append(const log4cpp::LoggingEvent & event) {
47 std::string formatted = _getLayout().format(event);
48 #if 0 // XXX
49 assert(1 == _fd);
50 write(_fd, formatted.c_str(), formatted.size());
51 #else
52 if(isatty(_fd) && '\0' != *_statusline) {
53 formatted = gEraseLine + formatted;
54 write(_fd, formatted.c_str(), formatted.size());
55 if(formatted.empty()) return;
56 std::lock_guard<std::mutex> guard(_lockStatusline);
57 write(_fd, _statusline, strlen(_statusline));
58 write(_fd, gCurTo0, sizeof(gCurTo0));
59 } else {
60 if(formatted.empty()) return;
61 write(_fd, formatted.c_str(), formatted.size());
62 }
63 #endif
64 }
65
66 void
67 StdoutStreamAppender::update_status_text(const char * msg) {
68 std::lock_guard<std::mutex> guard(_lockStatusline);
69 if(isatty(_fd)) {
70 write(_fd, gEraseLine, sizeof(gEraseLine));
71 write(_fd, msg, strlen(msg));
72 write(_fd, gCurTo0, sizeof(gCurTo0));
73 }
74 strncpy(_statusline, msg, sizeof(_statusline));
75 }
76
77 Log4cppAppendersFactoryResult_t
78 create_ostream_appender(const log4cpp::AppendersFactory::params_t & p) {
79 std::string name; //, statuslineFormat;
80 //bool enableStatusline = true;
81 p.get_for("stdout appender")
82 .required("name", name)
83 //.optional("statusline", enableStatusline)
84 // ("statuslineFormat", statuslineFormat)
85 ;
86 //return Log4cppAppendersFactoryResult_t(new log4cpp::OstreamAppender(name, &std::cout));
87 return Log4cppAppendersFactoryResult_t(
88 new StdoutStreamAppender(name));
89 }
90
91 void
92 inject_extras_to_log4cpp() {
93 log4cpp::AppendersFactory::getInstance().registerCreator( "stdout"
94 , create_ostream_appender );
95 log4cpp::LayoutsFactory::getInstance().registerCreator("extended-pattern"
96 , PatternLayout::create );
97
98 //#if defined(Geant4_FOUND) && Geant4_FOUND
99 //log4cpp::AppendersFactory::getInstance().registerCreator( "Geant4 UI"
100 // , Geant4UISessionAppender::create );
101 //assert(log4cpp::AppendersFactory::getInstance().registered("Geant4 UI"));
102 //#endif
103 // ^^^ TODO: move it to extensions/mc
104 }
105
106
107 //
108 // Custom pattern layout
109
110 struct StaticComponent : public PatternLayout::iComponent {
111 const std::string str;
112 StaticComponent(const std::string & s) : str(s) {}
113 virtual void append_msg(const log4cpp::LoggingEvent &, std::ostringstream & oss) override
114 { oss << str; }
115
116 static iComponent * create_endl(const std::unordered_map<std::string, std::string> &) {
117 std::ostringstream oss;
118 oss << std::endl;
119 return new StaticComponent(oss.str());
120 }
121 };
122
123
124 struct CategoryComponent : public PatternLayout::iComponent {
125 virtual void append_msg(const log4cpp::LoggingEvent & lev, std::ostringstream & oss) override
126 { oss << lev.categoryName; }
127
128 static iComponent * create(const std::unordered_map<std::string, std::string> & p) {
129 return new CategoryComponent();
130 }
131 };
132
133
134 struct MessageComponent : public PatternLayout::iComponent {
135 virtual void append_msg(const log4cpp::LoggingEvent & lev, std::ostringstream & oss) override
136 { oss << lev.message; }
137
138 static iComponent * create(const std::unordered_map<std::string, std::string> & p) {
139 return new MessageComponent();
140 }
141 };
142
143
144 struct NDCComponent : public PatternLayout::iComponent {
145 virtual void append_msg(const log4cpp::LoggingEvent & lev, std::ostringstream & oss) override
146 { oss << lev.ndc; }
147
148 static iComponent * create(const std::unordered_map<std::string, std::string> & p) {
149 return new NDCComponent();
150 }
151 };
152
153
154 struct PriorityComponent : public PatternLayout::iComponent {
155 /// Priority name will be trancated to a single letter
156 bool isShort;
157
158 virtual void append_msg(const log4cpp::LoggingEvent & lev, std::ostringstream &) override;
159
160 static iComponent * create(const std::unordered_map<std::string, std::string> & p) {
161 auto ptr = new PriorityComponent();
162 auto it = p.find("short");
163 if(it != p.end() && (it->second.empty() || it->second != "false")) {
164 ptr->isShort = true;
165 } else {
166 ptr->isShort = false;
167 }
168 return ptr;
169 }
170 };
171
172 void
173 PriorityComponent::append_msg( const log4cpp::LoggingEvent & lev
174 , std::ostringstream & oss
175 ) {
176 std::string nm = log4cpp::Priority::getPriorityName(lev.priority);
177 if(isShort) {
178 nm = nm.substr(0, 1);
179 }
180 oss << nm;
181 }
182
183 struct ThreadNameComponent : public PatternLayout::iComponent {
184 virtual void append_msg(const log4cpp::LoggingEvent & lev, std::ostringstream & oss) override
185 { oss << lev.threadName; }
186
187 static iComponent * create(const std::unordered_map<std::string, std::string> & p) {
188 return new ThreadNameComponent();
189 }
190 };
191
192 struct TimeSinceStartComponent : public PatternLayout::iComponent {
193 bool raw;
194 TimeSinceStartComponent(bool raw_) : raw(raw_) {}
195 virtual void append_msg(const log4cpp::LoggingEvent & event, std::ostringstream & out) override {
196 double t = event.timeStamp.getSeconds() -
197 log4cpp::TimeStamp::getStartTime().getSeconds();
198 t += ( event.timeStamp.getMilliSeconds() -
199 - log4cpp::TimeStamp::getStartTime().getMilliSeconds())*1e-3;
200 if(raw) {
201 out << std::setiosflags(std::ios::fixed)
202 << std::setprecision(0) << t;
203 return;
204 }
205 char buf[64];
206 util::seconds_to_hmsm(t, buf, sizeof(buf));
207 out << buf;
208 }
209
210 static iComponent * create(const std::unordered_map<std::string, std::string> & p) {
211 auto it = p.find("raw");
212 if(p.end() != it && it->second != "false") {
213 return new TimeSinceStartComponent(true);
214 } else {
215 return new TimeSinceStartComponent(false);
216 }
217 }
218 };
219
220 struct CPUTimeComponent : public PatternLayout::iComponent {
221 virtual void append_msg(const log4cpp::LoggingEvent & lev, std::ostringstream & oss) override
222 { oss << std::clock(); }
223
224 static iComponent * create(const std::unordered_map<std::string, std::string> & p) {
225 return new CPUTimeComponent();
226 }
227 };
228
229 static const struct {
230 const std::string s[4];
231 } gColors[] = {
232 // title-bgn, text-bgn, title-end, text-end
233 { "\e[1;5;37;41m", "\e[0;1;31m" "\e[0m", "\e[0m"}, // FATAL
234 { "\e[1;5;37;41m", "\e[1;31m", "\e[0m", "\e[0m"}, // ALERT
235 { "\e[1;43;31m", "\e[1;31m", "\e[0m", "\e[0m"}, // CRIT
236 { "\e[1;31m", "\e[31m", "\e[0m", "\e[0m"}, // ERROR
237 { "\e[1;2;31;43m", "\e[1m", "\e[0m", "\e[0m"}, // WARN
238 { "\e[1;30;46m", "\e[1m", "\e[0m", "\e[0m"}, // NOTICE
239 { "\e[0;32m", "\e[0m", "\e[0m", "\e[0m"}, // INFO
240 { "\e[2;36m", "\e[2m", "\e[0m", "\e[0m"}, // DEBUG
241 { "\e[1;35m", "\e[0m", "\e[0m", "\e[0m"}, // NOTSET
242 };
243
244 struct SuffixComponent : public PatternLayout::iComponent {
245 int n;
246 SuffixComponent(int n_) : n(n_) {
247 assert(n < 4);
248 }
249
250 virtual void append_msg( const log4cpp::LoggingEvent & lev
251 , std::ostringstream & oss
252 ) override {
253 int numP = static_cast<int>(lev.priority);
254 if(numP > static_cast<int>(log4cpp::Priority::NOTSET)) {
255 numP = log4cpp::Priority::NOTSET;
256 }
257 numP /= 100;
258 assert((size_t) numP < sizeof(gColors)/sizeof(*gColors));
259 oss << gColors[numP].s[n];
260 }
261
262 static iComponent * create_title_begin(const std::unordered_map<std::string, std::string> &) {
263 auto ptr = new SuffixComponent(0);
264 return ptr;
265 }
266 static iComponent * create_text_begin(const std::unordered_map<std::string, std::string> &) {
267 auto ptr = new SuffixComponent(1);
268 return ptr;
269 }
270 static iComponent * create_title_end(const std::unordered_map<std::string, std::string> &) {
271 auto ptr = new SuffixComponent(2);
272 return ptr;
273 }
274 static iComponent * create_text_end(const std::unordered_map<std::string, std::string> &) {
275 auto ptr = new SuffixComponent(3);
276 return ptr;
277 }
278 };
279
280
281 //
282 // Layout
283
284 Log4cppLayoutFactoryResult_t
285 PatternLayout::create(const log4cpp::LayoutsFactory::params_t & params) {
286 PatternLayout * p = new PatternLayout();
287 auto it = params.find("pattern");
288 if(params.end() == it) {
289 throw std::runtime_error("Parameter \"pattern\" is not given.");
290 }
291 p->set_pattern(it->second);
292
293 Log4cppLayoutFactoryResult_t pp;
294 pp.reset(p);
295 return pp;
296 }
297
298 PatternLayout::PatternLayout() {
299 _constructors.emplace("category", CategoryComponent::create);
300 _constructors.emplace("message", MessageComponent::create);
301 _constructors.emplace("ndc", NDCComponent::create);
302 _constructors.emplace("priority", PriorityComponent::create);
303 _constructors.emplace("thread-name",ThreadNameComponent::create);
304 _constructors.emplace("cpu-time", CPUTimeComponent::create);
305 _constructors.emplace("run-time", TimeSinceStartComponent::create);
306 _constructors.emplace("endl", StaticComponent::create_endl);
307
308 _constructors.emplace("title-b", SuffixComponent::create_title_begin);
309 _constructors.emplace("title-e", SuffixComponent::create_title_end);
310 _constructors.emplace("txt-b", SuffixComponent::create_text_begin);
311 _constructors.emplace("txt-e", SuffixComponent::create_text_end);
312 }
313
314 std::string
315 PatternLayout::format(const log4cpp::LoggingEvent & lev) {
316 std::ostringstream oss;
317 for(auto cPtr : _components) {
318 cPtr->append_msg(lev, oss);
319 }
320 return oss.str();
321 }
322
323 static const std::regex gRxExprAsDelim(R"~(%\{[^}]+\}%)~")
324 , gRxFullExpression(R"~(%\{\s*([^:\s]+)\s*(?:\:\s*([^}]+)\s*)?\}%)~")
325 , gRxArgsExprDelim(R"~(\s*,\s*)~")
326 , gRxValuedOption(R"~(^([^\s]+)\s*=\s*(.+)$)~")
327 , gRxLogicOption(R"~([\-_a-zA-Z0-9]+)~")
328 ;
329
330 void
331 PatternLayout::set_pattern(const std::string & strexpr) {
332 // a "parameterized delimiter token" of the form %{...}%
333 std::sregex_token_iterator str(strexpr.begin(), strexpr.end()
334 , gRxExprAsDelim, {-1, 0});
335 // fill string with tokens splitted by "parameterized delimiter"
336 std::vector<std::string> tokens;
337 std::remove_copy_if( str
338 , std::sregex_token_iterator()
339 , std::back_inserter(tokens)
340 , [](std::string const &s) { return s.empty(); }
341 );
342 // get parameters from "p.delimiter"
343 std::smatch sm;
344 // every `p' here correspnds to a component
345 for( const auto & p : tokens ) {
346 if(!std::regex_match(p, sm, gRxFullExpression)) {
347 // no "name", static component
348 _components.push_back(new StaticComponent(p));
349 continue;
350 }
351 // retrieve group match by number directly from matchobject
352 const std::string componentName = sm[1]
353 , strArgsExpr = sm[2];
354 std::unordered_map<std::string, std::string> arguments;
355 if(!strArgsExpr.empty()) {
356 // split ("arguments") string by comma delimiter, trimming spaces
357 std::sregex_token_iterator firstArgIt{ strArgsExpr.begin()
358 , strArgsExpr.end()
359 , gRxArgsExprDelim
360 , -1 }
361 , lastArgIt;
362 for(auto argIt = firstArgIt; lastArgIt != argIt; ++argIt ) {
363 std::string optStr(argIt->str());
364 if(std::regex_match(optStr, gRxLogicOption)) {
365 // logic option, equivalent to <name>=true
366 auto ir = arguments.emplace(optStr, "true");
367 if(!ir.second) {
368 throw std::runtime_error("Option name \"" + optStr
369 + "\" given twice.");
370 }
371 } else if(std::regex_match(optStr, sm, gRxValuedOption)) {
372 auto ir = arguments.emplace(sm[1].str(), sm[2].str());
373 if(!ir.second) {
374 throw std::runtime_error("Option name \"" + sm[1].str()
375 + "\" given twice.");
376 }
377 } else {
378 throw std::runtime_error("Bad argument expression \"" + argIt->str() + "\"");
379 } // args loop
380 } // for every argument in list
381 } // if(!strArgsExpr.empty())
382 // instantiate component
383 auto ctrIt = _constructors.find(componentName);
384 if(_constructors.end() == ctrIt) {
385 throw std::runtime_error("No log layout component named \""
386 + componentName + "\".");
387 }
388 _components.push_back(ctrIt->second(arguments));
389 } // tokens
390 }
391
392 PatternLayout::~PatternLayout() {
393 for(auto c : _components) {
394 delete c;
395 }
396 }
397
398 } // namespace ::na64dp::util
399 } // namespace na64dp
400
401