OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromeos/network/network_event_log.h" | 5 #include "chromeos/network/network_event_log.h" |
6 | 6 |
| 7 #include "base/files/file_path.h" |
7 #include "base/i18n/time_formatting.h" | 8 #include "base/i18n/time_formatting.h" |
| 9 #include "base/json/json_writer.h" |
8 #include "base/logging.h" | 10 #include "base/logging.h" |
9 #include "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
10 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
| 13 #include "base/strings/string_tokenizer.h" |
11 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
| 15 #include "base/values.h" |
| 16 #include "net/base/escape.h" |
12 | 17 |
13 namespace chromeos { | 18 namespace chromeos { |
14 | 19 |
15 namespace network_event_log { | 20 namespace network_event_log { |
16 | 21 |
17 namespace { | 22 namespace { |
18 | 23 |
19 struct LogEntry { | 24 struct LogEntry { |
20 LogEntry(const std::string& module, | 25 LogEntry(const std::string& file, |
| 26 int file_line, |
| 27 LogLevel log_level, |
21 const std::string& event, | 28 const std::string& event, |
22 const std::string& description); | 29 const std::string& description); |
23 | 30 |
24 std::string ToString() const; | 31 std::string ToString(bool show_time, |
| 32 bool show_file, |
| 33 bool show_desc, |
| 34 bool format_html) const; |
| 35 |
| 36 std::string GetNormalText(bool show_desc) const; |
| 37 std::string GetHtmlText(bool show_desc) const; |
| 38 |
| 39 void SendToVLogOrErrorLog() const; |
25 | 40 |
26 bool ContentEquals(const LogEntry& other) const; | 41 bool ContentEquals(const LogEntry& other) const; |
27 | 42 |
28 std::string module; | 43 std::string file; |
| 44 int file_line; |
| 45 LogLevel log_level; |
29 std::string event; | 46 std::string event; |
30 std::string description; | 47 std::string description; |
31 base::Time time; | 48 base::Time time; |
32 int count; | 49 int count; |
33 }; | 50 }; |
34 | 51 |
35 LogEntry::LogEntry(const std::string& module, | 52 LogEntry::LogEntry(const std::string& file, |
| 53 int file_line, |
| 54 LogLevel log_level, |
36 const std::string& event, | 55 const std::string& event, |
37 const std::string& description) | 56 const std::string& description) |
38 : module(module), | 57 : file(file), |
| 58 file_line(file_line), |
| 59 log_level(log_level), |
39 event(event), | 60 event(event), |
40 description(description), | 61 description(description), |
41 time(base::Time::Now()), | 62 time(base::Time::Now()), |
42 count(1) { | 63 count(1) { |
43 } | 64 } |
44 | 65 |
45 std::string LogEntry::ToString() const { | 66 std::string LogEntry::ToString(bool show_time, |
| 67 bool show_file, |
| 68 bool show_desc, |
| 69 bool format_html) const { |
46 std::string line; | 70 std::string line; |
47 line += "[" + UTF16ToUTF8(base::TimeFormatShortDateAndTime(time)) + "]"; | 71 if (show_time) |
48 line += " " + module + ":" + event; | 72 line += "[" + UTF16ToUTF8(base::TimeFormatShortDateAndTime(time)) + "] "; |
49 if (!description.empty()) | 73 if (show_file) { |
50 line += ": " + description; | 74 std::string filestr = format_html ? net::EscapeForHTML(file) : file; |
| 75 line += base::StringPrintf("%s:%d ", filestr.c_str(), file_line); |
| 76 } |
| 77 line += format_html ? GetHtmlText(show_desc) : GetNormalText(show_desc); |
51 if (count > 1) | 78 if (count > 1) |
52 line += base::StringPrintf(" (%d)", count); | 79 line += base::StringPrintf(" (%d)", count); |
53 line += "\n"; | 80 line += "\n"; |
54 return line; | 81 return line; |
55 } | 82 } |
56 | 83 |
| 84 std::string LogEntry::GetNormalText(bool show_desc) const { |
| 85 std::string text = event; |
| 86 if (show_desc && !description.empty()) |
| 87 text += ": " + description; |
| 88 return text; |
| 89 } |
| 90 |
| 91 std::string LogEntry::GetHtmlText(bool show_desc) const { |
| 92 std::string text; |
| 93 if (log_level == LOG_LEVEL_DEBUG) |
| 94 text += "<i>"; |
| 95 else if (log_level == LOG_LEVEL_ERROR) |
| 96 text += "<b>"; |
| 97 |
| 98 text += event; |
| 99 if (show_desc && !description.empty()) |
| 100 text += ": " + net::EscapeForHTML(description); |
| 101 |
| 102 if (log_level == LOG_LEVEL_DEBUG) |
| 103 text += "</i>"; |
| 104 else if (log_level == LOG_LEVEL_ERROR) |
| 105 text += "</b>"; |
| 106 return text; |
| 107 } |
| 108 |
| 109 void LogEntry::SendToVLogOrErrorLog() const { |
| 110 const bool show_time = true; |
| 111 const bool show_file = true; |
| 112 const bool show_desc = true; |
| 113 const bool format_html = false; |
| 114 std::string output = ToString(show_time, show_file, show_desc, format_html); |
| 115 if (log_level == LOG_LEVEL_ERROR) |
| 116 LOG(ERROR) << output; |
| 117 else |
| 118 VLOG(1) << output; |
| 119 } |
| 120 |
57 bool LogEntry::ContentEquals(const LogEntry& other) const { | 121 bool LogEntry::ContentEquals(const LogEntry& other) const { |
58 return module == other.module && | 122 return file == other.file && |
| 123 file_line == other.file_line && |
59 event == other.event && | 124 event == other.event && |
60 description == other.description; | 125 description == other.description; |
61 } | 126 } |
62 | 127 |
| 128 void GetFormat(const std::string& format_string, |
| 129 bool* show_time, |
| 130 bool* show_file, |
| 131 bool* show_desc, |
| 132 bool* format_html) { |
| 133 base::StringTokenizer tokens(format_string, ","); |
| 134 *show_time = false; |
| 135 *show_file = false; |
| 136 *show_desc = false; |
| 137 *format_html = false; |
| 138 while (tokens.GetNext()) { |
| 139 std::string tok(tokens.token()); |
| 140 if (tok == "time") |
| 141 *show_time = true; |
| 142 if (tok == "file") |
| 143 *show_file = true; |
| 144 if (tok == "desc") |
| 145 *show_desc = true; |
| 146 if (tok == "html") |
| 147 *format_html = true; |
| 148 } |
| 149 } |
| 150 |
63 typedef std::deque<LogEntry> LogEntryList; | 151 typedef std::deque<LogEntry> LogEntryList; |
64 | 152 |
65 class NetworkEventLog { | 153 class NetworkEventLog { |
66 public: | 154 public: |
67 NetworkEventLog() {} | 155 NetworkEventLog() {} |
68 ~NetworkEventLog() {} | 156 ~NetworkEventLog() {} |
69 | 157 |
70 void AddEntry(const LogEntry& entry); | 158 void AddLogEntry(const LogEntry& entry); |
71 | 159 |
72 std::string GetAsString(StringOrder order, | 160 std::string GetAsString(StringOrder order, |
| 161 const std::string& format, |
| 162 LogLevel max_level, |
73 size_t max_events); | 163 size_t max_events); |
74 | 164 |
75 private: | 165 private: |
76 LogEntryList entries_; | 166 LogEntryList entries_; |
77 | 167 |
78 DISALLOW_COPY_AND_ASSIGN(NetworkEventLog); | 168 DISALLOW_COPY_AND_ASSIGN(NetworkEventLog); |
79 }; | 169 }; |
80 | 170 |
81 void NetworkEventLog::AddEntry(const LogEntry& entry) { | 171 void NetworkEventLog::AddLogEntry(const LogEntry& entry) { |
82 if (!entries_.empty()) { | 172 if (!entries_.empty()) { |
83 LogEntry& last = entries_.back(); | 173 LogEntry& last = entries_.back(); |
84 if (last.ContentEquals(entry)) { | 174 if (last.ContentEquals(entry)) { |
85 // Update count and time for identical events to avoid log spam. | 175 // Update count and time for identical events to avoid log spam. |
86 ++last.count; | 176 ++last.count; |
| 177 last.log_level = std::min(last.log_level, entry.log_level); |
87 last.time = base::Time::Now(); | 178 last.time = base::Time::Now(); |
88 return; | 179 return; |
89 } | 180 } |
90 } | 181 } |
91 if (entries_.size() >= kMaxNetworkEventLogEntries) | 182 if (entries_.size() >= kMaxNetworkEventLogEntries) |
92 entries_.pop_front(); | 183 entries_.pop_front(); |
93 entries_.push_back(entry); | 184 entries_.push_back(entry); |
94 VLOG(1) << entry.ToString(); | 185 entry.SendToVLogOrErrorLog(); |
95 } | 186 } |
96 | 187 |
97 std::string NetworkEventLog::GetAsString(StringOrder order, | 188 std::string NetworkEventLog::GetAsString(StringOrder order, |
| 189 const std::string& format, |
| 190 LogLevel max_level, |
98 size_t max_events) { | 191 size_t max_events) { |
99 if (entries_.empty()) | 192 if (entries_.empty()) |
100 return "No Log Entries."; | 193 return "No Log Entries."; |
101 | 194 |
| 195 bool show_time, show_file, show_desc, format_html; |
| 196 GetFormat(format, &show_time, &show_file, &show_desc, &format_html); |
| 197 |
102 std::string result; | 198 std::string result; |
103 if (order == OLDEST_FIRST) { | 199 if (order == OLDEST_FIRST) { |
104 size_t offset = 0; | 200 size_t offset = 0; |
105 if (max_events > 0 && max_events < entries_.size()) | 201 if (max_events > 0 && max_events < entries_.size()) { |
106 offset = entries_.size() - max_events; | 202 // Iterate backwards through the list skipping uninteresting entries to |
| 203 // determine the first entry to include. |
| 204 size_t shown_events = 0; |
| 205 LogEntryList::const_reverse_iterator riter; |
| 206 for (riter = entries_.rbegin(); riter != entries_.rend(); ++riter) { |
| 207 if (riter->log_level > max_level) |
| 208 continue; |
| 209 if (++shown_events >= max_events) |
| 210 break; |
| 211 } |
| 212 if (riter != entries_.rend()) |
| 213 offset = entries_.rend() - riter - 1; |
| 214 } |
107 for (LogEntryList::const_iterator iter = entries_.begin() + offset; | 215 for (LogEntryList::const_iterator iter = entries_.begin() + offset; |
108 iter != entries_.end(); ++iter) { | 216 iter != entries_.end(); ++iter) { |
109 result += (*iter).ToString(); | 217 if (iter->log_level > max_level) |
| 218 continue; |
| 219 result += (*iter).ToString(show_time, show_file, show_desc, format_html); |
110 } | 220 } |
111 } else { | 221 } else { |
112 size_t nlines = 0; | 222 size_t nlines = 0; |
113 // Iterate backwards through the list to show the most recent entries first. | 223 // Iterate backwards through the list to show the most recent entries first. |
114 for (LogEntryList::const_reverse_iterator riter = entries_.rbegin(); | 224 for (LogEntryList::const_reverse_iterator riter = entries_.rbegin(); |
115 riter != entries_.rend(); ++riter) { | 225 riter != entries_.rend(); ++riter) { |
116 result += (*riter).ToString(); | 226 if (riter->log_level > max_level) |
| 227 continue; |
| 228 result += (*riter).ToString(show_time, show_file, show_desc, format_html); |
117 if (max_events > 0 && ++nlines >= max_events) | 229 if (max_events > 0 && ++nlines >= max_events) |
118 break; | 230 break; |
119 } | 231 } |
120 } | 232 } |
121 return result; | 233 return result; |
122 } | 234 } |
123 | 235 |
124 } // namespace | 236 } // namespace |
125 | 237 |
126 NetworkEventLog* g_network_event_log = NULL; | 238 NetworkEventLog* g_network_event_log = NULL; |
| 239 const LogLevel kDefaultLogLevel = LOG_LEVEL_EVENT; |
127 const size_t kMaxNetworkEventLogEntries = 1000; | 240 const size_t kMaxNetworkEventLogEntries = 1000; |
128 | 241 |
129 void Initialize() { | 242 void Initialize() { |
130 if (g_network_event_log) | 243 if (g_network_event_log) |
131 delete g_network_event_log; // reset log | 244 delete g_network_event_log; // reset log |
132 g_network_event_log = new NetworkEventLog(); | 245 g_network_event_log = new NetworkEventLog(); |
133 } | 246 } |
134 | 247 |
135 void Shutdown() { | 248 void Shutdown() { |
136 delete g_network_event_log; | 249 delete g_network_event_log; |
137 g_network_event_log = NULL; | 250 g_network_event_log = NULL; |
138 } | 251 } |
139 | 252 |
140 bool IsInitialized() { | 253 bool IsInitialized() { |
141 return g_network_event_log != NULL; | 254 return g_network_event_log != NULL; |
142 } | 255 } |
143 | 256 |
144 void AddEntry(const std::string& module, | 257 namespace internal { |
| 258 |
| 259 void AddEntry(const char* file, |
| 260 int file_line, |
| 261 LogLevel log_level, |
145 const std::string& event, | 262 const std::string& event, |
146 const std::string& description) { | 263 const std::string& description) { |
147 LogEntry entry(module, event, description); | 264 std::string filestr; |
| 265 if (file) |
| 266 filestr = base::FilePath(std::string(file)).BaseName().value(); |
| 267 LogEntry entry(filestr, file_line, log_level, event, description); |
148 if (!g_network_event_log) { | 268 if (!g_network_event_log) { |
149 VLOG(1) << entry.ToString(); | 269 entry.SendToVLogOrErrorLog(); |
150 return; | 270 return; |
151 } | 271 } |
152 g_network_event_log->AddEntry(entry); | 272 g_network_event_log->AddLogEntry(entry); |
153 } | 273 } |
154 | 274 |
155 std::string GetAsString(StringOrder order, size_t max_events) { | 275 } // namespace internal |
| 276 |
| 277 std::string GetAsString(StringOrder order, |
| 278 const std::string& format, |
| 279 LogLevel max_level, |
| 280 size_t max_events) { |
156 if (!g_network_event_log) | 281 if (!g_network_event_log) |
157 return "NetworkEventLog not intitialized."; | 282 return "NetworkEventLog not initialized."; |
158 return g_network_event_log->GetAsString(order, max_events); | 283 return g_network_event_log->GetAsString(order, format, max_level, max_events); |
| 284 } |
| 285 |
| 286 std::string ValueAsString(const base::Value& value) { |
| 287 std::string vstr; |
| 288 base::JSONWriter::WriteWithOptions( |
| 289 &value, base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &vstr); |
| 290 return vstr.empty() ? "''" : vstr; |
159 } | 291 } |
160 | 292 |
161 } // namespace network_event_log | 293 } // namespace network_event_log |
162 | 294 |
163 } // namespace chromeos | 295 } // namespace chromeos |
OLD | NEW |