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