1 /*
2 * Copyright (c) 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 "log_file_writer.h"
17
18 #include <algorithm>
19 #include <cerrno>
20 #include <chrono>
21 #include <iomanip>
22 #include <vector>
23
24 #include "file_util.h"
25 #include "hiview_global.h"
26 #include "hiview_logger.h"
27 #include "parameter_ex.h"
28 #include "string_util.h"
29 #include "time_util.h"
30
31 namespace OHOS {
32 namespace HiviewDFX {
33 DEFINE_LOG_TAG("HiView-LogFileWriter");
34 namespace {
35 constexpr size_t DEFAULT_FILE_INDEX = 1;
36
GetLogFileDir()37 std::string GetLogFileDir()
38 {
39 auto& context = HiviewGlobal::GetInstance();
40 if (context == nullptr) {
41 HIVIEW_LOGE("hiview context is null");
42 return "";
43 }
44 std::string workPath = context->GetHiViewDirectory(HiviewContext::DirectoryType::WORK_DIRECTORY);
45 std::string logDir = FileUtil::IncludeTrailingPathDelimiter(workPath).append("sys_event_log");
46 if (!FileUtil::FileExists(logDir) && !FileUtil::ForceCreateDirectory(logDir, FileUtil::FILE_PERM_770)) {
47 HIVIEW_LOGE("failed to create log directory: %{public}s", logDir.c_str());
48 return "";
49 }
50 return logDir;
51 }
52
GetOrderedLogFileList(const std::string & namePrefix,std::list<std::string> & logFileList)53 void GetOrderedLogFileList(const std::string& namePrefix, std::list<std::string>& logFileList)
54 {
55 std::vector<std::string> allFileList;
56 std::string logFileDir = GetLogFileDir();
57 FileUtil::GetDirFiles(logFileDir, allFileList);
58
59 for (const auto& fileItem : allFileList) {
60 std::string fileName = FileUtil::ExtractFileName(fileItem);
61 if (StringUtil::StartWith(fileName, namePrefix)) {
62 logFileList.emplace_back(fileItem);
63 }
64 }
65
66 // sort file list
67 logFileList.sort([] (const std::string& file, const std::string& nextFile) {
68 return file.size() < nextFile.size() || (file.size() == nextFile.size() && file < nextFile);
69 });
70 }
71
BuildLogFilePath(const std::string & fileNamePrefix,size_t index)72 std::string BuildLogFilePath(const std::string& fileNamePrefix, size_t index)
73 {
74 std::string fileName = fileNamePrefix;
75 fileName.append("_").append(std::to_string(index));
76 std::string filePath = FileUtil::IncludeTrailingPathDelimiter(GetLogFileDir()) + fileName;
77 HIVIEW_LOGD("create new log file with name: %{public}s", fileName.c_str());
78 return filePath;
79 }
80
CloseFileStream(std::ofstream & fileStream)81 void CloseFileStream(std::ofstream& fileStream)
82 {
83 if (fileStream.is_open()) {
84 fileStream.close();
85 }
86 }
87
ParseLogFileIndexFromPath(const std::string & filePath)88 size_t ParseLogFileIndexFromPath(const std::string& filePath)
89 {
90 if (filePath.empty()) {
91 return DEFAULT_FILE_INDEX;
92 }
93 size_t lastUnderlineIndex = filePath.find_last_of("_");
94 if (lastUnderlineIndex == std::string::npos) {
95 HIVIEW_LOGE("name of %{public}s is invalid", FileUtil::ExtractFileName(filePath).c_str());
96 return DEFAULT_FILE_INDEX;
97 }
98 std::string indexStr = filePath.substr(++lastUnderlineIndex);
99 size_t index = DEFAULT_FILE_INDEX;
100 StringUtil::ConvertStringTo(indexStr, index);
101 return index;
102 }
103 }
104
LogFileWriter(const LogStrategy & strategy)105 LogFileWriter::LogFileWriter(const LogStrategy& strategy)
106 {
107 InitByStrategy(strategy);
108 }
109
~LogFileWriter()110 LogFileWriter::~LogFileWriter()
111 {
112 CloseFileStream(logFileStream_);
113 }
114
ResetLogFileStreamByFileIndex(size_t fileIndex)115 void LogFileWriter::ResetLogFileStreamByFileIndex(size_t fileIndex)
116 {
117 curFileIndex_ = fileIndex;
118
119 CloseFileStream(logFileStream_);
120 std::string logFilePath = BuildLogFilePath(logStrategy_.fileNamePrefix, curFileIndex_);
121 logFileStream_.open(logFilePath, std::ios::app);
122 if (logFileStream_.is_open()) {
123 curFileSize_ = FileUtil::GetFileSize(logFilePath);
124 }
125 }
126
InitByStrategy(const LogStrategy & strategy)127 void LogFileWriter::InitByStrategy(const LogStrategy& strategy)
128 {
129 logStrategy_ = strategy;
130
131 std::list<std::string> logFileList;
132 GetOrderedLogFileList(logStrategy_.fileNamePrefix, logFileList);
133 if (logFileList.empty()) {
134 ResetLogFileStreamByFileIndex(DEFAULT_FILE_INDEX);
135 return;
136 }
137
138 ResetLogFileStreamByFileIndex(ParseLogFileIndexFromPath(logFileList.back()));
139 }
140
DeleteOutNumberLogFiles()141 void LogFileWriter::DeleteOutNumberLogFiles()
142 {
143 std::list<std::string> logFileList;
144 GetOrderedLogFileList(logStrategy_.fileNamePrefix, logFileList);
145 while (logFileList.size() > logStrategy_.fileMaxCnt) {
146 auto logFilePath = logFileList.front();
147 if (!FileUtil::RemoveFile(logFilePath)) {
148 HIVIEW_LOGE("failed to delete log file: %{public}s", FileUtil::ExtractFileName(logFilePath).c_str());
149 }
150 logFileList.pop_front();
151 }
152 }
153
Write(const std::string & content)154 void LogFileWriter::Write(const std::string& content)
155 {
156 // append formatted timestamp
157 std::string logContent = TimeUtil::TimestampFormatToDate(TimeUtil::GetSeconds(), "%Y/%m/%d %H:%M:%S");
158 logContent.append(" ").append(content);
159
160 std::lock_guard<std::mutex> lock(writeMutex_);
161 uint64_t logContentSize = logContent.size();
162 if (curFileSize_ + logContentSize > logStrategy_.singleFileMaxSize) {
163 // exceed single file size limit
164 ResetLogFileStreamByFileIndex(curFileIndex_ + 1); // index from n to n + 1
165 DeleteOutNumberLogFiles();
166 }
167
168 if (!logFileStream_.is_open()) {
169 HIVIEW_LOGE("%{public}s isn't opened", BuildLogFilePath(logStrategy_.fileNamePrefix, curFileIndex_).c_str());
170 return;
171 }
172 logFileStream_ << logContent << std::endl;
173 curFileSize_ += logContentSize;
174 }
175 } // namespace HiviewDFX
176 } // namespace OHOS