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