artdaq_mfextensions  v1_05_00
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  oss << (strstr(&msg.filename()[0], &filename_delimit_[0]) != nullptr // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
247  ? strstr(&msg.filename()[0], &filename_delimit_[0]) + filename_delimit_.size() // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
248  : msg.filename());
249  }
250  break;
251  case 'h':
252  oss << hostname_;
253  break; // host name
254  case 'L':
255  oss << xid.severity().getName();
256  break; // severity
257  case 'm': // message
258  // ossstr.clear(); // incase message is repeated
259  for (auto const& val : msg.items())
260  {
261  ossstr += val; // Print the contents.
262  }
263  if (!ossstr.empty())
264  { // allow/check for "no message"
265  if (ossstr.compare(0, 1, "\n") == 0)
266  {
267  ossstr.erase(0, 1); // remove leading "\n" if present
268  }
269  if (ossstr.compare(ossstr.size() - 1, 1, "\n") == 0)
270  {
271  ossstr.erase(ossstr.size() - 1, 1); // remove trailing "\n" if present
272  }
273  oss << ossstr;
274  }
275  msg_printed = true;
276  break;
277  case 'N':
278  oss << id;
279  break; // category
280  case 'P':
281  oss << pid_;
282  break; // processID
283  case 'r':
284  oss << mf::GetIteration();
285  break; // run/iteration/event no #pre-events
286  case 's':
287  sev = xid.severity().getName()[0] | 0x20; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
288  oss << sev;
289  break; // severity lower case
290  case 'T':
291  oss << format_.timestamp(msg.timestamp());
292  break; // timestamp
293  case 'u':
294  oss << std::to_string(msg.lineNumber());
295  break; // linenumber
296  case '%':
297  oss << '%';
298  break; // a '%' character
299  default:
300  oss << '%' << *cp;
301  break; // unknown - just print it w/ it's '%'
302  }
303  }
304  if (!msg_printed)
305  {
306  for (auto const& val : msg.items())
307  {
308  ossstr += val; // Print the contents.
309  }
310  if (ossstr.compare(0, 1, "\n") == 0)
311  {
312  ossstr.erase(0, 1); // remove leading "\n" if present
313  }
314  if (ossstr.compare(ossstr.size() - 1, 1, "\n") == 0)
315  {
316  ossstr.erase(ossstr.size() - 1, 1); // remove trailing "\n" if present
317  }
318  oss << ossstr;
319  }
320 }
321 
322 //======================================================================
323 // Message filler ( overriddes ELdestination::fillUsrMsg )
324 //======================================================================
325 void ELOTS::fillUsrMsg(std::ostringstream& oss __attribute__((__unused__)),
326  const ErrorObj& msg __attribute__((__unused__)))
327 {
328  // UsrMsg filled above
329 }
330 
331 //======================================================================
332 // Message router ( overriddes ELdestination::routePayload )
333 //======================================================================
334 void ELOTS::routePayload(const std::ostringstream& oss, const ErrorObj& /*msg*/) { std::cout << oss.str() << std::endl; }
335 } // end namespace mfplugins
336 //======================================================================
337 //
338 // makePlugin function
339 //
340 //======================================================================
341 
342 #ifndef EXTERN_C_FUNC_DECLARE_START
343 #define EXTERN_C_FUNC_DECLARE_START extern "C" {
344 #endif
345 
346 EXTERN_C_FUNC_DECLARE_START auto makePlugin(const std::string& /*unused*/, const fhicl::ParameterSet& pset)
347 {
348  return std::make_unique<mfplugins::ELOTS>(pset);
349 }
350 } // namespace mfplugins
351 
352 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