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