artdaq_core  v3_02_01
configureMessageFacility.cc
1 #include "artdaq-core/Utilities/configureMessageFacility.hh"
2 #include "messagefacility/MessageLogger/MessageLogger.h"
3 #if CANVAS_HEX_VERSION >= 0x20002 // art v2_07_03 means a new versions of fhicl, boost, etc
4 # include "fhiclcpp/ParameterSet.h"
5 # include <boost/lexical_cast.hpp>
6 #endif
7 #include "fhiclcpp/make_ParameterSet.h"
8 #include "cetlib_except/exception.h"
9 #include <boost/filesystem.hpp>
10 #include <unistd.h>
11 #include <fstream>
12 #include <sstream>
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;
48  }
49  else
50  {
51  logfileDir = logRootString;
52  logfileDir.append("/");
53  logfileDir.append(progname);
54 
55  // As long as the top-level directory exists, I don't think we
56  // really care if we have to create application directories...
57  if (!BFS::exists(logfileDir))
58  BFS::create_directory(logfileDir);
59 
60  time_t rawtime;
61  struct tm* timeinfo;
62  char timeBuff[256];
63  time(&rawtime);
64  timeinfo = localtime(&rawtime);
65  strftime(timeBuff, 256, "%Y%m%d%H%M%S", timeinfo);
66 
67  char hostname[256];
68  std::string hostString = "";
69  if (gethostname(&hostname[0], 256) == 0)
70  {
71  std::string tmpString(hostname);
72  hostString = tmpString;
73  size_t pos = hostString.find(".");
74  if (pos != std::string::npos && pos > 2)
75  {
76  hostString = hostString.substr(0, pos);
77  }
78  }
79 
80  logfileName.append(logfileDir);
81  logfileName.append("/");
82  logfileName.append(progname);
83  logfileName.append("-");
84  if (hostString.size() > 0 && logfileName.find(hostString) == std::string::npos)
85  {
86  logfileName.append(hostString);
87  logfileName.append("-");
88  }
89  logfileName.append(timeBuff);
90  logfileName.append("-");
91  logfileName.append(boost::lexical_cast<std::string>(getpid()));
92  logfileName.append(".log");
93  }
94  }
95 
96  std::ostringstream ss;
97  ss << "debugModules:[\"*\"] "
98  << " destinations : { ";
99 
100  if (useConsole)
101  {
102  std::string outputLevel = "\"INFO\" ";
103  if (printDebug) outputLevel = "\"DEBUG\" ";
104  if (artdaqMfextensionsDir != nullptr && useMFExtensions)
105  {
106  ss << " console : { "
107  << " type : \"ANSI\" threshold : " << outputLevel;
108  if (!printTimestampsToConsole)
109  {
110 #if MESSAGEFACILITY_HEX_VERSION < 0x20103
111  ss << " noTimeStamps : true ";
112 #else
113  ss << " format: { timestamp: none } ";
114 #endif
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 #if MESSAGEFACILITY_HEX_VERSION < 0x20103
126  ss << " noTimeStamps : true ";
127 #else
128  ss << " format: { timestamp: none } ";
129 #endif
130  }
131  ss << " } ";
132  }
133  }
134 
135  if (logfileDir.size())
136  {
137  ss << " file: {";
138  ss << " type: \"GenFile\" threshold: \"DEBUG\" seperator: \"-\"";
139  ss << " pattern: \"" << progname << "-%?H%t-%p.log" << "\"";
140  ss << " timestamp_pattern: \"%Y%m%d%H%M%S\"";
141  ss << " directory: \"" << logfileDir << "\"";
142  ss << " append : false";
143  ss << " }";
144  }
145 #if 0 // ELF 01/17/2018 Removed because it violates the "every EVB art process must have identical configuration" rule
146  else if (logfileName.length() > 0)
147  {
148  ss << " file : { "
149  << " type : \"file\" threshold : \"DEBUG\" "
150  << " filename : \"" << logfileName << "\" "
151  << " append : false "
152  << " } ";
153  }
154 #endif
155 
156  if (artdaqMfextensionsDir != nullptr && useMFExtensions)
157  {
158  ss << " trace : { "
159  << " type : \"TRACE\" threshold : \"DEBUG\" format:{noLineBreaks: true} lvls: 0x7 lvlm: 0xF"
160  << " } ";
161  }
162 
163  if (logFhiclCode != nullptr)
164  {
165  std::ifstream logfhicl(logFhiclCode);
166 
167  if (logfhicl.is_open())
168  {
169  std::stringstream fhiclstream;
170  fhiclstream << logfhicl.rdbuf();
171  ss << fhiclstream.str();
172  }
173  else
174  {
175  throw cet::exception("configureMessageFacility") << "Unable to open requested fhicl file \"" << logFhiclCode << "\".";
176  }
177  }
178 
179  ss << " } ";
180 
181  std::string pstr(ss.str());
182 
183  //Canonicalize string:
184  fhicl::ParameterSet tmp_pset;
185  fhicl::make_ParameterSet(pstr, tmp_pset);
186  return tmp_pset.to_string();
187 }
188 // generateMessageFacilityConfiguration
189 
190 void artdaq::configureTRACE(fhicl::ParameterSet &trace_pset)
191 {
192  /* The following code handles this example fhicl:
193  TRACE:{
194  TRACE_NUMENTS:500000
195  TRACE_ARGSMAX:10
196  TRACE_MSGMAX:0
197  TRACE_FILE:"/tmp/trace_buffer_%u" # this is the default
198  TRACE_LIMIT_MS:[8,80,800]
199  TRACE_MODE:0xf
200  TRACE_NAMLVLSET:{
201  #name:[lvlsmskM,lvlsmskS[,lvlsmskT]] lvlsmskT is optional
202  name0:[0x1f,0x7]
203  name1:[0x2f,0xf]
204  name2:[0x3f,0x7,0x1]
205  }
206  }
207  */
208  std::vector<std::string> names = trace_pset.get_names();
209  std::vector<std::string> trace_envs = {//"TRACE_NUMENTS", "TRACE_ARGSMAX", "TRACE_MSGMAX", "TRACE_FILE",
210  "TRACE_LIMIT_MS", "TRACE_MODE", "TRACE_NAMLVLSET" };
211  std::unordered_map<std::string, bool> envs_set_to_unset;
212  for (auto env : trace_envs) envs_set_to_unset[env] = false;
213  // tricky - some env. vars. will over ride info in "mapped" (file) context while others cannot.
214  for (auto name : names)
215  {
216  if (name == "TRACE_NUMENTS" || name == "TRACE_ARGSMAX"
217  || name == "TRACE_MSGMAX" || name == "TRACE_FILE") // only applicable if env.var. set before before traceInit
218  // don't override and don't "set_to_unset" (if "mapping", want any subprocess to map also)
219  setenv(name.c_str(), trace_pset.get<std::string>(name).c_str(), 0);
220  // These next 3 are looked at when TRACE_CNTL("namlvlset") is called. And, if mapped, get into file! (so may want to unset env???)
221  else if (name == "TRACE_LIMIT_MS")
222  { // there is also TRACE_CNTL
223  if (!getenv(name.c_str()))
224  {
225  envs_set_to_unset[name] = true;
226  std::vector<uint32_t> limit = trace_pset.get<std::vector<uint32_t>>(name);
227  // could check that it is size()==3???
228  std::string limits = std::to_string(limit[0]) + "," + std::to_string(limit[1]) + "," + std::to_string(limit[2]);
229  setenv(name.c_str(), limits.c_str(), 0);
230  }
231  }
232  else if (name == "TRACE_MODE")
233  { // env.var. only applicable if TRACE_NAMLVLSET is set, BUT could TRACE_CNTL("mode",mode)???
234  if (!getenv(name.c_str()))
235  {
236  envs_set_to_unset[name] = true;
237  setenv(name.c_str(), trace_pset.get<std::string>(name).c_str(), 0);
238  }
239  }
240  else if (name == "TRACE_NAMLVLSET")
241  {
242  if (!getenv(name.c_str()))
243  {
244  envs_set_to_unset[name] = true;
245  std::stringstream lvlsbldr; // levels builder
246  fhicl::ParameterSet lvls_pset = trace_pset.get<fhicl::ParameterSet>(name);
247  std::vector<std::string> tnames = lvls_pset.get_names();
248  for (auto tname : tnames)
249  {
250  lvlsbldr << tname;
251  std::vector<uint64_t> msks = lvls_pset.get<std::vector<uint64_t>>(tname);
252  for (auto msk : msks)
253  {
254  lvlsbldr << " 0x" << std::hex << (unsigned long long)msk;
255  }
256  lvlsbldr << "\n";
257  }
258  setenv(name.c_str(), lvlsbldr.str().c_str(), 0); // 0 means: won't overwrite
259  }
260  }
261  }
262  TRACE_CNTL("namlvlset"); // acts upon env.var.
263  for (auto env : trace_envs) if (envs_set_to_unset[env]) unsetenv(env.c_str());
264 }
265 
266 void artdaq::configureMessageFacility(char const* progname, bool useConsole, bool printDebug)
267 {
268  auto pstr = generateMessageFacilityConfiguration(progname, useConsole, printDebug);
269  fhicl::ParameterSet pset;
270  fhicl::make_ParameterSet(pstr, pset);
271 
272  fhicl::ParameterSet trace_pset;
273  if (!pset.get_if_present<fhicl::ParameterSet>("TRACE", trace_pset))
274  {
275  fhicl::ParameterSet trace_dflt_pset;
276  fhicl::make_ParameterSet("TRACE:{TRACE_MSGMAX:0 TRACE_LIMIT_MS:[10,500,1500]}", trace_dflt_pset);
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 #if CANVAS_HEX_VERSION >= 0x30300 // art v2_11_00
285  mf::StartMessageFacility(pset, progname);
286 
287 #elif CANVAS_HEX_VERSION >= 0x20002 // art v2_07_03 means a new versions of fhicl, boost, etc
288  mf::StartMessageFacility(pset);
289 
290  mf::SetApplicationName(progname);
291 
292  mf::setEnabledState("");
293 # else
294  mf::StartMessageFacility(mf::MessageFacilityService::MultiThread, pset);
295 
296  mf::SetModuleName(progname);
297  mf::SetContext(progname);
298 # endif
299  TLOG(TLVL_TRACE) << "Message Facility Config input is: " << pstr;
300  TLOG(TLVL_INFO) << "Message Facility Application " << progname << " configured with: " << pset.to_string();
301 }
302 
303 std::string artdaq::setMsgFacAppName(const std::string& appType, unsigned short port)
304 {
305  std::string appName(appType);
306 
307  char hostname[256];
308  if (gethostname(&hostname[0], 256) == 0)
309  {
310  std::string hostString(hostname);
311  size_t pos = hostString.find(".");
312  if (pos != std::string::npos && pos > 2)
313  {
314  hostString = hostString.substr(0, pos);
315  }
316  appName.append("-");
317  appName.append(hostString);
318  }
319 
320  appName.append("-");
321  appName.append(boost::lexical_cast<std::string>(port));
322 
323  mf::SetApplicationName(appName);
324  return appName;
325 }
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...