ATLAS Offline Software
Loading...
Searching...
No Matches
TrigMessageSvc.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4#include "TrigMessageSvc.h"
5#include "GaudiKernel/IAppMgrUI.h"
6#include "GaudiKernel/IIncidentSvc.h"
7#include "GaudiKernel/ITHistSvc.h"
8#include "GaudiKernel/Message.h"
9#include "GaudiKernel/StatusCode.h"
10#include "GaudiKernel/System.h"
13
14#include "ers/ers.h"
15
16#include <fstream>
17#include <iostream>
18#include <sstream>
19
20// Declare ERS issue type
21ERS_DECLARE_ISSUE(ers, HLTMessage, , )
22
23static const std::string levelNames[MSG::NUM_LEVELS] = {"NIL", "VERBOSE", "DEBUG", "INFO",
24 "WARNING", "ERROR", "FATAL", "ALWAYS"};
25
26namespace {
28 size_t msgHash(const Message& msg)
29 {
30 std::string s = msg.getSource() + msg.getMessage();
31 s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return std::isdigit(c); }), s.end());
32 return std::hash<std::string>()(s);
33 }
34} // namespace
35
36TrigMessageSvc::TrigMessageSvc(const std::string& name, ISvcLocator* svcloc) :
37 base_class(name, svcloc)
38{
39 m_outputLevel.declareUpdateHandler([svcloc](Gaudi::Details::PropertyBase&) {
40 SmartIF<IAppMgrUI> app = svcloc;
41 if (app) app->outputLevelUpdate();
42 });
43
44 for (int ic = 0; ic < MSG::NUM_LEVELS; ++ic) {
45 m_msgLimit[ic].declareUpdateHandler(&TrigMessageSvc::setupLimits, this);
46 m_thresholdProp[ic].declareUpdateHandler(&TrigMessageSvc::setupThreshold, this);
47 }
48
49 std::fill(std::begin(m_msgCount), std::end(m_msgCount), 0);
50}
51
53{
54 StatusCode sc = Service::initialize();
55 if (sc.isFailure()) return sc;
56
57 ServiceHandle<IIncidentSvc> incSvc("IncidentSvc", name());
58 sc = incSvc.retrieve();
59 if (sc.isFailure()) {
60 reportMessage(name(), MSG::WARNING, "Cannot find IncidentSvc");
61 }
62 else {
63 incSvc->addListener(this, AthenaInterprocess::UpdateAfterFork::type());
64 }
65
67
68 if (m_color) {
69 std::cout << "TrigMessageSvc WARNING: Colors are not supported by TrigMessageSvc" << std::endl;
70 }
71 return StatusCode::SUCCESS;
72}
73
75{
76 m_state = Gaudi::StateMachine::OFFLINE;
77 StatusCode sc = initialize();
78 if ( sc.isSuccess() ) m_state = Gaudi::StateMachine::INITIALIZED;
79 return sc;
80}
81
83{
85 m_doPublish = true;
87 return StatusCode::SUCCESS;
88}
89
91{
92 // Disable asynchronous reporting again
93 if (m_asyncReporting) {
94 m_messageActionsQueue.emplace( [this]() {m_asyncReporting = false;} );
95 reportMessage(name(), MSG::INFO, "Disabling asynchronous message reporting");
96 m_thread.join();
97 }
98
99 m_doPublish = false;
101 return StatusCode::SUCCESS;
102}
103
104void TrigMessageSvc::handle(const Incident& incident)
105{
106 if (incident.type() == AthenaInterprocess::UpdateAfterFork::type()) {
107 reportMessage(name(), MSG::INFO, "Enabling asynchronous message reporting");
108 m_thread = std::thread( &TrigMessageSvc::asyncReporting, this);
109 }
110}
111
113{
114 ServiceHandle<ITHistSvc> histSvc("THistSvc", name());
115 if ( histSvc.retrieve().isFailure() ) {
116 reportMessage(name(), MSG::WARNING, "Cannot find THistSvc. Message stats will not be published.");
117 m_doPublish = false;
118 return;
119 }
120
121 // monitoring information root directory
122 const std::string path = "/EXPERT/HLTFramework/" + name() + "/";
123 const int nLevelBins = MSG::NUM_LEVELS - m_publishLevel;
124 m_msgCountHist = new TH1I("MessageCount", "Messages while RUNNING;Severity;Count",
125 nLevelBins, 0, nLevelBins);
126
127 const int nSrcBins = 1;
128 m_msgCountSrcHist = new TH2I("MessageCountBySource", "Messages while RUNNING;Severity;Source",
129 nLevelBins, 0, nLevelBins, nSrcBins, 0, nSrcBins);
130
131 for (int i=m_publishLevel; i<MSG::NUM_LEVELS; i++) {
132 m_msgCountHist->GetXaxis()->SetBinLabel(i-m_publishLevel+1, levelNames[i].c_str());
133 m_msgCountSrcHist->GetXaxis()->SetBinLabel(i-m_publishLevel+1, levelNames[i].c_str());
134 }
135
136 if ( histSvc->regHist(path + m_msgCountHist->GetName(), m_msgCountHist).isFailure() ) {
137 reportMessage(name(), MSG::WARNING, "Cannot register monitoring histogram 'MessageCount'");
138 }
139 if ( histSvc->regHist(path + m_msgCountSrcHist->GetName(), m_msgCountSrcHist).isFailure() ) {
140 reportMessage(name(), MSG::WARNING, "Cannot register monitoring histogram 'MessageCountBySource'");
141 }
142}
143
144
145void TrigMessageSvc::setupLimits(Gaudi::Details::PropertyBase& prop)
146{
147 // Just report problems in the settings of the limits and unknown limit
148 // parameters
149 if (prop.name() == "alwaysLimit") {
150 Gaudi::Property<int>* p = dynamic_cast<Gaudi::Property<int>*>(&prop);
151 if (p && p->value() != 0) {
152 std::cout << "TrigMessageSvc ERROR: cannot suppress ALWAYS messages" << std::endl;
153 p->setValue(0);
154 }
155 }
156 else if (prop.name() == "defaultLimit") {
157 for (int i = MSG::VERBOSE; i < MSG::NUM_LEVELS; ++i) {
158 if (i != MSG::ALWAYS) {
159 m_msgLimit[i] = m_msgLimit[MSG::NIL].value();
160 }
161 }
162 }
163 else if (prop.name() != "fatalLimit" && prop.name() != "errorLimit" &&
164 prop.name() != "warningLimit" && prop.name() == "infoLimit" &&
165 prop.name() == "debugLimit" && prop.name() == "verboseLimit") {
166 std::cout << "TrigMessageSvc ERROR: Unknown message limit parameter: " << prop.name()
167 << std::endl;
168 return;
169 }
170}
171
172void TrigMessageSvc::setupThreshold(Gaudi::Details::PropertyBase& prop)
173{
174 static const std::array<std::pair<const char*, MSG::Level>, 7> tbl{{{"setFatal", MSG::FATAL},
175 {"setError", MSG::ERROR},
176 {"setWarning", MSG::WARNING},
177 {"setInfo", MSG::INFO},
178 {"setDebug", MSG::DEBUG},
179 {"setVerbose", MSG::VERBOSE},
180 {"setAlways", MSG::ALWAYS}}};
181
182 auto i = std::find_if(
183 std::begin(tbl), std::end(tbl),
184 [&](const std::pair<const char*, MSG::Level>& t) { return prop.name() == t.first; });
185 if (i == std::end(tbl)) {
186 std::cerr << "TrigMessageSvc ERROR: Unknown message threshold parameter: " << prop.name()
187 << std::endl;
188 return;
189 }
190 int ic = i->second;
191
192 Gaudi::Property<std::vector<std::string>>* sap =
193 dynamic_cast<Gaudi::Property<std::vector<std::string>>*>(&prop);
194 if (!sap) {
195 std::cerr << "could not dcast " << prop.name()
196 << " to a Gaudi::Property<std::vector<std::string>> (which it "
197 "should be!)"
198 << std::endl;
199 }
200 else {
201 for (auto& i : sap->value()) setOutputLevel(i, ic);
202 }
203}
204
206{
207 m_suppress = false;
208 std::ostringstream os;
209
210 if (m_stats) {
211 os << "Summarizing all message counts"
212 << " (severity >= " << levelNames[m_statLevel] << ")" << std::endl;
213 }
214 else {
215 os << "Listing sources of suppressed message: " << std::endl;
216 }
217
218 os << "=====================================================" << std::endl;
219 os << " Message Source | Level | Count" << std::endl;
220 os << "-----------------------------+---------+-------------" << std::endl;
221
222 bool found(false);
223
224 for (auto itr = m_sourceMap.begin(); itr != m_sourceMap.end(); ++itr) {
225 for (unsigned int ic = 0; ic < MSG::NUM_LEVELS; ++ic) {
226 if ((m_suppress && itr->second.msg[ic] >= abs(m_msgLimit[ic]) && m_msgLimit[ic] != 0) ||
227 (m_stats && itr->second.msg[ic] > 0 && ic >= m_statLevel.value())) {
228 os << " ";
229 os.width(28);
230 os.setf(std::ios_base::left, std::ios_base::adjustfield);
231 os << itr->first;
232 os << "|";
233
234 os.width(8);
235 os.setf(std::ios_base::right, std::ios_base::adjustfield);
236 os << levelNames[ic];
237 os << " |";
238
239 os.width(9);
240 os << itr->second.msg[ic];
241 os << std::endl;
242
243 found = true;
244 }
245 }
246 }
247 os << "=====================================================" << std::endl;
248 if (found || m_stats) {
249 reportMessage(name(), MSG::INFO, os.str());
250 }
251
252 return StatusCode::SUCCESS;
253}
254
256{
257 if (m_asyncReporting) {
258 // msg has to be copied as the reference may become invalid by the time it is used
259 //coverity[PASS_BY_VALUE]
260 m_messageActionsQueue.emplace([this, m=Message(msg), outputLevel]() {
261 this->i_reportMessage(m, outputLevel); });
262 }
263 else {
265 }
266}
267
269{
270 m_asyncReporting = true;
271 std::function<void()> action;
272 while ( m_asyncReporting || !m_messageActionsQueue.empty() ) {
273 m_messageActionsQueue.pop(action);
274 action();
275 }
276}
277
278
283{
284 const int key = msg.getType();
285 ++m_msgCount[key];
286
287 const Message* cmsg = &msg;
288 bool doPrint = true;
289 std::unique_ptr<Message> newMessage;
290
291 if (m_doSuppress || m_stats.value()) {
292
293 // Increase the counter of 'key' type of messages for the source and
294 // get the new value.
295 int nmsg = ++(m_sourceMap[msg.getSource()].msg[key]);
296
297 const int msgLimit = m_msgLimit[key].value();
298 if (m_doSuppress) {
299 if (msgLimit > 0) { // regular suppression
300 if (nmsg > msgLimit) doPrint = false;
301 if (nmsg == msgLimit) {
302 std::string txt = levelNames[key] + " message limit (" + std::to_string(msgLimit) +
303 ") reached for " + msg.getSource() + ". Suppressing further output.";
304 newMessage = std::make_unique<Message>(msg.getSource(), MSG::WARNING, std::move(txt));
305 cmsg = newMessage.get();
306 }
307 }
308 }
309 else if (msgLimit < 0) { // logarithmic suppression
310 // Calculate message hash
311 const unsigned int mh = msgHash(*cmsg);
312 // Check if we saw this message already and increase counter
313 auto m = m_msgHashCount.find(mh);
314 if (m != m_msgHashCount.end()) {
315 nmsg = ++m->second;
316 }
317 else {
318 nmsg = m_msgHashCount[mh] = 1;
319 }
320 if (nmsg == abs(msgLimit)) {
321 std::ostringstream os;
322 os << msg.getMessage() << " [Message limit (" << abs(msgLimit)
323 << ") reached. Log-suppression of further output.]";
324 newMessage = std::make_unique<Message>(msg.getSource(), msg.getType(), os.str());
325 cmsg = newMessage.get();
326 }
327 else if (nmsg > abs(msgLimit)) {
328 const int everyNth = (int)exp10((int)log10(nmsg));
329 if ((nmsg % everyNth) == 0) {
330 std::ostringstream os;
331 os << msg.getMessage() << " [suppressed " << everyNth << " similar messages]";
332 newMessage = std::make_unique<Message>(msg.getSource(), msg.getType(), os.str());
333 cmsg = newMessage.get();
334 }
335 }
336 }
337 }
338
339 // Print the message
340 if (doPrint && key >= outputLevel) {
341 if (m_eventIDLevel != MSG::NIL && key >= static_cast<int>(m_eventIDLevel)) {
342 cmsg->setFormat(m_defaultFormat + " %E");
343 }
344 else {
345 cmsg->setFormat(m_defaultFormat);
346 }
347 cmsg->setTimeFormat(m_defaultTimeFormat);
348 (*m_defaultStream) << *cmsg << std::endl << std::flush;
349
350 // ERS forwarding
351 if (passErsFilter(cmsg->getSource(), m_useERS[key]) && passErsLimit(*cmsg)) {
352 i_reportERS(*cmsg);
353 }
354 }
355
356 // Publish message statistics if enabled and only while RUNNING
357 if ( m_doPublish && key>=static_cast<int>(m_publishLevel) ) {
358 m_msgCountHist->Fill(key-m_publishLevel, 1);
359 if (m_msgCountSrcHist->GetYaxis()->FindFixBin(msg.getSource().c_str())<0) [[unlikely]] {
360 // Adding bins on the fly needs to be protected by mutex
362 m_msgCountSrcHist->Fill(key-m_publishLevel, msg.getSource().c_str(), 1);
363 m_msgCountSrcHist->LabelsDeflate("Y");
364 }
365 else {
366 m_msgCountSrcHist->Fill(key-m_publishLevel, msg.getSource().c_str(), 1);
367 }
368 }
369}
370
374void TrigMessageSvc::i_reportERS(const Message& msg) const
375{
376 /*
377 * Create ERS context object
378 *
379 * The (cross-node) MRS throttling is based on filename+line_number, i.e.
380 * ignoring the message text itself. We therefor use the message source as
381 * filename and the message hash as line_number. That way the same message
382 * from different nodes gets properly throttled by MRS.
383 */
384 const char* filename = msg.getSource().c_str();
385 const char* function_name = "";
386 const int line_number = msgHash(msg);
387 const char* package_name = "HLT";
388 ers::LocalContext hlt_context_info(package_name, filename, line_number, function_name);
389
390 // Create ERS issue object
391 Message m(msg);
392 if (m_eventIDLevel != MSG::NIL && msg.getType() >= static_cast<int>(m_eventIDLevel)) {
393 m.setFormat(m_ersFormat + " %E");
394 }
395 else {
396 m.setFormat(m_ersFormat);
397 }
398 std::ostringstream oss;
399 oss << m;
400 ers::HLTMessage ersMsg(hlt_context_info, oss.str());
401 ersMsg.add_qualifier("HLT"); // useful for filtering
402
403 // Forward Message to ERS
404 switch (msg.getType()) {
405 case MSG::NIL: break;
406 case MSG::VERBOSE: ers::debug(ersMsg, 2); break;
407 case MSG::DEBUG: ers::debug(ersMsg, 1); break;
408 case MSG::INFO: ers::info(ersMsg); break;
409 case MSG::WARNING: ers::warning(ersMsg); break;
410 case MSG::ERROR: ers::error(ersMsg); break;
411 case MSG::FATAL: ers::fatal(ersMsg); break;
412 default:
413 std::ostringstream oss;
414 oss << "Unknown message severity level: " << msg.getType() << " Original message was: " << m;
415 ers::error(ers::HLTMessage(ERS_HERE, oss.str()));
416 }
417}
418
420{
421 reportMessage(msg, outputLevel(msg.getSource()));
422}
423
424void TrigMessageSvc::reportMessage(std::string source, int type, std::string message)
425{
426 reportMessage(Message{std::move(source), type, std::move(message)});
427}
428
430{
431 return m_outputLevel;
432}
433
434int TrigMessageSvc::outputLevel(std::string_view source) const
435{
436 std::unique_lock<std::recursive_mutex> lock(m_thresholdMapMutex);
437 auto it = m_thresholdMap.find(source);
438 return it != m_thresholdMap.end() ? it->second : m_outputLevel.value();
439}
440
442{
443 m_outputLevel = new_level;
444}
445
446void TrigMessageSvc::setOutputLevel(std::string_view source, int level)
447{
448 std::unique_lock<std::recursive_mutex> lock(m_thresholdMapMutex);
449
450 // only write if we really have to...
451 auto i = m_thresholdMap.find( source );
452 if ( i == m_thresholdMap.end() ) {
453 m_thresholdMap.emplace( source, level );
454 } else if ( i->second != level ) {
455 i->second = level;
456 }
457}
458
459int TrigMessageSvc::messageCount(MSG::Level level) const
460{
461 return m_msgCount[level];
462}
463
464bool TrigMessageSvc::passErsFilter(const std::string& source,
465 const std::vector<std::string>& filter) const
466{
467 if (filter.empty()) return false; // forward none
468 auto it = filter.begin();
469 if (filter.size() == 1 && (*it) == "*") return true; // forward all
470
471 bool pass(false);
472 for (; it != filter.end(); ++it) {
473 if ((*it) == "*") pass = true; // forward except if there is a veto later
474 if (source == (*it)) return true; // forward specific source
475 if ("!" + source == (*it)) return false; // veto specific source
476 }
477 return pass;
478}
479
481{
482 if (m_ersEventLimit < 0) return true;
483
484 const EventContext::ContextID_t slot = msg.getEventSlot();
485 const EventContext::ContextEvt_t evt = msg.getEventNumber();
486 // get or create message statistics for this slot
487 auto [itr, inserted] = m_slotMsgCount.insert( {slot, {evt, MsgAry()} } );
488
489 // if new event in slot then reset counters
490 if ( itr->second.first != evt ) {
491 itr->second = {evt, MsgAry()};
492 }
493
494 // increment number of messages for this level and slot
495 const int N = ++itr->second.second.msg[msg.getType()];
496
497 return N <= m_ersEventLimit;
498}
static constexpr std::string levelNames[MSG::NUM_LEVELS]
virtual void lock()=0
Interface to allow an object to lock itself when made const in SG.
static Double_t sc
OH histogram lock header file.
static const std::string & type()
Incident type.
Definition Incidents.h:49
bool passErsFilter(const std::string &source, const std::vector< std::string > &filter) const
Gaudi::Property< std::string > m_ersFormat
tbb::concurrent_bounded_queue< std::function< void()> > m_messageActionsQueue
void setupLimits(Gaudi::Details::PropertyBase &prop)
virtual int outputLevel() const override
virtual StatusCode finalize() override
void i_reportMessage(const Message &msg, int outputLevel)
Internal implementation of reportMessage(const Message&,int) without lock.
bool passErsLimit(const Message &msg)
std::array< Gaudi::Property< int >, MSG::NUM_LEVELS > m_msgLimit
virtual StatusCode start() override
Gaudi::Property< std::string > m_defaultTimeFormat
std::map< std::string, MsgAry > m_sourceMap
counts per source
std::map< size_t, unsigned int > m_msgHashCount
counts per message hash
std::array< Gaudi::Property< std::vector< std::string > >, MSG::NUM_LEVELS > m_thresholdProp
std::unordered_map< EventContext::ContextID_t, std::pair< EventContext::ContextEvt_t, MsgAry > > m_slotMsgCount
counts per slot and level
std::array< Gaudi::Property< std::vector< std::string > >, MSG::NUM_LEVELS > m_useERS
Special properties to control output to ERS of individual sources.
Gaudi::Property< bool > m_color
std::recursive_mutex m_thresholdMapMutex
virtual void reportMessage(const Message &message) override
TH1I * m_msgCountHist
Message counting per level histogram.
void i_reportERS(const Message &msg) const
Report message to online messaging system (ERS).
bool m_doPublish
are we publishing message statistics?
Gaudi::Property< std::string > m_defaultFormat
ThresholdMap m_thresholdMap
Output level threshold map.
void setupThreshold(Gaudi::Details::PropertyBase &prop)
Gaudi::Property< bool > m_stats
virtual StatusCode stop() override
Gaudi::Property< bool > m_suppressRunningOnly
virtual int messageCount(MSG::Level logLevel) const override
bool m_doSuppress
is suppression currently enabled?
TH2I * m_msgCountSrcHist
Message counting per message source.
Gaudi::Property< int > m_ersEventLimit
Gaudi::Property< unsigned int > m_statLevel
virtual void setOutputLevel(int new_level) override
std::array< int, MSG::NUM_LEVELS > m_msgCount
counts per level
virtual StatusCode reinitialize() override
virtual void handle(const Incident &incident) override
Gaudi::Property< unsigned int > m_eventIDLevel
Gaudi::Property< bool > m_suppress
std::thread m_thread
Thread for asynchronous reporting.
Gaudi::Property< unsigned int > m_publishLevel
virtual StatusCode initialize() override
TrigMessageSvc(const std::string &name, ISvcLocator *svcloc)
Scoped lock to be used for threaded histogram operations.
static char * package_name
Definition cmdline.cxx:754
ERS_DECLARE_ISSUE(HLTMPPUIssue, EformatIssue, "Eformat Issue: \""<< type<< "\".",((const char *) type)) std
Definition MsgLevel.h:28
STL namespace.
DataModel_detail::iterator< DVL > remove_if(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end, Predicate pred)
Specialization of remove_if for DataVector/List.
#define unlikely(x)
Private helper class to keep the count of messages of a type (MSG::LEVEL).
MsgStream & msg
Definition testRead.cxx:32