1/* 2 * Copyright (c) 2024 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 18#include <cstdarg> 19#include <mutex> 20#include <securec.h> 21#include <set> 22 23#include <base/containers/iterator.h> 24#include <base/containers/string.h> 25#include <base/containers/string_view.h> 26#include <base/containers/type_traits.h> 27#include <base/containers/unique_ptr.h> 28#include <base/containers/vector.h> 29#include <base/namespace.h> 30#include <base/util/uid.h> 31#include <core/intf_logger.h> 32#include <core/log.h> 33#include <core/namespace.h> 34 35#ifdef PLATFORM_HAS_JAVA 36#include <os/java/java_internal.h> 37#endif 38 39#include "log/logger_output.h" 40 41CORE_BEGIN_NAMESPACE() 42using BASE_NS::string; 43using BASE_NS::string_view; 44using BASE_NS::Uid; 45 46namespace { 47// Note: these must match the LogLevel enum. 48constexpr int LOG_LEVEL_COUNT = 7; 49constexpr const char* LOG_LEVEL_NAMES[LOG_LEVEL_COUNT] = { 50 "Verbose", 51 "Debug", 52 "Info", 53 "Warning", 54 "Error", 55 "Fatal", 56 "None", 57}; 58 59constexpr const char* LOG_LEVEL_NAMES_SHORT[LOG_LEVEL_COUNT] = { 60 "V", 61 "D", 62 "I", 63 "W", 64 "E", 65 "F", 66 "N", 67}; 68constexpr const size_t MAX_BUFFER_SIZE = 1024; 69} // namespace 70 71string_view Logger::GetLogLevelName(LogLevel logLevel, bool shortName) 72{ 73 const int level = static_cast<int>(logLevel); 74 CORE_ASSERT(level >= 0 && level < LOG_LEVEL_COUNT); 75 76 return shortName ? LOG_LEVEL_NAMES_SHORT[level] : LOG_LEVEL_NAMES[level]; 77} 78 79Logger::Logger(bool defaultOutputs) 80#ifdef NDEBUG 81 : logLevel_(LogLevel::LOG_ERROR) 82#endif 83{ 84 if (defaultOutputs) { 85#if CORE_LOG_TO_CONSOLE == 1 86 AddOutput(CreateLoggerConsoleOutput()); 87#endif 88 89#if CORE_LOG_TO_DEBUG_OUTPUT == 1 90 AddOutput(CreateLoggerDebugOutput()); 91#endif 92 93#if CORE_LOG_TO_FILE == 1 94 AddOutput(CreateLoggerFileOutput("./logfile.txt")); 95#endif 96 } 97} 98 99void Logger::VLog( 100 LogLevel logLevel, const string_view filename, int lineNumber, const string_view format, std::va_list args) 101{ 102 CORE_ASSERT_MSG(logLevel != LogLevel::LOG_NONE, "'None' is not a valid log level for writing to the log."); 103 104 if (logLevel_ > logLevel) { 105 return; 106 } 107 108 // we need to make a copy of the args, since the va_list can be in an undefined state after use. 109 std::va_list tmp; 110 va_copy(tmp, args); 111 112 // use vsnprintf to calculate the required size (not supported by the _s variant) 113 const int sizeNeeded = vsnprintf(nullptr, 0, format.data(), args) + 1; 114 115 std::lock_guard guard(loggerMutex_); 116 117 if (sizeNeeded > 0 && static_cast<size_t>(sizeNeeded) > buffer_.size()) { 118 buffer_.resize(static_cast<size_t>(sizeNeeded)); 119 } 120 121 int ret = vsnprintf_s(buffer_.data(), buffer_.size(), buffer_.size() - 1, format.data(), tmp); 122 va_end(tmp); 123 if (ret < 0) { 124 return; 125 } 126 127 for (auto& output : outputs_) { 128 output->Write(logLevel, filename, lineNumber, buffer_.data()); 129 } 130} 131 132void Logger::VLogOnce(const string_view id, LogLevel logLevel, const string_view filename, int lineNumber, 133 const string_view format, std::va_list args) 134{ 135 std::lock_guard<std::mutex> guard(onceMutex_); 136 137 auto const [pos, inserted] = registeredOnce_.insert(string(id)); 138 if (inserted) { 139 VLog(logLevel, filename, lineNumber, format, args); 140 } 141} 142 143bool Logger::VLogAssert(const string_view filename, int lineNumber, bool expression, const string_view expressionString, 144 const string_view format, std::va_list args) 145{ 146 if (!expression) { 147 char buffer[MAX_BUFFER_SIZE]; 148 const int numWritten = vsnprintf_s(buffer, MAX_BUFFER_SIZE, MAX_BUFFER_SIZE - 1, format.data(), args); 149 if (numWritten >= 0) { 150 buffer[numWritten] = '\0'; 151 } else { 152 buffer[0] = '\0'; 153 } 154 155 Log(LogLevel::LOG_FATAL, filename, lineNumber, "Assert failed (%s). %s", expressionString.data(), buffer); 156 157#ifdef PLATFORM_HAS_JAVA 158 // Print also a java trace if available 159 Log(LogLevel::LOG_FATAL, filename, lineNumber, "Java trace:"); 160 JNIEnv* env = java_internal::GetJavaEnv(); 161 if (env) { 162 jclass cls = env->FindClass("java/lang/Exception"); 163 if (cls) { 164 jmethodID constructor = env->GetMethodID(cls, "<init>", "()V"); 165 jobject exception = env->NewObject(cls, constructor); 166 jmethodID printStackTrace = env->GetMethodID(cls, "printStackTrace", "()V"); 167 env->CallVoidMethod(exception, printStackTrace); 168 env->DeleteLocalRef(exception); 169 } 170 } 171#endif 172 } 173 174 return expression; 175} 176 177void Logger::CheckOnceReset() 178{ 179 std::lock_guard<std::mutex> guard(onceMutex_); 180 registeredOnce_.clear(); 181} 182 183FORMAT_FUNC(5, 6) 184void Logger::Log( 185 LogLevel logLevel, const string_view filename, int lineNumber, FORMAT_ATTRIBUTE const char* format, ...) 186{ 187 std::va_list vl; 188 va_start(vl, format); 189 VLog(logLevel, filename, lineNumber, format, vl); 190 va_end(vl); 191} 192 193FORMAT_FUNC(6, 7) 194bool Logger::LogAssert(const string_view filename, int lineNumber, bool expression, const string_view expressionString, 195 FORMAT_ATTRIBUTE const char* format, ...) 196{ 197 if (!expression) { 198 std::va_list vl; 199 va_start(vl, format); 200 VLogAssert(filename, lineNumber, expression, expressionString, format, vl); 201 va_end(vl); 202 } 203 return expression; 204} 205 206ILogger::LogLevel Logger::GetLogLevel() const 207{ 208 return logLevel_; 209} 210 211void Logger::SetLogLevel(LogLevel logLevel) 212{ 213 logLevel_ = logLevel; 214} 215 216void Logger::AddOutput(IOutput::Ptr output) 217{ 218 if (output) { 219 std::lock_guard<std::mutex> guard(loggerMutex_); 220 outputs_.push_back(move(output)); 221 } 222} 223 224const IInterface* Logger::GetInterface(const Uid& uid) const 225{ 226 if ((uid == ILogger::UID) || (uid == IInterface::UID)) { 227 return this; 228 } 229 return nullptr; 230} 231 232IInterface* Logger::GetInterface(const Uid& uid) 233{ 234 if ((uid == ILogger::UID) || (uid == IInterface::UID)) { 235 return this; 236 } 237 return nullptr; 238} 239 240void Logger::Ref() {} 241 242void Logger::Unref() {} 243CORE_END_NAMESPACE() 244