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