148f512ceSopenharmony_ci/*
248f512ceSopenharmony_ci * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
348f512ceSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
448f512ceSopenharmony_ci * you may not use this file except in compliance with the License.
548f512ceSopenharmony_ci * You may obtain a copy of the License at
648f512ceSopenharmony_ci *
748f512ceSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
848f512ceSopenharmony_ci *
948f512ceSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1048f512ceSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1148f512ceSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1248f512ceSopenharmony_ci * See the License for the specific language governing permissions and
1348f512ceSopenharmony_ci * limitations under the License.
1448f512ceSopenharmony_ci */
1548f512ceSopenharmony_ci
1648f512ceSopenharmony_ci#include "debug_logger.h"
1748f512ceSopenharmony_ci
1848f512ceSopenharmony_ci#include <ratio>
1948f512ceSopenharmony_ci
2048f512ceSopenharmony_ci#include "option.h"
2148f512ceSopenharmony_ci#if defined(is_ohos) && is_ohos
2248f512ceSopenharmony_ci#include "hiperf_hilog.h"
2348f512ceSopenharmony_ci#endif
2448f512ceSopenharmony_ci
2548f512ceSopenharmony_ciusing namespace std::literals::chrono_literals;
2648f512ceSopenharmony_ciusing namespace std::chrono;
2748f512ceSopenharmony_cinamespace OHOS {
2848f512ceSopenharmony_cinamespace Developtools {
2948f512ceSopenharmony_cinamespace HiPerf {
3048f512ceSopenharmony_ciDebugLogger::DebugLogger() : timeStamp_(steady_clock::now()), logPath_(DEFAULT_LOG_PATH)
3148f512ceSopenharmony_ci{
3248f512ceSopenharmony_ci    OpenLog();
3348f512ceSopenharmony_ci}
3448f512ceSopenharmony_ci
3548f512ceSopenharmony_ciScopeDebugLevel::ScopeDebugLevel(DebugLevel level, bool mix)
3648f512ceSopenharmony_ci{
3748f512ceSopenharmony_ci    savedDebugLevel_ = DebugLogger::GetInstance()->SetLogLevel(level);
3848f512ceSopenharmony_ci    savedMixOutput_ = DebugLogger::GetInstance()->SetMixLogOutput(mix);
3948f512ceSopenharmony_ci}
4048f512ceSopenharmony_ci
4148f512ceSopenharmony_ciScopeDebugLevel::~ScopeDebugLevel()
4248f512ceSopenharmony_ci{
4348f512ceSopenharmony_ci    DebugLogger::GetInstance()->SetLogLevel(savedDebugLevel_);
4448f512ceSopenharmony_ci    DebugLogger::GetInstance()->SetMixLogOutput(savedMixOutput_);
4548f512ceSopenharmony_ci}
4648f512ceSopenharmony_ci
4748f512ceSopenharmony_ciDebugLogger::~DebugLogger()
4848f512ceSopenharmony_ci{
4948f512ceSopenharmony_ci    Disable();
5048f512ceSopenharmony_ci    if (file_ != nullptr) {
5148f512ceSopenharmony_ci        fclose(file_);
5248f512ceSopenharmony_ci        file_ = nullptr;
5348f512ceSopenharmony_ci    }
5448f512ceSopenharmony_ci}
5548f512ceSopenharmony_ci
5648f512ceSopenharmony_civoid DebugLogger::Disable(bool disable)
5748f512ceSopenharmony_ci{
5848f512ceSopenharmony_ci    if (logDisabled_ != disable) {
5948f512ceSopenharmony_ci        logDisabled_ = disable;
6048f512ceSopenharmony_ci        if (!disable) {
6148f512ceSopenharmony_ci            // reopen the log file
6248f512ceSopenharmony_ci            OpenLog();
6348f512ceSopenharmony_ci        }
6448f512ceSopenharmony_ci    }
6548f512ceSopenharmony_ci}
6648f512ceSopenharmony_ci
6748f512ceSopenharmony_ci#if is_ohos
6848f512ceSopenharmony_ci#ifndef CONFIG_NO_HILOG
6948f512ceSopenharmony_ciint DebugLogger::HiLog(std::string &buffer) const
7048f512ceSopenharmony_ci{
7148f512ceSopenharmony_ci    size_t lastLF = buffer.find_last_of('\n');
7248f512ceSopenharmony_ci    if (lastLF != std::string::npos) {
7348f512ceSopenharmony_ci        buffer.erase(lastLF, 1);
7448f512ceSopenharmony_ci    }
7548f512ceSopenharmony_ci    return OHOS::HiviewDFX::HiLog::Info(HIPERF_HILOG_LABLE[MODULE_DEFAULT], "%{public}s",
7648f512ceSopenharmony_ci                                        buffer.c_str());
7748f512ceSopenharmony_ci}
7848f512ceSopenharmony_ci#endif
7948f512ceSopenharmony_ci#endif
8048f512ceSopenharmony_ci
8148f512ceSopenharmony_ciint DebugLogger::Log(DebugLevel level, const std::string &logTag, const char *fmt, ...) const
8248f512ceSopenharmony_ci{
8348f512ceSopenharmony_ci    constexpr const int DEFAULT_STRING_BUF_SIZE = 4096;
8448f512ceSopenharmony_ci#ifdef HIPERF_DEBUG_TIME
8548f512ceSopenharmony_ci    const auto startSprintf = steady_clock::now();
8648f512ceSopenharmony_ci#endif
8748f512ceSopenharmony_ci    const auto startTime = steady_clock::now();
8848f512ceSopenharmony_ci    if (!ShouldLog(level, logTag) or logDisabled_ or fmt == nullptr) {
8948f512ceSopenharmony_ci#ifdef HIPERF_DEBUG_TIME
9048f512ceSopenharmony_ci        logTimes_ += duration_cast<microseconds>(steady_clock::now() - startSprintf);
9148f512ceSopenharmony_ci#endif
9248f512ceSopenharmony_ci        return 0;
9348f512ceSopenharmony_ci    }
9448f512ceSopenharmony_ci    va_list va;
9548f512ceSopenharmony_ci    int ret = 0;
9648f512ceSopenharmony_ci
9748f512ceSopenharmony_ci    std::string buffer(DEFAULT_STRING_BUF_SIZE, '\0');
9848f512ceSopenharmony_ci    va_start(va, fmt);
9948f512ceSopenharmony_ci    ret = vsnprintf_s(buffer.data(), buffer.size(), buffer.size() >= 1 ? buffer.size() - 1 : 0, fmt, va);
10048f512ceSopenharmony_ci    va_end(va);
10148f512ceSopenharmony_ci#ifdef HIPERF_DEBUG_TIME
10248f512ceSopenharmony_ci    logSprintfTimes_ += duration_cast<microseconds>(steady_clock::now() - startSprintf);
10348f512ceSopenharmony_ci#endif
10448f512ceSopenharmony_ci    if ((mixLogOutput_ and level < LEVEL_FATAL) or level == LEVEL_FATAL) {
10548f512ceSopenharmony_ci        ret = fprintf(stdout, "%s", buffer.data()); // to the stdout
10648f512ceSopenharmony_ci    }
10748f512ceSopenharmony_ci
10848f512ceSopenharmony_ci    if (enableHilog_) {
10948f512ceSopenharmony_ci#if is_ohos && !defined(CONFIG_NO_HILOG)
11048f512ceSopenharmony_ci        std::lock_guard<std::recursive_mutex> lock(logMutex_);
11148f512ceSopenharmony_ci        ret = HiLog(buffer); // to the hilog
11248f512ceSopenharmony_ci#endif
11348f512ceSopenharmony_ci    } else if (file_ != nullptr) {
11448f512ceSopenharmony_ci        std::lock_guard<std::recursive_mutex> lock(logMutex_);
11548f512ceSopenharmony_ci#ifdef HIPERF_DEBUG_TIME
11648f512ceSopenharmony_ci        const auto startWriteTime = steady_clock::now();
11748f512ceSopenharmony_ci#endif
11848f512ceSopenharmony_ci        milliseconds timeStamp = duration_cast<milliseconds>(startTime - timeStamp_);
11948f512ceSopenharmony_ci        fprintf(file_, "%05" PRId64 "ms %s", (int64_t)timeStamp.count(), buffer.data()); // to the file
12048f512ceSopenharmony_ci#ifdef HIPERF_DEBUG_TIME
12148f512ceSopenharmony_ci        logWriteTimes_ += duration_cast<microseconds>(steady_clock::now() - startWriteTime);
12248f512ceSopenharmony_ci#endif
12348f512ceSopenharmony_ci    }
12448f512ceSopenharmony_ci
12548f512ceSopenharmony_ci#ifdef HIPERF_DEBUG_TIME
12648f512ceSopenharmony_ci    logTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
12748f512ceSopenharmony_ci    logCount_++;
12848f512ceSopenharmony_ci#endif
12948f512ceSopenharmony_ci    if (level == LEVEL_FATAL && exitOnFatal_) {
13048f512ceSopenharmony_ci        fflush(file_);
13148f512ceSopenharmony_ci        logDisabled_ = true;
13248f512ceSopenharmony_ci        exit(-1);
13348f512ceSopenharmony_ci    }
13448f512ceSopenharmony_ci    return ret;
13548f512ceSopenharmony_ci}
13648f512ceSopenharmony_ci
13748f512ceSopenharmony_cibool DebugLogger::EnableHiLog(bool enable)
13848f512ceSopenharmony_ci{
13948f512ceSopenharmony_ci    enableHilog_ = enable;
14048f512ceSopenharmony_ci    if (enable) {
14148f512ceSopenharmony_ci        if (fprintf(stdout, "change to use hilog\n") < 0) {
14248f512ceSopenharmony_ci            // what can we do here ???
14348f512ceSopenharmony_ci        }
14448f512ceSopenharmony_ci    }
14548f512ceSopenharmony_ci    return enableHilog_;
14648f512ceSopenharmony_ci}
14748f512ceSopenharmony_ci
14848f512ceSopenharmony_cibool DebugLogger::ShouldLog(DebugLevel level, const std::string &logtag) const
14948f512ceSopenharmony_ci{
15048f512ceSopenharmony_ci    return GetLogLevelByTag(logtag) <= level;
15148f512ceSopenharmony_ci}
15248f512ceSopenharmony_ci
15348f512ceSopenharmony_ciDebugLevel DebugLogger::SetLogLevel(DebugLevel debugLevel)
15448f512ceSopenharmony_ci{
15548f512ceSopenharmony_ci    DebugLevel lastLevel = DebugLogger::GetInstance()->debugLevel_;
15648f512ceSopenharmony_ci    debugLevel_ = debugLevel;
15748f512ceSopenharmony_ci    // force print
15848f512ceSopenharmony_ci    printf("setLogLevel %d\n", debugLevel);
15948f512ceSopenharmony_ci    return lastLevel;
16048f512ceSopenharmony_ci}
16148f512ceSopenharmony_ci
16248f512ceSopenharmony_cibool DebugLogger::SetMixLogOutput(bool enable)
16348f512ceSopenharmony_ci{
16448f512ceSopenharmony_ci    bool lastMixLogOutput = mixLogOutput_;
16548f512ceSopenharmony_ci    mixLogOutput_ = enable;
16648f512ceSopenharmony_ci    return lastMixLogOutput;
16748f512ceSopenharmony_ci}
16848f512ceSopenharmony_ci
16948f512ceSopenharmony_cibool DebugLogger::SetLogPath(const std::string &newLogPath)
17048f512ceSopenharmony_ci{
17148f512ceSopenharmony_ci    // make sure not write happend when rename
17248f512ceSopenharmony_ci    std::lock_guard<std::recursive_mutex> lock(logMutex_);
17348f512ceSopenharmony_ci    if (newLogPath.empty() and newLogPath != logPath_) {
17448f512ceSopenharmony_ci        return false;
17548f512ceSopenharmony_ci    }
17648f512ceSopenharmony_ci    if (file_ != nullptr) {
17748f512ceSopenharmony_ci        fclose(file_);
17848f512ceSopenharmony_ci        file_ = nullptr;
17948f512ceSopenharmony_ci        if (rename(logPath_.c_str(), newLogPath.c_str()) != 0) {
18048f512ceSopenharmony_ci            // reopen the old log file path
18148f512ceSopenharmony_ci            OpenLog();
18248f512ceSopenharmony_ci            return false;
18348f512ceSopenharmony_ci        }
18448f512ceSopenharmony_ci    }
18548f512ceSopenharmony_ci    logPath_ = newLogPath;
18648f512ceSopenharmony_ci    return OpenLog();
18748f512ceSopenharmony_ci}
18848f512ceSopenharmony_ci
18948f512ceSopenharmony_civoid DebugLogger::SetLogTags(const std::string &tags)
19048f512ceSopenharmony_ci{
19148f512ceSopenharmony_ci    HLOGI(" tags is '%s'", tags.c_str());
19248f512ceSopenharmony_ci    auto tagLevels = StringSplit(tags, ",");
19348f512ceSopenharmony_ci    logTagLevelmap_.clear();
19448f512ceSopenharmony_ci    for (auto tagLevel : tagLevels) {
19548f512ceSopenharmony_ci        auto tagLevelPair = StringSplit(tagLevel, ":");
19648f512ceSopenharmony_ci        if (tagLevelPair.size() == 1) { // only tag
19748f512ceSopenharmony_ci            logTagLevelmap_[tagLevelPair[0]] = LEVEL_MUCH;
19848f512ceSopenharmony_ci        } else { // tag:level
19948f512ceSopenharmony_ci            logTagLevelmap_[tagLevelPair[0]] = GetLogLevelByName(tagLevelPair[1].c_str());
20048f512ceSopenharmony_ci        }
20148f512ceSopenharmony_ci    }
20248f512ceSopenharmony_ci    for (auto it = logTagLevelmap_.begin(); it != logTagLevelmap_.end(); it++) {
20348f512ceSopenharmony_ci        HLOGD(" '%s'='%s'", it->first.c_str(), GetLogLevelName(it->second).c_str());
20448f512ceSopenharmony_ci    }
20548f512ceSopenharmony_ci}
20648f512ceSopenharmony_ci
20748f512ceSopenharmony_ciDebugLevel DebugLogger::GetLogLevelByTag(const std::string &tag) const
20848f512ceSopenharmony_ci{
20948f512ceSopenharmony_ci    if (logTagLevelmap_.count(tag) > 0) {
21048f512ceSopenharmony_ci        return logTagLevelmap_.at(tag);
21148f512ceSopenharmony_ci    } else {
21248f512ceSopenharmony_ci        return GetLogLevel();
21348f512ceSopenharmony_ci    }
21448f512ceSopenharmony_ci}
21548f512ceSopenharmony_ci
21648f512ceSopenharmony_ciconst std::string DebugLogger::GetLogLevelName(DebugLevel level) const
21748f512ceSopenharmony_ci{
21848f512ceSopenharmony_ci    return DebugLevelMap.at(level);
21948f512ceSopenharmony_ci}
22048f512ceSopenharmony_ci
22148f512ceSopenharmony_ciDebugLevel DebugLogger::GetLogLevelByName(const std::string &name) const
22248f512ceSopenharmony_ci{
22348f512ceSopenharmony_ci    for (auto it = DebugLevelMap.begin(); it != DebugLevelMap.end(); it++) {
22448f512ceSopenharmony_ci        if (it->second == name) {
22548f512ceSopenharmony_ci            return it->first;
22648f512ceSopenharmony_ci        }
22748f512ceSopenharmony_ci    }
22848f512ceSopenharmony_ci    // not found ?
22948f512ceSopenharmony_ci    return LEVEL_MUCH;
23048f512ceSopenharmony_ci}
23148f512ceSopenharmony_ci
23248f512ceSopenharmony_ci// only use for UT
23348f512ceSopenharmony_civoid DebugLogger::Reset()
23448f512ceSopenharmony_ci{
23548f512ceSopenharmony_ci    EnableHiLog(false);
23648f512ceSopenharmony_ci    SetLogLevel(LEVEL_VERBOSE);
23748f512ceSopenharmony_ci    Disable(false);
23848f512ceSopenharmony_ci    SetLogPath(DEFAULT_LOG_PATH);
23948f512ceSopenharmony_ci    SetLogTags("");
24048f512ceSopenharmony_ci}
24148f512ceSopenharmony_ci
24248f512ceSopenharmony_cibool DebugLogger::RestoreLog()
24348f512ceSopenharmony_ci{
24448f512ceSopenharmony_ci    // use append not write for continually write
24548f512ceSopenharmony_ci    return OpenLog(logPath_, "a");
24648f512ceSopenharmony_ci}
24748f512ceSopenharmony_ci
24848f512ceSopenharmony_cibool DebugLogger::OpenLog(const std::string &tempLogPath, const std::string &flags)
24948f512ceSopenharmony_ci{
25048f512ceSopenharmony_ci    std::lock_guard<std::recursive_mutex> lock(logMutex_);
25148f512ceSopenharmony_ci
25248f512ceSopenharmony_ci    if (logDisabled_) {
25348f512ceSopenharmony_ci        // don't reopen it when we crash or something else.
25448f512ceSopenharmony_ci        return false;
25548f512ceSopenharmony_ci    }
25648f512ceSopenharmony_ci    if (!tempLogPath.empty()) {
25748f512ceSopenharmony_ci        if (file_ != nullptr) {
25848f512ceSopenharmony_ci            fclose(file_);
25948f512ceSopenharmony_ci        }
26048f512ceSopenharmony_ci        std::string resolvedPath = CanonicalizeSpecPath(tempLogPath.c_str());
26148f512ceSopenharmony_ci        file_ = fopen(resolvedPath.c_str(), flags.c_str());
26248f512ceSopenharmony_ci    }
26348f512ceSopenharmony_ci    if (file_ != nullptr) {
26448f512ceSopenharmony_ci        // already open
26548f512ceSopenharmony_ci        return true;
26648f512ceSopenharmony_ci    } else {
26748f512ceSopenharmony_ci        std::string resolvedPath = CanonicalizeSpecPath(logPath_.c_str());
26848f512ceSopenharmony_ci        file_ = fopen(resolvedPath.c_str(), "w");
26948f512ceSopenharmony_ci    }
27048f512ceSopenharmony_ci    if (file_ == nullptr) {
27148f512ceSopenharmony_ci        fprintf(stdout, "unable save log file to '%s' because '%d'\n", logPath_.c_str(), errno);
27248f512ceSopenharmony_ci        return false;
27348f512ceSopenharmony_ci    } else {
27448f512ceSopenharmony_ci        fseek(file_, 0, SEEK_SET);
27548f512ceSopenharmony_ci        // ecach log can save 6ms (29ms -> 23ms)
27648f512ceSopenharmony_ci        fprintf(stdout, "log will save at '%s'\n", logPath_.c_str());
27748f512ceSopenharmony_ci        return true;
27848f512ceSopenharmony_ci    }
27948f512ceSopenharmony_ci}
28048f512ceSopenharmony_ci
28148f512ceSopenharmony_ci__attribute__((weak)) DebugLevel DebugLogger::debugLevel_ = LEVEL_DEBUG;
28248f512ceSopenharmony_ci__attribute__((weak)) bool DebugLogger::logDisabled_ = true;
28348f512ceSopenharmony_cistd::unique_ptr<DebugLogger> DebugLogger::logInstance_;
28448f512ceSopenharmony_ci
28548f512ceSopenharmony_ciDebugLogger *DebugLogger::GetInstance()
28648f512ceSopenharmony_ci{
28748f512ceSopenharmony_ci    if (logInstance_ == nullptr) {
28848f512ceSopenharmony_ci        logInstance_ = std::make_unique<DebugLogger>();
28948f512ceSopenharmony_ci    }
29048f512ceSopenharmony_ci    return logInstance_.get();
29148f512ceSopenharmony_ci}
29248f512ceSopenharmony_ci} // namespace HiPerf
29348f512ceSopenharmony_ci} // namespace Developtools
29448f512ceSopenharmony_ci} // namespace OHOS
295