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