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