Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(99)

Side by Side Diff: chrome/browser/chromeos/system_logs/single_log_source.cc

Issue 2956513004: Rename SingleLogSource to SingleLogFileLogSource (Closed)
Patch Set: Rebased Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/system_logs/single_log_source.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/process/process_info.h"
11 #include "base/strings/string_split.h"
12 #include "base/task_scheduler/post_task.h"
13 #include "base/time/time.h"
14 #include "content/public/browser/browser_thread.h"
15
16 namespace system_logs {
17
18 namespace {
19
20 constexpr char kDefaultSystemLogDirPath[] = "/var/log";
21 constexpr int kMaxNumAllowedLogRotationsDuringFileRead = 3;
22
23 // For log files that contain old logging, start reading from the first
24 // timestamp that is less than this amount of time before the current session of
25 // Chrome started.
26 constexpr base::TimeDelta kLogCutoffTimeBeforeChromeStart =
27 base::TimeDelta::FromMinutes(10);
28
29 // A custom timestamp for when the current Chrome session started. Used during
30 // testing to override the actual time.
31 const base::Time* g_chrome_start_time_for_test = nullptr;
32
33 // Converts a logs source type to the corresponding file path, relative to the
34 // base system log directory path. In the future, if non-file source types are
35 // added, this function should return an empty file path.
36 base::FilePath GetLogFileSourceRelativeFilePath(
37 SingleLogSource::SupportedSource source) {
38 switch (source) {
39 case SingleLogSource::SupportedSource::kMessages:
40 return base::FilePath("messages");
41 case SingleLogSource::SupportedSource::kUiLatest:
42 return base::FilePath("ui/ui.LATEST");
43 case SingleLogSource::SupportedSource::kAtrusLog:
44 return base::FilePath("atrus.log");
45 }
46 NOTREACHED();
47 return base::FilePath();
48 }
49
50 // Returns the inode value of file at |path|, or 0 if it doesn't exist or is
51 // otherwise unable to be accessed for file system info.
52 ino_t GetInodeValue(const base::FilePath& path) {
53 struct stat file_stats;
54 if (stat(path.value().c_str(), &file_stats) != 0)
55 return 0;
56 return file_stats.st_ino;
57 }
58
59 // Attempts to store a string |value| in |*response| under |key|. If there is
60 // already a string in |*response| under |key|, appends |value| to the existing
61 // string value.
62 void AppendToSystemLogsResponse(SystemLogsResponse* response,
63 const std::string& key,
64 const std::string& value) {
65 auto iter = response->find(key);
66 if (iter == response->end())
67 response->emplace(key, value);
68 else
69 iter->second += value;
70 }
71
72 // Returns the time that the current Chrome process started. Will instead return
73 // |*g_chrome_start_time_for_test| if it is set.
74 base::Time GetChromeStartTime() {
75 if (g_chrome_start_time_for_test)
76 return *g_chrome_start_time_for_test;
77 return base::CurrentProcessInfo::CreationTime();
78 }
79
80 // Returns the file offset into |path| of the first line that starts with a
81 // timestamp no earlier than |time|. Returns 0 if no such offset could be
82 // determined (e.g. can't open file, no timestamps present).
83 size_t GetFirstFileOffsetWithTime(const base::FilePath& path,
84 const base::Time& time) {
85 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
86 if (!file.IsValid())
87 return 0;
88
89 const size_t file_size = file.GetLength();
90 if (file_size == 0)
91 return 0;
92
93 std::string file_contents;
94 file_contents.resize(file_size);
95 size_t size_read = file.ReadAtCurrentPos(&file_contents[0], file_size);
96
97 if (size_read < file_size)
98 return 0;
99
100 std::vector<base::StringPiece> lines = base::SplitStringPiece(
101 file_contents, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
102
103 bool any_timestamp_found = false;
104
105 // Find the first line with timestamp >= |time|. If a line has no timestamp,
106 // just advance to the next line.
107 size_t offset = 0;
108 base::Time timestamp;
109 for (const auto& line : lines) {
110 if (base::Time::FromString(line.as_string().c_str(), &timestamp)) {
111 any_timestamp_found = true;
112
113 if (timestamp >= time)
114 break;
115 }
116
117 // Include the newline in the offset.
118 offset += line.length() + 1;
119 }
120
121 // If the file does not have any timestamps at all, don't skip any contents.
122 if (!any_timestamp_found)
123 return 0;
124
125 if (offset > 0 && offset >= file_size && lines.back().as_string().empty()) {
126 // The last line may or may not have ended with a newline. If it ended with
127 // a newline, |lines| would end with an extra empty line after the newline.
128 // This would have resulted in an extra nonexistent newline being counted
129 // during the computation of |offset|.
130 --offset;
131 }
132 return offset;
133 }
134
135 } // namespace
136
137 SingleLogSource::SingleLogSource(SupportedSource source_type)
138 : SystemLogsSource(GetLogFileSourceRelativeFilePath(source_type).value()),
139 source_type_(source_type),
140 log_file_dir_path_(kDefaultSystemLogDirPath),
141 num_bytes_read_(0),
142 file_inode_(0),
143 weak_ptr_factory_(this) {}
144
145 SingleLogSource::~SingleLogSource() {}
146
147 // static
148 void SingleLogSource::SetChromeStartTimeForTesting(
149 const base::Time* start_time) {
150 g_chrome_start_time_for_test = start_time;
151 }
152
153 void SingleLogSource::Fetch(const SysLogsSourceCallback& callback) {
154 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
155 DCHECK(!callback.is_null());
156
157 SystemLogsResponse* response = new SystemLogsResponse;
158 base::PostTaskWithTraitsAndReply(
159 FROM_HERE,
160 base::TaskTraits(base::MayBlock(), base::TaskPriority::BACKGROUND),
161 base::Bind(&SingleLogSource::ReadFile, weak_ptr_factory_.GetWeakPtr(),
162 kMaxNumAllowedLogRotationsDuringFileRead, response),
163 base::Bind(callback, base::Owned(response)));
164 }
165
166 base::FilePath SingleLogSource::GetLogFilePath() const {
167 return base::FilePath(log_file_dir_path_).Append(source_name());
168 }
169
170 void SingleLogSource::ReadFile(size_t num_rotations_allowed,
171 SystemLogsResponse* result) {
172 // Attempt to open the file if it was not previously opened.
173 if (!file_.IsValid()) {
174 file_.Initialize(GetLogFilePath(),
175 base::File::FLAG_OPEN | base::File::FLAG_READ);
176 if (!file_.IsValid())
177 return;
178
179 // Determine actual offset from which to start reading.
180 if (source_type_ == SupportedSource::kMessages) {
181 const base::Time earliest_log_time =
182 GetChromeStartTime() - kLogCutoffTimeBeforeChromeStart;
183
184 num_bytes_read_ =
185 GetFirstFileOffsetWithTime(GetLogFilePath(), earliest_log_time);
186 } else {
187 num_bytes_read_ = 0;
188 }
189 file_.Seek(base::File::FROM_BEGIN, num_bytes_read_);
190
191 file_inode_ = GetInodeValue(GetLogFilePath());
192 }
193
194 // Check for file size reset.
195 const size_t length = file_.GetLength();
196 if (length < num_bytes_read_) {
197 num_bytes_read_ = 0;
198 file_.Seek(base::File::FROM_BEGIN, 0);
199 }
200
201 // Read from file until end.
202 const size_t size_to_read = length - num_bytes_read_;
203 std::string result_string;
204 result_string.resize(size_to_read);
205 size_t size_read = file_.ReadAtCurrentPos(&result_string[0], size_to_read);
206 result_string.resize(size_read);
207
208 const bool file_was_rotated = file_inode_ != GetInodeValue(GetLogFilePath());
209 const bool should_handle_file_rotation =
210 file_was_rotated && num_rotations_allowed > 0;
211
212 // The reader may only read complete lines. The exception is when there is a
213 // rotation, in which case all the remaining contents of the old log file
214 // should be read before moving on to read the new log file.
215 if ((result_string.empty() || result_string.back() != '\n') &&
216 !should_handle_file_rotation) {
217 // If an incomplete line was read, return only the part that includes whole
218 // lines.
219 size_t last_newline_pos = result_string.find_last_of('\n');
220 if (last_newline_pos == std::string::npos) {
221 file_.Seek(base::File::FROM_CURRENT, -size_read);
222 AppendToSystemLogsResponse(result, source_name(), "");
223 return;
224 }
225 // The part of the string that will be returned includes the newline itself.
226 size_t adjusted_size_read = last_newline_pos + 1;
227 file_.Seek(base::File::FROM_CURRENT, -size_read + adjusted_size_read);
228 result_string.resize(adjusted_size_read);
229
230 // Update |size_read| to reflect that the read was only up to the last
231 // newline.
232 size_read = adjusted_size_read;
233 }
234
235 num_bytes_read_ += size_read;
236
237 // Pass it back to the callback.
238 AppendToSystemLogsResponse(result, source_name(),
239 anonymizer_.Anonymize(result_string));
240
241 // If the file was rotated, close the file handle and call this function
242 // again, to read from the new file.
243 if (should_handle_file_rotation) {
244 file_.Close();
245 num_bytes_read_ = 0;
246 file_inode_ = 0;
247 ReadFile(num_rotations_allowed - 1, result);
248 }
249 }
250
251 } // namespace system_logs
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698