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 25using namespace std::literals::chrono_literals; 26using namespace std::chrono; 27namespace OHOS { 28namespace Developtools { 29namespace NativeDaemon { 30DebugLogger::DebugLogger() : timeStamp_(steady_clock::now()), logPath_(DEFAULT_LOG_PATH) 31{ 32 logFileBuffer_.resize(LOG_BUFFER_SIZE); 33 OpenLog(); 34} 35 36ScopeDebugLevel::ScopeDebugLevel(DebugLevel level, bool mix) 37{ 38 savedDebugLevel_ = DebugLogger::GetInstance()->SetLogLevel(level); 39 savedMixOutput_ = DebugLogger::GetInstance()->SetMixLogOutput(mix); 40} 41 42ScopeDebugLevel::~ScopeDebugLevel() 43{ 44 DebugLogger::GetInstance()->SetLogLevel(savedDebugLevel_); 45 DebugLogger::GetInstance()->SetMixLogOutput(savedMixOutput_); 46} 47 48DebugLogger::~DebugLogger() 49{ 50 Disable(); 51 if (file_ != nullptr) { 52 fclose(file_); 53 file_ = nullptr; 54 } 55} 56 57void 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 70void 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_BASE_DEBUG(LOG_CORE, "%{public}s", buffer.c_str()); 77} 78#endif 79#endif 80 81int 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 137bool 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 146bool DebugLogger::ShouldLog(DebugLevel level, const std::string &logtag) const 147{ 148 return GetLogLevelByTag(logtag) <= level; 149} 150 151DebugLevel 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 160bool DebugLogger::SetMixLogOutput(bool enable) 161{ 162 bool lastMixLogOutput = mixLogOutput_; 163 mixLogOutput_ = enable; 164 return lastMixLogOutput; 165} 166 167bool 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 187void 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 205DebugLevel 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 214const std::string DebugLogger::GetLogLevelName(DebugLevel level) const 215{ 216 return DebugLevelMap.at(level); 217} 218 219DebugLevel 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 230bool 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; 259std::unique_ptr<DebugLogger> DebugLogger::logInstance_; 260 261DebugLogger *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