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