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