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