artdaq_core  v3_01_02
GenFile_mfPlugin.cc
1 #include "cetlib/PluginTypeDeducer.h"
2 #include "cetlib/ostream_handle.h"
3 #include "fhiclcpp/ParameterSet.h"
4 
5 #include "boost/date_time/posix_time/posix_time.hpp"
6 
7 #include "messagefacility/MessageService/ELdestination.h"
8 # include "messagefacility/Utilities/ELseverityLevel.h"
9 #if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
10 # include "messagefacility/MessageService/ELcontextSupplier.h"
11 # include "messagefacility/MessageLogger/MessageDrop.h"
12 #endif
13 #include "messagefacility/Utilities/exception.h"
14 
15 #include <fstream>
16 #include "trace.h"
17 
18 namespace mfplugins
19 {
20  using mf::service::ELdestination;
21  using mf::ELseverityLevel;
22  using mf::ErrorObj;
23 #if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
24  using mf::service::ELcontextSupplier;
25 #endif
26 
31  class ELGenFileOutput : public ELdestination
32  {
33 #if MESSAGEFACILITY_HEX_VERSION >= 0x20103
34  struct Config
35  {
36  fhicl::TableFragment<ELdestination::Config> elDestConfig;
37  fhicl::Atom<bool> append{ fhicl::Name{"append"},fhicl::Comment {"Whether to append to the file or recreate it"},true };
38  fhicl::Atom<std::string> baseDir{ fhicl::Name{"directory"},fhicl::Comment{"The directory into which files will be saved"},"/tmp" };
39  fhicl::Atom<std::string> sep{ fhicl::Name{"separator"},fhicl::Comment{"Separator to use after optional replacement parameters"}, "-" };
40  fhicl::Atom<std::string> timePattern{ fhicl::Name{"timestamp_pattern"},fhicl::Comment{"Pattern to use for %t strftime replacement"},"%Y%m%d%H%M%S" };
41  fhicl::Atom<std::string> filePattern{ fhicl::Name{ "pattern" },fhicl::Comment{ "Pattern to use for file naming.\n"
42  " Supported parameters are:\n"
43  " %%: Print a % sign\n"
44  " %N: Print the executable name, as retrieved from /proc/<pid>/exe\n"
45  " %?N: Print the executable name only if it does not already appear in the parsed format. Format is parsed left-to-right.\n"
46  " These options add a separator AFTER if they are filled and if they are not the last token in the file pattern, before the last '.' character.\n"
47  " %H: Print the hostname, without any domain specifiers (i.e. work.fnal.gov will become work)\n"
48  " %?H: Print the hostname only if it does not already appear in the parsed format.\n"
49  " %p: Print the PID of the application configuring MessageFacility\n"
50  " %t: Print the timestamp using the format specified by timestamp_pattern\n"
51  " %T: Print the timestamp in ISO format"
52  },"%N-%?H%t-%p.log" };
53 
54 
55  };
56  using Parameters = fhicl::WrappedTable<Config>;
57 #endif
58 
59  public:
60 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
61  ELGenFileOutput(const fhicl::ParameterSet& pset);
62 #else
63  ELGenFileOutput(Parameters const& pset);
64 #endif
65 
66  virtual ~ELGenFileOutput() {}
67 
68  virtual void routePayload(const std::ostringstream&, const ErrorObj&
69 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
70  , const ELcontextSupplier&
71 #endif
72  ) override;
73 
74  virtual void flush(
75 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
76  const ELcontextSupplier&
77 #endif
78  ) override;
79 
80  private:
81  std::unique_ptr<cet::ostream_handle> output_;
82  };
83 
84  // END DECLARATION
85  //======================================================================
86  // BEGIN IMPLEMENTATION
87 
88 
89  //======================================================================
90  // ELGenFileOutput c'tor
91  //======================================================================
92 #if MESSAGEFACILITY_HEX_VERSION < 0x20103
93  ELGenFileOutput::ELGenFileOutput(const fhicl::ParameterSet& pset)
94  : ELdestination(pset)
95  {
96  bool append = pset.get<bool>("append", true);
97  std::string baseDir = pset.get<std::string>("directory", "/tmp");
98  std::string sep = pset.get<std::string>("separator", "-");
99  std::string timePattern = pset.get<std::string>("timestamp_pattern", "%Y%m%d%H%M%S"); // For strftime
100  std::string filePattern = pset.get<std::string>("pattern", "%N-%?H%t-%p.log");
101 #else
102  ELGenFileOutput::ELGenFileOutput(Parameters const& pset) : ELdestination(pset().elDestConfig())
103  {
104  bool append = pset().append();
105  std::string baseDir = pset().baseDir();
106  std::string sep = pset().sep();
107  std::string timePattern = pset().timePattern();
108  std::string filePattern = pset().filePattern();
109 #endif
110 
111  auto pid = getpid();
112  std::string exeString = "";
113  std::string hostString = "";
114  std::string timeBuffISO = ""; // Using boost::posix_time::to_iso_string (%T)
115  std::string timeBuff = ""; // Using timestamp_pattern (%t)
116 
117 
118  // Determine image name
119  if (filePattern.find("%N") != std::string::npos || filePattern.find("%?N") != std::string::npos)
120  {
121  // get process name from '/proc/pid/exe'
122  std::string exe;
123  std::ostringstream pid_ostr;
124  pid_ostr << "/proc/" << pid << "/exe";
125  exe = std::string(realpath(pid_ostr.str().c_str(), NULL));
126 
127  size_t end = exe.find('\0');
128  size_t start = exe.find_last_of('/', end);
129  exeString = exe.substr(start + 1, end - start - 1);
130  }
131 
132  // Get Host name
133  if (filePattern.find("%H") != std::string::npos || filePattern.find("%?H") != std::string::npos)
134  {
135  char hostname[256];
136  if (gethostname(&hostname[0], 256) == 0)
137  {
138  std::string tmpString(hostname);
139  hostString = tmpString;
140  size_t pos = hostString.find(".");
141  if (pos != std::string::npos && pos > 2)
142  {
143  hostString = hostString.substr(0, pos);
144  }
145  }
146  }
147  if (filePattern.find("%T") != std::string::npos)
148  {
149  timeBuffISO = boost::posix_time::to_iso_string(boost::posix_time::second_clock::universal_time());
150  }
151  if (filePattern.find("%t") != std::string::npos)
152  {
153 
154  time_t rawtime;
155  struct tm* timeinfo;
156  char timeBuffC[256];
157  time(&rawtime);
158  timeinfo = localtime(&rawtime);
159  strftime(timeBuffC, 256, timePattern.c_str(), timeinfo);
160  timeBuff = std::string(timeBuffC);
161  }
162 
163  size_t pos = 0;
164  TLOG_DEBUG("GenFile") << "filePattern is: " << filePattern << TLOG_ENDL;
165  while (filePattern.find("%", pos) != std::string::npos)
166  {
167  pos = filePattern.find("%", pos) + 1;
168  TLOG_ARB(5, "GenFile") << "Found % at " << std::to_string(pos - 1) << ", next char: " << filePattern[pos] << "." << TLOG_ENDL;
169  switch (filePattern[pos])
170  {
171  case '%': // "%%"
172  filePattern = filePattern.replace(pos - 1, 2, "%");
173  pos--;
174  break;
175  case '?':
176  {
177  char next = filePattern[pos + 1];
178  switch (next) {
179  case 'N':
180  if (filePattern.find(exeString) != std::string::npos) {
181  filePattern = filePattern.erase(pos - 1, 3);
182  }
183  else {
184  std::string repString = exeString;
185  // Only append separator if we're not at the end of the pattern
186  if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.'))) {
187  repString += sep;
188  }
189  filePattern = filePattern.replace(pos - 1, 3, repString);
190  }
191  break;
192  case 'H':
193  if (filePattern.find(hostString) != std::string::npos) {
194  filePattern = filePattern.erase(pos - 1, 3);
195  }
196  else {
197  std::string repString = hostString;
198  // Only append separator if we're not at the end of the pattern
199  if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.'))) {
200  repString += sep;
201  }
202  filePattern = filePattern.replace(pos - 1, 3, repString);
203  break;
204  }
205  }
206  pos -= 3;
207  }
208  break;
209  case 'N':
210  filePattern = filePattern.replace(pos - 1, 2, exeString);
211  pos -= 2;
212  break;
213  case 'H':
214  filePattern = filePattern.replace(pos - 1, 2, hostString);
215  pos -= 2;
216  break;
217  case 'p':
218  filePattern = filePattern.replace(pos - 1, 2, std::to_string(pid));
219  pos -= 2;
220  break;
221  case 't':
222  filePattern = filePattern.replace(pos - 1, 2, timeBuff);
223  pos -= 2;
224  break;
225  case 'T':
226  filePattern = filePattern.replace(pos - 1, 2, timeBuffISO);
227  pos -= 2;
228  break;
229  }
230  TLOG_ARB(6, "GenFile") << "filePattern is now: " << filePattern << TLOG_ENDL;
231  }
232  std::string fileName = baseDir + "/" + filePattern;
233  TLOG_DEBUG("GenFile") << "fileName is: " << fileName << TLOG_ENDL;
234 
235  output_ = std::make_unique<cet::ostream_handle>(fileName.c_str(), append ? std::ios::app : std::ios::trunc);
236  }
237 
238  //======================================================================
239  // Message router ( overriddes ELdestination::routePayload )
240  //======================================================================
241  void ELGenFileOutput::routePayload(const std::ostringstream& oss, const ErrorObj&
242 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
243  , ELcontextSupplier const& sup
244 #endif
245  )
246  {
247  *output_ << oss.str();
248  flush(
249 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
250  sup
251 #endif
252  );
253  }
254 
255  void ELGenFileOutput::flush(
256 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
257  ELcontextSupplier const&
258 #endif
259  )
260  {
261  output_->flush();
262  }
263 } // end namespace mfplugins
264 
265  //======================================================================
266  //
267  // makePlugin function
268  //
269  //======================================================================
270 
271 extern "C"
272 {
273  auto makePlugin(const std::string&,
274  const fhicl::ParameterSet& pset)
275  {
276  return std::make_unique<mfplugins::ELGenFileOutput>(pset);
277  }
278 }
279 
280 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
Message Facility destination which generates the output file name based on some combination of PID...