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