• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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