• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
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 "sp_log.h"
17 #include "securec.h"
18 #include <sys/stat.h>
19 #include <string>
20 #include <sstream>
21 #include <iostream>
22 #include <cstring>
23 #include <cstdint>
24 #include <dirent.h>
25 #include <unistd.h>
26 #include <mutex>
27 #include <ctime>
28 #include <fstream>
29 #include <vector>
30 #include <filesystem>
31 #include <iomanip>
32 #include <chrono>
33 #include <regex>
34 #include <thread>
35 #include <cstdlib>
36 #include <cstdio>
37 #include "include/smartperf_command.h"
38 #include "include/sp_utils.h"
39 
40 #ifdef HI_LOG_ENABLE
41 #include "hilog/log.h"
42 #endif
43 
44 namespace OHOS {
45 namespace SmartPerf {
46 const int32_t LOG_MAX_LEN = 10000;
47 const long long MAX_FILE_SIZE = 4 * 1024 * 1024;  // 4MB
48 const double MAX_FILE_KEEP_TIME = 60 * 60 * 24 * 7; // 7days
49 const int MAX_FILE_COUNT = 256;
50 const mode_t LOG_FILE_MODE = 0755;
51 std::mutex g_mtx;
52 std::string g_currentLogFilePath;
53 std::string g_currentDate;
54 bool g_writeEnable = false;
55 
SpLogOut(SpLogLevel logLevel,const char * logBuf)56 static void SpLogOut(SpLogLevel logLevel, const char *logBuf)
57 {
58 #ifdef HI_LOG_ENABLE
59     LogLevel hiLogLevel = LOG_INFO;
60     switch (logLevel) {
61         case SP_LOG_DEBUG:
62             hiLogLevel = LOG_DEBUG;
63             break;
64         case SP_LOG_INFO:
65             hiLogLevel = LOG_INFO;
66             break;
67         case SP_LOG_WARN:
68             hiLogLevel = LOG_WARN;
69             break;
70         case SP_LOG_ERROR:
71             hiLogLevel = LOG_ERROR;
72             break;
73         default:
74             break;
75     }
76     (void)HiLogPrint(LOG_CORE, hiLogLevel, LOG_DOMAIN, "SP_daemon", "%{public}s", logBuf);
77 #else
78     switch (logLevel) {
79         case SP_LOG_DEBUG:
80             printf("[D]%s\n", logBuf);
81             break;
82         case SP_LOG_INFO:
83             printf("[I]%s\n", logBuf);
84             break;
85         case SP_LOG_WARN:
86             printf("[W]%s\n", logBuf);
87             break;
88         case SP_LOG_ERROR:
89             printf("[E]%s\n", logBuf);
90             break;
91         default:
92             break;
93     }
94 #endif
95 }
96 
EnsureLogDirectoryExists()97 static bool EnsureLogDirectoryExists()
98 {
99     if (!std::__fs::filesystem::exists(LOG_FILE_DIR)) {
100         LOGD("Try create dir: %s", LOG_FILE_DIR.c_str());
101         try {
102             if (mkdir(LOG_FILE_DIR.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) != 0) {
103                 LOGE("Failed to create log directory: %s", strerror(errno));
104                 return false;
105             }
106 
107             if (!std::__fs::filesystem::exists(LOG_FILE_DIR)) {
108                 LOGE("Failed to create log directory.");
109                 return false;
110             }
111         } catch (const std::exception& e) {
112             LOGE("Exception while creating log directory: %s", e.what());
113             return false;
114         }
115     }
116 
117     return true;
118 }
119 
GetCurrentDate()120 static std::string GetCurrentDate()
121 {
122     auto now = std::chrono::system_clock::now();
123     std::time_t time = std::chrono::system_clock::to_time_t(now);
124     std::tm* tm = std::localtime(&time);
125     if (tm == nullptr) {
126         return "";
127     }
128 
129     std::ostringstream oss;
130     oss << std::put_time(tm, "%Y%m%d");
131     return oss.str();
132 }
GetCurrentLogFilePath(int maxLogNumber)133 static std::string GetCurrentLogFilePath(int maxLogNumber)
134 {
135     auto now = std::chrono::system_clock::now();
136     std::time_t time = std::chrono::system_clock::to_time_t(now);
137     std::tm* tm = std::localtime(&time);
138     if (tm == nullptr) {
139         return "";
140     }
141     std::ostringstream oss;
142     oss << LOG_FILE_DIR << "log." << maxLogNumber << "." <<
143         g_currentDate << "-" << std::put_time(tm, "%H%M%S");
144     return oss.str();
145 }
IsFilePathValid(const std::string & filePath)146 static bool IsFilePathValid(const std::string& filePath)
147 {
148     char filePathChar[PATH_MAX] = {0x00};
149     if ((realpath(filePath.c_str(), filePathChar) == nullptr)) {
150         LOGE("Log file %s is not exist.", filePath.c_str());
151         return false;
152     }
153     std::ifstream file(filePathChar, std::ios::binary | std::ios::ate);
154     if (file.is_open()) {
155         return file.tellg() <= MAX_FILE_SIZE;
156     }
157     return false;
158 }
159 
CheckFileNameAndGetNumber(const std::string & fileName,const std::string & date,bool * currentDateFlag)160 static int CheckFileNameAndGetNumber(const std::string& fileName, const std::string& date, bool* currentDateFlag)
161 {
162     std::regex pattern(R"(^log\.(\d+)\.(\d{8})-\d{6}$)");
163     std::smatch matches;
164 
165     if (std::regex_match(fileName, matches, pattern)) {
166         std::string fileDate = matches[2].str();
167         if (fileDate == date) {
168             *currentDateFlag = true;
169         }
170         return SPUtilesTye::StringToSometype<int>(matches[1].str());
171     }
172 
173     return 0;
174 }
175 
Chmod(const std::string & sourceFilePath,mode_t mode)176 static bool Chmod(const std::string& sourceFilePath, mode_t mode)
177 {
178     int retCode = chmod(sourceFilePath.c_str(), mode);
179     if (retCode != 0) {
180         LOGE("Failed to set %s permission, error code %d.", sourceFilePath.c_str(), retCode);
181         return false;
182     }
183 
184     return true;
185 }
186 
TarFile(const std::string & sourceFileName)187 static void TarFile(const std::string& sourceFileName)
188 {
189     std::ostringstream compressedFilePath;
190     compressedFilePath << LOG_FILE_DIR << sourceFileName << ".tar.gz";
191 
192     std::string tarStr = compressedFilePath.str() + " -C " + LOG_FILE_DIR + " " + sourceFileName;
193     std::string tarCommand = CMD_COMMAND_MAP.at(CmdCommand::TAR) + tarStr;
194     std::string cmdResult;
195     if (!SPUtils::LoadCmd(tarCommand, cmdResult)) {
196         LOGE("Failed to compress file %s", sourceFileName.c_str());
197         return;
198     }
199 
200     Chmod(compressedFilePath.str(), LOG_FILE_MODE);
201 
202     tarCommand = CMD_COMMAND_MAP.at(CmdCommand::REMOVE) + LOG_FILE_DIR + sourceFileName;
203     if (!SPUtils::LoadCmd(tarCommand, cmdResult)) {
204         LOGE("Failed to delete original file %s", sourceFileName.c_str());
205         return;
206     }
207 
208     LOGD("Successfully compressed and deleted file: %s", sourceFileName.c_str());
209     return;
210 }
211 
GetLogFilePath()212 static bool GetLogFilePath()
213 {
214     std::string date = GetCurrentDate();
215     if (date == "") {
216         LOGE("Get current date failed");
217         return false;
218     }
219     if ((date == g_currentDate) && IsFilePathValid(g_currentLogFilePath)) {
220         return true;
221     }
222     LOGE("Current log file path invalid: %s", g_currentLogFilePath.c_str());
223     g_currentDate = date;
224     std::string fileName;
225     bool currentDateFlag = false;
226     int fileNumber = 0;
227     for (const auto& entry : std::__fs::filesystem::directory_iterator(LOG_FILE_DIR)) {
228         if (entry.is_regular_file()) {
229             fileName = entry.path().filename().string();
230             fileNumber = CheckFileNameAndGetNumber(fileName, date, &currentDateFlag);
231             if (fileNumber != 0) {
232                 break;
233             }
234         }
235     }
236     if (fileNumber == 0) {
237         g_currentLogFilePath = GetCurrentLogFilePath(1);
238         if (g_currentLogFilePath == "") {
239             LOGE("Get current log file data is null");
240             return false;
241         }
242         return true;
243     }
244     std::string filePath = LOG_FILE_DIR + fileName;
245     if (currentDateFlag && IsFilePathValid(filePath)) {
246         g_currentLogFilePath = filePath;
247         return true;
248     }
249     TarFile(fileName);
250     if (fileNumber >= MAX_FILE_COUNT) {
251         LOGE("Log file full!");
252         return false;
253     }
254     g_currentLogFilePath = GetCurrentLogFilePath(fileNumber + 1);
255     return true;
256 }
257 
WriteMessage(const char * logMessage)258 static void WriteMessage(const char* logMessage)
259 {
260     bool chmodFlag = !std::__fs::filesystem::exists(g_currentLogFilePath);
261 
262     char logFilePathChar[PATH_MAX] = {0x00};
263     if ((realpath(g_currentLogFilePath.c_str(), logFilePathChar) == nullptr)) {
264         errno_t result = strncpy_s(logFilePathChar, PATH_MAX,
265             g_currentLogFilePath.c_str(), g_currentLogFilePath.size());
266         if (result != 0) {
267             LOGE("strncpy_s failed with error: %d", result);
268             return;
269         }
270         LOGI("Log file %s is not exist, will create", g_currentLogFilePath.c_str());
271     }
272 
273     std::ofstream logFile(logFilePathChar, std::ios::app);
274     if (logFile.is_open()) {
275         if (chmodFlag) {
276             if (!Chmod(logFilePathChar, LOG_FILE_MODE)) {
277                 logFile.close();
278                 return;
279             }
280         }
281 
282         auto now = std::chrono::system_clock::now();
283         std::time_t time = std::chrono::system_clock::to_time_t(now);
284         std::tm* tm = std::localtime(&time);
285         if (tm == nullptr) {
286             LOGE("Write Message get current data is null");
287             logFile.close();
288             return;
289         }
290 
291         std::ostringstream timeStamp;
292         timeStamp << std::put_time(tm, "[%H:%M:%S]");
293         logFile << timeStamp.str() << logMessage << std::endl;
294 
295         logFile.close();
296     } else {
297         LOGE("Unable to open log file for writing: %s", logFilePathChar);
298     }
299 
300     return;
301 }
302 
EnableWriteLogAndDeleteOldLogFiles()303 void EnableWriteLogAndDeleteOldLogFiles()
304 {
305     g_writeEnable = true;
306     std::lock_guard<std::mutex> lock(g_mtx);
307 
308     if (!EnsureLogDirectoryExists()) {
309         return;
310     }
311 
312     DIR* dir = opendir(LOG_FILE_DIR.c_str());
313     if (dir == nullptr) {
314         LOGE("Failed to open log directory: %s", LOG_FILE_DIR.c_str());
315         return;
316     }
317 
318     struct dirent* entry;
319     time_t currentTime = time(nullptr);
320 
321     while ((entry = readdir(dir)) != nullptr) {
322         if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
323             continue;
324         }
325 
326         std::string filePath = LOG_FILE_DIR + entry->d_name;
327         char filePathChar[PATH_MAX] = {0x00};
328         if ((realpath(filePath.c_str(), filePathChar) == nullptr)) {
329             LOGE("Log file %s is not exist.", filePath.c_str());
330             continue;
331         }
332 
333         struct stat fileStat = {0};
334         if (stat(filePathChar, &fileStat) != 0) {
335             LOGE("Failed to get stats for file: %s", filePathChar);
336             continue;
337         }
338 
339         double diffSeconds = difftime(currentTime, fileStat.st_mtime);
340         if (diffSeconds > MAX_FILE_KEEP_TIME) {
341             if (remove(filePathChar) == 0) {
342                 LOGD("Deleted file: %s, last modified: %.2f seconds ago", filePathChar, diffSeconds);
343             } else {
344                 LOGE("Failed to delete file: %s", filePathChar);
345             }
346         }
347     }
348 
349     closedir(dir);
350 }
351 
EscapeForCSV(std::string str)352 void EscapeForCSV(std::string str)
353 {
354     std::string escapedStr;
355     for (char ch : str) {
356         if (ch == '"') {
357             escapedStr += "\"\"";
358         } else if (ch == ',' || ch == '\n' || ch == '\r') {
359             escapedStr += '"';
360             escapedStr += ch;
361             escapedStr += '"';
362         } else {
363             escapedStr += ch;
364         }
365     }
366     str = escapedStr;
367 }
368 
SpLog(SpLogLevel logLevel,bool isWriteLog,const char * fmt,...)369 void SpLog(SpLogLevel logLevel, bool isWriteLog, const char *fmt, ...)
370 {
371     if (fmt == nullptr) {
372         SpLogOut(logLevel, "SP log format string is NULL.");
373         return;
374     }
375     char logBuf[LOG_MAX_LEN] = {0};
376     int32_t ret = 0;
377     va_list arg;
378     va_start(arg, fmt);
379     ret = vsnprintf_s(logBuf, sizeof(logBuf), sizeof(logBuf) - 1, fmt, arg);
380     va_end(arg);
381     if (ret < 0) {
382         SpLogOut(logLevel, "SP log length error.");
383         return;
384     }
385     if (ret >= static_cast<int32_t>(sizeof(logBuf) - 1)) {
386         SpLogOut(logLevel, "SP log error: log message truncated.");
387         return;
388     }
389     std::string logStr(logBuf);
390     EscapeForCSV(logStr);
391     SpLogOut(logLevel, logStr.c_str());
392 
393     if (!isWriteLog) {
394         return;
395     }
396 
397     std::lock_guard<std::mutex> lock(g_mtx);
398 
399     if (!g_writeEnable || !EnsureLogDirectoryExists() || !GetLogFilePath()) {
400         return;
401     }
402 
403     WriteMessage(logStr.c_str());
404     return;
405 }
406 } // namespace SmartPerf
407 } // namespace OHOS