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