• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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