Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / chromeos / network / network_event_log.cc
blob3f6d123f9ea4870c6c419768fcfe350df7eac4d3
1 // Copyright (c) 2012 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.
5 #include "chromeos/network/network_event_log.h"
7 #include <cmath>
8 #include <list>
10 #include "base/files/file_path.h"
11 #include "base/json/json_string_value_serializer.h"
12 #include "base/json/json_writer.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/strings/string_tokenizer.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "net/base/escape.h"
21 namespace chromeos {
22 namespace network_event_log {
24 namespace {
26 std::string DateAndTimeWithMicroseconds(const base::Time& time) {
27 base::Time::Exploded exploded;
28 time.LocalExplode(&exploded);
29 // base::Time::Exploded does not include microseconds, but sometimes we need
30 // microseconds, so append '.' + usecs to the end of the formatted string.
31 int usecs = static_cast<int>(fmod(time.ToDoubleT() * 1000000, 1000000));
32 return base::StringPrintf("%04d/%02d/%02d %02d:%02d:%02d.%06d",
33 exploded.year,
34 exploded.month,
35 exploded.day_of_month,
36 exploded.hour,
37 exploded.minute,
38 exploded.second,
39 usecs);
42 std::string TimeWithSeconds(const base::Time& time) {
43 base::Time::Exploded exploded;
44 time.LocalExplode(&exploded);
45 return base::StringPrintf("%02d:%02d:%02d",
46 exploded.hour,
47 exploded.minute,
48 exploded.second);
51 std::string TimeWithMillieconds(const base::Time& time) {
52 base::Time::Exploded exploded;
53 time.LocalExplode(&exploded);
54 return base::StringPrintf("%02d:%02d:%02d.%03d",
55 exploded.hour,
56 exploded.minute,
57 exploded.second,
58 exploded.millisecond);
61 class NetworkEventLog;
62 NetworkEventLog* g_network_event_log = NULL;
63 size_t g_max_network_event_log_entries = 4000;
64 const char* kLogLevelName[] = {"Error", "User", "Event", "Debug"};
66 struct LogEntry {
67 LogEntry(const std::string& file,
68 int file_line,
69 LogLevel log_level,
70 const std::string& event,
71 const std::string& description);
73 std::string ToString(bool show_time,
74 bool show_file,
75 bool show_level,
76 bool show_desc,
77 bool format_html) const;
78 void ToDictionary(base::DictionaryValue*) const;
80 std::string GetNormalText(bool show_desc) const;
81 std::string GetHtmlText(bool show_desc) const;
82 std::string GetAsJSON() const;
84 void SendToVLogOrErrorLog() const;
86 bool ContentEquals(const LogEntry& other) const;
88 std::string file;
89 int file_line;
90 LogLevel log_level;
91 std::string event;
92 std::string description;
93 base::Time time;
94 int count;
97 LogEntry::LogEntry(const std::string& file,
98 int file_line,
99 LogLevel log_level,
100 const std::string& event,
101 const std::string& description)
102 : file(file),
103 file_line(file_line),
104 log_level(log_level),
105 event(event),
106 description(description),
107 time(base::Time::Now()),
108 count(1) {}
110 std::string LogEntry::ToString(bool show_time,
111 bool show_file,
112 bool show_level,
113 bool show_desc,
114 bool format_html) const {
115 std::string line;
116 if (show_time)
117 line += "[" + TimeWithMillieconds(time) + "] ";
118 if (show_level) {
119 const char* kLevelDesc[] = {
120 "ERROR",
121 "USER",
122 "EVENT",
123 "DEBUG"
125 line += base::StringPrintf("%s: ", kLevelDesc[log_level]);
127 if (show_file) {
128 std::string filestr = format_html ? net::EscapeForHTML(file) : file;
129 line += base::StringPrintf("%s:%d ", file.c_str(), file_line);
131 line += format_html ? GetHtmlText(show_desc) : GetNormalText(show_desc);
132 if (count > 1)
133 line += base::StringPrintf(" (%d)", count);
134 return line;
137 void LogEntry::ToDictionary(base::DictionaryValue* output) const {
138 output->SetString("timestamp", DateAndTimeWithMicroseconds(time));
139 output->SetString("timestampshort", TimeWithSeconds(time));
140 output->SetString("level", kLogLevelName[log_level]);
141 output->SetString("file",
142 base::StringPrintf("%s:%d ", file.c_str(), file_line));
143 output->SetString("event", event);
144 output->SetString("description", description);
147 std::string LogEntry::GetAsJSON() const {
148 base::DictionaryValue entry;
149 ToDictionary(&entry);
150 std::string json;
151 JSONStringValueSerializer serializer(&json);
152 if (!serializer.Serialize(entry)) {
153 LOG(ERROR) << "Failed to serialize to JSON";
155 return json;
158 std::string LogEntry::GetNormalText(bool show_desc) const {
159 std::string text = event;
160 if (show_desc && !description.empty())
161 text += ": " + description;
162 return text;
165 std::string LogEntry::GetHtmlText(bool show_desc) const {
166 std::string text;
167 if (log_level == LOG_LEVEL_DEBUG)
168 text += "<i>";
169 else if (log_level == LOG_LEVEL_USER)
170 text += "<b>";
171 else if (log_level == LOG_LEVEL_ERROR)
172 text += "<b><i>";
174 text += event;
175 if (show_desc && !description.empty())
176 text += ": " + net::EscapeForHTML(description);
178 if (log_level == LOG_LEVEL_DEBUG)
179 text += "</i>";
180 else if (log_level == LOG_LEVEL_USER)
181 text += "</b>";
182 else if (log_level == LOG_LEVEL_ERROR)
183 text += "</i></b>";
184 return text;
187 void LogEntry::SendToVLogOrErrorLog() const {
188 if (log_level != LOG_LEVEL_ERROR && !VLOG_IS_ON(1))
189 return;
190 const bool show_time = true;
191 const bool show_file = true;
192 const bool show_level = false;
193 const bool show_desc = true;
194 const bool format_html = false;
195 std::string output =
196 ToString(show_time, show_file, show_level, show_desc, format_html);
197 if (log_level == LOG_LEVEL_ERROR)
198 LOG(ERROR) << output;
199 else
200 VLOG(1) << output;
203 bool LogEntry::ContentEquals(const LogEntry& other) const {
204 return file == other.file &&
205 file_line == other.file_line &&
206 event == other.event &&
207 description == other.description;
210 void GetFormat(const std::string& format_string,
211 bool* show_time,
212 bool* show_file,
213 bool* show_level,
214 bool* show_desc,
215 bool* format_html,
216 bool* format_json) {
217 base::StringTokenizer tokens(format_string, ",");
218 *show_time = false;
219 *show_file = false;
220 *show_level = false;
221 *show_desc = false;
222 *format_html = false;
223 *format_json = false;
224 while (tokens.GetNext()) {
225 std::string tok(tokens.token());
226 if (tok == "time")
227 *show_time = true;
228 if (tok == "file")
229 *show_file = true;
230 if (tok == "level")
231 *show_level = true;
232 if (tok == "desc")
233 *show_desc = true;
234 if (tok == "html")
235 *format_html = true;
236 if (tok == "json")
237 *format_json = true;
241 typedef std::list<LogEntry> LogEntryList;
243 class NetworkEventLog {
244 public:
245 NetworkEventLog() {}
246 ~NetworkEventLog() {}
248 void AddLogEntry(const LogEntry& entry);
250 std::string GetAsString(StringOrder order,
251 const std::string& format,
252 LogLevel max_level,
253 size_t max_events);
255 LogEntryList& entries() { return entries_; }
257 private:
258 LogEntryList entries_;
260 DISALLOW_COPY_AND_ASSIGN(NetworkEventLog);
263 void NetworkEventLog::AddLogEntry(const LogEntry& entry) {
264 if (!entries_.empty()) {
265 LogEntry& last = entries_.back();
266 if (last.ContentEquals(entry)) {
267 // Update count and time for identical events to avoid log spam.
268 ++last.count;
269 last.log_level = std::min(last.log_level, entry.log_level);
270 last.time = base::Time::Now();
271 return;
274 if (entries_.size() >= g_max_network_event_log_entries) {
275 const size_t max_error_entries = g_max_network_event_log_entries / 2;
276 // Remove the first (oldest) non-error entry, or the oldest entry if more
277 // than half the entries are errors.
278 size_t error_count = 0;
279 for (LogEntryList::iterator iter = entries_.begin(); iter != entries_.end();
280 ++iter) {
281 if (iter->log_level != LOG_LEVEL_ERROR) {
282 entries_.erase(iter);
283 break;
285 if (++error_count > max_error_entries) {
286 // Too many error entries, remove the oldest entry.
287 entries_.pop_front();
288 break;
292 entries_.push_back(entry);
293 entry.SendToVLogOrErrorLog();
296 std::string NetworkEventLog::GetAsString(StringOrder order,
297 const std::string& format,
298 LogLevel max_level,
299 size_t max_events) {
300 if (entries_.empty())
301 return "No Log Entries.";
303 bool show_time, show_file, show_level, show_desc, format_html, format_json;
304 GetFormat(format,
305 &show_time,
306 &show_file,
307 &show_level,
308 &show_desc,
309 &format_html,
310 &format_json);
312 std::string result;
313 base::ListValue log_entries;
314 if (order == OLDEST_FIRST) {
315 size_t offset = 0;
316 if (max_events > 0 && max_events < entries_.size()) {
317 // Iterate backwards through the list skipping uninteresting entries to
318 // determine the first entry to include.
319 size_t shown_events = 0;
320 size_t num_entries = 0;
321 for (LogEntryList::const_reverse_iterator riter = entries_.rbegin();
322 riter != entries_.rend();
323 ++riter) {
324 ++num_entries;
325 if (riter->log_level > max_level)
326 continue;
327 if (++shown_events >= max_events)
328 break;
330 offset = entries_.size() - num_entries;
332 for (LogEntryList::const_iterator iter = entries_.begin();
333 iter != entries_.end();
334 ++iter) {
335 if (offset > 0) {
336 --offset;
337 continue;
339 if (iter->log_level > max_level)
340 continue;
341 if (format_json) {
342 log_entries.AppendString((*iter).GetAsJSON());
343 } else {
344 result += (*iter).ToString(
345 show_time, show_file, show_level, show_desc, format_html);
346 result += "\n";
349 } else {
350 size_t nlines = 0;
351 // Iterate backwards through the list to show the most recent entries first.
352 for (LogEntryList::const_reverse_iterator riter = entries_.rbegin();
353 riter != entries_.rend();
354 ++riter) {
355 if (riter->log_level > max_level)
356 continue;
357 if (format_json) {
358 log_entries.AppendString((*riter).GetAsJSON());
359 } else {
360 result += (*riter).ToString(
361 show_time, show_file, show_level, show_desc, format_html);
362 result += "\n";
364 if (max_events > 0 && ++nlines >= max_events)
365 break;
368 if (format_json) {
369 JSONStringValueSerializer serializer(&result);
370 serializer.Serialize(log_entries);
373 return result;
376 } // namespace
378 const LogLevel kDefaultLogLevel = LOG_LEVEL_EVENT;
380 void Initialize() {
381 if (g_network_event_log)
382 delete g_network_event_log; // reset log
383 g_network_event_log = new NetworkEventLog();
386 void Shutdown() {
387 delete g_network_event_log;
388 g_network_event_log = NULL;
391 bool IsInitialized() { return g_network_event_log != NULL; }
393 namespace internal {
395 size_t GetMaxLogEntries() { return g_max_network_event_log_entries; }
397 void SetMaxLogEntries(size_t max_entries) {
398 g_max_network_event_log_entries = max_entries;
399 if (!g_network_event_log)
400 return;
401 while (g_network_event_log->entries().size() > max_entries)
402 g_network_event_log->entries().pop_front();
405 void AddEntry(const char* file,
406 int file_line,
407 LogLevel log_level,
408 const std::string& event,
409 const std::string& description) {
410 std::string filestr;
411 if (file)
412 filestr = base::FilePath(std::string(file)).BaseName().value();
413 LogEntry entry(filestr, file_line, log_level, event, description);
414 if (!g_network_event_log) {
415 entry.SendToVLogOrErrorLog();
416 return;
418 g_network_event_log->AddLogEntry(entry);
421 } // namespace internal
423 std::string GetAsString(StringOrder order,
424 const std::string& format,
425 LogLevel max_level,
426 size_t max_events) {
427 if (!g_network_event_log)
428 return "NetworkEventLog not initialized.";
429 return g_network_event_log->GetAsString(order, format, max_level, max_events);
432 std::string ValueAsString(const base::Value& value) {
433 std::string vstr;
434 base::JSONWriter::WriteWithOptions(
435 &value, base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &vstr);
436 return vstr.empty() ? "''" : vstr;
439 } // namespace network_event_log
440 } // namespace chromeos