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