artdaq_core  3.10.01
configureMessageFacility.cc
1 #include "artdaq-core/Utilities/configureMessageFacility.hh"
2 #include "messagefacility/MessageLogger/MessageLogger.h"
3 
4 #include <unistd.h>
5 #include <boost/filesystem.hpp>
6 #include <boost/lexical_cast.hpp>
7 #include <fstream>
8 #include <sstream>
9 #include "artdaq-core/Utilities/ExceptionHandler.hh"
10 #include "cetlib_except/exception.h"
11 #include "fhiclcpp/ParameterSet.h"
12 #define TRACE_NAME "configureMessageFacility"
13 #include "TRACE/tracemf.h" // TRACE_CNTL, TRACE
14 
15 namespace BFS = boost::filesystem;
16 
17 namespace {
23 fhicl::ParameterSet make_pset(std::string const& config_str)
24 {
25  return fhicl::ParameterSet::make(config_str);
26 }
27 } // namespace
28 
29 std::string artdaq::generateMessageFacilityConfiguration(char const* progname, bool useConsole, bool printDebug, char const* fileExtraName)
30 {
31  std::string logPathProblem;
32  char* logRootString = getenv("ARTDAQ_LOG_ROOT");
33  char* logFhiclCode = getenv("ARTDAQ_LOG_FHICL");
34  char* artdaqMfextensionsDir = getenv("ARTDAQ_MFEXTENSIONS_DIR");
35  char* useMFExtensionsS = getenv("ARTDAQ_MFEXTENSIONS_ENABLED");
36  char* run_number = getenv("ARTDAQ_RUN_NUMBER");
37  bool useMFExtensions = false;
38  if (useMFExtensionsS != nullptr && !(strncmp(useMFExtensionsS, "0", 1) == 0))
39  {
40  useMFExtensions = true;
41  }
42 
43  char* printTimestampsToConsoleS = getenv("ARTDAQ_LOG_TIMESTAMPS_TO_CONSOLE");
44  bool printTimestampsToConsole = true;
45  if (printTimestampsToConsoleS != nullptr && strncmp(printTimestampsToConsoleS, "0", 1) == 0)
46  {
47  printTimestampsToConsole = false;
48  }
49 
50  std::string logfileDir;
51  if (logRootString != nullptr)
52  {
53  if (!BFS::exists(logRootString))
54  {
55  logPathProblem = "Log file root directory ";
56  logPathProblem.append(logRootString);
57  logPathProblem.append(" does not exist!");
58  throw cet::exception("ConfigureMessageFacility") << logPathProblem; // NOLINT(cert-err60-cpp)
59  }
60 
61  logfileDir = logRootString;
62  logfileDir.append("/");
63  logfileDir.append(progname);
64 
65  // As long as the top-level directory exists, I don't think we
66  // really care if we have to create application directories...
67  if (!BFS::exists(logfileDir))
68  {
69  BFS::create_directory(logfileDir);
70  BFS::permissions(logfileDir, BFS::add_perms | BFS::owner_all | BFS::group_all | BFS::others_read);
71  }
72  }
73 
74  std::ostringstream ss;
75  // ss << "debugModules:[\"*\"] "
76  ss << " destinations : { ";
77 
78  if (useConsole)
79  {
80  std::string outputLevel = "\"INFO\" ";
81  if (printDebug)
82  {
83  outputLevel = "\"DEBUG\" ";
84  }
85  if (artdaqMfextensionsDir != nullptr && useMFExtensions)
86  {
87  ss << " console : { "
88  << " type : \"ANSI\" threshold : " << outputLevel;
89  if (!printTimestampsToConsole)
90  {
91  ss << " format: { timestamp: none } ";
92  }
93  ss << " bell_on_error: true ";
94  ss << " } ";
95  }
96  else
97  {
98  ss << " console : { "
99  << " type : \"cout\" threshold :" << outputLevel;
100  if (!printTimestampsToConsole)
101  {
102  ss << " format: { timestamp: none } ";
103  }
104  ss << " } ";
105  }
106  }
107 
108  if (!logfileDir.empty())
109  {
110  ss << " file: {";
111  ss << R"( type: "GenFile" threshold: "DEBUG" seperator: "-")";
112  // ss << " pattern: \"" << progname << fileExtraName << "-%?H%t-%p.log"
113  // << "\"";
114  if (run_number == nullptr) {
115  ss << " pattern: \"" << progname << fileExtraName << "-%?H%t-%p.log" << "\"";
116  }
117  else {
118 //-----------------------------------------------------------------------------
119 // Mu2e case: run number is defined
120 //-----------------------------------------------------------------------------
121  char c[10];
122  sprintf(c,"%06i",std::stoi(run_number));
123  ss << " pattern: \"" << progname << "-" << c << fileExtraName << "-%?H%t-%p.log" << "\"";
124  }
125 
126  ss << " timestamp_pattern: \"%Y%m%d%H%M%S\"";
127  ss << " directory: \"" << logfileDir << "\"";
128  ss << " append : false";
129  ss << " }";
130  }
131 
132  if (artdaqMfextensionsDir != nullptr && useMFExtensions)
133  {
134  ss << " trace : { "
135  << R"( type : "TRACE" threshold : "DEBUG" format:{noLineBreaks: true} lvls: 0x7 lvlm: 0xF)"
136  << " } ";
137  }
138 
139  if (logFhiclCode != nullptr)
140  {
141  std::ifstream logfhicl(logFhiclCode);
142 
143  if (logfhicl.is_open())
144  {
145  std::stringstream fhiclstream;
146  fhiclstream << logfhicl.rdbuf();
147  ss << fhiclstream.str();
148  }
149  else
150  {
151  TLOG(TLVL_ERROR) << "Unable to open requested fhicl file ARTDAQ_LOG_FHICL=\"" << logFhiclCode << "\".";
152  throw cet::exception("ConfigureMessageFacility") << "Unable to open requested fhicl file ARTDAQ_LOG_FHICL=\"" << logFhiclCode << "\"."; // NOLINT(cert-err60-cpp)
153  }
154  }
155 
156  ss << " } ";
157 
158  std::string pstr(ss.str());
159 
160  // Canonicalize string:
161  fhicl::ParameterSet tmp_pset;
162  try
163  {
164  tmp_pset = make_pset(pstr);
165  }
166  catch (cet::exception const& ex)
167  {
168  ExceptionHandler(ExceptionHandlerRethrow::yes, std::string("Exception occurred while processing fhicl ParameterSet string ") + pstr + ":");
169  }
170  return tmp_pset.to_string();
171 }
172 // generateMessageFacilityConfiguration
173 
174 void artdaq::configureTRACE(fhicl::ParameterSet& trace_pset)
175 {
176  /* The following code handles this example fhicl:
177  TRACE:{
178  TRACE_NUMENTS:500000
179  TRACE_ARGSMAX:10
180  TRACE_MSGMAX:0
181  TRACE_FILE:"/tmp/trace_buffer_%u" # this is the default
182  TRACE_LIMIT_MS:[8,80,800]
183  TRACE_MODE:0xf
184  TRACE_NAMLVLSET:{
185  #name:[lvlsmskM,lvlsmskS[,lvlsmskT]] lvlsmskT is optional
186  name0:[0x1f,0x7]
187  name1:[0x2f,0xf]
188  name2:[0x3f,0x7,0x1]
189  }
190  }
191  */
192  std::vector<std::string> names = trace_pset.get_names();
193  std::vector<std::string> trace_envs = {//"TRACE_NUMENTS", "TRACE_ARGSMAX", "TRACE_MSGMAX", "TRACE_FILE",
194  "TRACE_LIMIT_MS", "TRACE_MODE", "TRACE_NAMLVLSET"};
195  std::unordered_map<std::string, bool> envs_set_to_unset;
196  for (const auto& env : trace_envs)
197  {
198  envs_set_to_unset[env] = false;
199  }
200  // tricky - some env. vars. will over ride info in "mapped" (file) context while others cannot.
201  for (const auto& name : names)
202  {
203  if (name == "TRACE_NUMENTS" || name == "TRACE_ARGSMAX" || name == "TRACE_MSGMAX" || name == "TRACE_FILE")
204  { // only applicable if env.var. set before before traceInit
205  // don't override and don't "set_to_unset" (if "mapping", want any subprocess to map also)
206  setenv(name.c_str(), trace_pset.get<std::string>(name).c_str(), 0);
207  // These next 3 are looked at when TRACE_CNTL("namlvlset") is called. And, if mapped, get into file! (so may want to unset env???)
208  }
209  else if (name == "TRACE_LIMIT_MS")
210  { // there is also TRACE_CNTL
211  if (getenv(name.c_str()) == nullptr)
212  {
213  envs_set_to_unset[name] = true;
214  auto limit = trace_pset.get<std::vector<uint32_t>>(name);
215  // could check that it is size()==3???
216  std::string limits = std::to_string(limit[0]) + "," + std::to_string(limit[1]) + "," + std::to_string(limit[2]);
217  setenv(name.c_str(), limits.c_str(), 0);
218  }
219  }
220  else if (name == "TRACE_MODE")
221  { // env.var. only applicable if TRACE_NAMLVLSET is set, BUT could TRACE_CNTL("mode",mode)???
222  if (getenv(name.c_str()) == nullptr)
223  {
224  envs_set_to_unset[name] = true;
225  setenv(name.c_str(), trace_pset.get<std::string>(name).c_str(), 0);
226  }
227  }
228  else if (name == "TRACE_NAMLVLSET")
229  {
230  if (getenv(name.c_str()) == nullptr)
231  {
232  envs_set_to_unset[name] = true;
233  std::stringstream lvlsbldr; // levels builder
234  auto lvls_pset = trace_pset.get<fhicl::ParameterSet>(name);
235  std::vector<std::string> tnames = lvls_pset.get_names();
236  for (const auto& tname : tnames)
237  {
238  lvlsbldr << tname;
239  auto msks = lvls_pset.get<std::vector<double>>(tname);
240  for (auto msk : msks)
241  {
242  lvlsbldr << " 0x" << std::hex << static_cast<unsigned long long>(msk); // NOLINT(google-runtime-int)
243  }
244  lvlsbldr << "\n";
245  }
246  setenv(name.c_str(), lvlsbldr.str().c_str(), 0); // 0 means: won't overwrite
247  }
248  }
249  }
250  TRACE_CNTL("namlvlset"); // acts upon env.var.
251  for (const auto& env : trace_envs)
252  {
253  if (envs_set_to_unset[env])
254  {
255  unsetenv(env.c_str());
256  }
257  }
258 }
259 
260 void artdaq::configureMessageFacility(char const* progname, bool useConsole, bool printDebug)
261 {
262  auto pstr = generateMessageFacilityConfiguration(progname, useConsole, printDebug);
263  fhicl::ParameterSet pset;
264  try
265  {
266  pset = make_pset(pstr);
267  }
268  catch (cet::exception const&)
269  {
270  ExceptionHandler(ExceptionHandlerRethrow::yes, std::string("Exception occurred while processing fhicl ParameterSet string ") + pstr + ":");
271  }
272 
273  fhicl::ParameterSet trace_pset;
274  if (!pset.get_if_present<fhicl::ParameterSet>("TRACE", trace_pset))
275  {
276  auto trace_dflt_pset = make_pset("TRACE:{TRACE_MSGMAX:0 TRACE_LIMIT_MS:[10,500,1500]}");
277  pset.put<fhicl::ParameterSet>("TRACE", trace_dflt_pset.get<fhicl::ParameterSet>("TRACE"));
278  trace_pset = pset.get<fhicl::ParameterSet>("TRACE");
279  }
280  configureTRACE(trace_pset);
281  pstr = pset.to_string();
282  pset.erase("TRACE");
283 
284  mf::StartMessageFacility(pset, progname);
285 
286  TLOG(TLVL_DEBUG + 33) << "Message Facility Config input is: " << pstr;
287  TLOG(TLVL_INFO) << "Message Facility Application " << progname << " configured with: " << pset.to_string();
288 }
289 
290 std::string artdaq::setMsgFacAppName(const std::string& appType, unsigned short port)
291 {
292  std::string appName(appType);
293 
294  char hostname[256];
295  if (gethostname(&hostname[0], 256) == 0)
296  {
297  std::string hostString(hostname);
298  size_t pos = hostString.find('.');
299  if (pos != std::string::npos && pos > 2)
300  {
301  hostString = hostString.substr(0, pos);
302  }
303  appName.append("-");
304  appName.append(hostString);
305  }
306 
307  appName.append("-");
308  appName.append(boost::lexical_cast<std::string>(port));
309 
310  mf::SetApplicationName(appName);
311  return appName;
312 }
fhicl::ParameterSet make_pset(std::string const &config_str)
Make a fhicl::ParameterSet from a string (shim for compatibility)
void configureTRACE(fhicl::ParameterSet &trace_pset)
Configure TRACE.
void configureMessageFacility(char const *progname, bool useConsole=true, bool printDebug=false)
Configure and start the message facility. Provide the program name so that messages will be appropria...
std::string setMsgFacAppName(const std::string &appType, unsigned short port)
Set the message facility application name using the specified application type and port number...
std::string generateMessageFacilityConfiguration(char const *progname, bool useConsole=true, bool printDebug=false, char const *fileExtraName="")
Create the MessageFacility configuration Fhicl string.
void ExceptionHandler(ExceptionHandlerRethrow decision, const std::string &optional_message)
The ExceptionHandler class prints out all available information about an excection, then optionally re-throws.