artdaq_core  v3_04_09
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 {
19  using mf::service::ELdestination;
20  using mf::ELseverityLevel;
21  using mf::ErrorObj;
22 
27  class ELGenFileOutput : public ELdestination
28  {
29  struct Config
30  {
31  fhicl::TableFragment<ELdestination::Config> elDestConfig;
32  fhicl::Atom<bool> append{ fhicl::Name{"append"},fhicl::Comment {"Whether to append to the file or recreate it"},true };
33  fhicl::Atom<std::string> baseDir{ fhicl::Name{"directory"},fhicl::Comment{"The directory into which files will be saved"},"/tmp" };
34  fhicl::Atom<std::string> sep{ fhicl::Name{"seperator"},fhicl::Comment{"Separator to use after optional replacement parameters"}, "-" };
35  fhicl::Atom<std::string> timePattern{ fhicl::Name{"timestamp_pattern"},fhicl::Comment{"Pattern to use for %t strftime replacement"},"%Y%m%d%H%M%S" };
36  fhicl::Atom<std::string> filePattern{ fhicl::Name{ "pattern" },fhicl::Comment{ "Pattern to use for file naming.\n"
37  " Supported parameters are:\n"
38  " %%: Print a % sign\n"
39  " %N: Print the executable name, as retrieved from /proc/<pid>/exe\n"
40  " %?N: Print the executable name only if it does not already appear in the parsed format. Format is parsed left-to-right.\n"
41  " 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"
42  " %H: Print the hostname, without any domain specifiers (i.e. work.fnal.gov will become work)\n"
43  " %?H: Print the hostname only if it does not already appear in the parsed format.\n"
44  " %p: Print the PID of the application configuring MessageFacility\n"
45  " %t: Print the timestamp using the format specified by timestamp_pattern\n"
46  " %T: Print the timestamp in ISO format"
47  },"%N-%?H%t-%p.log" };
48 
49 
50  };
51  using Parameters = fhicl::WrappedTable<Config>;
52 
53  public:
58  ELGenFileOutput(Parameters const& pset);
59 
61  virtual ~ELGenFileOutput() {}
62 
68  virtual void routePayload(const std::ostringstream& o, const ErrorObj& e) override;
69 
73  virtual void flush() override;
74 
75  private:
76  std::unique_ptr<cet::ostream_handle> output_;
77  };
78 
79  // END DECLARATION
80  //======================================================================
81  // BEGIN IMPLEMENTATION
82 
83 
84  //======================================================================
85  // ELGenFileOutput c'tor
86  //======================================================================
87 
88  ELGenFileOutput::ELGenFileOutput(Parameters const& pset) : ELdestination(pset().elDestConfig())
89  {
90  bool append = pset().append();
91  std::string baseDir = pset().baseDir();
92  std::string sep = pset().sep();
93  std::string timePattern = pset().timePattern();
94  std::string filePattern = pset().filePattern();
95 
96  auto pid = getpid();
97  std::string exeString = "";
98  std::string hostString = "";
99  std::string timeBuffISO = ""; // Using boost::posix_time::to_iso_string (%T)
100  std::string timeBuff = ""; // Using timestamp_pattern (%t)
101 
102 
103  // Determine image name
104  if (filePattern.find("%N") != std::string::npos || filePattern.find("%?N") != std::string::npos)
105  {
106  // get process name from '/proc/pid/exe'
107  std::string exe;
108  std::ostringstream pid_ostr;
109  pid_ostr << "/proc/" << pid << "/exe";
110  exe = std::string(realpath(pid_ostr.str().c_str(), NULL));
111 
112  size_t end = exe.find('\0');
113  size_t start = exe.find_last_of('/', end);
114  exeString = exe.substr(start + 1, end - start - 1);
115  }
116 
117  // Get Host name
118  if (filePattern.find("%H") != std::string::npos || filePattern.find("%?H") != std::string::npos)
119  {
120  char hostname[256];
121  if (gethostname(&hostname[0], 256) == 0)
122  {
123  std::string tmpString(hostname);
124  hostString = tmpString;
125  size_t pos = hostString.find(".");
126  if (pos != std::string::npos && pos > 2)
127  {
128  hostString = hostString.substr(0, pos);
129  }
130  }
131  }
132  if (filePattern.find("%T") != std::string::npos)
133  {
134  timeBuffISO = boost::posix_time::to_iso_string(boost::posix_time::second_clock::universal_time());
135  }
136  if (filePattern.find("%t") != std::string::npos)
137  {
138 
139  time_t rawtime;
140  struct tm* timeinfo;
141  char timeBuffC[256];
142  time(&rawtime);
143  timeinfo = localtime(&rawtime);
144  strftime(timeBuffC, 256, timePattern.c_str(), timeinfo);
145  timeBuff = std::string(timeBuffC);
146  }
147 
148  size_t pos = 0;
149  TLOG(TLVL_DEBUG) << "filePattern is: " << filePattern;
150  while (filePattern.find("%", pos) != std::string::npos)
151  {
152  pos = filePattern.find("%", pos) + 1;
153  TLOG(5,1) << "Found % at " << (pos - 1) << ", next char: " << filePattern[pos] << ".";
154  switch (filePattern[pos])
155  {
156  case '%': // "%%"
157  filePattern = filePattern.replace(pos - 1, 2, "%");
158  pos--;
159  break;
160  case '?':
161  {
162  char next = filePattern[pos + 1];
163  switch (next)
164  {
165  case 'N':
166  if (filePattern.find(exeString) != std::string::npos)
167  {
168  filePattern = filePattern.erase(pos - 1, 3);
169  }
170  else
171  {
172  std::string repString = exeString;
173  // Only append separator if we're not at the end of the pattern
174  if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.')))
175  {
176  repString += sep;
177  }
178  filePattern = filePattern.replace(pos - 1, 3, repString);
179  }
180  break;
181  case 'H':
182  if (filePattern.find(hostString) != std::string::npos)
183  {
184  filePattern = filePattern.erase(pos - 1, 3);
185  }
186  else
187  {
188  std::string repString = hostString;
189  // Only append separator if we're not at the end of the pattern
190  if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.')))
191  {
192  repString += sep;
193  }
194  filePattern = filePattern.replace(pos - 1, 3, repString);
195  break;
196  }
197  }
198  pos -= 3;
199  }
200  break;
201  case 'N':
202  filePattern = filePattern.replace(pos - 1, 2, exeString);
203  pos -= 2;
204  break;
205  case 'H':
206  filePattern = filePattern.replace(pos - 1, 2, hostString);
207  pos -= 2;
208  break;
209  case 'p':
210  filePattern = filePattern.replace(pos - 1, 2, std::to_string(pid));
211  pos -= 2;
212  break;
213  case 't':
214  filePattern = filePattern.replace(pos - 1, 2, timeBuff);
215  pos -= 2;
216  break;
217  case 'T':
218  filePattern = filePattern.replace(pos - 1, 2, timeBuffISO);
219  pos -= 2;
220  break;
221  }
222  TLOG(6) << "filePattern is now: " << filePattern;
223  }
224  std::string fileName = baseDir + "/" + filePattern;
225  TLOG(TLVL_DEBUG) << "fileName is: " << fileName;
226 
227  output_ = std::make_unique<cet::ostream_handle>(fileName.c_str(), append ? std::ios::app : std::ios::trunc);
228  }
229 
230  //======================================================================
231  // Message router ( overriddes ELdestination::routePayload )
232  //======================================================================
233  void ELGenFileOutput::routePayload(const std::ostringstream& oss, const ErrorObj&
234  )
235  {
236  *output_ << oss.str();
237  flush();
238  }
239 
241  {
242  output_->flush();
243  }
244  } // end namespace mfplugins
245 
246  //======================================================================
247  //
248  // makePlugin function
249  //
250  //======================================================================
251 
252 extern "C"
253 {
254  MAKE_PLUGIN_START(auto, std::string const&, fhicl::ParameterSet const& pset)
255  {
256  return std::make_unique<mfplugins::ELGenFileOutput>(pset);
257  } MAKE_PLUGIN_END
258 }
259 
260 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.
Message Facility destination which generates the output file name based on some combination of PID...
ELGenFileOutput(Parameters const &pset)
ELGenFileOutput Constructor.
virtual void flush() override
Flush any text in the ostream buffer to disk.
virtual ~ELGenFileOutput()
Default virtual destructor.