artdaq_core  v3_02_01
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  ELGenFileOutput(const fhicl::ParameterSet& pset);
60 #else
61  ELGenFileOutput(Parameters const& pset);
62 #endif
63 
64  virtual ~ELGenFileOutput() {}
65 
66  virtual void routePayload(const std::ostringstream&, const ErrorObj&
67  ) override;
68 
69  virtual void flush(
70  ) override;
71 
72  private:
73  std::unique_ptr<cet::ostream_handle> output_;
74  };
75 
76  // END DECLARATION
77  //======================================================================
78  // BEGIN IMPLEMENTATION
79 
80 
81  //======================================================================
82  // ELGenFileOutput c'tor
83  //======================================================================
84 #if MESSAGEFACILITY_HEX_VERSION < 0x20103
85  ELGenFileOutput::ELGenFileOutput(const fhicl::ParameterSet& pset)
86  : ELdestination(pset)
87  {
88  bool append = pset.get<bool>("append", true);
89  std::string baseDir = pset.get<std::string>("directory", "/tmp");
90  std::string sep = pset.get<std::string>("separator", "-");
91  std::string timePattern = pset.get<std::string>("timestamp_pattern", "%Y%m%d%H%M%S"); // For strftime
92  std::string filePattern = pset.get<std::string>("pattern", "%N-%?H%t-%p.log");
93 #else
94  ELGenFileOutput::ELGenFileOutput(Parameters const& pset) : ELdestination(pset().elDestConfig())
95  {
96  bool append = pset().append();
97  std::string baseDir = pset().baseDir();
98  std::string sep = pset().sep();
99  std::string timePattern = pset().timePattern();
100  std::string filePattern = pset().filePattern();
101 #endif
102 
103  auto pid = getpid();
104  std::string exeString = "";
105  std::string hostString = "";
106  std::string timeBuffISO = ""; // Using boost::posix_time::to_iso_string (%T)
107  std::string timeBuff = ""; // Using timestamp_pattern (%t)
108 
109 
110  // Determine image name
111  if (filePattern.find("%N") != std::string::npos || filePattern.find("%?N") != std::string::npos)
112  {
113  // get process name from '/proc/pid/exe'
114  std::string exe;
115  std::ostringstream pid_ostr;
116  pid_ostr << "/proc/" << pid << "/exe";
117  exe = std::string(realpath(pid_ostr.str().c_str(), NULL));
118 
119  size_t end = exe.find('\0');
120  size_t start = exe.find_last_of('/', end);
121  exeString = exe.substr(start + 1, end - start - 1);
122  }
123 
124  // Get Host name
125  if (filePattern.find("%H") != std::string::npos || filePattern.find("%?H") != std::string::npos)
126  {
127  char hostname[256];
128  if (gethostname(&hostname[0], 256) == 0)
129  {
130  std::string tmpString(hostname);
131  hostString = tmpString;
132  size_t pos = hostString.find(".");
133  if (pos != std::string::npos && pos > 2)
134  {
135  hostString = hostString.substr(0, pos);
136  }
137  }
138  }
139  if (filePattern.find("%T") != std::string::npos)
140  {
141  timeBuffISO = boost::posix_time::to_iso_string(boost::posix_time::second_clock::universal_time());
142  }
143  if (filePattern.find("%t") != std::string::npos)
144  {
145 
146  time_t rawtime;
147  struct tm* timeinfo;
148  char timeBuffC[256];
149  time(&rawtime);
150  timeinfo = localtime(&rawtime);
151  strftime(timeBuffC, 256, timePattern.c_str(), timeinfo);
152  timeBuff = std::string(timeBuffC);
153  }
154 
155  size_t pos = 0;
156  TLOG(TLVL_DEBUG) << "filePattern is: " << filePattern;
157  while (filePattern.find("%", pos) != std::string::npos)
158  {
159  pos = filePattern.find("%", pos) + 1;
160  TLOG(5) << "Found % at " << std::to_string(pos - 1) << ", next char: " << filePattern[pos] << ".";
161  switch (filePattern[pos])
162  {
163  case '%': // "%%"
164  filePattern = filePattern.replace(pos - 1, 2, "%");
165  pos--;
166  break;
167  case '?':
168  {
169  char next = filePattern[pos + 1];
170  switch (next)
171  {
172  case 'N':
173  if (filePattern.find(exeString) != std::string::npos)
174  {
175  filePattern = filePattern.erase(pos - 1, 3);
176  }
177  else
178  {
179  std::string repString = exeString;
180  // Only append separator if we're not at the end of the pattern
181  if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.')))
182  {
183  repString += sep;
184  }
185  filePattern = filePattern.replace(pos - 1, 3, repString);
186  }
187  break;
188  case 'H':
189  if (filePattern.find(hostString) != std::string::npos)
190  {
191  filePattern = filePattern.erase(pos - 1, 3);
192  }
193  else
194  {
195  std::string repString = hostString;
196  // Only append separator if we're not at the end of the pattern
197  if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.')))
198  {
199  repString += sep;
200  }
201  filePattern = filePattern.replace(pos - 1, 3, repString);
202  break;
203  }
204  }
205  pos -= 3;
206  }
207  break;
208  case 'N':
209  filePattern = filePattern.replace(pos - 1, 2, exeString);
210  pos -= 2;
211  break;
212  case 'H':
213  filePattern = filePattern.replace(pos - 1, 2, hostString);
214  pos -= 2;
215  break;
216  case 'p':
217  filePattern = filePattern.replace(pos - 1, 2, std::to_string(pid));
218  pos -= 2;
219  break;
220  case 't':
221  filePattern = filePattern.replace(pos - 1, 2, timeBuff);
222  pos -= 2;
223  break;
224  case 'T':
225  filePattern = filePattern.replace(pos - 1, 2, timeBuffISO);
226  pos -= 2;
227  break;
228  }
229  TLOG(6) << "filePattern is now: " << filePattern;
230  }
231  std::string fileName = baseDir + "/" + filePattern;
232  TLOG(TLVL_DEBUG) << "fileName is: " << fileName;
233 
234  output_ = std::make_unique<cet::ostream_handle>(fileName.c_str(), append ? std::ios::app : std::ios::trunc);
235  }
236 
237  //======================================================================
238  // Message router ( overriddes ELdestination::routePayload )
239  //======================================================================
240  void ELGenFileOutput::routePayload(const std::ostringstream& oss, const ErrorObj&
241  )
242  {
243  *output_ << oss.str();
244  flush();
245  }
246 
247  void ELGenFileOutput::flush()
248  {
249  output_->flush();
250  }
251 } // end namespace mfplugins
252 
253  //======================================================================
254  //
255  // makePlugin function
256  //
257  //======================================================================
258 
259 extern "C"
260 {
261 
262 #if MESSAGEFACILITY_HEX_VERSION >= 0x20106 // v2_01_06 => cetlib v3_02_00 => new clang support
263  MAKE_PLUGIN_START(auto, std::string const&, fhicl::ParameterSet const& pset)
264  {
265  return std::make_unique<mfplugins::ELGenFileOutput>(pset);
266  } MAKE_PLUGIN_END
267 #else
268  auto makePlugin(std::string const&, fhicl::ParameterSet const& pset)
269  {
270  return std::make_unique<mfplugins::ELGenFileOutput>(pset);
271  }
272 #endif
273 }
274 
275 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
Message Facility destination which generates the output file name based on some combination of PID...