1 /*
2 * Copyright (c) 2021-2024 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 "util.h"
17 #include <fstream>
18 #include <regex>
19
20 #include <sys/prctl.h>
21 #include <sys/stat.h>
22 #include <sys/syscall.h>
23 #include <unistd.h>
24
25 #include "aggregator.h"
26 #include "error_multimodal.h"
27 #include "securec.h"
28
29 #undef MMI_LOG_TAG
30 #define MMI_LOG_TAG "Util"
31
32 namespace OHOS {
33 namespace MMI {
34 namespace {
35 constexpr int32_t FILE_SIZE_MAX { 0x6C445 };
36 constexpr int32_t MAX_PRO_FILE_SIZE { 128000 };
37 constexpr int32_t INVALID_FILE_SIZE { -1 };
38 constexpr int32_t MIN_INTERVALTIME { 36 };
39 constexpr int32_t MAX_INTERVALTIME { 100 };
40 constexpr int32_t MIN_DELAYTIME { 300 };
41 constexpr int32_t MAX_DELAYTIME { 1000 };
42 constexpr int32_t COMMENT_SUBSCRIPT { 0 };
43 const std::string CONFIG_ITEM_REPEAT = "Key.autorepeat";
44 const std::string CONFIG_ITEM_DELAY = "Key.autorepeat.delaytime";
45 const std::string CONFIG_ITEM_INTERVAL = "Key.autorepeat.intervaltime";
46 const std::string CONFIG_ITEM_TYPE = "Key.keyboard.type";
47 const std::string CURSORSTYLE_PATH = "/system/etc/multimodalinput/mouse_icon/";
48 const std::string DATA_PATH = "/data";
49 const std::string INPUT_PATH = "/system/";
50 const std::string SYS_PROD_PATH = "/sys_prod/";
51 const std::string KEY_PATH = "/vendor/etc/keymap/";
52 constexpr size_t BUF_TID_SIZE { 10 };
53 constexpr size_t BUF_CMD_SIZE { 512 };
54 constexpr size_t PROGRAM_NAME_SIZE { 256 };
55 constexpr int32_t TIME_CONVERSION_UNIT { 1000 };
56 constexpr int32_t COLOR_FIXEX_WIDTH { 6 };
57 const std::string COLOR_PREFIX = "#";
58 const char COLOR_FILL = '0';
59 constexpr int64_t TIME_ROUND_UP { 999 };
60 } // namespace
61
GetSysClockTime()62 int64_t GetSysClockTime()
63 {
64 struct timespec ts = { 0, 0 };
65 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
66 MMI_HILOGD("clock_gettime failed:%{public}d", errno);
67 return 0;
68 }
69
70 // Prevent integer overflow
71 if (static_cast<uint64_t>(ts.tv_sec) > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) / (
72 TIME_CONVERSION_UNIT * TIME_CONVERSION_UNIT)) {
73 MMI_HILOGE("Integer overflow detected!");
74 return 0;
75 }
76
77 uint64_t totalMicroSeconds = static_cast<uint64_t>(ts.tv_sec) * TIME_CONVERSION_UNIT *
78 TIME_CONVERSION_UNIT + static_cast<uint64_t>(ts.tv_nsec) / TIME_CONVERSION_UNIT;
79
80 if (totalMicroSeconds > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
81 MMI_HILOGE("Total time value integer overflow detected!");
82 return 0;
83 }
84 return static_cast<int64_t>(totalMicroSeconds);
85 }
86
GetMillisTime()87 int64_t GetMillisTime()
88 {
89 auto timeNow = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now());
90 auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow.time_since_epoch());
91 return tmp.count();
92 }
93
GetTimeToMilli(int64_t timeDT)94 int64_t GetTimeToMilli(int64_t timeDT)
95 {
96 return (timeDT + TIME_ROUND_UP) / TIME_CONVERSION_UNIT;
97 }
98
GetThisThreadIdOfString()99 static std::string GetThisThreadIdOfString()
100 {
101 thread_local std::string threadLocalId;
102 if (threadLocalId.empty()) {
103 long tid = syscall(SYS_gettid);
104 char buf[BUF_TID_SIZE] = {};
105 const int32_t ret = sprintf_s(buf, BUF_TID_SIZE, "%06d", tid);
106 if (ret < 0) {
107 printf("ERR: in %s, #%d, call sprintf_s failed, ret = %d", __func__, __LINE__, ret);
108 return threadLocalId;
109 }
110 buf[BUF_TID_SIZE - 1] = '\0';
111 threadLocalId = buf;
112 }
113
114 return threadLocalId;
115 }
116
GetThisThreadId()117 uint64_t GetThisThreadId()
118 {
119 std::string stid = GetThisThreadIdOfString();
120 auto tid = std::atoll(stid.c_str());
121 return tid;
122 }
123
StringToken(std::string & str,const std::string & sep,std::string & token)124 static size_t StringToken(std::string &str, const std::string &sep, std::string &token)
125 {
126 token = "";
127 if (str.empty()) {
128 return str.npos;
129 }
130 size_t pos = str.npos;
131 size_t tmp = 0;
132 for (auto &item : sep) {
133 tmp = str.find(item);
134 if (str.npos != tmp) {
135 pos = (std::min)(pos, tmp);
136 }
137 }
138 if (str.npos != pos) {
139 token = str.substr(0, pos);
140 if (str.npos != pos + 1) {
141 str = str.substr(pos + 1, str.npos);
142 }
143 if (pos == 0) {
144 return StringToken(str, sep, token);
145 }
146 } else {
147 token = str;
148 str = "";
149 }
150 return token.size();
151 }
152
StringSplit(const std::string & str,const std::string & sep,std::vector<std::string> & vecList)153 size_t StringSplit(const std::string &str, const std::string &sep, std::vector<std::string> &vecList)
154 {
155 size_t size;
156 auto strs = str;
157 std::string token;
158 while (str.npos != (size = StringToken(strs, sep, token))) {
159 vecList.push_back(token);
160 }
161 return vecList.size();
162 }
163
IdsListToString(const std::vector<int32_t> & list,const std::string & sep)164 std::string IdsListToString(const std::vector<int32_t> &list, const std::string &sep)
165 {
166 std::string str;
167 for (const auto &it : list) {
168 str += std::to_string(it) + sep;
169 }
170 if (str.size() > 0) {
171 str.resize(str.size() - sep.size());
172 }
173 return str;
174 }
175
GetPid()176 int32_t GetPid()
177 {
178 return static_cast<int32_t>(getpid());
179 }
180
GetFileName(const std::string & strPath)181 static std::string GetFileName(const std::string &strPath)
182 {
183 size_t nPos = strPath.find_last_of('/');
184 if (strPath.npos == nPos) {
185 nPos = strPath.find_last_of('\\');
186 }
187 if (strPath.npos == nPos) {
188 return strPath;
189 }
190
191 return strPath.substr(nPos + 1, strPath.npos);
192 }
193
GetProgramName()194 const char *GetProgramName()
195 {
196 static char programName[PROGRAM_NAME_SIZE] = {};
197 if (programName[0] != '\0') {
198 return programName;
199 }
200
201 char buf[BUF_CMD_SIZE] = { 0 };
202 if (sprintf_s(buf, BUF_CMD_SIZE, "/proc/%d/cmdline", static_cast<int32_t>(getpid())) == -1) {
203 KMSG_LOGE("GetProcessInfo sprintf_s /proc/.../cmdline error");
204 return "";
205 }
206 FILE *fp = fopen(buf, "rb");
207 CHKPS(fp);
208 static constexpr size_t bufLineSize = 512;
209 char bufLine[bufLineSize] = { 0 };
210 if ((fgets(bufLine, bufLineSize, fp) == nullptr)) {
211 KMSG_LOGE("fgets failed");
212 if (fclose(fp) != 0) {
213 KMSG_LOGW("Close file:%s failed", buf);
214 }
215 fp = nullptr;
216 return "";
217 }
218 if (fclose(fp) != 0) {
219 KMSG_LOGW("Close file:%s failed", buf);
220 }
221 fp = nullptr;
222
223 std::string tempName(bufLine);
224 tempName = GetFileName(tempName);
225 if (tempName.empty()) {
226 KMSG_LOGE("tempName is empty");
227 return "";
228 }
229 const size_t copySize = std::min(tempName.size(), PROGRAM_NAME_SIZE - 1);
230 if (copySize == 0) {
231 KMSG_LOGE("The copySize is 0");
232 return "";
233 }
234 errno_t ret = memcpy_s(programName, PROGRAM_NAME_SIZE, tempName.c_str(), copySize);
235 if (ret != EOK) {
236 return "";
237 }
238 KMSG_LOGI("GetProgramName success. programName = %s", programName);
239
240 return programName;
241 }
242
SetThreadName(const std::string & name)243 void SetThreadName(const std::string &name)
244 {
245 prctl(PR_SET_NAME, name.c_str());
246 }
247
IsFileExists(const std::string & fileName)248 static bool IsFileExists(const std::string &fileName)
249 {
250 return (access(fileName.c_str(), F_OK) == 0);
251 }
252
CheckFileExtendName(const std::string & filePath,const std::string & checkExtension)253 static bool CheckFileExtendName(const std::string &filePath, const std::string &checkExtension)
254 {
255 std::string::size_type pos = filePath.find_last_of('.');
256 if (pos == std::string::npos) {
257 MMI_HILOGE("File is not find extension");
258 return false;
259 }
260 return (filePath.substr(pos + 1, filePath.npos) == checkExtension);
261 }
262
GetFileSize(const std::string & filePath)263 static int32_t GetFileSize(const std::string &filePath)
264 {
265 struct stat statbuf = { 0 };
266 if (stat(filePath.c_str(), &statbuf) != 0) {
267 MMI_HILOGE("Get file size error");
268 return INVALID_FILE_SIZE;
269 }
270 return statbuf.st_size;
271 }
272
ReadFile(const std::string & filePath)273 static std::string ReadFile(const std::string &filePath)
274 {
275 FILE *fp = fopen(filePath.c_str(), "r");
276 CHKPS(fp);
277 std::string dataStr;
278 char buf[256] = {};
279 while (fgets(buf, sizeof(buf), fp) != nullptr) {
280 dataStr += buf;
281 }
282 if (fclose(fp) != 0) {
283 MMI_HILOGW("Close file failed");
284 }
285 return dataStr;
286 }
287
IsValidPath(const std::string & rootDir,const std::string & filePath)288 static bool IsValidPath(const std::string &rootDir, const std::string &filePath)
289 {
290 return (filePath.compare(0, rootDir.size(), rootDir) == 0);
291 }
292
IsValidJsonPath(const std::string & filePath)293 bool IsValidJsonPath(const std::string &filePath)
294 {
295 return (IsValidPath(DATA_PATH, filePath) ||
296 IsValidPath(INPUT_PATH, filePath) ||
297 IsValidPath(SYS_PROD_PATH, filePath));
298 }
299
IsValidProPath(const std::string & filePath)300 static bool IsValidProPath(const std::string &filePath)
301 {
302 return IsValidPath(KEY_PATH, filePath);
303 }
304
IsValidTomlPath(const std::string & filePath)305 static bool IsValidTomlPath(const std::string &filePath)
306 {
307 return IsValidPath(KEY_PATH, filePath);
308 }
309
ReadProFile(const std::string & filePath,int32_t deviceId,std::map<int32_t,std::map<int32_t,int32_t>> & configMap)310 void ReadProFile(const std::string &filePath, int32_t deviceId,
311 std::map<int32_t, std::map<int32_t, int32_t>> &configMap)
312 {
313 CALL_DEBUG_ENTER;
314 if (filePath.empty()) {
315 MMI_HILOGE("FilePath is empty");
316 return;
317 }
318 char realPath[PATH_MAX] = {};
319 CHKPV(realpath(filePath.c_str(), realPath));
320 if (!IsValidProPath(realPath)) {
321 MMI_HILOGE("File path is error");
322 return;
323 }
324 if (!IsFileExists(realPath)) {
325 MMI_HILOGE("File is not existent");
326 return;
327 }
328 if (!CheckFileExtendName(realPath, "pro")) {
329 MMI_HILOGE("Unable to parse files other than json format");
330 return;
331 }
332 auto fileSize = GetFileSize(realPath);
333 if ((fileSize == INVALID_FILE_SIZE) || (fileSize >= MAX_PRO_FILE_SIZE)) {
334 MMI_HILOGE("The configuration file size is incorrect");
335 return;
336 }
337 ReadProConfigFile(realPath, deviceId, configMap);
338 }
339
IsNum(const std::string & str)340 static inline bool IsNum(const std::string &str)
341 {
342 std::istringstream sin(str);
343 double num;
344 return (sin >> num) && sin.eof();
345 }
346
ReadProConfigFile(const std::string & realPath,int32_t deviceId,std::map<int32_t,std::map<int32_t,int32_t>> & configKey)347 void ReadProConfigFile(const std::string &realPath, int32_t deviceId,
348 std::map<int32_t, std::map<int32_t, int32_t>> &configKey)
349 {
350 CALL_DEBUG_ENTER;
351 std::ifstream reader(realPath);
352 if (!reader.is_open()) {
353 MMI_HILOGE("Failed to open config file");
354 return;
355 }
356 std::string strLine;
357 int32_t sysKeyValue;
358 int32_t nativeKeyValue;
359 int32_t elementKey = 0;
360 int32_t elementValue = 0;
361 std::map<int32_t, int32_t> tmpConfigKey;
362 while (std::getline(reader, strLine)) {
363 const char* line = strLine.c_str();
364 int32_t len = strlen(line);
365 char* realLine = static_cast<char*>(malloc(len + 1));
366 CHKPV(realLine);
367 if (strcpy_s(realLine, len + 1, line) != EOK) {
368 MMI_HILOGE("strcpy_s error");
369 free(realLine);
370 realLine = nullptr;
371 return;
372 }
373 *(realLine + len + 1) = '\0';
374 int32_t ret = ReadConfigInfo(realLine, len, &elementKey, &elementValue);
375 free(realLine);
376 realLine = nullptr;
377 if (ret != RET_OK) {
378 MMI_HILOGE("Failed to read from line of config info");
379 reader.close();
380 return;
381 }
382 nativeKeyValue = elementKey;
383 sysKeyValue = elementValue;
384 MMI_HILOGD("The nativeKeyValue is:%{public}d, sysKeyValue is:%{public}d", nativeKeyValue, sysKeyValue);
385 tmpConfigKey.insert(std::pair<int32_t, int32_t>(nativeKeyValue, sysKeyValue));
386 }
387 reader.close();
388 auto iter = configKey.insert(std::make_pair(deviceId, tmpConfigKey));
389 if (!iter.second) {
390 MMI_HILOGE("The file name is duplicated");
391 return;
392 }
393 }
394
ReadJsonFile(const std::string & filePath)395 std::string ReadJsonFile(const std::string &filePath)
396 {
397 if (filePath.empty()) {
398 MMI_HILOGE("FilePath is empty");
399 return "";
400 }
401 char realPath[PATH_MAX] = {};
402 CHKPS(realpath(filePath.c_str(), realPath));
403 if (!IsValidJsonPath(realPath)) {
404 MMI_HILOGE("File path is error");
405 return "";
406 }
407 if (!CheckFileExtendName(realPath, "json")) {
408 MMI_HILOGE("Unable to parse files other than json format");
409 return "";
410 }
411 if (!IsFileExists(realPath)) {
412 MMI_HILOGE("File is not existent");
413 return "";
414 }
415 int32_t fileSize = GetFileSize(realPath);
416 if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
417 MMI_HILOGE("File size out of read range");
418 return "";
419 }
420 return ReadFile(filePath);
421 }
422
ConfigItemSwitch(const std::string & configItem,const std::string & value,DeviceConfig & devConf)423 static int32_t ConfigItemSwitch(const std::string &configItem, const std::string &value, DeviceConfig &devConf)
424 {
425 CALL_DEBUG_ENTER;
426 if (configItem.empty() || value.empty()) {
427 MMI_HILOGE("Get key config item is invalid");
428 return RET_ERR;
429 }
430 if (!IsNum(value)) {
431 MMI_HILOGE("Get key config item is invalid");
432 return RET_ERR;
433 }
434 if (configItem == CONFIG_ITEM_REPEAT) {
435 devConf.autoSwitch = atoi(value.c_str());
436 } else if (configItem == CONFIG_ITEM_DELAY) {
437 devConf.delayTime = atoi(value.c_str());
438 if (devConf.delayTime < MIN_DELAYTIME || devConf.delayTime > MAX_DELAYTIME) {
439 MMI_HILOGE("Unusual the delaytime");
440 return RET_ERR;
441 }
442 } else if (configItem == CONFIG_ITEM_INTERVAL) {
443 devConf.intervalTime = atoi(value.c_str());
444 if (devConf.intervalTime < MIN_INTERVALTIME || devConf.intervalTime > MAX_INTERVALTIME) {
445 MMI_HILOGE("Unusual the intervaltime");
446 return RET_ERR;
447 }
448 } else if (configItem == CONFIG_ITEM_TYPE) {
449 devConf.keyboardType = atoi(value.c_str());
450 }
451 return RET_OK;
452 }
453
ReadConfigFile(const std::string & realPath,DeviceConfig & devConf)454 static int32_t ReadConfigFile(const std::string &realPath, DeviceConfig &devConf)
455 {
456 CALL_DEBUG_ENTER;
457 std::ifstream cfgFile(realPath);
458 if (!cfgFile.is_open()) {
459 MMI_HILOGE("Failed to open config file");
460 return FILE_OPEN_FAIL;
461 }
462 std::string tmp;
463 while (std::getline(cfgFile, tmp)) {
464 RemoveSpace(tmp);
465 size_t pos = tmp.find('#');
466 if (pos != tmp.npos && pos != COMMENT_SUBSCRIPT) {
467 MMI_HILOGE("File format is error");
468 cfgFile.close();
469 return RET_ERR;
470 }
471 if (tmp.empty() || tmp.front() == '#') {
472 continue;
473 }
474 pos = tmp.find('=');
475 if ((pos == std::string::npos) || (tmp.back() == '=')) {
476 MMI_HILOGE("Find config item error");
477 cfgFile.close();
478 return RET_ERR;
479 }
480 std::string configItem = tmp.substr(0, pos);
481 std::string value = tmp.substr(pos + 1);
482 if (ConfigItemSwitch(configItem, value, devConf) == RET_ERR) {
483 MMI_HILOGE("Configuration item error");
484 cfgFile.close();
485 return RET_ERR;
486 }
487 }
488 cfgFile.close();
489 return RET_OK;
490 }
491
ReadTomlFile(const std::string & filePath,DeviceConfig & devConf)492 int32_t ReadTomlFile(const std::string &filePath, DeviceConfig &devConf)
493 {
494 if (filePath.empty()) {
495 MMI_HILOGE("FilePath is empty");
496 return RET_ERR;
497 }
498 char realPath[PATH_MAX] = {};
499 CHKPR(realpath(filePath.c_str(), realPath), RET_ERR);
500 if (!IsValidTomlPath(realPath)) {
501 MMI_HILOGE("File path is error");
502 return RET_ERR;
503 }
504 if (!IsFileExists(realPath)) {
505 MMI_HILOGE("File is not existent");
506 return RET_ERR;
507 }
508 if (!CheckFileExtendName(realPath, "TOML")) {
509 MMI_HILOGE("Unable to parse files other than json format");
510 return RET_ERR;
511 }
512 int32_t fileSize = GetFileSize(realPath);
513 if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
514 MMI_HILOGE("File size out of read range");
515 return RET_ERR;
516 }
517 if (ReadConfigFile(realPath, devConf) == RET_ERR) {
518 MMI_HILOGE("Read device config file failed");
519 return RET_ERR;
520 }
521 return RET_OK;
522 }
523
ReadCursorStyleFile(const std::string & filePath)524 int32_t ReadCursorStyleFile(const std::string &filePath)
525 {
526 CALL_DEBUG_ENTER;
527 if (filePath.empty()) {
528 MMI_HILOGE("FilePath is empty");
529 return RET_ERR;
530 }
531 if (!IsFileExists(filePath)) {
532 MMI_HILOGE("File is not existent");
533 return RET_ERR;
534 }
535 char realPath[PATH_MAX] = {};
536 CHKPR(realpath(filePath.c_str(), realPath), RET_ERR);
537 int32_t fileSize = GetFileSize(realPath);
538 if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
539 MMI_HILOGE("File size out of read range");
540 return RET_ERR;
541 }
542 return RET_OK;
543 }
544
StringPrintf(const char * format,...)545 std::string StringPrintf(const char *format, ...)
546 {
547 char space[1024];
548
549 va_list ap;
550 va_start(ap, format);
551 std::string result;
552 int32_t ret = vsnprintf_s(space, sizeof(space), sizeof(space) - 1, format, ap);
553 if (ret >= RET_OK && (size_t)ret < sizeof(space)) {
554 result = space;
555 } else {
556 MMI_HILOGE("The buffer is overflow");
557 }
558 va_end(ap);
559 return result;
560 }
561
FileVerification(std::string & filePath,const std::string & checkExtension)562 std::string FileVerification(std::string &filePath, const std::string &checkExtension)
563 {
564 if (filePath.empty()) {
565 MMI_HILOGE("FilePath is empty");
566 return "";
567 }
568 char realPath[PATH_MAX] = {};
569 CHKPS(realpath(filePath.c_str(), realPath));
570 if (!IsFileExists(realPath)) {
571 MMI_HILOGE("File is not existent");
572 return "";
573 }
574 if (!CheckFileExtendName(realPath, checkExtension)) {
575 MMI_HILOGE("Unable to parse files other than json format");
576 return "";
577 }
578 int32_t fileSize = GetFileSize(realPath);
579 if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
580 MMI_HILOGE("File size out of read range");
581 return "";
582 }
583 return realPath;
584 }
585
ReadFile(const std::string & path,std::string & content)586 bool ReadFile(const std::string &path, std::string &content)
587 {
588 if (path.empty()) {
589 MMI_HILOGE("path is empty");
590 return false;
591 }
592 char realPath[PATH_MAX] = {};
593 CHKPF(realpath(path.c_str(), realPath));
594 std::ifstream file(realPath);
595 if (!file) {
596 return false;
597 }
598 std::stringstream buffer;
599 buffer << file.rdbuf();
600 content = buffer.str();
601 return true;
602 }
603
IntToHexRGB(int32_t color)604 std::string IntToHexRGB(int32_t color)
605 {
606 std::ostringstream oss;
607 oss << COLOR_PREFIX << std::setfill(COLOR_FILL) << std::setw(COLOR_FIXEX_WIDTH) << std::hex << std::uppercase
608 << color;
609 return oss.str();
610 }
611
StringReplace(std::string & str,const std::string & oldStr,const std::string & newStr)612 void StringReplace(std::string &str, const std::string &oldStr, const std::string &newStr)
613 {
614 std::regex re(oldStr);
615 str = std::regex_replace(str, re, newStr);
616 }
617
IsInteger(const char * target)618 bool IsInteger(const char *target)
619 {
620 CHKPF(target);
621 std::regex pattern("^\\s*-?(0|([1-9]\\d*))\\s*$");
622 return std::regex_match(target, pattern);
623 }
624
Record(const LogHeader & lh,const std::string & key,const std::string & record)625 bool Aggregator::Record(const LogHeader &lh, const std::string &key, const std::string &record)
626 {
627 constexpr int32_t oneSecond = 1000;
628 if (timerId_ != -1) {
629 resetTimer_(timerId_);
630 } else {
631 timerId_ = addTimer_(oneSecond, 1, [this, lh]() {
632 FlushRecords(lh);
633 timerId_ = -1;
634 });
635 }
636 if (key == key_) {
637 auto now = std::chrono::system_clock::now();
638 records_.push_back({record, now});
639 if (records_.size() >= maxRecordCount_) {
640 FlushRecords(lh);
641 }
642 return true;
643 } else {
644 FlushRecords(lh, key, record);
645 key_ = key;
646 return false;
647 }
648 }
649
FlushRecords(const LogHeader & lh,const std::string & key,const std::string & extraRecord)650 void Aggregator::FlushRecords(const LogHeader &lh, const std::string &key, const std::string &extraRecord)
651 {
652 constexpr uint32_t milliSecondWidth = 3;
653 constexpr uint32_t microToMilli = 1000;
654 size_t recordCount = records_.size();
655 std::ostringstream oss;
656 if (!records_.empty()) {
657 oss << key_;
658 oss << ", first: " << records_.front().record << "-(";
659 auto firstTime = records_.front().timestamp;
660 time_t firstTimeT = std::chrono::system_clock::to_time_t(firstTime);
661 std::tm *bt = std::localtime(&firstTimeT);
662 if (bt == nullptr) {
663 MMI_HILOGE("The bt is nullptr, this is a invalid time");
664 return;
665 }
666 oss << std::put_time(bt, "%Y-%m-%d %H:%M:%S");
667 auto since_epoch = firstTime.time_since_epoch();
668 auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(since_epoch).count() % microToMilli;
669 oss << '.' << std::setfill('0') << std::setw(milliSecondWidth) << millis << "ms)";
670
671 if (records_.size() > 1) {
672 size_t i = records_.size() - 1;
673 const auto &recordInfo = records_[i];
674 oss << ", " << recordInfo.record;
675 }
676 records_.clear();
677 oss << ", count: " << recordCount;
678 }
679 if (!extraRecord.empty()) {
680 if (!oss.str().empty()) {
681 oss << ", last: ";
682 }
683 oss << key << ": " << extraRecord;
684 }
685 if (!oss.str().empty()) {
686 MMI_HILOG_HEADER(LOG_INFO, lh, "%{public}s", oss.str().c_str());
687 }
688 }
689
~Aggregator()690 Aggregator::~Aggregator()
691 {
692 FlushRecords(MMI_LOG_HEADER);
693 }
694 } // namespace MMI
695 } // namespace OHOS
696