Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  master (d98a2936)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
MessageSvcSink.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2025 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
12 #include <Gaudi/BaseSink.h>
14 #include <Gaudi/MonitoringHub.h>
15 #include <GaudiKernel/MsgStream.h>
16 #include <boost/algorithm/string.hpp>
17 #include <fmt/core.h>
18 #include <fmt/format.h>
19 #include <map>
20 #include <sstream>
21 #include <string_view>
22 
23 namespace {
35  static const auto registry = std::map<std::string_view, std::string_view>{
36  { "counter", "{0:nEntries|10d}" }, // all unknown counters, and default
37  { "counter:AveragingCounter", "{0:nEntries|10d} |{0:sum|11.7g} |{0:mean|#11.5g}" },
38  { "counter:SigmaCounter", "{0:nEntries|10d} |{0:sum|11.7g} |{0:mean|#11.5g} |{0:standard_deviation|#11.5g}" },
39  { "counter:StatCounter", "{0:nEntries|10d} |{0:sum|11.7g} |{0:mean|#11.5g} |{0:standard_deviation|#11.5g} "
40  "|{0:min|#12.5g} |{0:max|#12.5g}" },
41  { "counter:BinomialCounter",
42  "{0:nEntries|10d} |{0:nTrueEntries|11d} |({0:efficiency|#9.7p} +- {0:efficiencyErr|-#8.7p})%" },
43  };
44 
45  // the int says in which bunch of counters to put the histogram. We separate indeed
46  // regular counters (0) from 1d/2d/3d histogram (1, 2 and 3) from 1d/2d/3d profile
47  // histograms (4, 5 and 6)
48  using HistoBinHandler =
49  std::pair<unsigned int,
50  std::function<std::string( std::string_view, Gaudi::Monitoring::Hub::Entity const&, unsigned int )>>;
51  namespace Acc = ::Gaudi::Accumulators;
56  std::map<std::type_index, HistoBinHandler> const binRegistry = {
58  { 4, &Gaudi::Histograming::Sink::printProfileHisto1D<Acc::atomicity::full, double> } },
60  { 4, &Gaudi::Histograming::Sink::printProfileHisto1D<Acc::atomicity::none, double> } },
62  { 4, &Gaudi::Histograming::Sink::printProfileHisto1D<Acc::atomicity::full, float> } },
64  { 4, &Gaudi::Histograming::Sink::printProfileHisto1D<Acc::atomicity::none, float> } },
66  { 5, &Gaudi::Histograming::Sink::printProfileHisto2D<Acc::atomicity::full, double> } },
68  { 5, &Gaudi::Histograming::Sink::printProfileHisto2D<Acc::atomicity::none, double> } },
70  { 5, &Gaudi::Histograming::Sink::printProfileHisto2D<Acc::atomicity::full, float> } },
72  { 5, &Gaudi::Histograming::Sink::printProfileHisto2D<Acc::atomicity::none, float> } },
74  { 6, &Gaudi::Histograming::Sink::printProfileHisto3D<Acc::atomicity::full, double> } },
76  { 6, &Gaudi::Histograming::Sink::printProfileHisto3D<Acc::atomicity::none, double> } },
78  { 6, &Gaudi::Histograming::Sink::printProfileHisto3D<Acc::atomicity::full, float> } },
80  { 6, &Gaudi::Histograming::Sink::printProfileHisto3D<Acc::atomicity::none, float> } },
81  };
82 
83  // Helper to fix custom formatting of nlohmann::json version 3.10.5
84  // See https://gitlab.cern.ch/gaudi/Gaudi/-/issues/220
85  struct json_fmt_arg {
86  json_fmt_arg( const nlohmann::json& j ) : payload{ j } {}
87  const nlohmann::json& payload;
88  };
89 
90 } // namespace
91 
101 template <>
102 class fmt::formatter<json_fmt_arg> {
103 public:
104  template <typename ParseContext>
105  constexpr auto parse( ParseContext& ctx ) {
106  auto fmt_begin = ctx.begin();
107  auto fmt_end = std::find( fmt_begin, ctx.end(), '}' );
108  if ( fmt_begin == fmt_end ) {
109  // we are dealing with {}, only make sure currentFormat is empty
110  currentFormat = "";
111  } else {
112  // non empty format, let's split name from format
113  auto fmt_colon = std::find( fmt_begin, fmt_end, '|' );
114  currentName = std::string( fmt_begin, fmt_colon - fmt_begin );
115  currentFormat = std::string( fmt_colon + 1, fmt_end - fmt_colon - 1 );
116  }
117  return fmt_end;
118  }
119  template <typename FormatContext>
120  auto format( const json_fmt_arg& json_arg, FormatContext& ctx ) const {
121  const auto& j = json_arg.payload;
122  if ( currentFormat.size() == 0 ) {
123  // dealing with {} format, let's find entry for our type in registry
124  const auto type = j.at( "type" ).get<std::string>();
125  // first looking for the entry, then we drop on ":abc" suffix at a time and try again
126  std::string_view type_key{ type };
127  // look for the full entry
128  auto entry = registry.find( type_key );
129  // we check if we have type separators before entering the loop
130  auto sep = type_key.rfind( ':' );
131  while ( sep != type_key.npos && entry == registry.end() ) {
132  // not found, remove the trailing ":abc" section
133  type_key.remove_suffix( type_key.size() - sep );
134  // check if we have another chunk to strip
135  sep = type_key.rfind( ':' );
136  // see if the shorter key works
137  entry = registry.find( type_key );
138  }
139  // if still not found, we use the basic "counter"
140  if ( entry == registry.end() ) entry = registry.find( "counter" );
141  assert( entry != registry.end() );
142  // print the json string according to format found
143  // This actually will call this formatter again a number of times
144  return fmt::format_to( ctx.out(), fmt::runtime( entry->second ), json_arg );
145  } else {
146  // dealing with a {:name|fmt} format
147  auto actualFormat = "{:" + currentFormat + '}';
148  switch ( currentFormat.back() ) {
149  case 'd':
150  return fmt::format_to( ctx.out(), fmt::runtime( actualFormat ),
151  j.at( currentName ).template get<unsigned int>() );
152  case 'g':
153  return fmt::format_to( ctx.out(), fmt::runtime( actualFormat ), j.at( currentName ).template get<double>() );
154  case 'p':
155  actualFormat[actualFormat.size() - 2] = 'g';
156  return fmt::format_to( ctx.out(), fmt::runtime( actualFormat ),
157  j.at( currentName ).template get<double>() * 100 );
158  default:
159  return fmt::format_to( ctx.out(), "Unknown counter format : {}", currentFormat );
160  }
161  }
162  }
163 
164 private:
165  std::string currentFormat;
166  std::string currentName;
167 };
168 
169 namespace {
170 
171  void printCounter( std::ostringstream& log, std::string_view id, const nlohmann::json& j ) {
172  const auto type = j.at( "type" ).get<std::string>();
173  // for backward compatibility, we need to deal with statentity in a special way
174  // this block should be dropped when StatEntities are gone
175  if ( type == "statentity" ) {
176  using boost::algorithm::icontains;
177  bool isBinomial = icontains( id, "eff" ) || icontains( id, "acc" ) || icontains( id, "filt" ) ||
178  icontains( id, "fltr" ) || icontains( id, "pass" );
179  auto nj = j;
180  nj["type"] = isBinomial ? "counter:BinomialCounter" : "counter:StatCounter";
181  return printCounter( log, id, nj );
182  }
183  // binomial counters are slightly different ('*' character)
184  // fmt::runtime is required when compiling with GCC 11 but
185  // can be dropped when GCC 11 is no longer supported
186  log << fmt::format( fmt::runtime( " |{}{:48}|{} |" ),
187  ( std::string_view{ type }.substr( 0, 23 ) == "counter:BinomialCounter" ? '*' : ' ' ),
188  fmt::format( fmt::runtime( "\"{}\"" ), id ), json_fmt_arg{ j } );
189  }
190 
191 } // namespace
192 
193 namespace Gaudi::Monitoring {
194 
203  MessageSvcSink( const std::string& name, ISvcLocator* svcloc ) : BaseSink( name, svcloc ) {
204  // only deal with counters, statentity and histograms
205  setProperty( "TypesToSave", std::vector<std::string>{ "counter:.*", "statentity", "histogram:.*" } )
206  .orThrow( "Unable to set TypesToSave property", "Gaudi::Monitoring::MessageSvcSink" );
207  // require a non-empty name for the owner
208  setProperty( "ComponentsToSave", std::vector<std::string>{ "^.+$" } )
209  .orThrow( "Unable to set ComponentsToSave property", "Gaudi::Monitoring::MessageSvcSink" );
210  }
212  void flush( bool ) override;
213  Gaudi::Property<unsigned int> m_histoStringsWidth{ this, "HistoStringsWidth", 45,
214  "length of histograms names and titles in the output" };
215  };
216 
217  DECLARE_COMPONENT( MessageSvcSink )
218 } // namespace Gaudi::Monitoring
219 
221  std::string curAlgo = "";
222  // an array of counters, separating (in this order)
223  // regular counters from 1d / 2d / 3d histograms, from
224  // 1d / 2d / 3d profile histograms
225  std::array<std::ostringstream, 7> curLog;
226  // associated counters of non empty entities, in order
227  // to know whether to print or not the given section
228  std::array<unsigned int, 7> nbNonEmptyEntities{};
229  auto dumpAlgoCounters = [&]() {
230  MsgStream log{ msgSvc(), curAlgo };
231  // Regular counters first
232  if ( nbNonEmptyEntities[0] > 0 ) {
233  log << MSG::INFO << "Number of counters : " << nbNonEmptyEntities[0] << "\n"
234  << " | Counter | # | "
235  << " sum | mean/eff^* | rms/err^* | min | max |";
236  log << curLog[0].str() << endmsg;
237  }
238  // Now histograms
239  unsigned int nHistos = nbNonEmptyEntities[1] + nbNonEmptyEntities[2] + nbNonEmptyEntities[3] +
240  nbNonEmptyEntities[4] + nbNonEmptyEntities[5] + nbNonEmptyEntities[6];
241  if ( nHistos > 0 ) {
242  log << MSG::INFO << "Booked " << nHistos << " Histogram(s) : 1D=" << nbNonEmptyEntities[1]
243  << " 2D=" << nbNonEmptyEntities[2] << " 3D=" << nbNonEmptyEntities[3] << " 1DProf=" << nbNonEmptyEntities[4]
244  << " 2DProf=" << nbNonEmptyEntities[5] << " 3DProf=" << nbNonEmptyEntities[6] << endmsg;
245  if ( nbNonEmptyEntities[1] > 0 ) {
246  log << MSG::INFO << "1D histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[1] << "\n"
247  << fmt::format(
248  fmt::runtime(
249  " | {:{}s} | {:{}s} | # | Mean | RMS | Skewness | Kurtosis |" ),
250  "ID", m_histoStringsWidth.value(), "Title", m_histoStringsWidth.value() )
251  << curLog[1].str() << endmsg;
252  }
253  if ( nbNonEmptyEntities[2] > 0 ) {
254  log << MSG::INFO << "2D histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[2]
255  << curLog[2].str() << endmsg;
256  }
257  if ( nbNonEmptyEntities[3] > 0 ) {
258  log << MSG::INFO << "3D histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[3]
259  << curLog[3].str() << endmsg;
260  }
261  if ( nbNonEmptyEntities[4] > 0 ) {
262  log << MSG::INFO << "1D profile histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[4]
263  << "\n"
264  << fmt::format(
265  fmt::runtime(
266  " | {:{}s} | {:{}s} | # | Mean | RMS | Skewness | Kurtosis |" ),
267  "ID", m_histoStringsWidth.value(), "Title", m_histoStringsWidth.value() )
268  << curLog[4].str() << endmsg;
269  }
270  if ( nbNonEmptyEntities[5] > 0 ) {
271  log << MSG::INFO << "2D profile histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[5]
272  << curLog[5].str() << endmsg;
273  }
274  if ( nbNonEmptyEntities[6] > 0 ) {
275  log << MSG::INFO << "3D profile histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[6]
276  << curLog[6].str() << endmsg;
277  }
278  }
279  };
281  [&]( std::string const& algo, std::string const& name, Monitoring::Hub::Entity const& ent ) {
282  // Did we change to new component ?
283  if ( algo != curAlgo ) {
284  dumpAlgoCounters();
285  curAlgo = algo;
286  nbNonEmptyEntities = { 0, 0, 0, 0, 0, 0, 0 };
287  curLog = { std::ostringstream{}, std::ostringstream{}, std::ostringstream{}, std::ostringstream{},
288  std::ostringstream{}, std::ostringstream{}, std::ostringstream{} };
289  }
290  // first try dedicated binary printers for profile histograms
291  auto typeIndex = ent.typeIndex();
292  auto binWriter = binRegistry.find( typeIndex );
293  if ( binWriter != binRegistry.end() ) {
294  auto index = binWriter->second.first;
295  ++nbNonEmptyEntities[index];
296  std::string logLine = binWriter->second.second( name, ent, m_histoStringsWidth );
297  if ( logLine.size() > 0 ) { curLog[index] << "\n" << logLine; }
298  } else {
299  // use json representation of the entity
300  nlohmann::json const j = ent;
301  // do we have an histogram ?
302  const auto type = j.at( "type" ).get<std::string>();
303  if ( type.find( "histogram" ) == 0 ) {
304  if ( !j.at( "empty" ).template get<bool>() ) {
305  unsigned int d = j.at( "dimension" ).get<int>();
306  auto subtype = std::string_view( type ).substr( 10 ); // remove "histogram:" in front
307  bool isProfile = subtype.substr( 0, 15 ) == "WeightedProfile" || subtype.substr( 0, 7 ) == "Profile";
308  unsigned int index = ( isProfile ? 3 : 0 ) + d;
309  auto title = j.at( "title" ).get<std::string>();
310  std::string logLine{ "" };
311  switch ( d ) {
312  case 1:
314  break;
315  case 2:
317  break;
318  case 3:
320  break;
321  }
322  if ( logLine.size() > 0 ) {
323  curLog[index] << "\n" << logLine;
324  ++nbNonEmptyEntities[index];
325  }
326  }
327  } else {
328  // regular counter. Is current counter empty ?
329  if ( !j.at( "empty" ).template get<bool>() ) {
330  ++nbNonEmptyEntities[0];
331  curLog[0] << "\n";
332  printCounter( curLog[0], name, j );
333  }
334  }
335  }
336  } );
337  // last component
338  dumpAlgoCounters();
339 }
Gaudi::Monitoring::BaseSink
Base class for all Sinks registering to the Monitoring Hub Should be extended by actual Sinks.
Definition: BaseSink.h:40
Gaudi::Histograming::Sink::printHistogram2D
std::string printHistogram2D(std::string_view type, std::string_view name, std::string_view title, const nlohmann::json &j, unsigned int stringsWidth=45)
Definition: Utils.h:911
Gaudi.Configuration.log
log
Definition: Configuration.py:28
cpluginsvc.registry
def registry()
Definition: cpluginsvc.py:83
MSG::INFO
@ INFO
Definition: IMessageSvc.h:22
MonitoringHub.h
fmt::formatter< json_fmt_arg >::currentFormat
std::string currentFormat
Definition: MessageSvcSink.cpp:165
PropertyHolder< CommonMessaging< implements< IService, IProperty, IStateful > > >::setProperty
StatusCode setProperty(const Gaudi::Details::PropertyBase &p)
Set the property from a property.
Definition: IProperty.h:38
Gaudi::Accumulators::HistogramingCounterBase
A base counter dealing with Histograms.
Definition: StaticHistogram.h:660
ISvcLocator
Definition: ISvcLocator.h:42
jsonFromLHCbLog.json
json
Definition: jsonFromLHCbLog.py:86
Gaudi::Monitoring::MessageSvcSink::flush
void flush(bool) override
stop method, handles the printing
Definition: MessageSvcSink.cpp:220
Gaudi::Monitoring
Definition: JSONSink.cpp:19
AvalancheSchedulerErrorTest.msgSvc
msgSvc
Definition: AvalancheSchedulerErrorTest.py:80
Utils.h
Gaudi::Monitoring::MessageSvcSink
Sink dedicated to printing messages to the MessageSvc.
Definition: MessageSvcSink.cpp:202
GaudiPython.Pythonizations.ctx
ctx
Definition: Pythonizations.py:578
Service::name
const std::string & name() const override
Retrieve name of the service
Definition: Service.cpp:333
ProduceConsume.j
j
Definition: ProduceConsume.py:104
fmt::formatter< json_fmt_arg >::currentName
std::string currentName
Definition: MessageSvcSink.cpp:166
Gaudi::Property::value
const ValueType & value() const
Definition: Property.h:229
Gaudi::Histograming::Sink::printHistogram1D
std::string printHistogram1D(std::string_view type, std::string_view name, std::string_view title, const nlohmann::json &j, unsigned int stringsWidth=45)
Definition: Utils.h:867
Gaudi::Monitoring::Hub::Entity::typeIndex
std::type_index typeIndex() const
function to get internal type
Definition: MonitoringHub.h:87
format
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:93
endmsg
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:198
Gaudi::Monitoring::BaseSink::applyToAllSortedEntities
void applyToAllSortedEntities(Callable func) const
applies a callable to all monitoring entities ordered by component the callable will be called once p...
Definition: BaseSink.h:110
MsgStream
Definition: MsgStream.h:29
Gaudi::Accumulators
Definition: CounterArray.h:18
gaudirun.type
type
Definition: gaudirun.py:160
fmt::formatter< json_fmt_arg >::parse
constexpr auto parse(ParseContext &ctx)
Definition: MessageSvcSink.cpp:105
DECLARE_COMPONENT
#define DECLARE_COMPONENT(type)
Definition: PluginServiceV1.h:45
BaseSink.h
Gaudi::Histograming::Sink::printHistogram3D
std::string printHistogram3D(std::string_view type, std::string_view name, std::string_view title, const nlohmann::json &j, unsigned int stringsWidth=45)
Definition: Utils.h:956
Gaudi::Monitoring::MessageSvcSink::m_histoStringsWidth
Gaudi::Property< unsigned int > m_histoStringsWidth
Definition: MessageSvcSink.cpp:213
Gaudi::Monitoring::Hub::Entity
Wrapper class for arbitrary monitoring objects.
Definition: MonitoringHub.h:62
Gaudi::Monitoring::MessageSvcSink::MessageSvcSink
MessageSvcSink(const std::string &name, ISvcLocator *svcloc)
Definition: MessageSvcSink.cpp:203
fmt::formatter< json_fmt_arg >::format
auto format(const json_fmt_arg &json_arg, FormatContext &ctx) const
Definition: MessageSvcSink.cpp:120
Gaudi::Property< unsigned int >
MsgStream.h
Gaudi::ParticleProperties::index
size_t index(const Gaudi::ParticleProperty *property, const Gaudi::Interfaces::IParticlePropertySvc *service)
helper utility for mapping of Gaudi::ParticleProperty object into non-negative integral sequential id...
Definition: IParticlePropertySvc.cpp:39
StaticHistogram.h