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