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 componentMask;
39 auto loadComponents = [&componentMask](auto components) {
40 for (const auto &s : components) {
41 componentMask |= 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 loadComponents(options.GetLogFatal());
49 level = Level::FATAL;
50 } else if (options.WasSetLogError()) {
51 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
52 loadComponents(options.GetLogError());
53 level = Level::ERROR;
54 } else if (options.WasSetLogWarning()) {
55 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
56 loadComponents(options.GetLogWarning());
57 level = Level::WARNING;
58 } else if (options.WasSetLogInfo()) {
59 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
60 loadComponents(options.GetLogInfo());
61 level = Level::INFO;
62 } else if (options.WasSetLogDebug()) {
63 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
64 loadComponents(options.GetLogDebug());
65 level = Level::DEBUG;
66 } else {
67 ASSERT_PRINT(level == Level::LAST, "There are conflicting logger options");
68 loadComponents(options.GetLogComponents());
69 level = Logger::LevelFromString(options.GetLogLevel());
70 }
71
72 if (options.GetLogStream() == "std") {
73 Logger::InitializeStdLogging(level, componentMask);
74 } else if (options.GetLogStream() == "file" || options.GetLogStream() == "fast-file") {
75 const std::string &fileName = options.GetLogFile();
76 Logger::InitializeFileLogging(fileName, level, componentMask, options.GetLogStream() == "fast-file");
77 } else if (options.GetLogStream() == "dummy") {
78 Logger::InitializeDummyLogging(level, componentMask);
79 } else {
80 UNREACHABLE();
81 }
82 }
83
84 #ifndef NDEBUG
85 /// In debug builds this function allowes or disallowes proceeding with actual logging (i.e. creating Message{})
86 /* static */
IsMessageSuppressed(Level level,Component component)87 bool Logger::IsMessageSuppressed([[maybe_unused]] Level level, [[maybe_unused]] Component component)
88 {
89 // Allowing only to log if it's not a nested log, or it's nested and it's severity is suitable
90 return level >= Logger::logger_->nestedAllowedLevel_ && nesting_ > 0;
91 }
92
93 /// Increases log nesting (i.e. depth, or how many instances of Message{} is active atm) in a given thread
94 /* static */
LogNestingInc()95 void Logger::LogNestingInc()
96 {
97 nesting_++;
98 }
99
100 /// Decreases log nesting (i.e. depth, or how many instances of Message{} is active atm) in a given thread
101 /* static */
LogNestingDec()102 void Logger::LogNestingDec()
103 {
104 nesting_--;
105 }
106 #endif // NDEBUG
107
Printf(const char * format,...)108 auto Logger::Buffer::Printf(const char *format, ...) -> Buffer &
109 {
110 va_list args; // NOLINT(cppcoreguidelines-pro-type-vararg)
111 va_start(args, format); // NOLINT(cppcoreguidelines-pro-type-vararg)
112
113 [[maybe_unused]] int put = vsnprintf_s(this->Data(), this->Size(), this->Size() - 1, format, args);
114 ASSERT(put >= 0 && static_cast<size_t>(put) < BUFFER_SIZE);
115
116 va_end(args);
117 return *this;
118 }
119
120 os::memory::Mutex Logger::mutex_; // NOLINT(fuchsia-statically-constructed-objects)
121 FuncMobileLogPrint g_mlogBufPrint = nullptr;
122
~Message()123 Logger::Message::~Message()
124 {
125 if (printSystemError_) {
126 stream_ << ": " << os::Error(errno).ToString();
127 }
128
129 Logger::Log(level_, component_, stream_.str());
130 #ifndef NDEBUG
131 panda::Logger::LogNestingDec();
132 #endif
133
134 if (level_ == Level::FATAL) {
135 std::cerr << "FATAL ERROR" << std::endl;
136 std::cerr << "Backtrace [tid=" << os::thread::GetCurrentThreadId() << "]:\n";
137 PrintStack(std::cerr);
138 std::abort();
139 }
140 }
141
142 /* static */
Log(Level level,Component component,const std::string & str)143 void Logger::Log(Level level, Component component, const std::string &str)
144 {
145 if (!IsLoggingOn(level, component)) {
146 return;
147 }
148
149 os::memory::LockHolder<os::memory::Mutex> lock(mutex_);
150 if (!IsLoggingOn(level, component)) {
151 return;
152 }
153
154 size_t nl = str.find('\n');
155 if (nl == std::string::npos) {
156 logger_->LogLineInternal(level, component, str);
157 logger_->WriteMobileLog(level, GetComponentTag(component), str.c_str());
158 } else {
159 size_t i = 0;
160 while (nl != std::string::npos) {
161 std::string line = str.substr(i, nl - i);
162 logger_->LogLineInternal(level, component, line);
163 logger_->WriteMobileLog(level, GetComponentTag(component), line.c_str());
164 i = nl + 1;
165 nl = str.find('\n', i);
166 }
167
168 logger_->LogLineInternal(level, component, str.substr(i));
169 logger_->WriteMobileLog(level, GetComponentTag(component), str.substr(i).c_str());
170 }
171 }
172
173 /* static */
GetPrefix(Logger::Level level,Logger::Component component)174 std::string GetPrefix(Logger::Level level, Logger::Component component)
175 {
176 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
177 return helpers::string::Format("[TID %06x] %s/%s: ", os::thread::GetCurrentThreadId(), GetLevelTag(level),
178 GetComponentTag(component));
179 }
180
181 /* static */
InitializeFileLogging(const std::string & logFile,Level level,ComponentMask componentMask,bool isFastLogging)182 void Logger::InitializeFileLogging(const std::string &logFile, Level level, ComponentMask componentMask,
183 bool isFastLogging)
184 {
185 if (IsInitialized()) {
186 return;
187 }
188
189 os::memory::LockHolder<os::memory::Mutex> lock(mutex_);
190
191 if (IsInitialized()) {
192 return;
193 }
194
195 std::ofstream stream(logFile);
196 if (stream) {
197 if (isFastLogging) {
198 logger_ = new FastFileLogger(std::move(stream), level, componentMask);
199 } else {
200 logger_ = new FileLogger(std::move(stream), level, componentMask);
201 }
202 } else {
203 logger_ = new StderrLogger(level, componentMask);
204 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
205 std::string msg = helpers::string::Format("Fallback to stderr logging: cannot open log file '%s': %s",
206 logFile.c_str(), os::Error(errno).ToString().c_str());
207 logger_->LogLineInternal(Level::ERROR, Component::COMMON, msg);
208 }
209 #ifdef PANDA_TARGET_UNIX
210 if (DfxController::IsInitialized() && DfxController::GetOptionValue(DfxOptionHandler::MOBILE_LOG) == 0) {
211 Logger::SetMobileLogOpenFlag(false);
212 }
213 #endif
214 }
215
216 /* static */
InitializeStdLogging(Level level,ComponentMask componentMask)217 void Logger::InitializeStdLogging(Level level, ComponentMask componentMask)
218 {
219 if (IsInitialized()) {
220 return;
221 }
222
223 {
224 os::memory::LockHolder<os::memory::Mutex> lock(mutex_);
225
226 if (IsInitialized()) {
227 return;
228 }
229
230 logger_ = new StderrLogger(level, componentMask);
231 #ifdef PANDA_TARGET_UNIX
232 if (DfxController::IsInitialized() && DfxController::GetOptionValue(DfxOptionHandler::MOBILE_LOG) == 0) {
233 Logger::SetMobileLogOpenFlag(false);
234 }
235 #endif
236 }
237 }
238
239 /* static */
InitializeDummyLogging(Level level,ComponentMask componentMask)240 void Logger::InitializeDummyLogging(Level level, ComponentMask componentMask)
241 {
242 if (IsInitialized()) {
243 return;
244 }
245
246 {
247 os::memory::LockHolder<os::memory::Mutex> lock(mutex_);
248
249 if (IsInitialized()) {
250 return;
251 }
252
253 logger_ = new DummyLogger(level, componentMask);
254 }
255 }
256
257 /* static */
Destroy()258 void Logger::Destroy()
259 {
260 if (!IsInitialized()) {
261 return;
262 }
263
264 Logger *l = nullptr;
265
266 {
267 os::memory::LockHolder<os::memory::Mutex> lock(mutex_);
268
269 if (!IsInitialized()) {
270 return;
271 }
272
273 l = logger_;
274 logger_ = nullptr;
275 }
276
277 delete l;
278 }
279
280 /* static */
ProcessLogLevelFromString(std::string_view s)281 void Logger::ProcessLogLevelFromString(std::string_view s)
282 {
283 if (Logger::IsInLevelList(s)) {
284 Logger::SetLevel(Logger::LevelFromString(s));
285 } else {
286 LOG(ERROR, RUNTIME) << "Unknown level " << s;
287 }
288 }
289
290 /* static */
ProcessLogComponentsFromString(std::string_view s)291 void Logger::ProcessLogComponentsFromString(std::string_view s)
292 {
293 Logger::ResetComponentMask();
294 size_t lastPos = s.find_first_not_of(',', 0);
295 size_t pos = s.find(',', lastPos);
296 while (lastPos != std::string_view::npos) {
297 std::string_view componentStr = s.substr(lastPos, pos - lastPos);
298 lastPos = s.find_first_not_of(',', pos);
299 pos = s.find(',', lastPos);
300 if (Logger::IsInComponentList(componentStr)) {
301 Logger::EnableComponent(Logger::ComponentMaskFromString(componentStr));
302 } else {
303 LOG(ERROR, RUNTIME) << "Unknown component " << componentStr;
304 }
305 }
306 }
307
LogLineInternal(Level level,Component component,const std::string & str)308 void FileLogger::LogLineInternal(Level level, Component component, const std::string &str)
309 {
310 std::string prefix = GetPrefix(level, component);
311 stream_ << prefix << str << std::endl << std::flush;
312 }
313
LogLineInternal(Level level,Component component,const std::string & str)314 void FastFileLogger::LogLineInternal(Level level, Component component, const std::string &str)
315 {
316 std::string prefix = GetPrefix(level, component);
317 stream_ << prefix << str << '\n';
318 }
319
LogLineInternal(Level level,Component component,const std::string & str)320 void StderrLogger::LogLineInternal(Level level, Component component, const std::string &str)
321 {
322 std::string prefix = GetPrefix(level, component);
323 std::cerr << prefix << str << std::endl << std::flush;
324 }
325
SyncOutputResource()326 void FastFileLogger::SyncOutputResource()
327 {
328 stream_ << std::flush;
329 }
330
331 } // namespace panda
332