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