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