otsdaq_utilities  v2_05_02_indev
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 
10 namespace ots
11 {
12 // ConsoleSupervisor
13 // This class handles the presentation of Message Facility printouts to the web desktop
14 // Console
15 class ConsoleSupervisor : public CoreSupervisorBase
16 {
17  public:
18  public:
19  XDAQ_INSTANTIATOR();
20 
21  ConsoleSupervisor(xdaq::ApplicationStub* s);
22  virtual ~ConsoleSupervisor(void);
23 
24  void init(void);
25  void destroy(void);
26 
27  virtual void defaultPage(xgi::Input* in, xgi::Output* out) override;
28  virtual void request(const std::string& requestType,
29  cgicc::Cgicc& cgiIn,
30  HttpXmlDocument& xmlOut,
31  const WebUsers::RequestUserInfo& userInfo) override;
32 
33  virtual void forceSupervisorPropertyValues(void) override; // override to force
34  // supervisor property
35  // values (and ignore user
36  // settings)
37 
38  private:
39  static void messageFacilityReceiverWorkLoop(ConsoleSupervisor* cs);
40  void insertMessageRefresh(HttpXmlDocument* xmldoc, const size_t lastUpdateCount);
41 
42  // UDP Message Format:
43  // UDPMFMESSAGE|TIMESTAMP|SEQNUM|HOSTNAME|HOSTADDR|SEVERITY|CATEGORY|APPLICATION|PID|ITERATION|MODULE|(FILE|LINE)|MESSAGE
44  // FILE and LINE are only printed for s67+
45  struct ConsoleMessageStruct
46  {
47  ConsoleMessageStruct(const std::string& msg, const size_t count)
48  : countStamp(count)
49  {
50  std::string hostname, category, application, message, hostaddr, file, line,
51  module, eventID;
52  mf::ELseverityLevel sev;
53  timeval tv = {0, 0};
54  int pid = 0;
55  int seqNum = 0;
56 
57  boost::regex timestamp_regex_("(\\d{2}-[^-]*-\\d{4}\\s\\d{2}:\\d{2}:\\d{2})");
58  boost::regex file_line_regex_("^\\s*([^:]*\\.[^:]{1,3}):(\\d+)(.*)");
59 
60  boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
61  typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
62  tokenizer tokens(msg, sep);
63  tokenizer::iterator it = tokens.begin();
64 
65  // There may be syslog garbage in the first token before the timestamp...
66  boost::smatch res;
67  while(it != tokens.end() && !boost::regex_search(*it, res, timestamp_regex_))
68  {
69  ++it;
70  }
71 
72  struct tm tm;
73  time_t t;
74  std::string value(res[1].first, res[1].second);
75  strptime(value.c_str(), "%d-%b-%Y %H:%M:%S", &tm);
76  tm.tm_isdst = -1;
77  t = mktime(&tm);
78  tv.tv_sec = t;
79  tv.tv_usec = 0;
80  auto prevIt = it;
81  try
82  {
83  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
84  {
85  seqNum = std::stoi(*it);
86  }
87  }
88  catch(const std::invalid_argument& e)
89  {
90  it = prevIt;
91  }
92  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
93  {
94  hostname = *it;
95  }
96  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
97  {
98  hostaddr = *it;
99  }
100  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
101  {
102  sev = mf::ELseverityLevel(*it);
103  }
104  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
105  {
106  category = *it;
107  }
108  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
109  {
110  application = *it;
111  }
112  prevIt = it;
113  try
114  {
115  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
116  {
117  pid = std::stol(*it);
118  }
119  }
120  catch(const std::invalid_argument& e)
121  {
122  it = prevIt;
123  }
124  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
125  {
126  eventID = *it;
127  }
128  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
129  {
130  module = *it;
131  }
132  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
133  {
134  file = *it;
135  }
136  if(it != tokens.end() && ++it != tokens.end() /* Advances it */)
137  {
138  line = *it;
139  }
140  std::ostringstream oss;
141  bool first = true;
142  while(it != tokens.end() && ++it != tokens.end() /* Advances it */)
143  {
144  if(!first)
145  {
146  oss << "|";
147  }
148  else
149  {
150  first = false;
151  }
152  oss << *it;
153  }
154  message = oss.str();
155 
156  // init fields to position -1 (for unknown)s
157  // NOTE: must be in order of appearance in buffer
158  fields[FieldType::TIMESTAMP].set("Timestamp", 1, std::to_string(tv.tv_sec));
159  fields[FieldType::SEQID].set("SequenceID", 2, std::to_string(seqNum));
160  fields[FieldType::LEVEL].set("Level", 5, sev.getName());
161  fields[FieldType::LABEL].set("Label", 6, category);
162  fields[FieldType::SOURCEID].set(
163  "SourceID", 7, std::to_string(pid)); // number
164  fields[FieldType::SOURCE].set("Source", 9, application);
165  fields[FieldType::FILE].set("File", 10, file);
166  fields[FieldType::LINE].set("Line", 11, line);
167  fields[FieldType::MSG].set("Msg", 12, message);
168 
169 #if 0
170  for (auto& field : fields) {
171  std::cout << "Field " << field.second.fieldName << ": " << field.second.fieldValue
172  << std::endl;
173  }
174 #endif
175  }
176 
177  std::string getTime() const { return fields[FieldType::TIMESTAMP].fieldValue; }
178  std::string getMsg() const { return fields[FieldType::MSG].fieldValue; }
179  std::string getLabel() const { return fields[FieldType::LABEL].fieldValue; }
180  std::string getLevel() const { return fields[FieldType::LEVEL].fieldValue; }
181 
182  std::string getFile() const { return fields[FieldType::FILE].fieldValue; }
183  std::string getLine() const { return fields[FieldType::LINE].fieldValue; }
184 
185  std::string getSourceID() const { return fields[FieldType::SOURCEID].fieldValue; }
186  uint32_t getSourceIDAsNumber() const
187  {
188  auto val = fields[FieldType::SOURCEID].fieldValue;
189  if(val != "")
190  {
191  return std::stoul(val);
192  }
193  return 0;
194  }
195  std::string getSource() const { return fields[FieldType::SOURCE].fieldValue; }
196  std::string getSequenceID() const { return fields[FieldType::SEQID].fieldValue; }
197  size_t getSequenceIDAsNumber() const
198  {
199  auto val = fields[FieldType::SEQID].fieldValue;
200  if(val != "")
201  {
202  return std::stoul(val);
203  }
204  return 0;
205  }
206 
207  size_t getCount() const { return countStamp; }
208 
209  // define field structure
210  struct FieldStruct
211  {
212  void set(const std::string& fn, const int mc, const std::string& fv)
213  {
214  fieldName = fn;
215  fieldValue = fv;
216  markerCount = mc;
217  }
218 
219  std::string fieldName;
220  std::string fieldValue;
221  int markerCount;
222  };
223 
224  // define field index enum alias
225  enum class FieldType
226  { // must be in order of appearance in buffer
227  TIMESTAMP,
228  SEQID,
229  LEVEL, // aka SEVERITY
230  LABEL,
231  SOURCEID,
232  SOURCE,
233  FILE,
234  LINE,
235  MSG,
236  };
237 
238  mutable std::unordered_map<FieldType, FieldStruct> fields;
239 
240  private:
241  size_t countStamp;
242  };
243 
244  std::deque<ConsoleMessageStruct> messages_;
245  std::mutex messageMutex_;
246  size_t messageCount_; //"unique" incrementing ID for messages
247  size_t maxMessageCount_;
248 
249  // members for the refresh handler, ConsoleSupervisor::insertMessageRefresh
250  xercesc::DOMElement* refreshParent_;
251 };
252 } // namespace ots
253 
254 #endif