00001 #define TRACE_NAME "GenFileOutput"
00002
00003 #include "cetlib/PluginTypeDeducer.h"
00004 #include "cetlib/ostream_handle.h"
00005 #include "cetlib/ProvideMakePluginMacros.h"
00006 #include "fhiclcpp/ParameterSet.h"
00007
00008 #include "boost/date_time/posix_time/posix_time.hpp"
00009
00010 #include "messagefacility/MessageService/ELdestination.h"
00011 # include "messagefacility/Utilities/ELseverityLevel.h"
00012 #include "messagefacility/Utilities/exception.h"
00013
00014 #include <fstream>
00015 #include "trace.h"
00016
00017 namespace mfplugins
00018 {
00019 using mf::service::ELdestination;
00020 using mf::ELseverityLevel;
00021 using mf::ErrorObj;
00022
00027 class ELGenFileOutput : public ELdestination
00028 {
00029 struct Config
00030 {
00031 fhicl::TableFragment<ELdestination::Config> elDestConfig;
00032 fhicl::Atom<bool> append{ fhicl::Name{"append"},fhicl::Comment {"Whether to append to the file or recreate it"},true };
00033 fhicl::Atom<std::string> baseDir{ fhicl::Name{"directory"},fhicl::Comment{"The directory into which files will be saved"},"/tmp" };
00034 fhicl::Atom<std::string> sep{ fhicl::Name{"seperator"},fhicl::Comment{"Separator to use after optional replacement parameters"}, "-" };
00035 fhicl::Atom<std::string> timePattern{ fhicl::Name{"timestamp_pattern"},fhicl::Comment{"Pattern to use for %t strftime replacement"},"%Y%m%d%H%M%S" };
00036 fhicl::Atom<std::string> filePattern{ fhicl::Name{ "pattern" },fhicl::Comment{ "Pattern to use for file naming.\n"
00037 " Supported parameters are:\n"
00038 " %%: Print a % sign\n"
00039 " %N: Print the executable name, as retrieved from /proc/<pid>/exe\n"
00040 " %?N: Print the executable name only if it does not already appear in the parsed format. Format is parsed left-to-right.\n"
00041 " 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"
00042 " %H: Print the hostname, without any domain specifiers (i.e. work.fnal.gov will become work)\n"
00043 " %?H: Print the hostname only if it does not already appear in the parsed format.\n"
00044 " %p: Print the PID of the application configuring MessageFacility\n"
00045 " %t: Print the timestamp using the format specified by timestamp_pattern\n"
00046 " %T: Print the timestamp in ISO format"
00047 },"%N-%?H%t-%p.log" };
00048
00049
00050 };
00051 using Parameters = fhicl::WrappedTable<Config>;
00052
00053 public:
00058 ELGenFileOutput(Parameters const& pset);
00059
00061 virtual ~ELGenFileOutput() {}
00062
00068 virtual void routePayload(const std::ostringstream& o, const ErrorObj& e) override;
00069
00073 virtual void flush() override;
00074
00075 private:
00076 std::unique_ptr<cet::ostream_handle> output_;
00077 };
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088 ELGenFileOutput::ELGenFileOutput(Parameters const& pset) : ELdestination(pset().elDestConfig())
00089 {
00090 bool append = pset().append();
00091 std::string baseDir = pset().baseDir();
00092 std::string sep = pset().sep();
00093 std::string timePattern = pset().timePattern();
00094 std::string filePattern = pset().filePattern();
00095
00096 auto pid = getpid();
00097 std::string exeString = "";
00098 std::string hostString = "";
00099 std::string timeBuffISO = "";
00100 std::string timeBuff = "";
00101
00102
00103
00104 if (filePattern.find("%N") != std::string::npos || filePattern.find("%?N") != std::string::npos)
00105 {
00106
00107 std::string exe;
00108 std::ostringstream pid_ostr;
00109 pid_ostr << "/proc/" << pid << "/exe";
00110 exe = std::string(realpath(pid_ostr.str().c_str(), NULL));
00111
00112 size_t end = exe.find('\0');
00113 size_t start = exe.find_last_of('/', end);
00114 exeString = exe.substr(start + 1, end - start - 1);
00115 }
00116
00117
00118 if (filePattern.find("%H") != std::string::npos || filePattern.find("%?H") != std::string::npos)
00119 {
00120 char hostname[256];
00121 if (gethostname(&hostname[0], 256) == 0)
00122 {
00123 std::string tmpString(hostname);
00124 hostString = tmpString;
00125 size_t pos = hostString.find(".");
00126 if (pos != std::string::npos && pos > 2)
00127 {
00128 hostString = hostString.substr(0, pos);
00129 }
00130 }
00131 }
00132 if (filePattern.find("%T") != std::string::npos)
00133 {
00134 timeBuffISO = boost::posix_time::to_iso_string(boost::posix_time::second_clock::universal_time());
00135 }
00136 if (filePattern.find("%t") != std::string::npos)
00137 {
00138
00139 time_t rawtime;
00140 struct tm* timeinfo;
00141 char timeBuffC[256];
00142 time(&rawtime);
00143 timeinfo = localtime(&rawtime);
00144 strftime(timeBuffC, 256, timePattern.c_str(), timeinfo);
00145 timeBuff = std::string(timeBuffC);
00146 }
00147
00148 size_t pos = 0;
00149 TLOG(TLVL_DEBUG) << "filePattern is: " << filePattern;
00150 while (filePattern.find("%", pos) != std::string::npos)
00151 {
00152 pos = filePattern.find("%", pos) + 1;
00153 TLOG(5,1) << "Found % at " << (pos - 1) << ", next char: " << filePattern[pos] << ".";
00154 switch (filePattern[pos])
00155 {
00156 case '%':
00157 filePattern = filePattern.replace(pos - 1, 2, "%");
00158 pos--;
00159 break;
00160 case '?':
00161 {
00162 char next = filePattern[pos + 1];
00163 switch (next)
00164 {
00165 case 'N':
00166 if (filePattern.find(exeString) != std::string::npos)
00167 {
00168 filePattern = filePattern.erase(pos - 1, 3);
00169 }
00170 else
00171 {
00172 std::string repString = exeString;
00173
00174 if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.')))
00175 {
00176 repString += sep;
00177 }
00178 filePattern = filePattern.replace(pos - 1, 3, repString);
00179 }
00180 break;
00181 case 'H':
00182 if (filePattern.find(hostString) != std::string::npos)
00183 {
00184 filePattern = filePattern.erase(pos - 1, 3);
00185 }
00186 else
00187 {
00188 std::string repString = hostString;
00189
00190 if (!(pos + 1 == filePattern.size() - 1 || pos + 2 == filePattern.find_last_of('.')))
00191 {
00192 repString += sep;
00193 }
00194 filePattern = filePattern.replace(pos - 1, 3, repString);
00195 break;
00196 }
00197 }
00198 pos -= 3;
00199 }
00200 break;
00201 case 'N':
00202 filePattern = filePattern.replace(pos - 1, 2, exeString);
00203 pos -= 2;
00204 break;
00205 case 'H':
00206 filePattern = filePattern.replace(pos - 1, 2, hostString);
00207 pos -= 2;
00208 break;
00209 case 'p':
00210 filePattern = filePattern.replace(pos - 1, 2, std::to_string(pid));
00211 pos -= 2;
00212 break;
00213 case 't':
00214 filePattern = filePattern.replace(pos - 1, 2, timeBuff);
00215 pos -= 2;
00216 break;
00217 case 'T':
00218 filePattern = filePattern.replace(pos - 1, 2, timeBuffISO);
00219 pos -= 2;
00220 break;
00221 }
00222 TLOG(6) << "filePattern is now: " << filePattern;
00223 }
00224 std::string fileName = baseDir + "/" + filePattern;
00225 TLOG(TLVL_DEBUG) << "fileName is: " << fileName;
00226
00227 output_ = std::make_unique<cet::ostream_handle>(fileName.c_str(), append ? std::ios::app : std::ios::trunc);
00228 }
00229
00230
00231
00232
00233 void ELGenFileOutput::routePayload(const std::ostringstream& oss, const ErrorObj&
00234 )
00235 {
00236 *output_ << oss.str();
00237 flush();
00238 }
00239
00240 void ELGenFileOutput::flush()
00241 {
00242 output_->flush();
00243 }
00244 }
00245
00246
00247
00248
00249
00250
00251
00252 extern "C"
00253 {
00254 MAKE_PLUGIN_START(auto, std::string const&, fhicl::ParameterSet const& pset)
00255 {
00256 return std::make_unique<mfplugins::ELGenFileOutput>(pset);
00257 } MAKE_PLUGIN_END
00258 }
00259
00260 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)