otsdaq-utilities  3.02.00
ConsoleSupervisor.h
1 #ifndef _ots_ConsoleSupervisor_h_
2 #define _ots_ConsoleSupervisor_h_
3 
4 #include <boost/regex.hpp>
5 #include <boost/tokenizer.hpp>
6 #include "otsdaq/CoreSupervisors/CoreSupervisorBase.h"
7 
8 #include <mutex> //for std::mutex
9 #include <queue> //for std::queue
10 
11 namespace ots
12 {
13 
14 // clang-format off
15 
21 {
22  public:
23  XDAQ_INSTANTIATOR();
24 
26  {
27  std::vector<std::string> needleSubstrings; /* custom trigger needle substrings */
28  std::string action; /* action */
29  size_t triggeredMessageCountIndex = -1; /* message arrival count that fired the trigger */
30  size_t occurrences = 0;
31  size_t triggerOnCount = 1; /* number of times event occurs before triggering action */
32  bool doLoop = 0; /* Wether to keep triggering on further occurrences */
33  bool isArmed = 0; /* Wether to count occurrences towards trigger*/
34 
35  }; //end CustomTriggeredAction_t struct
36 
37 
38  ConsoleSupervisor (xdaq::ApplicationStub* s);
39  virtual ~ConsoleSupervisor (void);
40 
41  void init (void);
42  void destroy (void);
43 
44  xoap::MessageReference resetConsoleCounts (xoap::MessageReference message);
45 
46  virtual void defaultPage (xgi::Input* in, xgi::Output* out) override;
47  virtual void request (const std::string& requestType,
48  cgicc::Cgicc& cgiIn,
49  HttpXmlDocument& xmlOut,
50  const WebUsers::RequestUserInfo& userInfo) override;
51  virtual std::string getStatusProgressDetail (void) override;
52 
53  virtual void forceSupervisorPropertyValues (void) override;
57 
58  void doTriggeredAction (const CustomTriggeredAction_t& triggeredAction);
59 
60  std::atomic<bool> customTriggerActionThreadExists_ = false;
61  static const std::set<std::string> CUSTOM_TRIGGER_ACTIONS;
62 
63  private:
64  static void messageFacilityReceiverWorkLoop (ConsoleSupervisor* cs);
65  static void customTriggerActionThread (ConsoleSupervisor* cs);
66  void insertMessageRefresh (HttpXmlDocument* xmldoc, const size_t lastUpdateCount);
67  void prependHistoricMessages (HttpXmlDocument* xmlOut, const size_t earliestOnhandMessageCount);
68 
69  void addCustomTriggeredAction (const std::string& triggerNeedle,
70  const std::string& triggerAction,
71  uint32_t priority = -1,
72  uint32_t triggerOnCount = 1,
73  bool doLoop = false,
74  bool isArmed = false);
75  uint32_t modifyCustomTriggeredAction (const std::string& currentNeedle,
76  const std::string& modifyType,
77  const std::string& setNeedle,
78  const std::string& setAction,
79  uint32_t setPriority,
80  uint32_t setTriggerOnCount,
81  bool setDoLoop,
82  bool setIsArmed);
83 
84  void loadCustomCountList (void);
85  void saveCustomCountList (void);
86 
87  public:
92  {
93  ConsoleMessageStruct(const std::string& msg, const size_t count,
94  std::vector<CustomTriggeredAction_t>& priorityCustomTriggerList)
95  : countStamp(count)
96  {
97  boost::regex timestamp_regex_("(\\d{2}-[^-]*-\\d{4}\\s\\d{2}:\\d{2}:\\d{2})");
98  boost::regex file_line_regex_("^\\s*([^:]*\\.[^:]{1,3}):(\\d+)(.*)");
99 
100  boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
101  typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
102  tokenizer tokens(msg, sep);
103  tokenizer::iterator it = tokens.begin();
104 
105  // There may be syslog garbage in the first token before the timestamp...
106  boost::smatch res;
107  while(it != tokens.end() && !boost::regex_search(*it, res, timestamp_regex_))
108  {
109  ++it;
110  }
111 
112  struct tm tm;
113  std::string value(res[1].first, res[1].second);
114  strptime(value.c_str(), "%d-%b-%Y %H:%M:%S", &tm);
115  tm.tm_isdst = -1;
116  fields[FieldType::TIMESTAMP] = std::to_string(mktime(&tm));
117 
118  auto prevIt = it;
119  try
120  {
121  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
122  {
123  // seqNum = std::stoi(*it);
124  fields[FieldType::SEQID] = *it;
125  }
126  }
127  catch(const std::invalid_argument& e)
128  {
129  it = prevIt;
130  }
131  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
132  {
133  // hostname = *it;
134  fields[FieldType::HOSTNAME] = *it;
135  }
136  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
137  {
138  ; //not needed (yet): hostaddr = *it;
139  }
140  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
141  {
142  // sev = mf::ELseverityLevel(*it);
143  fields[FieldType::LEVEL] = mf::ELseverityLevel(*it).getName();
144  }
145  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
146  {
147  // category = *it;
148  fields[FieldType::LABEL] = *it;
149  }
150  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
151  {
152  // application = *it;
153  fields[FieldType::SOURCE] = *it;
154  }
155  prevIt = it;
156  try
157  {
158  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
159  {
160  // pid = std::stol(*it);
161  fields[FieldType::SOURCEID] = *it;
162  }
163  }
164  catch(const std::invalid_argument& e)
165  {
166  it = prevIt;
167  }
168  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
169  {
170  ; //not needed (yet): eventID = *it;
171  }
172  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
173  {
174  ; //not needed (yet): module = *it;
175  }
176  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
177  {
178  // file = *it;
179  fields[FieldType::FILE] = *it;
180  }
181  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
182  {
183  // line = *it;
184  fields[FieldType::LINE] = *it;
185  }
186  std::ostringstream oss;
187  bool first = true;
188  while(it != tokens.end() && ++it != tokens.end() /* Advances it */)
189  {
190  if(!first)
191  {
192  oss << "|";
193  }
194  else
195  {
196  first = false;
197  }
198  oss << *it;
199  }
200  fields[FieldType::MSG] = oss.str();
201 
202 #if 0
203  for (auto& field : fields) {
204  std::cout << "Field " << field.second.fieldName << ": " << field.second.fieldValue
205  << std::endl;
206  }
207 #endif
208 
209  //check custom triggers
210  //Note: to avoid recursive triggers, Label=Console can not trigger
211  for(auto& triggeredAction : priorityCustomTriggerList)
212  {
213  if(getLabel() == "Console") break;
214 
215  size_t pos = 0;
216  bool foundAll = false;
217  for(const auto& needleSubstring : triggeredAction.needleSubstrings)
218  if((pos = getMsg().find(needleSubstring)) == std::string::npos)
219  {
220  foundAll = false;
221  //FOR DEBUGGING std::cout << "Not a full match on '" << needleSubstring << ".' Message: " <<
222  // getSourceIDAsNumber() << ":" << getMsg().substr(0,100) << __E__;
223  break;
224  }
225  else
226  foundAll = true;
227 
228  if(foundAll)
229  {
230  //FOR DEBUGGING std::cout << "Full match of custom trigger! Message: " <<
231  // getSourceIDAsNumber() << ":" << getMsg().substr(0,100) << __E__;
232 
233  // Always count occurrences
234  triggeredAction.occurrences++;
235 
236  // Don't count towards action if not armed
237  if(!triggeredAction.isArmed)
238  continue;
239 
240  // Trigger only if the count is a multiple of the triggerOnCount
241  if(triggeredAction.occurrences % triggeredAction.triggerOnCount == 0)
242  {
243  customTriggerMatch = triggeredAction;
244  customTriggerMatch.triggeredMessageCountIndex = getCount();
245  }
246 
247  // Disarm if not looping
248  if(!triggeredAction.doLoop)
249  triggeredAction.isArmed = false;
250 
251  break;
252  }
253  } //end custom trigger search
254 
255  } //end ConsoleMessageStruct constructor
256 
257  void setCustomTriggerMatch(const CustomTriggeredAction_t& forcedCustomTriggerMatch) { customTriggerMatch = forcedCustomTriggerMatch; }
258  const CustomTriggeredAction_t& getCustomTriggerMatch() const { return customTriggerMatch; }
259  bool hasCustomTriggerMatchAction() const { return customTriggerMatch.action.size(); }
260  const std::string& getTime() const { return fields.at(FieldType::TIMESTAMP); }
261  void setTime(time_t t) { fields[FieldType::TIMESTAMP] = std::to_string(t); }
262  const std::string& getMsg() const { return fields.at(FieldType::MSG); }
263  const std::string& getLabel() const { return fields.at(FieldType::LABEL); }
264  const std::string& getLevel() const {
265  //identify 9+ levels as TRACE (mf calls them DEBUG)
266  if(getMsg().size() > 4)
267  {
268  if(getMsg()[0] == '9' && getMsg()[1] == ':') return ConsoleMessageStruct::LABEL_TRACE;
269  if(getMsg()[0] >= '1' && getMsg()[0] <= '3' &&
270  getMsg()[1] >= '0' && getMsg()[1] <= '9' &&
271  getMsg()[2] == ':') return ConsoleMessageStruct::LABEL_TRACE_PLUS;
272  }
273  return fields.at(FieldType::LEVEL);
274  }
275  const std::string& getFile() const { return fields.at(FieldType::FILE); }
276  const std::string& getLine() const { return fields.at(FieldType::LINE); }
277 
278  const std::string& getSourceID() const { return fields.at(FieldType::SOURCEID); }
279  uint32_t getSourceIDAsNumber() const
280  {
281  auto val = fields.at(FieldType::SOURCEID);
282  if(val != "")
283  {
284  return std::stoul(val);
285  }
286  return 0;
287  }
288  const std::string& getSource() const { return fields.at(FieldType::SOURCE); }
289  const std::string& getSequenceID() const { return fields.at(FieldType::SEQID); }
290  size_t getSequenceIDAsNumber() const
291  {
292  auto val = fields.at(FieldType::SEQID);
293  if(val != "")
294  {
295  return std::stoul(val);
296  }
297  return 0;
298  }
299 
300  //count is incrementing number across all sources created at ConsoleSupervisor
301  size_t getCount() const { return countStamp; }
302 
303  // define field index enum alias
304  enum class FieldType
305  { // must be in order of appearance in buffer
306  TIMESTAMP,
307  //count is incrementing number across all sources created at ConsoleSupervisor
308  SEQID,
309  LEVEL,
310  LABEL,
311  SOURCEID,
312  HOSTNAME,
313  SOURCE,
314  FILE,
315  LINE,
316  MSG,
317  };
318 
319  mutable std::unordered_map<FieldType, std::string /* fieldValue */> fields;
320  static const std::map<FieldType, std::string /* fieldNames */> fieldNames;
321 
322  private:
323  size_t countStamp;
324  CustomTriggeredAction_t customTriggerMatch;
325 
326  static const std::string LABEL_TRACE, LABEL_TRACE_PLUS;
327  }; //end ConsoleMessageStruct
328 
329  private:
330  void addMessageToResponse (HttpXmlDocument* xmlOut, ConsoleSupervisor::ConsoleMessageStruct& msg);
331 
332  std::deque<ConsoleMessageStruct> messages_;
333  std::mutex messageMutex_;
334  size_t messageCount_;
335  size_t maxMessageCount_, maxClientMessageRequest_;
336 
337  std::map<std::string /*TRACE hostname*/, std::string /*xdaq context hostname*/>
338  traceMapToXDAQHostname_;
339 
341  xercesc::DOMElement* refreshParent_;
342 
343  std::vector<CustomTriggeredAction_t> priorityCustomTriggerList_; // list of custom trigger actions
344  std::queue<CustomTriggeredAction_t> customTriggerActionQueue_; // queue of pending actions upon custom needle match
345 
347  size_t errorCount_ = 0, warnCount_ = 0, infoCount_ = 0;
348  std::string lastErrorMessage_, lastWarnMessage_, lastInfoMessage_, firstErrorMessage_, firstWarnMessage_, firstInfoMessage_;
349  time_t lastErrorMessageTime_ = 0, lastWarnMessageTime_ = 0, lastInfoMessageTime_ = 0, firstErrorMessageTime_ = 0, firstWarnMessageTime_ = 0, firstInfoMessageTime_ = 0;
350 };
351 
352 // clang-format on
353 } // namespace ots
354 
355 #endif
virtual void forceSupervisorPropertyValues(void) override
virtual void request(const std::string &requestType, cgicc::Cgicc &cgiIn, HttpXmlDocument &xmlOut, const WebUsers::RequestUserInfo &userInfo) override
static const std::set< std::string > CUSTOM_TRIGGER_ACTIONS
Count always happens, and System Message always happens for FSM commands.
virtual std::string getStatusProgressDetail(void) override
@ SEQID
sequence ID is incrementing number independent from each source
ConsoleMessageStruct(const std::string &msg, const size_t count, std::vector< CustomTriggeredAction_t > &priorityCustomTriggerList)