• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
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 void 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     HILOG_DEBUG(LOG_CORE, "%{public}s", buffer.c_str());
77 }
78 #endif
79 #endif
80 
Log(DebugLevel level,const std::string & logTag,const char * fmt,...) const81 int DebugLogger::Log(DebugLevel level, const std::string &logTag, const char *fmt, ...) const
82 {
83     constexpr const int DEFAULT_STRING_BUF_SIZE = 4096;
84 #ifdef HIPERF_DEBUG_TIME
85     const auto startSprintf = steady_clock::now();
86 #endif
87     const auto startTime = steady_clock::now();
88     if (!ShouldLog(level, logTag) or logDisabled_ or fmt == nullptr) {
89 #ifdef HIPERF_DEBUG_TIME
90         logTimes_ += duration_cast<microseconds>(steady_clock::now() - startSprintf);
91 #endif
92         return 0;
93     }
94     va_list va;
95     int ret = 0;
96 
97     std::string buffer(DEFAULT_STRING_BUF_SIZE, '\0');
98     va_start(va, fmt);
99     ret = vsnprintf_s(buffer.data(), buffer.size(), buffer.size() - 1, fmt, va);
100     va_end(va);
101 #ifdef HIPERF_DEBUG_TIME
102     logSprintfTimes_ += duration_cast<microseconds>(steady_clock::now() - startSprintf);
103 #endif
104     if ((mixLogOutput_ and level < LEVEL_FATAL) or level == LEVEL_FATAL) {
105         ret = fprintf(stdout, "%s", buffer.data()); // to the stdout
106     }
107 
108     if (enableHilog_) {
109 #if is_ohos && !defined(CONFIG_NO_HILOG)
110         std::lock_guard<std::mutex> lock(logMutex_);
111         HiLog(buffer); // to the hilog
112 #endif
113     } else if (file_ != nullptr) {
114         std::lock_guard<std::mutex> lock(logMutex_);
115 #ifdef HIPERF_DEBUG_TIME
116         const auto startWriteTime = steady_clock::now();
117 #endif
118         milliseconds timeStamp = duration_cast<milliseconds>(startTime - timeStamp_);
119         fprintf(file_, "%05" PRId64 " ms %s", (int64_t)timeStamp.count(), buffer.data()); // to the file
120 #ifdef HIPERF_DEBUG_TIME
121         logWriteTimes_ += duration_cast<microseconds>(steady_clock::now() - startWriteTime);
122 #endif
123     }
124 
125 #ifdef HIPERF_DEBUG_TIME
126     logTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
127     logCount_++;
128 #endif
129     if (level == LEVEL_FATAL && exitOnFatal_) {
130         fflush(file_);
131         logDisabled_ = true;
132         exit(-1);
133     }
134     return ret;
135 }
136 
EnableHiLog(bool enable)137 bool DebugLogger::EnableHiLog(bool enable)
138 {
139     enableHilog_ = enable;
140     if (fprintf(stdout, "change to use hilog\n") < 0) {
141         // what can we do here ???
142     }
143     return enableHilog_;
144 }
145 
ShouldLog(DebugLevel level,const std::string & logtag) const146 bool DebugLogger::ShouldLog(DebugLevel level, const std::string &logtag) const
147 {
148     return GetLogLevelByTag(logtag) <= level;
149 }
150 
SetLogLevel(DebugLevel debugLevel)151 DebugLevel DebugLogger::SetLogLevel(DebugLevel debugLevel)
152 {
153     DebugLevel lastLevel = DebugLogger::GetInstance()->debugLevel_;
154     debugLevel_ = debugLevel;
155     // force print
156     printf("setLogLevel %d\n", debugLevel);
157     return lastLevel;
158 }
159 
SetMixLogOutput(bool enable)160 bool DebugLogger::SetMixLogOutput(bool enable)
161 {
162     bool lastMixLogOutput = mixLogOutput_;
163     mixLogOutput_ = enable;
164     return lastMixLogOutput;
165 }
166 
SetLogPath(const std::string & newLogPath)167 bool DebugLogger::SetLogPath(const std::string &newLogPath)
168 {
169     // make sure not write happend when rename
170     std::lock_guard<std::mutex> lock(logMutex_);
171     if (newLogPath.empty()) {
172         return false;
173     }
174     if (file_ != nullptr) {
175         fclose(file_);
176         file_ = nullptr;
177         if (rename(logPath_.c_str(), newLogPath.c_str()) != 0) {
178             // reopen the old log file path
179             OpenLog();
180             return false;
181         }
182     }
183     logPath_ = newLogPath;
184     return OpenLog();
185 }
186 
SetLogTags(const std::string & tags)187 void DebugLogger::SetLogTags(const std::string &tags)
188 {
189     HLOGI(" tags is '%s'", tags.c_str());
190     auto tagLevels = StringSplit(tags, ",");
191     logTagLevelmap_.clear();
192     for (auto tagLevel : tagLevels) {
193         auto tagLevelPair = StringSplit(tagLevel, ":");
194         if (tagLevelPair.size() == 1) { // only tag
195             logTagLevelmap_[tagLevelPair[0]] = LEVEL_MUCH;
196         } else { // tag:level
197             logTagLevelmap_[tagLevelPair[0]] = GetLogLevelByName(tagLevelPair[1].c_str());
198         }
199     }
200     for (auto it = logTagLevelmap_.begin(); it != logTagLevelmap_.end(); it++) {
201         HLOGD(" '%s'='%s'", it->first.c_str(), GetLogLevelName(it->second).c_str());
202     }
203 }
204 
GetLogLevelByTag(const std::string & tag) const205 DebugLevel DebugLogger::GetLogLevelByTag(const std::string &tag) const
206 {
207     if (logTagLevelmap_.count(tag) > 0) {
208         return logTagLevelmap_.at(tag);
209     } else {
210         return GetLogLevel();
211     }
212 }
213 
GetLogLevelName(DebugLevel level) const214 const std::string DebugLogger::GetLogLevelName(DebugLevel level) const
215 {
216     return DebugLevelMap.at(level);
217 }
218 
GetLogLevelByName(const std::string & name) const219 DebugLevel DebugLogger::GetLogLevelByName(const std::string &name) const
220 {
221     for (auto it = DebugLevelMap.begin(); it != DebugLevelMap.end(); it++) {
222         if (it->second == name) {
223             return it->first;
224         }
225     }
226     // not found ?
227     return LEVEL_MUCH;
228 }
229 
OpenLog()230 bool DebugLogger::OpenLog()
231 {
232     if (logDisabled_) {
233         // don't reopen it when we crash or soemthing else.
234         return false;
235     }
236     if (file_ != nullptr) {
237         // already open
238         return true;
239     } else {
240         file_ = fopen(logPath_.c_str(), "w");
241     }
242     if (file_ == nullptr) {
243         const int bufSize = 256;
244         char buf[bufSize] = { 0 };
245         strerror_r(errno, buf, bufSize);
246         fprintf(stdout, "unable save log file to '%s' because '%d:%s'\n", logPath_.c_str(), errno, buf);
247         return false;
248     } else {
249         fseek(file_, 0, SEEK_SET);
250         // ecach log can save 6ms (29ms -> 23ms)
251         setvbuf(file_, logFileBuffer_.data(), _IOFBF, logFileBuffer_.size());
252         fprintf(stdout, "log will save at '%s'\n", logPath_.c_str());
253         return true;
254     }
255 }
256 
257 __attribute__((weak)) DebugLevel DebugLogger::debugLevel_ = LEVEL_DEBUG;
258 __attribute__((weak)) bool DebugLogger::logDisabled_ = true;
259 std::unique_ptr<DebugLogger> DebugLogger::logInstance_;
260 
GetInstance()261 DebugLogger *DebugLogger::GetInstance()
262 {
263     if (logInstance_ == nullptr) {
264         logInstance_ = std::make_unique<DebugLogger>();
265     }
266     return logInstance_.get();
267 }
268 } // namespace NativeDaemon
269 } // namespace Developtools
270 } // namespace OHOS
271