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