artdaq_core  v3_06_00
GenFile_mfPlugin.cc
1 
2 #include "tracemf.h"
3 #define TRACE_NAME "GenFileOutput"
4 
5 #include <boost/date_time/posix_time/posix_time.hpp>
6 #include <fstream>
7 #include "cetlib/PluginTypeDeducer.h"
8 #include "cetlib/ProvideMakePluginMacros.h"
9 #include "cetlib/ostream_handle.h"
10 #include "fhiclcpp/ParameterSet.h"
11 #include "messagefacility/MessageService/ELdestination.h"
12 #include "messagefacility/Utilities/ELseverityLevel.h"
13 #include "messagefacility/Utilities/exception.h"
14 
15 namespace mfplugins {
16 using mf::ErrorObj;
17 using mf::service::ELdestination;
18 
23 class ELGenFileOutput : public ELdestination
24 {
25 public:
29  struct Config
30  {
32  fhicl::TableFragment<ELdestination::Config> elDestConfig;
34  fhicl::Atom<bool> append = fhicl::Atom<bool>{
35  fhicl::Name{"append"}, fhicl::Comment{"Whether to append to the file or recreate it"}, true};
37  fhicl::Atom<std::string> baseDir = fhicl::Atom<std::string>{
38  fhicl::Name{"directory"}, fhicl::Comment{"The directory into which files will be saved"}, "/tmp"};
40  fhicl::Atom<std::string> sep = fhicl::Atom<std::string>{
41  fhicl::Name{"seperator"}, fhicl::Comment{"Separator to use after optional replacement parameters"}, "-"};
43  fhicl::Atom<std::string> timePattern = fhicl::Atom<std::string>{
44  fhicl::Name{"timestamp_pattern"}, fhicl::Comment{"Pattern to use for %t strftime replacement"}, "%Y%m%d%H%M%S"};
59  fhicl::Atom<std::string> filePattern = fhicl::Atom<std::string>{fhicl::Name{"pattern"}, fhicl::Comment{"Pattern to use for file naming.\n"
60  " Supported parameters are:\n"
61  " %%: Print a % sign\n"
62  " %N: Print the executable name, as retrieved from /proc/<pid>/exe\n"
63  " %?N: Print the executable name only if it does not already appear in the parsed format. "
64  "Format is parsed left-to-right.\n"
65  " These options add a seperator AFTER if they are filled and if they are not the last token in "
66  "the file pattern, before the last '.' character.\n"
67  " %H: Print the hostname, without any domain specifiers (i.e. work.fnal.gov will become work)\n"
68  " %?H: Print the hostname only if it does not already appear in the parsed format.\n"
69  " %p: Print the PID of the application configuring MessageFacility\n"
70  " %t: Print the timestamp using the format specified by timestamp_pattern\n"
71  " %T: Print the timestamp in ISO format"},
72  "%N-%?H%t-%p.log"};
73  };
75  using Parameters = fhicl::WrappedTable<Config>;
76 
77 public:
82  explicit ELGenFileOutput(Parameters const& pset);
83 
89  void routePayload(const std::ostringstream& o, const ErrorObj& e) override;
90 
94  void flush() override;
95 
96 private:
97  std::unique_ptr<cet::ostream_handle> output_;
98 };
99 
100 // END DECLARATION
101 //======================================================================
102 // BEGIN IMPLEMENTATION
103 
104 //======================================================================
105 // ELGenFileOutput c'tor
106 //======================================================================
107 
109  : ELdestination(pset().elDestConfig())
110 {
111  bool append = pset().append();
112  std::string baseDir = pset().baseDir();
113  std::string sep = pset().sep();
114  std::string timePattern = pset().timePattern();
115  std::string filePattern = pset().filePattern();
116 
117  auto pid = getpid();
118  std::string exeString;
119  std::string hostString;
120  std::string timeBuffISO; // Using boost::posix_time::to_iso_string (%T)
121  std::string timeBuff; // Using timestamp_pattern (%t)
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(), nullptr));
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  time_t rawtime;
159  struct tm* timeinfo;
160  char timeBuffC[256];
161  time(&rawtime);
162  timeinfo = localtime(&rawtime);
163  strftime(timeBuffC, 256, timePattern.c_str(), timeinfo);
164  timeBuff = std::string(timeBuffC);
165  }
166 
167  size_t pos = 0;
168  TLOG(TLVL_DEBUG) << "filePattern is: " << filePattern;
169  while (filePattern.find('%', pos) != std::string::npos)
170  {
171  pos = filePattern.find('%', pos) + 1;
172  TLOG(5, 1) << "Found % at " << (pos - 1) << ", next char: " << filePattern[pos] << ".";
173  switch (filePattern[pos])
174  {
175  case '%': // "%%"
176  filePattern = filePattern.replace(pos - 1, 2, "%");
177  pos--;
178  break;
179  case '?':
180  {
181  char next = filePattern[pos + 1];
182  switch (next)
183  {
184  case 'N':
185  if (filePattern.find(exeString) != std::string::npos)
186  {
187  filePattern = filePattern.erase(pos - 1, 3);
188  }
189  else
190  {
191  std::string repString = exeString;
192  // Only append separator if we're not at the end of the pattern
193  if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.')))
194  {
195  repString += sep;
196  }
197  filePattern = filePattern.replace(pos - 1, 3, repString);
198  }
199  break;
200  case 'H':
201  if (filePattern.find(hostString) != std::string::npos)
202  {
203  filePattern = filePattern.erase(pos - 1, 3);
204  }
205  else
206  {
207  std::string repString = hostString;
208  // Only append separator if we're not at the end of the pattern
209  if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.')))
210  {
211  repString += sep;
212  }
213  filePattern = filePattern.replace(pos - 1, 3, repString);
214  }
215  break;
216  default:
217  pos += 3;
218  break;
219  }
220  pos -= 3;
221  }
222  break;
223  case 'N':
224  filePattern = filePattern.replace(pos - 1, 2, exeString);
225  pos -= 2;
226  break;
227  case 'H':
228  filePattern = filePattern.replace(pos - 1, 2, hostString);
229  pos -= 2;
230  break;
231  case 'p':
232  filePattern = filePattern.replace(pos - 1, 2, std::to_string(pid));
233  pos -= 2;
234  break;
235  case 't':
236  filePattern = filePattern.replace(pos - 1, 2, timeBuff);
237  pos -= 2;
238  break;
239  case 'T':
240  filePattern = filePattern.replace(pos - 1, 2, timeBuffISO);
241  pos -= 2;
242  break;
243  default:
244  break;
245  }
246  TLOG(6) << "filePattern is now: " << filePattern;
247  }
248  std::string fileName = baseDir + "/" + filePattern;
249  TLOG(TLVL_DEBUG) << "fileName is: " << fileName;
250 
251  output_ = std::make_unique<cet::ostream_handle>(fileName.c_str(), append ? std::ios::app : std::ios::trunc);
252 }
253 
254 //======================================================================
255 // Message router ( overriddes ELdestination::routePayload )
256 //======================================================================
257 void ELGenFileOutput::routePayload(const std::ostringstream& oss, const ErrorObj& /*msg*/)
258 {
259  *output_ << oss.str();
260  flush();
261 }
262 
264 {
265  output_->flush();
266 }
267 } // end namespace mfplugins
268 
269 //======================================================================
270 //
271 // makePlugin function
272 //
273 //======================================================================
274 
275 extern "C" {
276 MAKE_PLUGIN_START(auto, std::string const&, fhicl::ParameterSet const& pset)
277 {
278  return std::make_unique<mfplugins::ELGenFileOutput>(pset);
279 }
280 MAKE_PLUGIN_END
281 }
282 
283 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
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.
void flush() override
Flush any text in the ostream buffer to disk.
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.