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 <chrono>
10 #include <mutex>
11 #include <type_traits>
12 #include <unordered_set>
13 #include <fmt/format.h>
14 #if FMT_VERSION >= 50300
15 #include <fmt/chrono.h>
16 #endif
17 #include "fs.h"
18 #include "stringutils.h"
19
20 namespace fcitx {
21
22 namespace {
23
24 FCITX_DEFINE_LOG_CATEGORY(defaultCategory, "default");
25
26 static std::ostream *defaultLogStream = &std::cerr;
27
validateLogLevel(std::underlying_type_t<LogLevel> l)28 bool validateLogLevel(std::underlying_type_t<LogLevel> l) {
29 return (l >= 0 &&
30 l <= std::underlying_type_t<LogLevel>(LogLevel::LastLogLevel));
31 }
32
33 class LogRegistry {
34 public:
instance()35 static LogRegistry &instance() {
36 static LogRegistry instance_;
37 return instance_;
38 }
39
registerCategory(LogCategory & category)40 void registerCategory(LogCategory &category) {
41 std::lock_guard<std::mutex> lock(mutex_);
42 if (!categories_.count(&category)) {
43 categories_.insert(&category);
44 applyRule(&category);
45 }
46 }
47
unregisterCategory(LogCategory & category)48 void unregisterCategory(LogCategory &category) {
49 std::lock_guard<std::mutex> lock(mutex_);
50 categories_.erase(&category);
51 }
52
setLogRule(const std::string & ruleString)53 void setLogRule(const std::string &ruleString) {
54 std::lock_guard<std::mutex> lock(mutex_);
55
56 rules_.clear();
57 auto rules = stringutils::split(ruleString, ",");
58 rules_.reserve(rules.size());
59 for (const auto &rule : rules) {
60 auto ruleItem = stringutils::split(rule, "=");
61 if (ruleItem.size() != 2) {
62 continue;
63 }
64 auto &name = ruleItem[0];
65 try {
66 auto level = std::stoi(ruleItem[1]);
67 if (validateLogLevel(level)) {
68 rules_.emplace_back(name, static_cast<LogLevel>(level));
69 }
70 } catch (const std::exception &) {
71 continue;
72 }
73 }
74
75 for (auto *category : categories_) {
76 applyRule(category);
77 }
78 }
79
applyRule(LogCategory * category)80 void applyRule(LogCategory *category) {
81 category->resetLogLevel();
82 for (auto &rule : rules_) {
83 if (rule.first == "*" || rule.first == category->name()) {
84 category->setLogLevel(rule.second);
85 }
86 }
87 }
88
89 private:
90 std::unordered_set<LogCategory *> categories_;
91 std::vector<std::pair<std::string, LogLevel>> rules_;
92 std::mutex mutex_;
93 };
94 } // namespace
95
96 class LogCategoryPrivate {
97 public:
LogCategoryPrivate(const char * name,LogLevel level)98 LogCategoryPrivate(const char *name, LogLevel level)
99 : name_(name), level_(level), defaultLevel_(level) {}
100
101 std::string name_;
102 LogLevel level_;
103 LogLevel defaultLevel_;
104 };
105
LogCategory(const char * name,LogLevel level)106 LogCategory::LogCategory(const char *name, LogLevel level)
107 : d_ptr(std::make_unique<LogCategoryPrivate>(name, level)) {
108 LogRegistry::instance().registerCategory(*this);
109 }
110
~LogCategory()111 LogCategory::~LogCategory() {
112 LogRegistry::instance().unregisterCategory(*this);
113 }
114
checkLogLevel(LogLevel l) const115 bool LogCategory::checkLogLevel(LogLevel l) const {
116 FCITX_D();
117 return l != LogLevel::NoLog &&
118 static_cast<std::underlying_type_t<LogLevel>>(l) <=
119 static_cast<std::underlying_type_t<LogLevel>>(d->level_);
120 }
121
resetLogLevel()122 void LogCategory::resetLogLevel() {
123 FCITX_D();
124 d->level_ = d->defaultLevel_;
125 }
126
setLogLevel(std::underlying_type_t<LogLevel> l)127 void LogCategory::setLogLevel(std::underlying_type_t<LogLevel> l) {
128 if (validateLogLevel(l)) {
129 setLogLevel(static_cast<LogLevel>(l));
130 }
131 }
132
setLogLevel(LogLevel l)133 void LogCategory::setLogLevel(LogLevel l) {
134 FCITX_D();
135 d->level_ = l;
136 }
137
logLevel() const138 LogLevel LogCategory::logLevel() const {
139 FCITX_D();
140 return d->level_;
141 }
142
name() const143 const std::string &LogCategory::name() const {
144 FCITX_D();
145 return d->name_;
146 }
147
fatalWrapper(LogLevel level) const148 bool LogCategory::fatalWrapper(LogLevel level) const {
149 // If level if fatal and we don't write fatal log, abort right away.
150 bool needLog = checkLogLevel(level);
151 if (level == LogLevel::Fatal && !needLog) {
152 std::abort();
153 }
154 return needLog;
155 }
156
fatalWrapper2(LogLevel level)157 bool LogCategory::fatalWrapper2(LogLevel level) {
158 if (level == LogLevel::Fatal) {
159 std::abort();
160 }
161 return false;
162 }
163
defaultCategory()164 const LogCategory &Log::defaultCategory() { return fcitx::defaultCategory(); }
165
setLogRule(const std::string & ruleString)166 void Log::setLogRule(const std::string &ruleString) {
167 LogRegistry::instance().setLogRule(ruleString);
168 }
169
setLogStream(std::ostream & stream)170 void Log::setLogStream(std::ostream &stream) { defaultLogStream = &stream; }
171
logStream()172 std::ostream &Log::logStream() { return *defaultLogStream; }
173
LogMessageBuilder(std::ostream & out,LogLevel l,const char * filename,int lineNumber)174 LogMessageBuilder::LogMessageBuilder(std::ostream &out, LogLevel l,
175 const char *filename, int lineNumber)
176 : out_(out) {
177 switch (l) {
178 case LogLevel::Fatal:
179 out_ << "F";
180 break;
181 case LogLevel::Debug:
182 out_ << "D";
183 break;
184 case LogLevel::Info:
185 out_ << "I";
186 break;
187 case LogLevel::Warn:
188 out_ << "W";
189 break;
190 case LogLevel::Error:
191 out_ << "E";
192 break;
193 default:
194 break;
195 }
196
197 #if FMT_VERSION >= 50300
198 auto now = std::chrono::system_clock::now();
199 auto floor = std::chrono::floor<std::chrono::seconds>(now);
200 auto micro =
201 std::chrono::duration_cast<std::chrono::microseconds>(now - floor);
202 auto t = fmt::localtime(std::chrono::system_clock::to_time_t(now));
203 out_ << fmt::format("{:%F %T}.{:06d}", t, micro.count()) << " ";
204 #endif
205 out_ << filename << ":" << lineNumber << "] ";
206 }
207
~LogMessageBuilder()208 LogMessageBuilder::~LogMessageBuilder() { out_ << std::endl; }
209 } // namespace fcitx
210