1/* 2 * Copyright (c) 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 "wukong_logger.h" 17 18#include <algorithm> 19#include <chrono> 20#include <cstdarg> 21#include <cstdio> 22#include <cstring> 23#include <dirent.h> 24#include <fstream> 25#include <hilog/log_c.h> 26#include <iostream> 27#include <sys/stat.h> 28#include <sys/time.h> 29#include <sys/types.h> 30#include <thread> 31#include <unistd.h> 32 33#include "securec.h" 34#include "string_ex.h" 35#include "wukong_define.h" 36#include "wukong_util.h" 37 38namespace OHOS { 39namespace WuKong { 40namespace { 41const std::string DEFAULT_DIR = "/data/local/tmp/wukong/report/"; 42const std::string LOGGER_THREAD_NAME = "wukong_logger"; 43const int LOG_CONTENT_LENGTH = 256; 44const int LOG_PRINTER_TIMEOUT = 500; 45std::mutex LOCK_PRINT_BUFFER; 46} // namespace 47 48WuKongLogger::WuKongLogger() : logPrinter_() 49{ /* init buf */ 50} 51std::mutex WuKongLogger::wukongMutex_; 52std::shared_ptr<WuKongLogger> WuKongLogger::wukongInstance_ = nullptr; 53/** 54 * @brief: release logfile fd 55 */ 56WuKongLogger::~WuKongLogger() 57{ 58 if (outputLevel_ <= LOG_LEVEL_TRACK) { 59 std::cout << "Logger::~Logger" << std::endl; 60 } 61 62 // check logPrinter thread is running, and stop 63 if (printerRunning_ && logPrinter_.IsRunning()) { 64 Stop(); 65 } 66 std::cout << "WuKongLogger::~WuKongLogger" << std::endl; 67} 68 69std::shared_ptr<WuKongLogger> WuKongLogger::GetInstance() 70{ 71 if (wukongInstance_ == nullptr) { 72 std::lock_guard<std::mutex> lock(wukongMutex_); 73 if (wukongInstance_ == nullptr) { 74 wukongInstance_ = DelayedSingleton::GetInstance(); 75 } 76 } 77 return wukongInstance_; 78} 79 80bool WuKongLogger::Start() 81{ 82 if (outputLevel_ <= LOG_LEVEL_TRACK) { 83 std::cout << "Logger::Start" << std::endl; 84 } 85 86 logFileName_ = WuKongUtil::GetInstance()->GetCurrentTestDir() + "wukong.log"; 87 88 if (logPrinter_.IsRunning()) { 89 DEBUG_LOG("Logger already started"); 90 return true; 91 } 92 93 /* start read Queue Thread */ 94 printerRunning_ = true; 95 logPrinter_.Start(LOGGER_THREAD_NAME); 96 97 // wait print thread started. 98 do { 99 std::unique_lock<std::mutex> lk(mtxThreadWait_); 100 cvWaitPrint_.wait(lk); 101 } while (false); 102 return true; 103} 104 105void WuKongLogger::Stop() 106{ 107 /* release readQueueThread */ 108 if (outputLevel_ <= LOG_LEVEL_TRACK) { 109 std::cout << "Logger::Stop" << std::endl; 110 } 111 if (printerRunning_ && logPrinter_.IsRunning()) { 112 printerRunning_ = false; 113 cvWaitPrint_.notify_all(); 114 logPrinter_.NotifyExitSync(); 115 } 116} 117 118void WuKongLogger::Print(LOG_LEVEL level, const char *format, ...) 119{ 120 if (!printerRunning_) { 121 return; 122 } 123 LOCK_PRINT_BUFFER.lock(); 124 char writeBuf[LOG_CONTENT_LENGTH] = {0}; 125 /* check logger_level */ 126 if (level < outputLevel_ && level < LOG_LEVEL_DEBUG) { 127 LOCK_PRINT_BUFFER.unlock(); 128 return; 129 } 130 /* format output content */ 131 va_list args; 132 va_start(args, format); 133 int ret = vsnprintf_s(writeBuf, LOG_CONTENT_LENGTH, LOG_CONTENT_LENGTH, format, args); 134 if (ret < 0) { 135 va_end(args); 136 LOCK_PRINT_BUFFER.unlock(); 137 return; 138 } 139 va_end(args); 140 141 // write lock avoid write conflicts 142 LogInfo logInfo; 143 if (outputLevel_ >= LOG_LEVEL_TRACK) { 144 time_t currentTime = time(0); 145 char *timeChar = ctime(¤tTime); 146 logInfo.logStr_.append(timeChar, strlen(timeChar) - 1); 147 logInfo.logStr_.append(" : "); 148 } 149 logInfo.logStr_.append(writeBuf, strlen(writeBuf)); 150 logInfo.level_ = level; 151 LOCK_PRINT_BUFFER.unlock(); 152 // if log level is less than LOG_LEVEL_DEBUG, cout print. 153 if (outputLevel_ <= LOG_LEVEL_TRACK) { 154 std::cout << logInfo.logStr_ << std::endl; 155 } 156 157 // push log to buffer queue. 158 mtxQueue_.lock(); 159 bufferQueue_.push(logInfo); 160 mtxQueue_.unlock(); 161 162 // notify print log to printer thread. 163 cvWaitPrint_.notify_all(); 164} 165 166/** 167 * @brief read queue and print log to file 168 */ 169bool WuKongLogger::PrinterThread::Run() 170{ 171 std::shared_ptr<WuKongLogger> self = WuKongLogger::GetInstance(); 172 if (!self->printerRunning_) { 173 return false; 174 } 175 self->cvWaitPrint_.notify_all(); 176 std::unique_lock<std::mutex> lk(self->mtxThreadWait_); 177 std::queue<LogInfo> tmpBuffer; 178 std::ofstream printer(self->logFileName_); 179 if (!printer.is_open()) { 180 std::cout << "ERR: Logger printer file cannot open" << std::endl; 181 return false; 182 } 183 /* read form queue output target fd */ 184 printer << "WuKongLogger::PrinterThread::Run start" << std::endl; 185 const auto timeout = std::chrono::milliseconds(LOG_PRINTER_TIMEOUT); 186 while (true) { 187 // wait new log 188 if (self->printerRunning_) { 189 if (self->outputLevel_ <= LOG_LEVEL_TRACK) { 190 printer << "WuKongLogger::PrinterThread::Run wait start" << std::endl; 191 } 192 self->cvWaitPrint_.wait_for(lk, timeout); 193 if (self->outputLevel_ <= LOG_LEVEL_TRACK) { 194 printer << "WuKongLogger::PrinterThread::Run wait end" << std::endl; 195 } 196 } 197 // read queue buffer to thread buffer. 198 self->mtxQueue_.lock(); 199 // the buffer queue is empty and main wait stop, return this thread. 200 if (self->bufferQueue_.empty() && !self->printerRunning_) { 201 self->mtxQueue_.unlock(); 202 break; 203 } 204 while (!self->bufferQueue_.empty()) { 205 tmpBuffer.push(self->bufferQueue_.front()); 206 self->bufferQueue_.pop(); 207 } 208 self->mtxQueue_.unlock(); 209 // print log to file and std::cout and hilog 210 while (!tmpBuffer.empty()) { 211 auto logInfo = tmpBuffer.front(); 212 tmpBuffer.pop(); 213 // output file fd 214 if (self->outputType_ & FILE_OUTPUT) { 215 printer << logInfo.logStr_ << std::endl; 216 } 217 // doesn't print STDOUT and HILOG, when log level less than output level. 218 if (logInfo.level_ < self->outputLevel_) { 219 continue; 220 } 221 // output STDOUT 222 if ((self->outputType_ & STD_OUTPUT) && (self->outputLevel_ > LOG_LEVEL_TRACK)) { 223 std::cout << logInfo.logStr_ << std::endl; 224 } 225 // output HILOG 226 if (self->outputType_ & HILOG_OUTPUT) { 227 HILOG_INFO(LOG_CORE, "%{public}s", logInfo.logStr_.c_str()); 228 } 229 } 230 } 231 printer << "WuKongLogger::PrinterThread::Run end" << std::endl; 232 printer.close(); 233 return false; 234} 235} // namespace WuKong 236} // namespace OHOS 237