1 /*
2 * Copyright (c) 2021 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 "debug_logger.h"
17
18 #include <ratio>
19
20 #include "option.h"
21 #if is_ohos
22 #include "hiperf_hilog.h"
23 #endif
24
25 using namespace std::literals::chrono_literals;
26 using namespace std::chrono;
27 namespace OHOS {
28 namespace Developtools {
29 namespace NativeDaemon {
DebugLogger()30 DebugLogger::DebugLogger() : timeStamp_(steady_clock::now()), logPath_(DEFAULT_LOG_PATH)
31 {
32 logFileBuffer_.resize(LOG_BUFFER_SIZE);
33 OpenLog();
34 }
35
ScopeDebugLevel(DebugLevel level,bool mix)36 ScopeDebugLevel::ScopeDebugLevel(DebugLevel level, bool mix)
37 {
38 savedDebugLevel_ = DebugLogger::GetInstance()->SetLogLevel(level);
39 savedMixOutput_ = DebugLogger::GetInstance()->SetMixLogOutput(mix);
40 }
41
~ScopeDebugLevel()42 ScopeDebugLevel::~ScopeDebugLevel()
43 {
44 DebugLogger::GetInstance()->SetLogLevel(savedDebugLevel_);
45 DebugLogger::GetInstance()->SetMixLogOutput(savedMixOutput_);
46 }
47
~DebugLogger()48 DebugLogger::~DebugLogger()
49 {
50 Disable();
51 if (file_ != nullptr) {
52 fclose(file_);
53 file_ = nullptr;
54 }
55 }
56
Disable(bool disable)57 void DebugLogger::Disable(bool disable)
58 {
59 if (logDisabled_ != disable) {
60 logDisabled_ = disable;
61 if (!disable) {
62 // reopen the log file
63 OpenLog();
64 }
65 }
66 }
67
68 #if is_ohos
69 #ifndef CONFIG_NO_HILOG
HiLog(std::string & buffer) const70 int DebugLogger::HiLog(std::string &buffer) const
71 {
72 size_t lastLF = buffer.find_last_of('\n');
73 if (lastLF != std::string::npos) {
74 buffer.erase(lastLF, 1);
75 }
76 return OHOS::HiviewDFX::HiLog::Debug(HIPERF_HILOG_LABLE[MODULE_DEFAULT], "%{public}s",
77 buffer.c_str());
78 }
79 #endif
80 #endif
81
Log(DebugLevel level,const std::string & logTag,const char * fmt,...) const82 int DebugLogger::Log(DebugLevel level, const std::string &logTag, const char *fmt, ...) const
83 {
84 constexpr const int DEFAULT_STRING_BUF_SIZE = 4096;
85 #ifdef HIPERF_DEBUG_TIME
86 const auto startSprintf = steady_clock::now();
87 #endif
88 const auto startTime = steady_clock::now();
89 if (!ShouldLog(level, logTag) or logDisabled_ or fmt == nullptr) {
90 #ifdef HIPERF_DEBUG_TIME
91 logTimes_ += duration_cast<microseconds>(steady_clock::now() - startSprintf);
92 #endif
93 return 0;
94 }
95 va_list va;
96 int ret = 0;
97
98 std::string buffer(DEFAULT_STRING_BUF_SIZE, '\0');
99 va_start(va, fmt);
100 ret = vsnprintf_s(buffer.data(), buffer.size(), buffer.size() - 1, fmt, va);
101 va_end(va);
102 #ifdef HIPERF_DEBUG_TIME
103 logSprintfTimes_ += duration_cast<microseconds>(steady_clock::now() - startSprintf);
104 #endif
105 if ((mixLogOutput_ and level < LEVEL_FATAL) or level == LEVEL_FATAL) {
106 ret = fprintf(stdout, "%s", buffer.data()); // to the stdout
107 }
108
109 if (enableHilog_) {
110 #if is_ohos && !defined(CONFIG_NO_HILOG)
111 std::lock_guard<std::mutex> lock(logMutex_);
112 ret = HiLog(buffer); // to the hilog
113 #endif
114 } else if (file_ != nullptr) {
115 std::lock_guard<std::mutex> lock(logMutex_);
116 #ifdef HIPERF_DEBUG_TIME
117 const auto startWriteTime = steady_clock::now();
118 #endif
119 milliseconds timeStamp = duration_cast<milliseconds>(startTime - timeStamp_);
120 fprintf(file_, "%05" PRId64 " ms %s", (int64_t)timeStamp.count(), buffer.data()); // to the file
121 #ifdef HIPERF_DEBUG_TIME
122 logWriteTimes_ += duration_cast<microseconds>(steady_clock::now() - startWriteTime);
123 #endif
124 }
125
126 #ifdef HIPERF_DEBUG_TIME
127 logTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
128 logCount_++;
129 #endif
130 if (level == LEVEL_FATAL && exitOnFatal_) {
131 fflush(file_);
132 logDisabled_ = true;
133 exit(-1);
134 }
135 return ret;
136 }
137
EnableHiLog(bool enable)138 bool DebugLogger::EnableHiLog(bool enable)
139 {
140 enableHilog_ = enable;
141 if (fprintf(stdout, "change to use hilog\n") < 0) {
142 // what can we do here ???
143 }
144 return enableHilog_;
145 }
146
ShouldLog(DebugLevel level,const std::string & logtag) const147 bool DebugLogger::ShouldLog(DebugLevel level, const std::string &logtag) const
148 {
149 return GetLogLevelByTag(logtag) <= level;
150 }
151
SetLogLevel(DebugLevel debugLevel)152 DebugLevel DebugLogger::SetLogLevel(DebugLevel debugLevel)
153 {
154 DebugLevel lastLevel = DebugLogger::GetInstance()->debugLevel_;
155 debugLevel_ = debugLevel;
156 // force print
157 printf("setLogLevel %d\n", debugLevel);
158 return lastLevel;
159 }
160
SetMixLogOutput(bool enable)161 bool DebugLogger::SetMixLogOutput(bool enable)
162 {
163 bool lastMixLogOutput = mixLogOutput_;
164 mixLogOutput_ = enable;
165 return lastMixLogOutput;
166 }
167
SetLogPath(const std::string & newLogPath)168 bool DebugLogger::SetLogPath(const std::string &newLogPath)
169 {
170 // make sure not write happend when rename
171 std::lock_guard<std::mutex> lock(logMutex_);
172 if (newLogPath.empty()) {
173 return false;
174 }
175 if (file_ != nullptr) {
176 fclose(file_);
177 file_ = nullptr;
178 if (rename(logPath_.c_str(), newLogPath.c_str()) != 0) {
179 // reopen the old log file path
180 OpenLog();
181 return false;
182 }
183 }
184 logPath_ = newLogPath;
185 return OpenLog();
186 }
187
SetLogTags(const std::string & tags)188 void DebugLogger::SetLogTags(const std::string &tags)
189 {
190 HLOGI(" tags is '%s'", tags.c_str());
191 auto tagLevels = StringSplit(tags, ",");
192 logTagLevelmap_.clear();
193 for (auto tagLevel : tagLevels) {
194 auto tagLevelPair = StringSplit(tagLevel, ":");
195 if (tagLevelPair.size() == 1) { // only tag
196 logTagLevelmap_[tagLevelPair[0]] = LEVEL_MUCH;
197 } else { // tag:level
198 logTagLevelmap_[tagLevelPair[0]] = GetLogLevelByName(tagLevelPair[1].c_str());
199 }
200 }
201 for (auto it = logTagLevelmap_.begin(); it != logTagLevelmap_.end(); it++) {
202 HLOGD(" '%s'='%s'", it->first.c_str(), GetLogLevelName(it->second).c_str());
203 }
204 }
205
GetLogLevelByTag(const std::string & tag) const206 DebugLevel DebugLogger::GetLogLevelByTag(const std::string &tag) const
207 {
208 if (logTagLevelmap_.count(tag) > 0) {
209 return logTagLevelmap_.at(tag);
210 } else {
211 return GetLogLevel();
212 }
213 }
214
GetLogLevelName(DebugLevel level) const215 const std::string DebugLogger::GetLogLevelName(DebugLevel level) const
216 {
217 return DebugLevelMap.at(level);
218 }
219
GetLogLevelByName(const std::string & name) const220 DebugLevel DebugLogger::GetLogLevelByName(const std::string &name) const
221 {
222 for (auto it = DebugLevelMap.begin(); it != DebugLevelMap.end(); it++) {
223 if (it->second == name) {
224 return it->first;
225 }
226 }
227 // not found ?
228 return LEVEL_MUCH;
229 }
230
OpenLog()231 bool DebugLogger::OpenLog()
232 {
233 if (logDisabled_) {
234 // don't reopen it when we crash or soemthing else.
235 return false;
236 }
237 if (file_ != nullptr) {
238 // already open
239 return true;
240 } else {
241 file_ = fopen(logPath_.c_str(), "w");
242 }
243 if (file_ == nullptr) {
244 const int bufSize = 256;
245 char buf[bufSize] = { 0 };
246 strerror_r(errno, buf, bufSize);
247 fprintf(stdout, "unable save log file to '%s' because '%d:%s'\n", logPath_.c_str(), errno, buf);
248 return false;
249 } else {
250 fseek(file_, 0, SEEK_SET);
251 // ecach log can save 6ms (29ms -> 23ms)
252 setvbuf(file_, logFileBuffer_.data(), _IOFBF, logFileBuffer_.size());
253 fprintf(stdout, "log will save at '%s'\n", logPath_.c_str());
254 return true;
255 }
256 }
257
258 __attribute__((weak)) DebugLevel DebugLogger::debugLevel_ = LEVEL_DEBUG;
259 __attribute__((weak)) bool DebugLogger::logDisabled_ = true;
260 std::unique_ptr<DebugLogger> DebugLogger::logInstance_;
261
GetInstance()262 DebugLogger *DebugLogger::GetInstance()
263 {
264 if (logInstance_ == nullptr) {
265 logInstance_ = std::make_unique<DebugLogger>();
266 }
267 return logInstance_.get();
268 }
269 } // namespace NativeDaemon
270 } // namespace Developtools
271 } // namespace OHOS
272