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