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