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.
5 #include "chromeos/network/network_event_log.h"
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"
22 namespace network_event_log
{
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",
35 exploded
.day_of_month
,
42 std::string
TimeWithSeconds(const base::Time
& time
) {
43 base::Time::Exploded exploded
;
44 time
.LocalExplode(&exploded
);
45 return base::StringPrintf("%02d:%02d:%02d",
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",
58 exploded
.millisecond
);
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"};
67 LogEntry(const std::string
& file
,
70 const std::string
& event
,
71 const std::string
& description
);
73 std::string
ToString(bool show_time
,
77 bool format_html
) const;
78 void ToDictionary(base::DictionaryValue
*) const;
80 std::string
GetNormalText(bool show_desc
) const;
81 std::string
GetHtmlText(bool show_desc
) const;
82 std::string
GetAsJSON() const;
84 void SendToVLogOrErrorLog() const;
86 bool ContentEquals(const LogEntry
& other
) const;
92 std::string description
;
97 LogEntry::LogEntry(const std::string
& file
,
100 const std::string
& event
,
101 const std::string
& description
)
103 file_line(file_line
),
104 log_level(log_level
),
106 description(description
),
107 time(base::Time::Now()),
110 std::string
LogEntry::ToString(bool show_time
,
114 bool format_html
) const {
117 line
+= "[" + TimeWithMillieconds(time
) + "] ";
119 const char* kLevelDesc
[] = {
125 line
+= base::StringPrintf("%s: ", kLevelDesc
[log_level
]);
128 std::string filestr
= format_html
? net::EscapeForHTML(file
) : file
;
129 line
+= base::StringPrintf("%s:%d ", file
.c_str(), file_line
);
131 line
+= format_html
? GetHtmlText(show_desc
) : GetNormalText(show_desc
);
133 line
+= base::StringPrintf(" (%d)", count
);
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
);
147 std::string
LogEntry::GetAsJSON() const {
148 base::DictionaryValue entry
;
149 ToDictionary(&entry
);
151 JSONStringValueSerializer
serializer(&json
);
152 if (!serializer
.Serialize(entry
)) {
153 LOG(ERROR
) << "Failed to serialize to JSON";
158 std::string
LogEntry::GetNormalText(bool show_desc
) const {
159 std::string text
= event
;
160 if (show_desc
&& !description
.empty())
161 text
+= ": " + description
;
165 std::string
LogEntry::GetHtmlText(bool show_desc
) const {
167 if (log_level
== LOG_LEVEL_DEBUG
)
169 else if (log_level
== LOG_LEVEL_USER
)
171 else if (log_level
== LOG_LEVEL_ERROR
)
175 if (show_desc
&& !description
.empty())
176 text
+= ": " + net::EscapeForHTML(description
);
178 if (log_level
== LOG_LEVEL_DEBUG
)
180 else if (log_level
== LOG_LEVEL_USER
)
182 else if (log_level
== LOG_LEVEL_ERROR
)
187 void LogEntry::SendToVLogOrErrorLog() const {
188 if (log_level
!= LOG_LEVEL_ERROR
&& !VLOG_IS_ON(1))
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;
196 ToString(show_time
, show_file
, show_level
, show_desc
, format_html
);
197 if (log_level
== LOG_LEVEL_ERROR
)
198 LOG(ERROR
) << output
;
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
;
210 void GetFormat(const std::string
& format_string
,
217 base::StringTokenizer
tokens(format_string
, ",");
222 *format_html
= false;
223 *format_json
= false;
224 while (tokens
.GetNext()) {
225 std::string
tok(tokens
.token());
241 typedef std::list
<LogEntry
> LogEntryList
;
243 class NetworkEventLog
{
246 ~NetworkEventLog() {}
248 void AddLogEntry(const LogEntry
& entry
);
250 std::string
GetAsString(StringOrder order
,
251 const std::string
& format
,
255 LogEntryList
& entries() { return entries_
; }
258 LogEntryList entries_
;
260 DISALLOW_COPY_AND_ASSIGN(NetworkEventLog
);
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.
269 last
.log_level
= std::min(last
.log_level
, entry
.log_level
);
270 last
.time
= base::Time::Now();
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();
281 if (iter
->log_level
!= LOG_LEVEL_ERROR
) {
282 entries_
.erase(iter
);
285 if (++error_count
> max_error_entries
) {
286 // Too many error entries, remove the oldest entry.
287 entries_
.pop_front();
292 entries_
.push_back(entry
);
293 entry
.SendToVLogOrErrorLog();
296 std::string
NetworkEventLog::GetAsString(StringOrder order
,
297 const std::string
& format
,
300 if (entries_
.empty())
301 return "No Log Entries.";
303 bool show_time
, show_file
, show_level
, show_desc
, format_html
, format_json
;
313 base::ListValue log_entries
;
314 if (order
== OLDEST_FIRST
) {
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();
325 if (riter
->log_level
> max_level
)
327 if (++shown_events
>= max_events
)
330 offset
= entries_
.size() - num_entries
;
332 for (LogEntryList::const_iterator iter
= entries_
.begin();
333 iter
!= entries_
.end();
339 if (iter
->log_level
> max_level
)
342 log_entries
.AppendString((*iter
).GetAsJSON());
344 result
+= (*iter
).ToString(
345 show_time
, show_file
, show_level
, show_desc
, format_html
);
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();
355 if (riter
->log_level
> max_level
)
358 log_entries
.AppendString((*riter
).GetAsJSON());
360 result
+= (*riter
).ToString(
361 show_time
, show_file
, show_level
, show_desc
, format_html
);
364 if (max_events
> 0 && ++nlines
>= max_events
)
369 JSONStringValueSerializer
serializer(&result
);
370 serializer
.Serialize(log_entries
);
378 const LogLevel kDefaultLogLevel
= LOG_LEVEL_EVENT
;
381 if (g_network_event_log
)
382 delete g_network_event_log
; // reset log
383 g_network_event_log
= new NetworkEventLog();
387 delete g_network_event_log
;
388 g_network_event_log
= NULL
;
391 bool IsInitialized() { return g_network_event_log
!= NULL
; }
395 size_t GetMaxLogEntries() { return g_max_network_event_log_entries
; }
397 void SetMaxLogEntries(size_t max_entries
) {
398 g_max_network_event_log_entries
= max_entries
;
399 if (!g_network_event_log
)
401 while (g_network_event_log
->entries().size() > max_entries
)
402 g_network_event_log
->entries().pop_front();
405 void AddEntry(const char* file
,
408 const std::string
& event
,
409 const std::string
& description
) {
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();
418 g_network_event_log
->AddLogEntry(entry
);
421 } // namespace internal
423 std::string
GetAsString(StringOrder order
,
424 const std::string
& format
,
427 if (!g_network_event_log
)
428 return "NetworkEventLog not initialized.";
429 return g_network_event_log
->GetAsString(order
, format
, max_level
, max_events
);
432 std::string
ValueAsString(const base::Value
& value
) {
434 base::JSONWriter::WriteWithOptions(
435 &value
, base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES
, &vstr
);
436 return vstr
.empty() ? "''" : vstr
;
439 } // namespace network_event_log
440 } // namespace chromeos