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 <ratio> 19 20#include "option.h" 21#if defined(is_ohos) && 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 HiPerf { 30DebugLogger::DebugLogger() : timeStamp_(steady_clock::now()), logPath_(DEFAULT_LOG_PATH) 31{ 32 OpenLog(); 33} 34 35ScopeDebugLevel::ScopeDebugLevel(DebugLevel level, bool mix) 36{ 37 savedDebugLevel_ = DebugLogger::GetInstance()->SetLogLevel(level); 38 savedMixOutput_ = DebugLogger::GetInstance()->SetMixLogOutput(mix); 39} 40 41ScopeDebugLevel::~ScopeDebugLevel() 42{ 43 DebugLogger::GetInstance()->SetLogLevel(savedDebugLevel_); 44 DebugLogger::GetInstance()->SetMixLogOutput(savedMixOutput_); 45} 46 47DebugLogger::~DebugLogger() 48{ 49 Disable(); 50 if (file_ != nullptr) { 51 fclose(file_); 52 file_ = nullptr; 53 } 54} 55 56void DebugLogger::Disable(bool disable) 57{ 58 if (logDisabled_ != disable) { 59 logDisabled_ = disable; 60 if (!disable) { 61 // reopen the log file 62 OpenLog(); 63 } 64 } 65} 66 67#if is_ohos 68#ifndef CONFIG_NO_HILOG 69int DebugLogger::HiLog(std::string &buffer) const 70{ 71 size_t lastLF = buffer.find_last_of('\n'); 72 if (lastLF != std::string::npos) { 73 buffer.erase(lastLF, 1); 74 } 75 return OHOS::HiviewDFX::HiLog::Info(HIPERF_HILOG_LABLE[MODULE_DEFAULT], "%{public}s", 76 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 ? buffer.size() - 1 : 0, 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::recursive_mutex> lock(logMutex_); 111 ret = HiLog(buffer); // to the hilog 112#endif 113 } else if (file_ != nullptr) { 114 std::lock_guard<std::recursive_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 (enable) { 141 if (fprintf(stdout, "change to use hilog\n") < 0) { 142 // what can we do here ??? 143 } 144 } 145 return enableHilog_; 146} 147 148bool DebugLogger::ShouldLog(DebugLevel level, const std::string &logtag) const 149{ 150 return GetLogLevelByTag(logtag) <= level; 151} 152 153DebugLevel DebugLogger::SetLogLevel(DebugLevel debugLevel) 154{ 155 DebugLevel lastLevel = DebugLogger::GetInstance()->debugLevel_; 156 debugLevel_ = debugLevel; 157 // force print 158 printf("setLogLevel %d\n", debugLevel); 159 return lastLevel; 160} 161 162bool DebugLogger::SetMixLogOutput(bool enable) 163{ 164 bool lastMixLogOutput = mixLogOutput_; 165 mixLogOutput_ = enable; 166 return lastMixLogOutput; 167} 168 169bool DebugLogger::SetLogPath(const std::string &newLogPath) 170{ 171 // make sure not write happend when rename 172 std::lock_guard<std::recursive_mutex> lock(logMutex_); 173 if (newLogPath.empty() and newLogPath != logPath_) { 174 return false; 175 } 176 if (file_ != nullptr) { 177 fclose(file_); 178 file_ = nullptr; 179 if (rename(logPath_.c_str(), newLogPath.c_str()) != 0) { 180 // reopen the old log file path 181 OpenLog(); 182 return false; 183 } 184 } 185 logPath_ = newLogPath; 186 return OpenLog(); 187} 188 189void DebugLogger::SetLogTags(const std::string &tags) 190{ 191 HLOGI(" tags is '%s'", tags.c_str()); 192 auto tagLevels = StringSplit(tags, ","); 193 logTagLevelmap_.clear(); 194 for (auto tagLevel : tagLevels) { 195 auto tagLevelPair = StringSplit(tagLevel, ":"); 196 if (tagLevelPair.size() == 1) { // only tag 197 logTagLevelmap_[tagLevelPair[0]] = LEVEL_MUCH; 198 } else { // tag:level 199 logTagLevelmap_[tagLevelPair[0]] = GetLogLevelByName(tagLevelPair[1].c_str()); 200 } 201 } 202 for (auto it = logTagLevelmap_.begin(); it != logTagLevelmap_.end(); it++) { 203 HLOGD(" '%s'='%s'", it->first.c_str(), GetLogLevelName(it->second).c_str()); 204 } 205} 206 207DebugLevel DebugLogger::GetLogLevelByTag(const std::string &tag) const 208{ 209 if (logTagLevelmap_.count(tag) > 0) { 210 return logTagLevelmap_.at(tag); 211 } else { 212 return GetLogLevel(); 213 } 214} 215 216const std::string DebugLogger::GetLogLevelName(DebugLevel level) const 217{ 218 return DebugLevelMap.at(level); 219} 220 221DebugLevel DebugLogger::GetLogLevelByName(const std::string &name) const 222{ 223 for (auto it = DebugLevelMap.begin(); it != DebugLevelMap.end(); it++) { 224 if (it->second == name) { 225 return it->first; 226 } 227 } 228 // not found ? 229 return LEVEL_MUCH; 230} 231 232// only use for UT 233void DebugLogger::Reset() 234{ 235 EnableHiLog(false); 236 SetLogLevel(LEVEL_VERBOSE); 237 Disable(false); 238 SetLogPath(DEFAULT_LOG_PATH); 239 SetLogTags(""); 240} 241 242bool DebugLogger::RestoreLog() 243{ 244 // use append not write for continually write 245 return OpenLog(logPath_, "a"); 246} 247 248bool DebugLogger::OpenLog(const std::string &tempLogPath, const std::string &flags) 249{ 250 std::lock_guard<std::recursive_mutex> lock(logMutex_); 251 252 if (logDisabled_) { 253 // don't reopen it when we crash or something else. 254 return false; 255 } 256 if (!tempLogPath.empty()) { 257 if (file_ != nullptr) { 258 fclose(file_); 259 } 260 std::string resolvedPath = CanonicalizeSpecPath(tempLogPath.c_str()); 261 file_ = fopen(resolvedPath.c_str(), flags.c_str()); 262 } 263 if (file_ != nullptr) { 264 // already open 265 return true; 266 } else { 267 std::string resolvedPath = CanonicalizeSpecPath(logPath_.c_str()); 268 file_ = fopen(resolvedPath.c_str(), "w"); 269 } 270 if (file_ == nullptr) { 271 fprintf(stdout, "unable save log file to '%s' because '%d'\n", logPath_.c_str(), errno); 272 return false; 273 } else { 274 fseek(file_, 0, SEEK_SET); 275 // ecach log can save 6ms (29ms -> 23ms) 276 fprintf(stdout, "log will save at '%s'\n", logPath_.c_str()); 277 return true; 278 } 279} 280 281__attribute__((weak)) DebugLevel DebugLogger::debugLevel_ = LEVEL_DEBUG; 282__attribute__((weak)) bool DebugLogger::logDisabled_ = true; 283std::unique_ptr<DebugLogger> DebugLogger::logInstance_; 284 285DebugLogger *DebugLogger::GetInstance() 286{ 287 if (logInstance_ == nullptr) { 288 logInstance_ = std::make_unique<DebugLogger>(); 289 } 290 return logInstance_.get(); 291} 292} // namespace HiPerf 293} // namespace Developtools 294} // namespace OHOS 295