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