• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&currentTime);
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     if (bufferQueue_.size() < LOG_MAX_NUM) {
160         bufferQueue_.push(logInfo);
161     }
162     mtxQueue_.unlock();
163 
164     // notify print log to printer thread.
165     cvWaitPrint_.notify_all();
166 }
167 
168 /**
169  * @brief read queue and print log to file
170  */
Run()171 bool WuKongLogger::PrinterThread::Run()
172 {
173     std::shared_ptr<WuKongLogger> self = WuKongLogger::GetInstance();
174     if (!self->printerRunning_) {
175         return false;
176     }
177     self->cvWaitPrint_.notify_all();
178     std::unique_lock<std::mutex> lk(self->mtxThreadWait_);
179     std::queue<LogInfo> tmpBuffer;
180     std::ofstream printer(self->logFileName_);
181     if (!printer.is_open()) {
182         std::cout << "ERR: Logger printer file cannot open" << std::endl;
183         return false;
184     }
185     /* read form queue output target fd */
186     printer << "WuKongLogger::PrinterThread::Run start" << std::endl;
187     const auto timeout = std::chrono::milliseconds(LOG_PRINTER_TIMEOUT);
188     while (true) {
189         // wait new log
190         if (self->printerRunning_) {
191             if (self->outputLevel_ <= LOG_LEVEL_TRACK) {
192                 printer << "WuKongLogger::PrinterThread::Run wait start" << std::endl;
193             }
194             self->cvWaitPrint_.wait_for(lk, timeout);
195             if (self->outputLevel_ <= LOG_LEVEL_TRACK) {
196                 printer << "WuKongLogger::PrinterThread::Run wait end" << std::endl;
197             }
198         }
199         // read queue buffer to thread buffer.
200         self->mtxQueue_.lock();
201         // the buffer queue is empty and main wait stop, return this thread.
202         if (self->bufferQueue_.empty() && !self->printerRunning_) {
203             self->mtxQueue_.unlock();
204             break;
205         }
206         while (!self->bufferQueue_.empty()) {
207             if (tmpBuffer.size() > LOG_MAX_NUM) {
208                 break;
209             }
210             tmpBuffer.push(self->bufferQueue_.front());
211             self->bufferQueue_.pop();
212         }
213         self->mtxQueue_.unlock();
214         // print log to file and std::cout and hilog
215         while (!tmpBuffer.empty()) {
216             auto logInfo = tmpBuffer.front();
217             tmpBuffer.pop();
218             // output file fd
219             if (self->outputType_ & FILE_OUTPUT) {
220                 printer << logInfo.logStr_ << std::endl;
221             }
222             // doesn't print STDOUT and HILOG, when log level less than output level.
223             if (logInfo.level_ < self->outputLevel_) {
224                 continue;
225             }
226             // output STDOUT
227             if ((self->outputType_ & STD_OUTPUT) && (self->outputLevel_ > LOG_LEVEL_TRACK)) {
228                 std::cout << logInfo.logStr_ << std::endl;
229             }
230             // output HILOG
231             if (self->outputType_ & HILOG_OUTPUT) {
232                 HILOG_INFO(LOG_CORE, "%{public}s", logInfo.logStr_.c_str());
233             }
234         }
235     }
236     printer << "WuKongLogger::PrinterThread::Run end" << std::endl;
237     printer.close();
238     return false;
239 }
240 }  // namespace WuKong
241 }  // namespace OHOS
242