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