artdaq_mfextensions  1.08.06
OTS_mfPlugin.cc
1 #include "cetlib/PluginTypeDeducer.h"
2 #include "fhiclcpp/ParameterSet.h"
3 #include "fhiclcpp/types/ConfigurationTable.h"
4 
5 #include "cetlib/compiler_macros.h"
6 #include "messagefacility/MessageLogger/MessageLogger.h"
7 #include "messagefacility/MessageService/ELdestination.h"
8 #include "messagefacility/Utilities/ELseverityLevel.h"
9 #include "messagefacility/Utilities/exception.h"
10 
11 // C/C++ includes
12 #include <arpa/inet.h>
13 #include <ifaddrs.h>
14 #include <netdb.h>
15 #include <netinet/in.h>
16 #include <algorithm>
17 #include <fstream>
18 #include <iostream>
19 #include <memory>
21 
22 #define TRACE_NAME "OTS_mfPlugin"
23 #include "trace.h"
24 
25 // Boost includes
26 #include <boost/algorithm/string.hpp>
27 
28 namespace mfplugins {
29 using mf::ErrorObj;
30 using mf::service::ELdestination;
31 
36 class ELOTS : public ELdestination
37 {
38 public:
42  struct Config
43  {
45  fhicl::TableFragment<ELdestination::Config> elDestConfig;
47  fhicl::Atom<std::string> format_string = fhicl::Atom<std::string>{
48  fhicl::Name{"format_string"}, fhicl::Comment{"Format specifier for printing to console. %% => '%' ... "},
49  "%L:%N:%f [%u] %m"};
51  fhicl::Atom<std::string> filename_delimit =
52  fhicl::Atom<std::string>{fhicl::Name{"filename_delimit"},
53  fhicl::Comment{"Grab path after this. \"/srcs/\" /x/srcs/y/z.cc => y/z.cc"}, "/"};
54  };
56  using Parameters = fhicl::WrappedTable<Config>;
57 
58 public:
63  ELOTS(Parameters const& pset);
64 
70  void fillPrefix(std::ostringstream& o, const ErrorObj& msg) override;
71 
77  void fillUsrMsg(std::ostringstream& o, const ErrorObj& msg) override;
78 
82  void fillSuffix(std::ostringstream& /*unused*/, const ErrorObj& /*msg*/) override {}
83 
89  void routePayload(const std::ostringstream& o, const ErrorObj& e) override;
90 
91 private:
92  // Other stuff
93  int64_t pid_;
94  std::string hostname_;
95  std::string hostaddr_;
96  std::string app_;
97  std::string format_string_;
98  std::string filename_delimit_;
99 };
100 
101 // END DECLARATION
102 //======================================================================
103 // BEGIN IMPLEMENTATION
104 
105 //======================================================================
106 // ELOTS c'tor
107 //======================================================================
108 
110  : ELdestination(pset().elDestConfig()), pid_(static_cast<int64_t>(getpid())), format_string_(pset().format_string()), filename_delimit_(pset().filename_delimit())
111 {
112  // hostname
113  char hostname_c[1024];
114  hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c : "Unkonwn Host";
115 
116  // host ip address
117  hostent* host = nullptr;
118  host = gethostbyname(hostname_c);
119 
120  if (host != nullptr)
121  {
122  // ip address from hostname if the entry exists in /etc/hosts
123  char* ip = inet_ntoa(*reinterpret_cast<struct in_addr*>(host->h_addr)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
124  hostaddr_ = ip;
125  }
126  else
127  {
128  // enumerate all network interfaces
129  struct ifaddrs* ifAddrStruct = nullptr;
130  struct ifaddrs* ifa = nullptr;
131  void* tmpAddrPtr = nullptr;
132 
133  if (getifaddrs(&ifAddrStruct) != 0)
134  {
135  // failed to get addr struct
136  hostaddr_ = "127.0.0.1";
137  }
138  else
139  {
140  // iterate through all interfaces
141  for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
142  {
143  if (ifa->ifa_addr->sa_family == AF_INET)
144  {
145  // a valid IPv4 addres
146  tmpAddrPtr = &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr))->sin_addr; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
147  char addressBuffer[INET_ADDRSTRLEN];
148  inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
149  hostaddr_ = addressBuffer;
150  }
151 
152  else if (ifa->ifa_addr->sa_family == AF_INET6)
153  {
154  // a valid IPv6 address
155  tmpAddrPtr = &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr))->sin6_addr; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
156  char addressBuffer[INET6_ADDRSTRLEN];
157  inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
158  hostaddr_ = addressBuffer;
159  }
160 
161  // find first non-local address
162  if (!hostaddr_.empty() && (hostaddr_ != "127.0.0.1") && (hostaddr_ != "::1"))
163  {
164  break;
165  }
166  }
167 
168  if (hostaddr_.empty())
169  { // failed to find anything
170  hostaddr_ = "127.0.0.1";
171  }
172  }
173  }
174 
175  // get process name from '/proc/pid/cmdline'
176  std::stringstream ss;
177  ss << "//proc//" << pid_ << "//cmdline";
178  std::ifstream procfile{ss.str().c_str()};
179 
180  std::string procinfo;
181 
182  if (procfile.is_open())
183  {
184  procfile >> procinfo;
185  procfile.close();
186  }
187 
188  size_t end = procinfo.find('\0');
189  size_t start = procinfo.find_last_of('/', end);
190 
191  app_ = procinfo.substr(start + 1, end - start - 1);
192 }
193 
194 //======================================================================
195 // Message prefix filler ( overriddes ELdestination::fillPrefix )
196 //======================================================================
197 void ELOTS::fillPrefix(std::ostringstream& oss, const ErrorObj& msg)
198 {
199  const auto& xid = msg.xid();
200 
201  const auto& id = xid.id();
202  const auto& module = xid.module();
203  auto app = app_;
204  char* cp = &format_string_[0]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
205  char sev;
206  bool msg_printed = false;
207  std::string ossstr;
208  // ossstr.reserve(100);
209 
210  for (; *cp != 0; ++cp) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
211  {
212  if (*cp != '%')
213  {
214  oss << *cp;
215  continue;
216  }
217  if (*++cp == '\0') // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
218  { // inc pas '%' and check if end
219  // ending '%' gets printed
220  oss << *cp;
221  break; // done
222  }
223  switch (*cp)
224  {
225  case 'A':
226  oss << app;
227  break; // application
228  case 'a':
229  oss << hostaddr_;
230  break; // host address
231  case 'd':
232  oss << module;
233  break; // module name # Early
234  case 'f': // filename
235  if (filename_delimit_.empty())
236  {
237  oss << msg.filename();
238  }
239  else if (filename_delimit_.size() == 1)
240  {
241  oss << (strrchr(&msg.filename()[0], filename_delimit_[0]) != nullptr // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
242  ? strrchr(&msg.filename()[0], filename_delimit_[0]) + 1 // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
243  : msg.filename());
244  }
245  else
246  {
247  const char* cp = strstr(&msg.filename()[0], &filename_delimit_[0]); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
248  if (cp != nullptr)
249  {
250  // make sure to remove a part that ends with '/'
251  cp += filename_delimit_.size() - 1; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
252  while (*cp && *cp != '/') ++cp;
253  ++cp; // increment past '/'
254  oss << cp;
255  }
256  else
257  oss << msg.filename();
258  }
259  break;
260  case 'h':
261  oss << hostname_;
262  break; // host name
263  case 'L':
264  oss << xid.severity().getName();
265  break; // severity
266  case 'm': // message
267  // ossstr.clear(); // incase message is repeated
268  for (auto const& val : msg.items())
269  {
270  ossstr += val; // Print the contents.
271  }
272  if (!ossstr.empty())
273  { // allow/check for "no message"
274  if (ossstr.compare(0, 1, "\n") == 0)
275  {
276  ossstr.erase(0, 1); // remove leading "\n" if present
277  }
278  if (ossstr.compare(ossstr.size() - 1, 1, "\n") == 0)
279  {
280  ossstr.erase(ossstr.size() - 1, 1); // remove trailing "\n" if present
281  }
282  oss << ossstr;
283  }
284  msg_printed = true;
285  break;
286  case 'N':
287  oss << id;
288  break; // category
289  case 'P':
290  oss << pid_;
291  break; // processID
292  case 'r':
293  oss << mf::GetIteration();
294  break; // run/iteration/event no #pre-events
295  case 's':
296  sev = xid.severity().getName()[0] | 0x20; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
297  oss << sev;
298  break; // severity lower case
299  case 'T':
300  oss << format_.timestamp(msg.timestamp());
301  break; // timestamp
302  case 'u':
303  oss << std::to_string(msg.lineNumber());
304  break; // linenumber
305  case '%':
306  oss << '%';
307  break; // a '%' character
308  default:
309  oss << '%' << *cp;
310  break; // unknown - just print it w/ it's '%'
311  }
312  }
313  if (!msg_printed)
314  {
315  for (auto const& val : msg.items())
316  {
317  ossstr += val; // Print the contents.
318  }
319  if (ossstr.compare(0, 1, "\n") == 0)
320  {
321  ossstr.erase(0, 1); // remove leading "\n" if present
322  }
323  if (ossstr.compare(ossstr.size() - 1, 1, "\n") == 0)
324  {
325  ossstr.erase(ossstr.size() - 1, 1); // remove trailing "\n" if present
326  }
327  oss << ossstr;
328  }
329 }
330 
331 //======================================================================
332 // Message filler ( overriddes ELdestination::fillUsrMsg )
333 //======================================================================
334 void ELOTS::fillUsrMsg(std::ostringstream& oss __attribute__((__unused__)),
335  const ErrorObj& msg __attribute__((__unused__)))
336 {
337  // UsrMsg filled above
338 }
339 
340 //======================================================================
341 // Message router ( overriddes ELdestination::routePayload )
342 //======================================================================
343 void ELOTS::routePayload(const std::ostringstream& oss, const ErrorObj& /*msg*/) { std::cout << oss.str() << std::endl; }
344 } // end namespace mfplugins
345 //======================================================================
346 //
347 // makePlugin function
348 //
349 //======================================================================
350 
351 #ifndef EXTERN_C_FUNC_DECLARE_START
352 #define EXTERN_C_FUNC_DECLARE_START extern "C" {
353 #endif
354 
355 EXTERN_C_FUNC_DECLARE_START auto makePlugin(const std::string& /*unused*/, const fhicl::ParameterSet& pset)
356 {
357  return std::make_unique<mfplugins::ELOTS>(pset);
358 }
359 } // namespace mfplugins
360 
361 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
void fillSuffix(std::ostringstream &, const ErrorObj &) override
Fill the &quot;Suffix&quot; portion of the message (Unused)
Definition: OTS_mfPlugin.cc:82
Configuration Parameters for ELOTS.
Definition: OTS_mfPlugin.cc:42
fhicl::Atom< std::string > filename_delimit
filename_delimit (Default: &quot;/&quot;): Grab path after this. &quot;/srcs/&quot; /x/srcs/y/z.cc =&gt; y/z...
Definition: OTS_mfPlugin.cc:51
fhicl::TableFragment< ELdestination::Config > elDestConfig
ELDestination common config parameters.
Definition: OTS_mfPlugin.cc:45
void routePayload(const std::ostringstream &o, const ErrorObj &e) override
Serialize a MessageFacility message to the output.
fhicl::Atom< std::string > format_string
format_string (Default: &quot;%L:%N:%f [%u] %m&quot;): Format specifier for printing to console. %% =&gt; &#39;&#39; ...
Definition: OTS_mfPlugin.cc:47
Message Facility OTS Console Destination Formats messages into Ryan&#39;s favorite format for OTS ...
Definition: OTS_mfPlugin.cc:36
void fillPrefix(std::ostringstream &o, const ErrorObj &msg) override
Fill the &quot;Prefix&quot; portion of the message.
fhicl::WrappedTable< Config > Parameters
Used for ParameterSet validation.
Definition: OTS_mfPlugin.cc:56
void fillUsrMsg(std::ostringstream &o, const ErrorObj &msg) override
Fill the &quot;User Message&quot; portion of the message.
ELOTS(Parameters const &pset)
ELOTS Constructor