Home | History | Annotate | Download | only in network
      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 
     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 
     74 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 
     91 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 
    100 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 
    111 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 
    118 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 
    140 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 
    152 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 
    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:
    190   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 
    200   LogEntryList& entries() { return entries_; }
    201 
    202  private:
    203   LogEntryList entries_;
    204 
    205   DISALLOW_COPY_AND_ASSIGN(NetworkEventLog);
    206 };
    207 
    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 
    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 
    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 
    326 void Shutdown() {
    327   delete g_network_event_log;
    328   g_network_event_log = NULL;
    329 }
    330 
    331 bool IsInitialized() { return g_network_event_log != NULL; }
    332 
    333 namespace internal {
    334 
    335 size_t GetMaxLogEntries() { return g_max_network_event_log_entries; }
    336 
    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 
    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 
    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 
    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