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