Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  master (f31105fd)
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-2024 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 \***********************************************************************************/
11 
13 #include <Gaudi/BaseSink.h>
15 #include <Gaudi/MonitoringHub.h>
16 #include <GaudiKernel/MsgStream.h>
17 
18 #include <boost/algorithm/string.hpp>
19 
20 #include <fmt/core.h>
21 #include <fmt/format.h>
22 
23 #include <map>
24 #include <sstream>
25 #include <string_view>
26 
27 namespace {
28 
41  { "counter", "{0:nEntries|10d}" }, // all unknown counters, and default
42  { "counter:AveragingCounter", "{0:nEntries|10d} |{0:sum|11.7g} |{0:mean|#11.5g}" },
43  { "counter:SigmaCounter", "{0:nEntries|10d} |{0:sum|11.7g} |{0:mean|#11.5g} |{0:standard_deviation|#11.5g}" },
44  { "counter:StatCounter", "{0:nEntries|10d} |{0:sum|11.7g} |{0:mean|#11.5g} |{0:standard_deviation|#11.5g} "
45  "|{0:min|#12.5g} |{0:max|#12.5g}" },
46  { "counter:BinomialCounter",
47  "{0:nEntries|10d} |{0:nTrueEntries|11d} |({0:efficiency|#9.7p} +- {0:efficiencyErr|-#8.7p})%" },
48  };
49 
50  // the int says in which bunch of counters to put the histogram. We separate indeed
51  // regular counters (0) from 1d/2d/3d histogram (1, 2 and 3) from 1d/2d/3d profile
52  // histograms (4, 5 and 6)
53  using HistoBinHandler =
54  std::pair<unsigned int,
55  std::function<std::string( std::string_view, Gaudi::Monitoring::Hub::Entity const&, unsigned int )>>;
56  namespace Acc = ::Gaudi::Accumulators;
61  std::map<std::type_index, HistoBinHandler> const binRegistry = {
63  { 4, &Gaudi::Histograming::Sink::printProfileHisto1D<Acc::atomicity::full, double> } },
65  { 4, &Gaudi::Histograming::Sink::printProfileHisto1D<Acc::atomicity::none, double> } },
67  { 4, &Gaudi::Histograming::Sink::printProfileHisto1D<Acc::atomicity::full, float> } },
69  { 4, &Gaudi::Histograming::Sink::printProfileHisto1D<Acc::atomicity::none, float> } },
71  { 5, &Gaudi::Histograming::Sink::printProfileHisto2D<Acc::atomicity::full, double> } },
73  { 5, &Gaudi::Histograming::Sink::printProfileHisto2D<Acc::atomicity::none, double> } },
75  { 5, &Gaudi::Histograming::Sink::printProfileHisto2D<Acc::atomicity::full, float> } },
77  { 5, &Gaudi::Histograming::Sink::printProfileHisto2D<Acc::atomicity::none, float> } },
79  { 6, &Gaudi::Histograming::Sink::printProfileHisto3D<Acc::atomicity::full, double> } },
81  { 6, &Gaudi::Histograming::Sink::printProfileHisto3D<Acc::atomicity::none, double> } },
83  { 6, &Gaudi::Histograming::Sink::printProfileHisto3D<Acc::atomicity::full, float> } },
85  { 6, &Gaudi::Histograming::Sink::printProfileHisto3D<Acc::atomicity::none, float> } },
86  };
87 
88  // Helper to fix custom formatting of nlohmann::json version 3.10.5
89  // See https://gitlab.cern.ch/gaudi/Gaudi/-/issues/220
90  struct json_fmt_arg {
91  json_fmt_arg( const nlohmann::json& j ) : payload{ j } {}
92  const nlohmann::json& payload;
93  };
94 
95 } // namespace
96 
106 template <>
107 class fmt::formatter<json_fmt_arg> {
108 public:
109  template <typename ParseContext>
110  constexpr auto parse( ParseContext& ctx ) {
111  auto fmt_begin = ctx.begin();
112  auto fmt_end = std::find( fmt_begin, ctx.end(), '}' );
113  if ( fmt_begin == fmt_end ) {
114  // we are dealing with {}, only make sure currentFormat is empty
115  currentFormat = "";
116  } else {
117  // non empty format, let's split name from format
118  auto fmt_colon = std::find( fmt_begin, fmt_end, '|' );
119  currentName = std::string( fmt_begin, fmt_colon - fmt_begin );
120  currentFormat = std::string( fmt_colon + 1, fmt_end - fmt_colon - 1 );
121  }
122  return fmt_end;
123  }
124  template <typename FormatContext>
125  auto format( const json_fmt_arg& json_arg, FormatContext& ctx ) const {
126  const auto& j = json_arg.payload;
127  if ( currentFormat.size() == 0 ) {
128  // dealing with {} format, let's find entry for our type in registry
129  const auto type = j.at( "type" ).get<std::string>();
130  // first looking for the entry, then we drop on ":abc" suffix at a time and try again
131  std::string_view type_key{ type };
132  // look for the full entry
133  auto entry = registry.find( type_key );
134  // we check if we have type separators before entering the loop
135  auto sep = type_key.rfind( ':' );
136  while ( sep != type_key.npos && entry == registry.end() ) {
137  // not found, remove the trailing ":abc" section
138  type_key.remove_suffix( type_key.size() - sep );
139  // check if we have another chunk to strip
140  sep = type_key.rfind( ':' );
141  // see if the shorter key works
142  entry = registry.find( type_key );
143  }
144  // if still not found, we use the basic "counter"
145  if ( entry == registry.end() ) entry = registry.find( "counter" );
146  assert( entry != registry.end() );
147  // print the json string according to format found
148  // This actually will call this formatter again a number of times
149  return fmt::format_to( ctx.out(), fmt::runtime( entry->second ), json_arg );
150  } else {
151  // dealing with a {:name|fmt} format
152  auto actualFormat = "{:" + currentFormat + '}';
153  switch ( currentFormat.back() ) {
154  case 'd':
155  return fmt::format_to( ctx.out(), fmt::runtime( actualFormat ),
156  j.at( currentName ).template get<unsigned int>() );
157  case 'g':
158  return fmt::format_to( ctx.out(), fmt::runtime( actualFormat ), j.at( currentName ).template get<double>() );
159  case 'p':
160  actualFormat[actualFormat.size() - 2] = 'g';
161  return fmt::format_to( ctx.out(), fmt::runtime( actualFormat ),
162  j.at( currentName ).template get<double>() * 100 );
163  default:
164  return fmt::format_to( ctx.out(), "Unknown counter format : {}", currentFormat );
165  }
166  }
167  }
168 
169 private:
172 };
173 
174 namespace {
175 
176  void printCounter( std::ostringstream& log, std::string_view id, const nlohmann::json& j ) {
177  const auto type = j.at( "type" ).get<std::string>();
178  // for backward compatibility, we need to deal with statentity in a special way
179  // this block should be dropped when StatEntities are gone
180  if ( type == "statentity" ) {
181  using boost::algorithm::icontains;
182  bool isBinomial = icontains( id, "eff" ) || icontains( id, "acc" ) || icontains( id, "filt" ) ||
183  icontains( id, "fltr" ) || icontains( id, "pass" );
184  auto nj = j;
185  nj["type"] = isBinomial ? "counter:BinomialCounter" : "counter:StatCounter";
186  return printCounter( log, id, nj );
187  }
188  // binomial counters are slightly different ('*' character)
189  // fmt::runtime is required when compiling with GCC 11 but
190  // can be dropped when GCC 11 is no longer supported
191  log << fmt::format( fmt::runtime( " |{}{:48}|{} |" ),
192  ( std::string_view{ type }.substr( 0, 23 ) == "counter:BinomialCounter" ? '*' : ' ' ),
193  fmt::format( fmt::runtime( "\"{}\"" ), id ), json_fmt_arg{ j } );
194  }
195 
196 } // namespace
197 
198 namespace Gaudi::Monitoring {
199 
208  MessageSvcSink( const std::string& name, ISvcLocator* svcloc ) : BaseSink( name, svcloc ) {
209  // only deal with counters, statentity and histograms
210  setProperty( "TypesToSave", std::vector<std::string>{ "counter:.*", "statentity", "histogram:.*" } )
211  .orThrow( "Unable to set TypesToSave property", "Gaudi::Monitoring::MessageSvcSink" );
212  // require a non-empty name for the owner
213  setProperty( "ComponentsToSave", std::vector<std::string>{ "^.+$" } )
214  .orThrow( "Unable to set ComponentsToSave property", "Gaudi::Monitoring::MessageSvcSink" );
215  }
217  void flush( bool ) override;
218  Gaudi::Property<unsigned int> m_histoStringsWidth{ this, "HistoStringsWidth", 45,
219  "length of histograms names and titles in the output" };
220  };
221 
222  DECLARE_COMPONENT( MessageSvcSink )
223 } // namespace Gaudi::Monitoring
224 
226  std::string curAlgo = "";
227  // an array of counters, separating (in this order)
228  // regular counters from 1d / 2d / 3d histograms, from
229  // 1d / 2d / 3d profile histograms
231  // associated counters of non empty entities, in order
232  // to know whether to print or not the given section
233  std::array<unsigned int, 7> nbNonEmptyEntities{};
234  auto dumpAlgoCounters = [&]() {
235  MsgStream log{ msgSvc(), curAlgo };
236  // Regular counters first
237  if ( nbNonEmptyEntities[0] > 0 ) {
238  log << MSG::INFO << "Number of counters : " << nbNonEmptyEntities[0] << "\n"
239  << " | Counter | # | "
240  << " sum | mean/eff^* | rms/err^* | min | max |";
241  log << curLog[0].str() << endmsg;
242  }
243  // Now histograms
244  unsigned int nHistos = nbNonEmptyEntities[1] + nbNonEmptyEntities[2] + nbNonEmptyEntities[3] +
245  nbNonEmptyEntities[4] + nbNonEmptyEntities[5] + nbNonEmptyEntities[6];
246  if ( nHistos > 0 ) {
247  log << MSG::INFO << "Booked " << nHistos << " Histogram(s) : 1D=" << nbNonEmptyEntities[1]
248  << " 2D=" << nbNonEmptyEntities[2] << " 3D=" << nbNonEmptyEntities[3] << " 1DProf=" << nbNonEmptyEntities[4]
249  << " 2DProf=" << nbNonEmptyEntities[5] << " 3DProf=" << nbNonEmptyEntities[6] << endmsg;
250  if ( nbNonEmptyEntities[1] > 0 ) {
251  log << MSG::INFO << "1D histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[1] << "\n"
252  << fmt::format(
253  fmt::runtime(
254  " | {:{}s} | {:{}s} | # | Mean | RMS | Skewness | Kurtosis |" ),
255  "ID", m_histoStringsWidth.value(), "Title", m_histoStringsWidth.value() )
256  << curLog[1].str() << endmsg;
257  }
258  if ( nbNonEmptyEntities[2] > 0 ) {
259  log << MSG::INFO << "2D histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[2]
260  << curLog[2].str() << endmsg;
261  }
262  if ( nbNonEmptyEntities[3] > 0 ) {
263  log << MSG::INFO << "3D histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[3]
264  << curLog[3].str() << endmsg;
265  }
266  if ( nbNonEmptyEntities[4] > 0 ) {
267  log << MSG::INFO << "1D profile histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[4]
268  << "\n"
269  << fmt::format(
270  fmt::runtime(
271  " | {:{}s} | {:{}s} | # | Mean | RMS | Skewness | Kurtosis |" ),
272  "ID", m_histoStringsWidth.value(), "Title", m_histoStringsWidth.value() )
273  << curLog[4].str() << endmsg;
274  }
275  if ( nbNonEmptyEntities[5] > 0 ) {
276  log << MSG::INFO << "2D profile histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[5]
277  << curLog[5].str() << endmsg;
278  }
279  if ( nbNonEmptyEntities[6] > 0 ) {
280  log << MSG::INFO << "3D profile histograms in directory \"" << curAlgo << "\" : " << nbNonEmptyEntities[6]
281  << curLog[6].str() << endmsg;
282  }
283  }
284  };
286  [&]( std::string const& algo, std::string const& name, Monitoring::Hub::Entity const& ent ) {
287  // Did we change to new component ?
288  if ( algo != curAlgo ) {
289  dumpAlgoCounters();
290  curAlgo = algo;
291  nbNonEmptyEntities = { 0, 0, 0, 0, 0, 0, 0 };
294  }
295  // first try dedicated binary printers for profile histograms
296  auto typeIndex = ent.typeIndex();
297  auto binWriter = binRegistry.find( typeIndex );
298  if ( binWriter != binRegistry.end() ) {
299  auto index = binWriter->second.first;
300  ++nbNonEmptyEntities[index];
301  std::string logLine = binWriter->second.second( name, ent, m_histoStringsWidth );
302  if ( logLine.size() > 0 ) { curLog[index] << "\n" << logLine; }
303  } else {
304  // use json representation of the entity
305  nlohmann::json const j = ent;
306  // do we have an histogram ?
307  const auto type = j.at( "type" ).get<std::string>();
308  if ( type.find( "histogram" ) == 0 ) {
309  if ( !j.at( "empty" ).template get<bool>() ) {
310  unsigned int d = j.at( "dimension" ).get<int>();
311  auto subtype = std::string_view( type ).substr( 10 ); // remove "histogram:" in front
312  bool isProfile = subtype.substr( 0, 15 ) == "WeightedProfile" || subtype.substr( 0, 7 ) == "Profile";
313  unsigned int index = ( isProfile ? 3 : 0 ) + d;
314  auto title = j.at( "title" ).get<std::string>();
315  std::string logLine{ "" };
316  switch ( d ) {
317  case 1:
319  break;
320  case 2:
322  break;
323  case 3:
325  break;
326  }
327  if ( logLine.size() > 0 ) {
328  curLog[index] << "\n" << logLine;
329  ++nbNonEmptyEntities[index];
330  }
331  }
332  } else {
333  // regular counter. Is current counter empty ?
334  if ( !j.at( "empty" ).template get<bool>() ) {
335  ++nbNonEmptyEntities[0];
336  curLog[0] << "\n";
337  printCounter( curLog[0], name, j );
338  }
339  }
340  }
341  } );
342  // last component
343  dumpAlgoCounters();
344 }
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:908
std::string
STL class.
Gaudi.Configuration.log
log
Definition: Configuration.py:28
cpluginsvc.registry
def registry()
Definition: cpluginsvc.py:83
MSG::INFO
@ INFO
Definition: IMessageSvc.h:25
MonitoringHub.h
std::pair
fmt::formatter< json_fmt_arg >::currentFormat
std::string currentFormat
Definition: MessageSvcSink.cpp:170
std::vector< std::string >
std::find
T find(T... args)
std::string::size
T size(T... args)
PropertyHolder< CommonMessaging< implements< IService, IProperty, IStateful > > >::setProperty
StatusCode setProperty(const Gaudi::Details::PropertyBase &p)
Set the property from a property.
Definition: IProperty.h:39
Gaudi::Accumulators::HistogramingCounterBase
A base counter dealing with Histograms.
Definition: StaticHistogram.h:660
ISvcLocator
Definition: ISvcLocator.h:46
jsonFromLHCbLog.json
json
Definition: jsonFromLHCbLog.py:86
Gaudi::Monitoring::MessageSvcSink::flush
void flush(bool) override
stop method, handles the printing
Definition: MessageSvcSink.cpp:225
std::type_index
Gaudi::Monitoring
Definition: JSONSink.cpp:19
std::function
AvalancheSchedulerErrorTest.msgSvc
msgSvc
Definition: AvalancheSchedulerErrorTest.py:80
Utils.h
Gaudi::Monitoring::MessageSvcSink
Sink dedicated to printing messages to the MessageSvc.
Definition: MessageSvcSink.cpp:207
GaudiPython.Pythonizations.ctx
ctx
Definition: Pythonizations.py:578
Service::name
const std::string & name() const override
Retrieve name of the service
Definition: Service.cpp:332
ProduceConsume.j
j
Definition: ProduceConsume.py:104
fmt::formatter< json_fmt_arg >::currentName
std::string currentName
Definition: MessageSvcSink.cpp:171
Gaudi::Property::value
const ValueType & value() const
Definition: Property.h:234
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:865
std::array
STL class.
Gaudi::Monitoring::Hub::Entity::typeIndex
std::type_index typeIndex() const
function to get internal type
Definition: MonitoringHub.h:90
format
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
endmsg
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:202
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
std::map
STL class.
MsgStream
Definition: MsgStream.h:33
std::ostringstream
STL class.
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:110
DECLARE_COMPONENT
#define DECLARE_COMPONENT(type)
Definition: PluginServiceV1.h:46
BaseSink.h
std::map::end
T end(T... args)
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:952
Gaudi::Monitoring::MessageSvcSink::m_histoStringsWidth
Gaudi::Property< unsigned int > m_histoStringsWidth
Definition: MessageSvcSink.cpp:218
Gaudi::Monitoring::Hub::Entity
Wrapper class for arbitrary monitoring objects.
Definition: MonitoringHub.h:65
Gaudi::Monitoring::MessageSvcSink::MessageSvcSink
MessageSvcSink(const std::string &name, ISvcLocator *svcloc)
Definition: MessageSvcSink.cpp:208
fmt::formatter< json_fmt_arg >::format
auto format(const json_fmt_arg &json_arg, FormatContext &ctx) const
Definition: MessageSvcSink.cpp:125
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