The Gaudi Framework  v36r9p1 (5c15b2bb)
MessageSvcSink.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2019 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 
12 #include "Gaudi/MonitoringHub.h"
13 #include "GaudiKernel/MsgStream.h"
14 #include "GaudiKernel/Service.h"
15 
16 #include <boost/algorithm/string.hpp>
17 
18 #include <fmt/core.h>
19 #include <fmt/format.h>
20 
21 #include <deque>
22 #include <map>
23 #include <string_view>
24 
25 namespace {
26 
39  { "counter", "{0:nEntries|10d}" }, // all unknown counters, and default
40  { "histogram", "{0:nEntries|10d}" }, // all histograms
41  { "counter:AveragingCounter", "{0:nEntries|10d} |{0:sum|11.7g} |{0:mean|#11.5g}" },
42  { "counter:SigmaCounter", "{0:nEntries|10d} |{0:sum|11.7g} |{0:mean|#11.5g} |{0:standard_deviation|#11.5g}" },
43  { "counter:StatCounter", "{0:nEntries|10d} |{0:sum|11.7g} |{0:mean|#11.5g} |{0:standard_deviation|#11.5g} "
44  "|{0:min|#12.5g} |{0:max|#12.5g}" },
45  { "counter:BinomialCounter",
46  "{0:nEntries|10d} |{0:nTrueEntries|11d} |({0:efficiency|#9.7p} +- {0:efficiencyErr|-#8.7p})%" },
47  };
48 
49  // Helper to fix custom formatting of nlohmann::json version 3.10.5
50  // See https://gitlab.cern.ch/gaudi/Gaudi/-/issues/220
51  struct json_fmt_arg {
52  json_fmt_arg( const nlohmann::json& j ) : payload{ j } {}
53  const nlohmann::json& payload;
54  };
55 } // namespace
56 
66 template <>
67 class fmt::formatter<json_fmt_arg> {
68 public:
69  template <typename ParseContext>
70  constexpr auto parse( ParseContext& ctx ) {
71  auto fmt_begin = ctx.begin();
72  auto fmt_end = std::find( fmt_begin, ctx.end(), '}' );
73  if ( fmt_begin == fmt_end ) {
74  // we are dealing with {}, only make sure currentFormat is empty
75  currentFormat = "";
76  } else {
77  // non empty format, let's split name from format
78  auto fmt_colon = std::find( fmt_begin, fmt_end, '|' );
79  currentName = std::string( fmt_begin, fmt_colon - fmt_begin );
80  currentFormat = std::string( fmt_colon + 1, fmt_end - fmt_colon - 1 );
81  }
82  return fmt_end;
83  }
84  template <typename FormatContext>
85  auto format( const json_fmt_arg& json_arg, FormatContext& ctx ) {
86  const auto& j = json_arg.payload;
87  if ( currentFormat.size() == 0 ) {
88  // dealing with {} format, let's find entry for our type in registry
89  const auto type = j.at( "type" ).get<std::string>();
90  // first looking for the entry, then we drop on ":abc" suffix at a time and try again
91  std::string_view type_key{ type };
92  // look for the full entry
93  auto entry = registry.find( type_key );
94  // we check if we have type separators before entering the loop
95  auto sep = type_key.rfind( ':' );
96  while ( sep != type_key.npos && entry == registry.end() ) {
97  // not found, remove the trailing ":abc" section
98  type_key.remove_suffix( type_key.size() - sep );
99  // check if we have another chunk to strip
100  sep = type_key.rfind( ':' );
101  // see if the shorter key works
102  entry = registry.find( type_key );
103  }
104  // if still not found, we use the basic "counter"
105  if ( entry == registry.end() ) entry = registry.find( "counter" );
106  assert( entry != registry.end() );
107  // print the json string according to format found
108  // This actually will call this formatter again a number of times
109  return fmt::format_to( ctx.out(), entry->second, json_arg );
110  } else {
111  // dealing with a {:name|fmt} format
112  auto actualFormat = fmt::format( "{{:{}", currentFormat ) + "}";
113  switch ( currentFormat.back() ) {
114  case 'd':
115  return fmt::format_to( ctx.out(), actualFormat, j.at( currentName ).template get<unsigned int>() );
116  case 'g':
117  return fmt::format_to( ctx.out(), actualFormat, j.at( currentName ).template get<double>() );
118  case 'p':
119  actualFormat[actualFormat.size() - 2] = 'g';
120  return fmt::format_to( ctx.out(), actualFormat, j.at( currentName ).template get<double>() * 100 );
121  default:
122  return fmt::format_to( ctx.out(), "Unknown counter format : {}", currentFormat );
123  }
124  }
125  }
126 
127 private:
130 };
131 
132 namespace {
133 
134  template <typename stream>
135  stream printCounter( stream& log, const std::string& id, const nlohmann::json& j ) {
136  const auto type = j.at( "type" ).get<std::string>();
137  // for backward compatibility, we need to deal with statentity in a special way
138  // this block should be dropped when StatEntities are gone
139  if ( type == "statentity" ) {
140  using boost::algorithm::icontains;
141  bool isBinomial = icontains( id, "eff" ) || icontains( id, "acc" ) || icontains( id, "filt" ) ||
142  icontains( id, "fltr" ) || icontains( id, "pass" );
143  auto nj = j;
144  nj["type"] = isBinomial ? "counter:BinomialCounter" : "counter:StatCounter";
145  return printCounter( log, id, nj );
146  }
147  // binomial counters are slightly different ('*' character)
148  return log << fmt::format( " |{}{:48}|{} |",
149  ( std::string_view{ type }.substr( 0, 23 ) == "counter:BinomialCounter" ? '*' : ' ' ),
150  fmt::format( "\"{}\"", id ), json_fmt_arg{ j } );
151  }
152 
153 } // namespace
154 
155 namespace Gaudi::Monitoring {
156 
157  class MessageSvcSink : public Service, public Hub::Sink {
158 
159  public:
160  using Service::Service;
161 
163  StatusCode initialize() override {
164  return Service::initialize().andThen( [&] {
165  // declare ourself as a monitoding sink
166  serviceLocator()->monitoringHub().addSink( this );
167  } );
168  }
169 
171  StatusCode stop() override;
172 
173  // Gaudi::Monitoring::Hub::Sink implementation
174  void registerEntity( Hub::Entity ent ) override {
175  if ( std::string_view( ent.type ).substr( 0, 8 ) == "counter:" || ent.type == "statentity" ||
176  ent.type == "histogram" ) {
177  m_monitoringEntities.emplace_back( std::move( ent ) );
178  }
179  }
180 
181  // Gaudi::Monitoring::Hub::Sink implementation
182  void removeEntity( Hub::Entity const& ent ) override {
184  if ( it != m_monitoringEntities.end() ) { m_monitoringEntities.erase( it ); }
185  }
186 
187  private:
189  };
190 
192 } // namespace Gaudi::Monitoring
193 
195  // We will try to mimic the old monitoring of counters, so we need to split
196  // them per Algo. The algo name can be extracted form the id of the entity
197  // as its format is "algoName/counterName"
198  // This map groups entities per algoName. For each name, the submap gives
199  // the counter name of each subentity and the associated json
201  // fill the sorted map
202  for ( auto& entity : m_monitoringEntities ) { sortedEntities[entity.component][entity.name] = entity.toJSON(); }
203  // dump all counters
204  for ( auto& [algoName, entityMap] : sortedEntities ) {
205  // check first whether there is any counter to log
206  unsigned int nbCounters =
207  std::accumulate( begin( entityMap ), end( entityMap ), 0, []( const unsigned int& a, const auto& j ) {
208  return a + ( j.second.at( "empty" ).template get<bool>() ? 0 : 1 );
209  } );
210  if ( 0 == nbCounters ) continue;
211  MsgStream log{ msgSvc(), algoName };
212  log << MSG::INFO << "Number of counters : " << nbCounters << "\n"
213  << " | Counter | # | "
214  << " sum | mean/eff^* | rms/err^* | min | max |";
215  std::for_each( begin( entityMap ), end( entityMap ), [&log]( auto& p ) {
216  // Do not print empty counters
217  if ( !p.second.at( "empty" ).template get<bool>() ) {
218  log << "\n";
219  printCounter( log, p.first, p.second );
220  }
221  } );
222  log << endmsg;
223  }
224  return Service::stop();
225 }
Gaudi::Monitoring::MessageSvcSink::initialize
StatusCode initialize() override
initialization, registers to Monitoring::Hub
Definition: MessageSvcSink.cpp:163
Gaudi::Monitoring::Hub::Sink
Interface reporting services must implement.
Definition: MonitoringHub.h:156
std::for_each
T for_each(T... args)
Write.stream
stream
Definition: Write.py:32
Service::initialize
StatusCode initialize() override
Definition: Service.cpp:118
std::string
STL class.
Gaudi::Monitoring::MessageSvcSink
Definition: MessageSvcSink.cpp:157
Gaudi.Configuration.log
log
Definition: Configuration.py:28
Gaudi::Monitoring::MessageSvcSink::stop
StatusCode stop() override
stop method, handles the printing
Definition: MessageSvcSink.cpp:194
StatusCode::andThen
StatusCode andThen(F &&f, ARGS &&... args) const
Chain code blocks making the execution conditional a success result.
Definition: StatusCode.h:163
std::move
T move(T... args)
MSG::INFO
@ INFO
Definition: IMessageSvc.h:25
MonitoringHub.h
fmt::formatter< json_fmt_arg >::currentFormat
std::string currentFormat
Definition: MessageSvcSink.cpp:128
std::find
T find(T... args)
Gaudi::Monitoring
Definition: JSONSink.cpp:21
jsonFromLHCbLog.json
dictionary json
Definition: jsonFromLHCbLog.py:87
Service
Definition: Service.h:46
GaudiPluginService.cpluginsvc.registry
def registry()
Definition: cpluginsvc.py:84
GaudiPython.Pythonizations.ctx
ctx
Definition: Pythonizations.py:588
StatusCode
Definition: StatusCode.h:65
CLHEP::begin
double * begin(CLHEP::HepVector &v)
Definition: TupleAlg.cpp:45
fmt::formatter< json_fmt_arg >::currentName
std::string currentName
Definition: MessageSvcSink.cpp:129
Gaudi::Monitoring::MessageSvcSink::removeEntity
void removeEntity(Hub::Entity const &ent) override
Definition: MessageSvcSink.cpp:182
std::accumulate
T accumulate(T... args)
std::deque
STL class.
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:203
std::map
STL class.
Gaudi::Monitoring::MessageSvcSink::registerEntity
void registerEntity(Hub::Entity ent) override
Definition: MessageSvcSink.cpp:174
MsgStream
Definition: MsgStream.h:34
IOTest.end
def end
Definition: IOTest.py:128
Gaudi::Monitoring::MessageSvcSink::m_monitoringEntities
std::deque< Hub::Entity > m_monitoringEntities
Definition: MessageSvcSink.cpp:188
Service.h
gaudirun.type
type
Definition: gaudirun.py:160
fmt::formatter< json_fmt_arg >::parse
constexpr auto parse(ParseContext &ctx)
Definition: MessageSvcSink.cpp:70
Service::stop
StatusCode stop() override
Definition: Service.cpp:181
DECLARE_COMPONENT
#define DECLARE_COMPONENT(type)
Definition: PluginServiceV1.h:46
Service::Service
Service(std::string name, ISvcLocator *svcloc)
Standard Constructor
Definition: Service.cpp:339
AsyncIncidents.msgSvc
msgSvc
Definition: AsyncIncidents.py:34
Gaudi::Monitoring::Hub::Entity
Wrapper class for arbitrary monitoring objects.
Definition: MonitoringHub.h:89
MsgStream.h
Service::serviceLocator
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator
Definition: Service.cpp:335
Gaudi::Monitoring::Hub::Entity::type
std::string type
type of the entity, see comment above concerning its format and usage
Definition: MonitoringHub.h:109
fmt::formatter< json_fmt_arg >::format
auto format(const json_fmt_arg &json_arg, FormatContext &ctx)
Definition: MessageSvcSink.cpp:85