network_event_log.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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.
4
5#include "chromeos/network/network_event_log.h"
6
7#include <list>
8
9#include "base/files/file_path.h"
10#include "base/i18n/time_formatting.h"
11#include "base/json/json_writer.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/stringprintf.h"
15#include "base/strings/string_tokenizer.h"
16#include "base/utf_string_conversions.h"
17#include "base/values.h"
18#include "net/base/escape.h"
19
20namespace chromeos {
21namespace network_event_log {
22
23namespace {
24
25class NetworkEventLog;
26NetworkEventLog* g_network_event_log = NULL;
27size_t g_max_network_event_log_entries = 1000;
28
29struct LogEntry {
30  LogEntry(const std::string& file,
31           int file_line,
32           LogLevel log_level,
33           const std::string& event,
34           const std::string& description);
35
36  std::string ToString(bool show_time,
37                       bool show_file,
38                       bool show_desc,
39                       bool format_html) const;
40
41  std::string GetNormalText(bool show_desc) const;
42  std::string GetHtmlText(bool show_desc) const;
43
44  void SendToVLogOrErrorLog() const;
45
46  bool ContentEquals(const LogEntry& other) const;
47
48  std::string file;
49  int file_line;
50  LogLevel log_level;
51  std::string event;
52  std::string description;
53  base::Time time;
54  int count;
55};
56
57LogEntry::LogEntry(const std::string& file,
58                   int file_line,
59                   LogLevel log_level,
60                   const std::string& event,
61                   const std::string& description)
62    : file(file),
63      file_line(file_line),
64      log_level(log_level),
65      event(event),
66      description(description),
67      time(base::Time::Now()),
68      count(1) {
69}
70
71std::string LogEntry::ToString(bool show_time,
72                               bool show_file,
73                               bool show_desc,
74                               bool format_html) const {
75  std::string line;
76  if (show_time)
77    line += "[" + UTF16ToUTF8(base::TimeFormatShortDateAndTime(time)) + "] ";
78  if (show_file) {
79    std::string filestr = format_html ? net::EscapeForHTML(file) : file;
80    line += base::StringPrintf("%s:%d ", filestr.c_str(), file_line);
81  }
82  line += format_html ? GetHtmlText(show_desc) : GetNormalText(show_desc);
83  if (count > 1)
84    line += base::StringPrintf(" (%d)", count);
85  line += "\n";
86  return line;
87}
88
89std::string LogEntry::GetNormalText(bool show_desc) const {
90  std::string text = event;
91  if (show_desc && !description.empty())
92    text += ": " + description;
93  return text;
94}
95
96std::string LogEntry::GetHtmlText(bool show_desc) const {
97  std::string text;
98  if (log_level == LOG_LEVEL_DEBUG)
99    text += "<i>";
100  else if (log_level == LOG_LEVEL_ERROR)
101    text += "<b>";
102
103  text += event;
104  if (show_desc && !description.empty())
105    text += ": " + net::EscapeForHTML(description);
106
107  if (log_level == LOG_LEVEL_DEBUG)
108    text += "</i>";
109  else if (log_level == LOG_LEVEL_ERROR)
110    text += "</b>";
111  return text;
112}
113
114void LogEntry::SendToVLogOrErrorLog() const {
115  const bool show_time = true;
116  const bool show_file = true;
117  const bool show_desc = true;
118  const bool format_html = false;
119  std::string output = ToString(show_time, show_file, show_desc, format_html);
120  if (log_level == LOG_LEVEL_ERROR)
121    LOG(ERROR) << output;
122  else
123    VLOG(1) << output;
124}
125
126bool LogEntry::ContentEquals(const LogEntry& other) const {
127  return file == other.file &&
128      file_line == other.file_line &&
129      event == other.event &&
130      description == other.description;
131}
132
133void GetFormat(const std::string& format_string,
134               bool* show_time,
135               bool* show_file,
136               bool* show_desc,
137               bool* format_html) {
138  base::StringTokenizer tokens(format_string, ",");
139  *show_time = false;
140  *show_file = false;
141  *show_desc = false;
142  *format_html = false;
143  while (tokens.GetNext()) {
144    std::string tok(tokens.token());
145    if (tok == "time")
146      *show_time = true;
147    if (tok == "file")
148      *show_file = true;
149    if (tok == "desc")
150      *show_desc = true;
151    if (tok == "html")
152      *format_html = true;
153  }
154}
155
156typedef std::list<LogEntry> LogEntryList;
157
158class NetworkEventLog {
159 public:
160  NetworkEventLog() {}
161  ~NetworkEventLog() {}
162
163  void AddLogEntry(const LogEntry& entry);
164
165  std::string GetAsString(StringOrder order,
166                          const std::string& format,
167                          LogLevel max_level,
168                          size_t max_events);
169
170  LogEntryList& entries() { return entries_; }
171
172 private:
173  LogEntryList entries_;
174
175  DISALLOW_COPY_AND_ASSIGN(NetworkEventLog);
176};
177
178void NetworkEventLog::AddLogEntry(const LogEntry& entry) {
179  if (!entries_.empty()) {
180    LogEntry& last = entries_.back();
181    if (last.ContentEquals(entry)) {
182      // Update count and time for identical events to avoid log spam.
183      ++last.count;
184      last.log_level = std::min(last.log_level, entry.log_level);
185      last.time = base::Time::Now();
186      return;
187    }
188  }
189  if (entries_.size() >= g_max_network_event_log_entries) {
190    const size_t max_error_entries = g_max_network_event_log_entries / 2;
191    // Remove the first (oldest) non-error entry, or the oldest entry if more
192    // than half the entries are errors.
193    size_t error_count = 0;
194    for (LogEntryList::iterator iter = entries_.begin();
195         iter != entries_.end(); ++iter) {
196      if (iter->log_level != LOG_LEVEL_ERROR) {
197        entries_.erase(iter);
198        break;
199      }
200      if (++error_count > max_error_entries) {
201        // Too many error entries, remove the oldest entry.
202        entries_.pop_front();
203        break;
204      }
205    }
206  }
207  entries_.push_back(entry);
208  entry.SendToVLogOrErrorLog();
209}
210
211std::string NetworkEventLog::GetAsString(StringOrder order,
212                                         const std::string& format,
213                                         LogLevel max_level,
214                                         size_t max_events) {
215  if (entries_.empty())
216    return "No Log Entries.";
217
218  bool show_time, show_file, show_desc, format_html;
219  GetFormat(format, &show_time, &show_file, &show_desc, &format_html);
220
221  std::string result;
222  if (order == OLDEST_FIRST) {
223    size_t offset = 0;
224    if (max_events > 0 && max_events < entries_.size()) {
225      // Iterate backwards through the list skipping uninteresting entries to
226      // determine the first entry to include.
227      size_t shown_events = 0;
228      size_t num_entries = 0;
229      for (LogEntryList::const_reverse_iterator riter = entries_.rbegin();
230           riter != entries_.rend(); ++riter) {
231        ++num_entries;
232        if (riter->log_level > max_level)
233          continue;
234        if (++shown_events >= max_events)
235          break;
236      }
237      offset = entries_.size() - num_entries;
238    }
239    for (LogEntryList::const_iterator iter = entries_.begin();
240         iter != entries_.end(); ++iter) {
241      if (offset > 0) {
242        --offset;
243        continue;
244      }
245      if (iter->log_level > max_level)
246        continue;
247      result += (*iter).ToString(show_time, show_file, show_desc, format_html);
248    }
249  } else {
250    size_t nlines = 0;
251    // Iterate backwards through the list to show the most recent entries first.
252    for (LogEntryList::const_reverse_iterator riter = entries_.rbegin();
253         riter != entries_.rend(); ++riter) {
254      if (riter->log_level > max_level)
255        continue;
256      result += (*riter).ToString(show_time, show_file, show_desc, format_html);
257      if (max_events > 0 && ++nlines >= max_events)
258        break;
259    }
260  }
261  return result;
262}
263
264}  // namespace
265
266const LogLevel kDefaultLogLevel = LOG_LEVEL_EVENT;
267
268void Initialize() {
269  if (g_network_event_log)
270    delete g_network_event_log;  // reset log
271  g_network_event_log = new NetworkEventLog();
272}
273
274void Shutdown() {
275  delete g_network_event_log;
276  g_network_event_log = NULL;
277}
278
279bool IsInitialized() {
280  return g_network_event_log != NULL;
281}
282
283namespace internal {
284
285size_t GetMaxLogEntries() {
286  return g_max_network_event_log_entries;
287}
288
289void SetMaxLogEntries(size_t max_entries) {
290  g_max_network_event_log_entries = max_entries;
291  if (!g_network_event_log)
292    return;
293  while (g_network_event_log->entries().size() > max_entries)
294    g_network_event_log->entries().pop_front();
295}
296
297void AddEntry(const char* file,
298              int file_line,
299              LogLevel log_level,
300              const std::string& event,
301              const std::string& description) {
302  std::string filestr;
303  if (file)
304    filestr = base::FilePath(std::string(file)).BaseName().value();
305  LogEntry entry(filestr, file_line, log_level, event, description);
306  if (!g_network_event_log) {
307    entry.SendToVLogOrErrorLog();
308    return;
309  }
310  g_network_event_log->AddLogEntry(entry);
311}
312
313}  // namespace internal
314
315std::string GetAsString(StringOrder order,
316                        const std::string& format,
317                        LogLevel max_level,
318                        size_t max_events) {
319  if (!g_network_event_log)
320    return "NetworkEventLog not initialized.";
321  return g_network_event_log->GetAsString(order, format, max_level, max_events);
322}
323
324std::string ValueAsString(const base::Value& value) {
325  std::string vstr;
326  base::JSONWriter::WriteWithOptions(
327      &value, base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &vstr);
328  return vstr.empty() ? "''" : vstr;
329}
330
331}  // namespace network_event_log
332}  // namespace chromeos
333