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
38 namespace OHOS {
39 namespace WuKong {
40 namespace {
41 const std::string DEFAULT_DIR = "/data/local/tmp/wukong/report/";
42 const std::string LOGGER_THREAD_NAME = "wukong_logger";
43 const int LOG_CONTENT_LENGTH = 256;
44 const int LOG_PRINTER_TIMEOUT = 500;
45 std::mutex LOCK_PRINT_BUFFER;
46 } // namespace
47
WuKongLogger()48 WuKongLogger::WuKongLogger() : logPrinter_()
49 { /* init buf */
50 }
51 std::mutex WuKongLogger::wukongMutex_;
52 std::shared_ptr<WuKongLogger> WuKongLogger::wukongInstance_ = nullptr;
53 /**
54 * @brief: release logfile fd
55 */
~WuKongLogger()56 WuKongLogger::~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
GetInstance()69 std::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
Start()80 bool 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
Stop()105 void 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
Print(LOG_LEVEL level,const char * format,...)118 void 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 */
Run()169 bool 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