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