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 "hiappevent_read.h"
17
18 #include <algorithm>
19 #include <ctime>
20 #include <dirent.h>
21 #include <fstream>
22
23 #include "hiappevent_base.h"
24 #include "hilog/log.h"
25
26 #undef LOG_DOMAIN
27 #define LOG_DOMAIN 0xD002D07
28
29 #undef LOG_TAG
30 #define LOG_TAG "Read"
31
32 using namespace std;
33
34 namespace OHOS {
35 namespace HiviewDFX {
36 namespace {
37 constexpr int FORMAT_TZ_SIZE = 9;
38 constexpr int MAX_LOG_COUNT = 1000;
39 constexpr int MILLI_TO_MICRO = 1000;
40 // same as definition in hiappevent_write.cpp
41 constexpr char APP_EVENT_DIR[] = "/hiappevent/";
42 // Date format of time stamp: YYYYmmdd, eg. 20140510
43 constexpr char DATE_FORMAT[] = "%Y%m%d";
44 // the default value of the beginTimestamp/endTimeStamp
45 // is -1LL
46 constexpr TimeStampVarType INVALID_TIMESTAMP = -1LL;
47 // The count of the regex matched results is always more
48 // than one and all results except the first one are the matched
49 // results we want, so the total count is 2 and 1 is the right
50 // index
51 constexpr smatch::size_type REGEX_MATCHED_RESULTS_COUNT = 2;
52 constexpr smatch::size_type REGEX_MATCHED_RESULT_INDEX = 1;
53 // Pattern for parsing the timestamp in the name of log files
54 // eg. 20140510
55 const regex TIME_STAMP_REGEX_PATTERN(".*_(.{8})\\..*");
56 // Pattern for reading the timestamp from the
57 // persisted log record, eg. 1626266996728
58 const regex MATCHED_LOG_REGEX_PATTERN(".*\"time_\":([0-9]{13}).*");
59 } // __UNIQUE_NAME_
60
Instance()61 LogAssistant& LogAssistant::Instance()
62 {
63 static LogAssistant assistant;
64 return assistant;
65 }
66
LogAssistant()67 LogAssistant::LogAssistant()
68 : logPersistDir(""),
69 realTimeLogUpdateListener(nullptr),
70 historyLogPulledListener(nullptr)
71 {}
72
~LogAssistant()73 LogAssistant::~LogAssistant()
74 {
75 this->RemoveAllListeners();
76 }
77
78 // A util method to find the matched result by the input
79 // pattern
FindMatchedRegex(const string & origin,const regex & regex)80 string LogAssistant::FindMatchedRegex(const string& origin, const regex& regex)
81 {
82 smatch regexMatchedResult;
83 if (std::regex_match(origin, regexMatchedResult, regex)) {
84 if (regexMatchedResult.size() == REGEX_MATCHED_RESULTS_COUNT) {
85 return regexMatchedResult[REGEX_MATCHED_RESULT_INDEX].str();
86 }
87 }
88 return string();
89 }
90
91 // Use regex to parse the timestamp from each persisted event log record
ParseLogLineTime(const string & line)92 string LogAssistant::ParseLogLineTime(const string& line)
93 {
94 return FindMatchedRegex(line, MATCHED_LOG_REGEX_PATTERN);
95 }
96
CheckMatchedLogLine(const string & logTimeStamp,TimeStampVarType beginTimeStamp,TimeStampVarType endTimeStamp)97 bool LogAssistant::CheckMatchedLogLine(const string& logTimeStamp,
98 TimeStampVarType beginTimeStamp, TimeStampVarType endTimeStamp)
99 {
100 // Comparing the timestamp value in the persited log record with
101 // the timestamp from libjvmtiagent calling
102 return ((beginTimeStamp == INVALID_TIMESTAMP) ||
103 logTimeStamp >= to_string(beginTimeStamp)) &&
104 ((endTimeStamp == INVALID_TIMESTAMP) ||
105 logTimeStamp <= to_string(endTimeStamp));
106 }
107
108 // Read the single log file and then find out all matched event record
ParseSingFileLogs(vector<string> & logs,const string & fileName,TimeStampVarType beginTimeStamp,TimeStampVarType endTimeStamp,int count)109 void LogAssistant::ParseSingFileLogs(vector<string>& logs,
110 const string& fileName, TimeStampVarType beginTimeStamp,
111 TimeStampVarType endTimeStamp, int count)
112 {
113 ifstream fin(logPersistDir + string(APP_EVENT_DIR) + fileName);
114 if (!fin) {
115 HILOG_ERROR(LOG_CORE, "single log file read failed.");
116 return;
117 }
118 string currentLine;
119 vector<string> currentFileLogs;
120 while (fin >> currentLine) {
121 string currentTimeStamp = ParseLogLineTime(currentLine);
122 if (CheckMatchedLogLine(currentTimeStamp, beginTimeStamp, endTimeStamp)) {
123 currentFileLogs.emplace_back(currentLine);
124 }
125 }
126 int logsSize = static_cast<int>(logs.size());
127 int currentFileLogSize = static_cast<int>(currentFileLogs.size());
128 if ((logsSize + currentFileLogSize) > count) {
129 logs.insert(logs.end(), currentFileLogs.rbegin(), currentFileLogs.rbegin() +
130 count - logsSize);
131 } else {
132 logs.insert(logs.end(), currentFileLogs.rbegin(), currentFileLogs.rend());
133 }
134 }
135
ParseAllHistoryLogs(vector<string> & logs,const vector<string> & logFiles,TimeStampVarType beginTimeStamp,TimeStampVarType endTimeStamp,int count)136 void LogAssistant::ParseAllHistoryLogs(vector<string>& logs,
137 const vector<string>& logFiles, TimeStampVarType beginTimeStamp,
138 TimeStampVarType endTimeStamp, int count)
139 {
140 logs.clear();
141 // Read the matched log files one by one
142 for (auto &logFileName : logFiles) {
143 ParseSingFileLogs(logs, logFileName, beginTimeStamp,
144 endTimeStamp, count);
145 int logSize = static_cast<int>(logs.size());
146 if (logSize >= count) {
147 break;
148 }
149 }
150 }
151
ParseLogFileTimeStamp(const string & fileName)152 string LogAssistant::ParseLogFileTimeStamp(const string& fileName)
153 {
154 return FindMatchedRegex(fileName, TIME_STAMP_REGEX_PATTERN);
155 }
156
TranslateLongToFormattedTimeStamp(TimeStampVarType timeStamp)157 string LogAssistant::TranslateLongToFormattedTimeStamp(TimeStampVarType timeStamp)
158 {
159 time_t ftt = (time_t)(timeStamp / MILLI_TO_MICRO);
160 struct tm tmLocal;
161 if (localtime_r(&ftt, &tmLocal) == nullptr) {
162 HILOG_ERROR(LOG_CORE, "failed to get local time.");
163 return string();
164 }
165 char formatTz[FORMAT_TZ_SIZE] = {0};
166 if (strftime(formatTz, sizeof(formatTz), DATE_FORMAT, &tmLocal) == 0) {
167 return string();
168 }
169 return string(formatTz);
170 }
171
172 /**
173 * Check whether the log file contains logs whose timestamp is between beginTimeStamp
174 * and endTimeStamp or not
175 */
IsMatchedLogFile(const string & fileName,TimeStampVarType beginTimeStamp,TimeStampVarType endTimeStamp)176 bool LogAssistant::IsMatchedLogFile(const string& fileName, TimeStampVarType beginTimeStamp,
177 TimeStampVarType endTimeStamp)
178 {
179 string logFileTimeStamp = ParseLogFileTimeStamp(fileName);
180 if (logFileTimeStamp.empty()) {
181 // The name of this log file isn't standard, so we ignore it directly
182 return false;
183 }
184 if (beginTimeStamp == INVALID_TIMESTAMP && endTimeStamp == INVALID_TIMESTAMP) {
185 return true;
186 } else if (beginTimeStamp == INVALID_TIMESTAMP) {
187 return logFileTimeStamp <= TranslateLongToFormattedTimeStamp(endTimeStamp);
188 } else if (endTimeStamp == INVALID_TIMESTAMP) {
189 return logFileTimeStamp >= TranslateLongToFormattedTimeStamp(beginTimeStamp);
190 } else {
191 return logFileTimeStamp >= TranslateLongToFormattedTimeStamp(beginTimeStamp) &&
192 logFileTimeStamp <= TranslateLongToFormattedTimeStamp(endTimeStamp);
193 }
194 }
195
196 /**
197 * Find out all macthed log files by comparing the parsed timestamp from file name with
198 * the beginTimeStamp/endTimeStamp
199 */
AllMatchedLogFiles(vector<string> & logFiles,TimeStampVarType beginTimeStamp,TimeStampVarType endTimeStamp)200 void LogAssistant::AllMatchedLogFiles(vector<string>& logFiles, TimeStampVarType beginTimeStamp,
201 TimeStampVarType endTimeStamp)
202 {
203 logFiles.clear();
204 DIR* dir = opendir((logPersistDir + APP_EVENT_DIR).c_str());
205 if (dir == nullptr) {
206 HILOG_ERROR(LOG_CORE, "log persisted directory opened failed.");
207 return;
208 }
209 struct dirent* ent;
210 while ((ent = readdir(dir))) {
211 if (IsMatchedLogFile(std::string(ent->d_name), beginTimeStamp, endTimeStamp)) {
212 logFiles.emplace_back(ent->d_name);
213 }
214 }
215 if (!logFiles.empty()) {
216 // Sort all the matched log files in descending order
217 sort(logFiles.begin(),
218 logFiles.end(),
219 [] (const string& file1, const string& file2)->bool {
220 return file1 > file2;
221 });
222 }
223 closedir(dir);
224 }
225
ReadHistoryLogFromPersistFile(vector<string> & historyLogs,TimeStampVarType beginTimeStamp,TimeStampVarType endTimeStamp,int count)226 void LogAssistant::ReadHistoryLogFromPersistFile(vector<string>& historyLogs,
227 TimeStampVarType beginTimeStamp, TimeStampVarType endTimeStamp,
228 int count)
229 {
230 historyLogs.clear();
231 if (!logPersistDir.empty()) {
232 vector<string> logFiles;
233 AllMatchedLogFiles(logFiles, beginTimeStamp, endTimeStamp);
234 if (logFiles.size() > 0) {
235 ParseAllHistoryLogs(historyLogs, logFiles, beginTimeStamp,
236 endTimeStamp, count);
237 }
238 }
239 }
240
UpdateHiAppEventLogDir(const std::string & dir)241 void LogAssistant::UpdateHiAppEventLogDir(const std::string& dir)
242 {
243 logPersistDir = dir;
244 }
245
PullEventHistoryLog(TimeStampVarType beginTimeStamp,TimeStampVarType endTimeStamp,int count)246 void LogAssistant::PullEventHistoryLog(TimeStampVarType beginTimeStamp,
247 TimeStampVarType endTimeStamp, int count)
248 {
249 if (count < 0) {
250 count = MAX_LOG_COUNT;
251 }
252 vector<string> historyLogs;
253 historyLogs.reserve(static_cast<unsigned int>(count));
254 HILOG_DEBUG(LOG_CORE, "log capacity set to %{public}d", count);
255 ReadHistoryLogFromPersistFile(historyLogs, beginTimeStamp, endTimeStamp, count);
256 // Sort all the matched logs in ascending order
257 sort(historyLogs.begin(),
258 historyLogs.end(),
259 [] (const string& log1, const string& log2)->bool {
260 return log1 < log2;
261 });
262 if (historyLogPulledListener != nullptr) {
263 historyLogPulledListener(historyLogs);
264 }
265 }
266
RegRealTimeAppLogListener(RealTimeEventLogListener listener)267 void LogAssistant::RegRealTimeAppLogListener(RealTimeEventLogListener listener)
268 {
269 realTimeLogUpdateListener = listener;
270 }
271
RegHistoryAppLogListener(HistoryEventLogListener listener)272 void LogAssistant::RegHistoryAppLogListener(HistoryEventLogListener listener)
273 {
274 historyLogPulledListener = listener;
275 }
276
RemoveAllListeners()277 void LogAssistant::RemoveAllListeners()
278 {
279 realTimeLogUpdateListener = nullptr;
280 historyLogPulledListener = nullptr;
281 }
282
RealTimeAppLogUpdate(const string & realTimeLog)283 void LogAssistant::RealTimeAppLogUpdate(const string& realTimeLog)
284 {
285 if (realTimeLogUpdateListener != nullptr) {
286 realTimeLogUpdateListener(realTimeLog);
287 }
288 }
289 } // namespace HiviewDFX
290 } // namespace OHOS
291
RegRealTimeAppLogListener(RealTimeEventLogListener listener)292 void RegRealTimeAppLogListener(RealTimeEventLogListener listener)
293 {
294 OHOS::HiviewDFX::LogAssistant::Instance().RegRealTimeAppLogListener(listener);
295 }
296
RegHistoryAppLogListener(HistoryEventLogListener listener)297 void RegHistoryAppLogListener(HistoryEventLogListener listener)
298 {
299 OHOS::HiviewDFX::LogAssistant::Instance().RegHistoryAppLogListener(listener);
300 }
301
RemoveAllListeners()302 void RemoveAllListeners()
303 {
304 OHOS::HiviewDFX::LogAssistant::Instance().RemoveAllListeners();
305 }
306
RealTimeAppLogUpdate(const string & realTimeLog)307 void RealTimeAppLogUpdate(const string& realTimeLog)
308 {
309 OHOS::HiviewDFX::LogAssistant::Instance().RealTimeAppLogUpdate(realTimeLog);
310 }
311
UpdateHiAppEventLogDir(const string & path)312 void UpdateHiAppEventLogDir(const string& path)
313 {
314 OHOS::HiviewDFX::LogAssistant::Instance().UpdateHiAppEventLogDir(path);
315 }
316
PullEventHistoryLog(TimeStampVarType beginTimeStamp,TimeStampVarType endTimeStamp,int count)317 void PullEventHistoryLog(TimeStampVarType beginTimeStamp,
318 TimeStampVarType endTimeStamp, int count)
319 {
320 OHOS::HiviewDFX::LogAssistant::Instance().PullEventHistoryLog(beginTimeStamp,
321 endTimeStamp, count);
322 }
323