• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /**
2   * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3   * Licensed under the Apache License, Version 2.0 (the "License");
4   * you may not use this file except in compliance with the License.
5   * You may obtain a copy of the License at
6   *
7   * http://www.apache.org/licenses/LICENSE-2.0
8   *
9   * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  #include "logger.h"
17  #include "os/thread.h"
18  #include "string_helpers.h"
19  #include "generated/base_options.h"
20  
21  #include <cstdarg>
22  #include <cstdlib>
23  #include <cstring>
24  
25  #include <fstream>
26  #include <iostream>
27  #include <string_view>
28  
29  namespace panda {
30  
31  Logger *Logger::logger = nullptr;
32  thread_local int Logger::nesting = 0;
33  
34  #include <logger_impl_gen.inc>
35  
Initialize(const base_options::Options & options)36  void Logger::Initialize(const base_options::Options &options)
37  {
38      panda::Logger::ComponentMask component_mask;
39      auto load_components = [&component_mask](auto components) {
40          for (const auto &s : components) {
41              component_mask |= Logger::ComponentMaskFromString(s);
42          }
43      };
44      Level level = Level::LAST;
45  
46      if (options.WasSetLogFatal()) {
47          ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
48          load_components(options.GetLogFatal());
49          level = Level::FATAL;
50      } else if (options.WasSetLogError()) {
51          ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
52          load_components(options.GetLogError());
53          level = Level::ERROR;
54      } else if (options.WasSetLogWarning()) {
55          ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
56          load_components(options.GetLogWarning());
57          level = Level::WARNING;
58      } else if (options.WasSetLogInfo()) {
59          ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
60          load_components(options.GetLogInfo());
61          level = Level::INFO;
62      } else if (options.WasSetLogDebug()) {
63          ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
64          load_components(options.GetLogDebug());
65          level = Level::DEBUG;
66      } else {
67          ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
68          load_components(options.GetLogComponents());
69          level = Logger::LevelFromString(options.GetLogLevel());
70      }
71  
72  #ifdef ENABLE_HILOG
73      Logger::InitializeHiLogging(level, component_mask);
74      return;
75  #endif
76  
77      if (options.GetLogStream() == "std") {
78          Logger::InitializeStdLogging(level, component_mask);
79      } else if (options.GetLogStream() == "file" || options.GetLogStream() == "fast-file") {
80          const std::string &file_name = options.GetLogFile();
81          Logger::InitializeFileLogging(file_name, level, component_mask, options.GetLogStream() == "fast-file");
82      } else if (options.GetLogStream() == "dummy") {
83          Logger::InitializeDummyLogging(level, component_mask);
84      } else {
85          UNREACHABLE();
86      }
87  }
88  
89  #ifndef NDEBUG
90  /**
91   * In debug builds this function allowes or disallowes proceeding with actual logging (i.e. creating Message{})
92   */
93  /* static */
IsMessageSuppressed(Level level,Component component)94  bool Logger::IsMessageSuppressed([[maybe_unused]] Level level, [[maybe_unused]] Component component)
95  {
96      // Allowing only to log if it's not a nested log, or it's nested and it's severity is suitable
97      return level >= Logger::logger->nested_allowed_level_ && nesting > 0;
98  }
99  
100  /**
101   * Increases log nesting (i.e. depth, or how many instances of Message{} is active atm) in a given thread
102   */
103  /* static */
LogNestingInc()104  void Logger::LogNestingInc()
105  {
106      nesting++;
107  }
108  
109  /**
110   * Decreases log nesting (i.e. depth, or how many instances of Message{} is active atm) in a given thread
111   */
112  /* static */
LogNestingDec()113  void Logger::LogNestingDec()
114  {
115      nesting--;
116  }
117  #endif  // NDEBUG
118  
printf(const char * format,...)119  auto Logger::Buffer::printf(const char *format, ...) -> Buffer &
120  {
121      va_list args;
122      va_start(args, format);  // NOLINT(cppcoreguidelines-pro-type-vararg)
123  
124      [[maybe_unused]] int put = vsnprintf_s(this->data(), this->size(), this->size() - 1, format, args);
125      ASSERT(put >= 0 && static_cast<size_t>(put) < BUFFER_SIZE);
126  
127      va_end(args);
128      return *this;
129  }
130  
131  os::memory::Mutex Logger::mutex;  // NOLINT(fuchsia-statically-constructed-objects)
132  FUNC_MOBILE_LOG_PRINT mlog_buf_print = nullptr;
133  
~Message()134  Logger::Message::~Message()
135  {
136      if (print_system_error_) {
137          stream_ << ": " << os::Error(errno).ToString();
138      }
139  
140      Logger::Log(level_, component_, stream_.str());
141  #ifndef NDEBUG
142      panda::Logger::LogNestingDec();
143  #endif
144  
145      if (level_ == Level::FATAL) {
146          std::cerr << "FATAL ERROR" << std::endl;
147          std::cerr << "Backtrace [tid=" << os::thread::GetCurrentThreadId() << "]:\n";
148          PrintStack(std::cerr);
149          std::abort();
150      }
151  }
152  
153  /* static */
Log(Level level,Component component,const std::string & str)154  void Logger::Log(Level level, Component component, const std::string &str)
155  {
156      if (!IsLoggingOn(level, component)) {
157          return;
158      }
159  
160      os::memory::LockHolder<os::memory::Mutex> lock(mutex);
161      if (!IsLoggingOn(level, component)) {
162          return;
163      }
164  
165      size_t nl = str.find('\n');
166      if (nl == std::string::npos) {
167          logger->LogLineInternal(level, component, str);
168          logger->WriteMobileLog(level, GetComponentTag(component), str.c_str());
169      } else {
170          size_t i = 0;
171          while (nl != std::string::npos) {
172              std::string line = str.substr(i, nl - i);
173              logger->LogLineInternal(level, component, line);
174              logger->WriteMobileLog(level, GetComponentTag(component), line.c_str());
175              i = nl + 1;
176              nl = str.find('\n', i);
177          }
178  
179          logger->LogLineInternal(level, component, str.substr(i));
180          logger->WriteMobileLog(level, GetComponentTag(component), str.substr(i).c_str());
181      }
182  }
183  
184  /* static */
GetPrefix(Logger::Level level,Logger::Component component)185  std::string GetPrefix(Logger::Level level, Logger::Component component)
186  {
187      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
188      return helpers::string::Format("[TID %06x] %s/%s: ", os::thread::GetCurrentThreadId(), GetLevelTag(level),
189                                     GetComponentTag(component));
190  }
191  
192  /* static */
InitializeFileLogging(const std::string & log_file,Level level,ComponentMask component_mask,bool is_fast_logging)193  void Logger::InitializeFileLogging(const std::string &log_file, Level level, ComponentMask component_mask,
194                                     bool is_fast_logging)
195  {
196      if (IsInitialized()) {
197          return;
198      }
199  
200      os::memory::LockHolder<os::memory::Mutex> lock(mutex);
201  
202      if (IsInitialized()) {
203          return;
204      }
205  
206      std::ofstream stream(log_file);
207      if (stream) {
208          if (is_fast_logging) {
209              logger = new FastFileLogger(std::move(stream), level, component_mask);
210          } else {
211              logger = new FileLogger(std::move(stream), level, component_mask);
212          }
213      } else {
214          logger = new StderrLogger(level, component_mask);
215          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
216          std::string msg = helpers::string::Format("Fallback to stderr logging: cannot open log file '%s': %s",
217                                                    log_file.c_str(), os::Error(errno).ToString().c_str());
218          logger->LogLineInternal(Level::ERROR, Component::COMMON, msg);
219      }
220  }
221  
222  #ifdef ENABLE_HILOG
223  /* static */
InitializeHiLogging(Level level,ComponentMask component_mask)224  void Logger::InitializeHiLogging(Level level, ComponentMask component_mask)
225  {
226      if (IsInitialized()) {
227          return;
228      }
229  
230      {
231          os::memory::LockHolder<os::memory::Mutex> lock(mutex);
232  
233          if (IsInitialized()) {
234              return;
235          }
236  
237          logger = new HiLogger(level, component_mask);
238      }
239  }
240  #endif
241  
242  /* static */
InitializeStdLogging(Level level,ComponentMask component_mask)243  void Logger::InitializeStdLogging(Level level, ComponentMask component_mask)
244  {
245      if (IsInitialized()) {
246          return;
247      }
248  
249      {
250          os::memory::LockHolder<os::memory::Mutex> lock(mutex);
251  
252          if (IsInitialized()) {
253              return;
254          }
255  
256          logger = new StderrLogger(level, component_mask);
257      }
258  }
259  
260  /* static */
InitializeDummyLogging(Level level,ComponentMask component_mask)261  void Logger::InitializeDummyLogging(Level level, ComponentMask component_mask)
262  {
263      if (IsInitialized()) {
264          return;
265      }
266  
267      {
268          os::memory::LockHolder<os::memory::Mutex> lock(mutex);
269  
270          if (IsInitialized()) {
271              return;
272          }
273  
274          logger = new DummyLogger(level, component_mask);
275      }
276  }
277  
278  /* static */
Destroy()279  void Logger::Destroy()
280  {
281      if (!IsInitialized()) {
282          return;
283      }
284  
285      Logger *l = nullptr;
286  
287      {
288          os::memory::LockHolder<os::memory::Mutex> lock(mutex);
289  
290          if (!IsInitialized()) {
291              return;
292          }
293  
294          l = logger;
295          logger = nullptr;
296      }
297  
298      delete l;
299  }
300  
301  /* static */
ProcessLogLevelFromString(std::string_view s)302  void Logger::ProcessLogLevelFromString(std::string_view s)
303  {
304      if (Logger::IsInLevelList(s)) {
305          Logger::SetLevel(Logger::LevelFromString(s));
306      } else {
307          LOG(ERROR, RUNTIME) << "Unknown level " << s;
308      }
309  }
310  
311  /* static */
ProcessLogComponentsFromString(std::string_view s)312  void Logger::ProcessLogComponentsFromString(std::string_view s)
313  {
314      Logger::ResetComponentMask();
315      size_t last_pos = s.find_first_not_of(',', 0);
316      size_t pos = s.find(',', last_pos);
317      while (last_pos != std::string_view::npos) {
318          std::string_view component_str = s.substr(last_pos, pos - last_pos);
319          last_pos = s.find_first_not_of(',', pos);
320          pos = s.find(',', last_pos);
321          if (Logger::IsInComponentList(component_str)) {
322              Logger::EnableComponent(Logger::ComponentMaskFromString(component_str));
323          } else {
324              LOG(ERROR, RUNTIME) << "Unknown component " << component_str;
325          }
326      }
327  }
328  
LogLineInternal(Level level,Component component,const std::string & str)329  void FileLogger::LogLineInternal(Level level, Component component, const std::string &str)
330  {
331      std::string prefix = GetPrefix(level, component);
332      stream_ << prefix << str << std::endl << std::flush;
333  }
334  
LogLineInternal(Level level,Component component,const std::string & str)335  void FastFileLogger::LogLineInternal(Level level, Component component, const std::string &str)
336  {
337      std::string prefix = GetPrefix(level, component);
338      stream_ << prefix << str << '\n';
339  }
340  
341  #ifdef ENABLE_HILOG
LogLineInternal(Level level,Component component,const std::string & str)342  void HiLogger::LogLineInternal(Level level, Component component, const std::string &str)
343  {
344      std::string prefix = GetPrefix(level, component);
345      stream_ << prefix << str;
346      switch (level) {
347          case Level::DEBUG:
348              OHOS::HiviewDFX::HiLog::Debug(LABEL, "%{public}s", stream_.str().c_str());
349              break;
350          case Level::INFO:
351              OHOS::HiviewDFX::HiLog::Info(LABEL, "%{public}s", stream_.str().c_str());
352              break;
353          case Level::ERROR:
354              OHOS::HiviewDFX::HiLog::Error(LABEL, "%{public}s", stream_.str().c_str());
355              break;
356          case Level::FATAL:
357              OHOS::HiviewDFX::HiLog::Fatal(LABEL, "%{public}s", stream_.str().c_str());
358              break;
359          case Level::WARNING:
360               OHOS::HiviewDFX::HiLog::Warn(LABEL, "%{public}s", stream_.str().c_str());
361              break;
362          default:
363              UNREACHABLE();
364      }
365  }
366  #endif
367  
LogLineInternal(Level level,Component component,const std::string & str)368  void StderrLogger::LogLineInternal(Level level, Component component, const std::string &str)
369  {
370      std::string prefix = GetPrefix(level, component);
371      std::cerr << prefix << str << std::endl << std::flush;
372  }
373  
SyncOutputResource()374  void FastFileLogger::SyncOutputResource()
375  {
376      stream_ << std::flush;
377  }
378  
379  }  // namespace panda
380