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