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