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