Fcitx
log.cpp
1 /*
2  * SPDX-FileCopyrightText: 2017-2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "log.h"
9 #include <charconv>
10 #include <chrono>
11 #include <cstdlib>
12 #include <iostream>
13 #include <memory>
14 #include <mutex>
15 #include <ostream>
16 #include <string>
17 #include <string_view>
18 #include <system_error>
19 #include <type_traits>
20 #include <unordered_set>
21 #include <utility>
22 #include <vector>
23 #include <format>
24 #include <syncstream>
25 #include "macros.h"
26 #include "stringutils.h"
27 
28 namespace fcitx {
29 
30 namespace {
31 
32 FCITX_DEFINE_LOG_CATEGORY(defaultCategory, "default");
33 
34 using LogRule = std::pair<std::string, LogLevel>;
35 
36 struct LogConfig {
37  static std::ostream *defaultLogStream;
38  static thread_local std::osyncstream localLogStream;
39  static bool showTimeDate;
40 };
41 
42 std::ostream *LogConfig::defaultLogStream = &std::cerr;
43 thread_local std::osyncstream LogConfig::localLogStream = []() {
44  std::osyncstream out(*LogConfig::defaultLogStream);
45  out.rdbuf()->set_emit_on_sync(true);
46  return out;
47 }();
48 bool LogConfig::showTimeDate = true;
49 
50 bool validateLogLevel(std::underlying_type_t<LogLevel> l) {
51  return (l >= 0 &&
52  l <= std::underlying_type_t<LogLevel>(LogLevel::LastLogLevel));
53 }
54 
55 class LogRegistry {
56 public:
57  static LogRegistry &instance() {
58  static LogRegistry instance_;
59  return instance_;
60  }
61 
62  void registerCategory(LogCategory &category) {
63  std::lock_guard<std::mutex> lock(mutex_);
64  if (!categories_.contains(&category)) {
65  categories_.insert(&category);
66  applyRule(&category);
67  }
68  }
69 
70  void unregisterCategory(LogCategory &category) {
71  std::lock_guard<std::mutex> lock(mutex_);
72  categories_.erase(&category);
73  }
74 
75  void setLogRules(const std::vector<LogRule> &rules) {
76  std::lock_guard<std::mutex> lock(mutex_);
77 
78  rules_ = rules;
79 
80  for (auto *category : categories_) {
81  applyRule(category);
82  }
83  }
84 
85  void applyRule(LogCategory *category) {
86  category->resetLogLevel();
87  for (auto &rule : rules_) {
88  if (rule.first == "*" || rule.first == category->name()) {
89  category->setLogLevel(rule.second);
90  }
91  }
92  }
93 
94 private:
95  std::unordered_set<LogCategory *> categories_;
96  std::vector<LogRule> rules_;
97  std::mutex mutex_;
98 };
99 } // namespace
100 
102 public:
103  LogCategoryPrivate(const char *name, LogLevel level)
104  : name_(name), level_(level), defaultLevel_(level) {}
105 
106  std::string name_;
107  LogLevel level_;
108  LogLevel defaultLevel_;
109 };
110 
111 LogCategory::LogCategory(const char *name, LogLevel level)
112  : d_ptr(std::make_unique<LogCategoryPrivate>(name, level)) {
113  LogRegistry::instance().registerCategory(*this);
114 }
115 
116 LogCategory::~LogCategory() {
117  LogRegistry::instance().unregisterCategory(*this);
118 }
119 
120 bool LogCategory::checkLogLevel(LogLevel l) const {
121  FCITX_D();
122  return l != LogLevel::NoLog &&
123  static_cast<std::underlying_type_t<LogLevel>>(l) <=
124  static_cast<std::underlying_type_t<LogLevel>>(d->level_);
125 }
126 
127 void LogCategory::resetLogLevel() {
128  FCITX_D();
129  d->level_ = d->defaultLevel_;
130 }
131 
132 void LogCategory::setLogLevel(std::underlying_type_t<LogLevel> l) {
133  if (validateLogLevel(l)) {
134  setLogLevel(static_cast<LogLevel>(l));
135  }
136 }
137 
138 void LogCategory::setLogLevel(LogLevel l) {
139  FCITX_D();
140  d->level_ = l;
141 }
142 
143 LogLevel LogCategory::logLevel() const {
144  FCITX_D();
145  return d->level_;
146 }
147 
148 const std::string &LogCategory::name() const {
149  FCITX_D();
150  return d->name_;
151 }
152 
153 bool LogCategory::fatalWrapper(LogLevel level) const {
154  // If level if fatal and we don't write fatal log, abort right away.
155  bool needLog = checkLogLevel(level);
156  if (level == LogLevel::Fatal && !needLog) {
157  std::abort();
158  }
159  return needLog;
160 }
161 
162 bool LogCategory::fatalWrapper2(LogLevel level) {
163  if (level == LogLevel::Fatal) {
164  std::abort();
165  }
166  return false;
167 }
168 
169 const LogCategory &Log::defaultCategory() { return fcitx::defaultCategory(); }
170 
171 void Log::setLogRule(const std::string &ruleString) {
172  std::vector<LogRule> parsedRules;
173  auto rules = stringutils::split(ruleString, ",");
174  for (const auto &rule : rules) {
175  if (rule == "notimedate") {
176  LogConfig::showTimeDate = false;
177  continue;
178  }
179 
180  auto ruleItem = stringutils::split(rule, "=");
181  if (ruleItem.size() != 2) {
182  continue;
183  }
184  auto &name = ruleItem[0];
185  int level;
186  if (std::from_chars(ruleItem[1].data(),
187  ruleItem[1].data() + ruleItem[1].size(), level)
188  .ec == std::errc()) {
189  if (validateLogLevel(level)) {
190  parsedRules.emplace_back(name, static_cast<LogLevel>(level));
191  }
192  }
193  }
194  LogRegistry::instance().setLogRules(parsedRules);
195 }
196 
197 void Log::setLogStream(std::ostream &stream) {
198  LogConfig::defaultLogStream = &stream;
199 }
200 
201 std::ostream &Log::logStream() {
202  auto *buf = LogConfig::defaultLogStream->rdbuf();
203  if (LogConfig::localLogStream.get_wrapped() != buf) {
204  LogConfig::localLogStream = std::osyncstream(buf);
205  LogConfig::localLogStream.rdbuf()->set_emit_on_sync(true);
206  }
207  return LogConfig::localLogStream;
208 }
209 
210 LogMessageBuilder::LogMessageBuilder(std::ostream &out, LogLevel l,
211  const char *filename, int lineNumber)
212  : out_(out) {
213  switch (l) {
214  case LogLevel::Fatal:
215  out_ << "F";
216  break;
217  case LogLevel::Debug:
218  out_ << "D";
219  break;
220  case LogLevel::Info:
221  out_ << "I";
222  break;
223  case LogLevel::Warn:
224  out_ << "W";
225  break;
226  case LogLevel::Error:
227  out_ << "E";
228  break;
229  default:
230  break;
231  }
232 
233  if (LogConfig::showTimeDate) {
234  try {
235  auto now = std::chrono::time_point_cast<std::chrono::microseconds>(
236  std::chrono::system_clock::now());
237 #if __cpp_lib_chrono >= 201907L
238  const auto *current_zone = std::chrono::current_zone();
239  std::chrono::zoned_time zoned_time{current_zone, now};
240 
241  auto timeString = std::format("{:%F %T}", zoned_time);
242 #else
243  auto timeString = std::format("{:%F %T}", now);
244 #endif
245  out_ << timeString << " ";
246  } catch (...) {
247  }
248  }
249 
250  out_ << filename << ":" << lineNumber << "] ";
251 }
252 
253 LogMessageBuilder::~LogMessageBuilder() {
254  out_ << '\n';
255  out_.flush();
256 }
257 
258 } // namespace fcitx
Definition: action.cpp:17
LogLevel
LogLevel from high to low.
Definition: log.h:41
std::vector< std::string > split(std::string_view str, std::string_view delim, SplitBehavior behavior)
Split the string by delim.
static void setLogStream(std::ostream &stream)
set the global log stream to be used by default.
Definition: log.cpp:197
static std::ostream & logStream()
Return the default log stream to be used.
Definition: log.cpp:201
String handle utilities.
Log utilities.
Fatal will always abort regardless of log or not.
Definition: log.h:44