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