Belle II Software  release-08-01-10
LogListener.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 #include "daq/slc/readout/LogListener.h"
9 
10 #include "daq/slc/readout/ProcessController.h"
11 
12 #include <daq/slc/runcontrol/RCCallback.h>
13 #include <daq/slc/runcontrol/RCCommand.h>
14 #include <daq/slc/nsm/NSMCommunicator.h>
15 
16 #include <daq/slc/system/File.h>
17 #include <daq/slc/system/FileReader.h>
18 
19 #include <daq/slc/base/IOException.h>
20 #include <daq/slc/base/StringUtil.h>
21 #include <daq/slc/system/LogFile.h>
22 
23 #include <iostream>
24 #include <sstream>
25 #include <ctype.h>
26 
27 namespace Belle2 {
34 
36 }
37 
38 using namespace Belle2;
39 
40 void LogListener::run()
41 {
42  File fd(m_pipe[0]);
43  PipeReader preader(fd);
44  char c;
45  std::stringstream ss;
46  std::string s;
47  LogFile::Priority priority = LogFile::UNKNOWN;
48  int count = 0;
49  try {
50  while (true) {
51  c = preader.readChar(); //read next character from pipe
52 
53  if (c != '\n' && iscntrl(c)) continue; //character is unprintable, skip
54 
55  if (c == '\n' && count > 0) { //newline received line was not empty -> message is assembled, submit
56  std::string assembledLogMessage(ss.str());
57 
58  // basf2 has a flag to escape newlines with "\\n" to write multi-line log messages into a single line
59  // see: https://gitlab.desy.de/belle2/software/basf2/-/issues/6353
60  // if m_enableUnescapeNewlines is set, these "\\n" will be replaced with "\n" newlines again before the log message is sent out
61 
62  if (m_enableUnescapeNewlines) {
63  assembledLogMessage = StringUtil::replace(assembledLogMessage, "\\n", "\n");
64  }
65 
66  s = m_con->getParName() + " : " + assembledLogMessage;
67  ss.str("");
68  //m_con->lock();
69  if (priority == LogFile::UNKNOWN) {
70  priority = LogFile::DEBUG;
71  }
72 
73  if (priority > LogFile::DEBUG) {
74  m_con->getCallback()->log(priority, s);
75  } else {
76  LogFile::debug(s);
77  }
78 
79  if (m_con->getCallback()->getNode().getState() == RCState::RUNNING_S) {
80  if (priority == LogFile::ERROR) {
81  // m_con->getCallback()->log(LogFile::ERROR, s);
82  } else if (priority == LogFile::FATAL) {
83  // m_con->getCallback()->log(LogFile::FATAL, s));
84  m_con->getCallback()->setState(RCState::ERROR_ES);
85  }
86  }
87  //m_con->unlock();
88  count = 0;
89  priority = LogFile::UNKNOWN;
90  } else if (isprint(c)) { //continue to assemble message
91  if (count == 0 && c == '[') { //start of a "[DEBUG]"-like priority identifier preceding each log line
92  ss << c;
93  while (true) {
94  c = preader.readChar();
95  if (c == ']') { //end of a "[DEBUG]"-like priority identifier
96  ss << c;
97  s = ss.str();
98  if (s == "[DEBUG]") priority = LogFile::DEBUG;
99  else if (s == "[INFO]") priority = LogFile::INFO;
100  else if (s == "[NOTICE]") priority = LogFile::NOTICE;
101  else if (s == "[WARNING]") priority = LogFile::WARNING;
102  else if (s == "[ERROR]") priority = LogFile::ERROR;
103  else if (s == "[FATAL]") priority = LogFile::FATAL;
104  else if (s.find("STOP") != std::string::npos) {
105  StringList sl = StringUtil::split(s, '=');
106  if (sl.size() > 1) {
107  std::string nodename = StringUtil::replace(sl[1], "]", "");
108  try {
109  NSMCommunicator::send(NSMMessage(NSMNode(nodename), RCCommand::STOP));
110  } catch (const std::exception& e) {
111  }
112  }
113  count = 0;
114  ss.str("");
115  break;
116  }
117  if (priority > 0) {
118  count = 0;
119  ss.str("");
120  } else {
121  priority = LogFile::DEBUG;
122  }
123  break;
124  }
125  ss << c;
126  }
127  } else {
128  if (c != ' ' || count > 0) {
129  ss << c;
130  count++;
131  }
132  }
133  }
134  }
135  } catch (const IOException& e) {
136  LogFile::debug(e.what());
137  if (count > 0) {
138  s = m_con->getParName() + " : " + ss.str();
139  ss.str("");
140  //m_con->lock();
141  if (priority == LogFile::UNKNOWN) {
142  priority = LogFile::DEBUG;
143  }
144  m_con->getCallback()->log(priority, s);
145  if (m_con->getCallback()->getNode().getState() == RCState::RUNNING_S) {
146  if (priority == LogFile::ERROR) {
147  //m_con->getCallback()->reply(NSMMessage(NSMCommand::ERROR, s));
148  } else if (priority == LogFile::FATAL) {
149  //m_con->getCallback()->reply(NSMMessage(NSMCommand::ERROR, s));
150  m_con->getCallback()->setState(RCState::ERROR_ES);
151  }
152  }
153  //m_con->unlock();
154  }
155  }
156  close(m_pipe[0]);
157 }
Abstract base class for different kinds of events.