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