1 /*
2  * Copyright (C) 2023 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 <string>
19 #include <cstdarg>
20 
21 #include <set>
22 
23 namespace lume
24 {
25 constexpr int LOG_BUFFER_SIZE = 1024;
getLogLevelName(LogLevel aLogLevel,bool aShortName)26 const char* Logger::getLogLevelName(LogLevel aLogLevel, bool aShortName)
27 {
28     // Note: these must match the LogLevel enum.
29     constexpr int LOG_LEVEL_COUNT = 7;
30     constexpr const char* LOG_LEVEL_NAMES[LOG_LEVEL_COUNT] =
31     {
32         "Verbose", "Debug", "Info", "Warning", "Error", "Fatal", "None",
33     };
34 
35     constexpr const char* LOG_LEVEL_NAMES_SHORT[LOG_LEVEL_COUNT] =
36     {
37         "V", "D", "I", "W", "E", "F", "N",
38     };
39 
40     const int logLevel = static_cast<int>(aLogLevel);
41     LUME_ASSERT(logLevel >= 0 && logLevel < LOG_LEVEL_COUNT);
42 
43     return aShortName ? LOG_LEVEL_NAMES_SHORT[logLevel] : LOG_LEVEL_NAMES[logLevel];
44 }
45 
46 
Logger(bool aDefaultOutputs)47 Logger::Logger(bool aDefaultOutputs)
48 {
49     if (aDefaultOutputs)
50     {
51         addOutput(createLoggerConsoleOutput());
52         addOutput(createLoggerDebugOutput());
53 
54         // Not writing to a file by default.
55         //addOutput(createLoggerFileOutput("./logfile.txt"));
56     }
57 }
58 
59 Logger::~Logger() = default;
60 
VLog(LogLevel aLogLevel,const char * aFilename,int aLinenumber,const char * aFormat,va_list aArgs)61 void Logger::VLog(LogLevel aLogLevel, const char *aFilename, int aLinenumber, const char *aFormat, va_list aArgs)
62 {
63     LUME_ASSERT_MSG(aLogLevel != LogLevel::None, "'None' is not a valid log level for writing to the log.");
64 
65     if (mLogLevel > aLogLevel) {
66         return;
67     }
68     char buffer[LOG_BUFFER_SIZE];
69 #if defined(__STDC_LIB_EXT1__) || defined(__STDC_WANT_SECURE_LIB__)
70     int ret = vsnprintf_s(buffer, sizeof(buffer), aFormat, aArgs);
71     va_end(aArgs);
72     if (ret < 0) {
73         return;
74     }
75 #else
76     vsnprintf(buffer, sizeof(buffer), aFormat, aArgs);
77 #endif
78     for (auto &output : mOutputs) {
79         output->write(aLogLevel, aFilename, aLinenumber, buffer);
80     }
81 }
82 
log(LogLevel aLogLevel,const char * aFilename,int aLinenumber,FORMAT_ATTRIBUTE const char * aFormat,...)83 FORMAT_FUNC(5, 6) void Logger::log(LogLevel aLogLevel, const char *aFilename, int aLinenumber, FORMAT_ATTRIBUTE const char *aFormat, ...)
84 {
85     va_list vl;
86     va_start(vl, aFormat);
87     VLog(aLogLevel, aFilename, aLinenumber, aFormat, vl);
88     va_end(vl);
89 }
90 
logAssert(const char * aFilename,int aLinenumber,bool expression,const char * expressionString,FORMAT_ATTRIBUTE const char * aFormat,...)91 FORMAT_FUNC(6, 7) bool Logger::logAssert(const char *aFilename, int aLinenumber, bool expression, const char *expressionString, FORMAT_ATTRIBUTE const char *aFormat, ...)
92 {
93     if (expression)
94     {
95         return true;
96     }
97 
98     va_list vl;
99     va_start(vl, aFormat);
100 
101     char buffer[LOG_BUFFER_SIZE];
102 #if defined(__STDC_LIB_EXT1__) || defined(__STDC_WANT_SECURE_LIB__)
103     const int numWritten = vsnprintf_s(buffer, sizeof(buffer), aFormat, vl);
104     if (numWritten < 0) {
105         buffer[0] = '\0';
106     }
107 #else
108     vsnprintf(buffer, sizeof(buffer), aFormat, vl);
109 #endif
110 
111     va_end(vl);
112 
113     log(LogLevel::Fatal, aFilename, aLinenumber, "Assert failed (%s). %s", expressionString, buffer);
114     return false;
115 }
116 
117 
getLogLevel() const118 ILogger::LogLevel Logger::getLogLevel() const
119 {
120     return mLogLevel;
121 }
122 
setLogLevel(LogLevel aLogLevel)123 void Logger::setLogLevel(LogLevel aLogLevel)
124 {
125     mLogLevel = aLogLevel;
126 }
127 
addOutput(std::unique_ptr<IOutput> aOutput)128 void Logger::addOutput(std::unique_ptr<IOutput> aOutput)
129 {
130     if (aOutput)
131     {
132         std::lock_guard<std::mutex> guard(mLoggerMutex);
133         mOutputs.push_back(std::move(aOutput));
134     }
135 }
136 
137 
138 namespace
139 {
140 Logger g_sLoggerInstance(true); // Global logger instance.
141 
142 std::set<std::string> sRegisteredOnce; // Global set of ids used by the LUME_ONCE macro.
143 std::mutex g_sOnceMutex;
144 } // empty namespace
145 
getLogger()146 ILogger &getLogger()
147 {
148     return g_sLoggerInstance;
149 }
150 
151 
CheckOnce(const char * aId)152 bool CheckOnce(const char *aId)
153 {
154     std::lock_guard<std::mutex> guard(g_sOnceMutex);
155 
156     size_t size = sRegisteredOnce.size();
157     sRegisteredOnce.insert(std::string(aId));
158 
159     // Something was inserted if the size changed.
160     return size != sRegisteredOnce.size();
161 }
162 
CheckOnceReset()163 void CheckOnceReset()
164 {
165     std::lock_guard<std::mutex> guard(g_sOnceMutex);
166     sRegisteredOnce.clear();
167 }
168 
169 
170 } // lume
171