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