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