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