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/i18n/time_formatting.h"
12 #include "base/json/json_string_value_serializer.h"
13 #include "base/json/json_writer.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/strings/string_tokenizer.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "net/base/escape.h"
21 #include "third_party/icu/source/i18n/unicode/datefmt.h"
22 #include "third_party/icu/source/i18n/unicode/dtptngen.h"
23 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
26 namespace network_event_log
{
30 std::string
IcuFormattedString(const base::Time
& time
,
31 const std::string
& format
) {
32 UErrorCode status
= U_ZERO_ERROR
;
33 scoped_ptr
<icu::DateTimePatternGenerator
> generator(
34 icu::DateTimePatternGenerator::createInstance(status
));
35 DCHECK(U_SUCCESS(status
));
36 icu::UnicodeString generated_pattern
=
37 generator
->getBestPattern(icu::UnicodeString(format
.c_str()), status
);
38 DCHECK(U_SUCCESS(status
));
39 icu::SimpleDateFormat
formatter(generated_pattern
, status
);
40 DCHECK(U_SUCCESS(status
));
41 icu::UnicodeString formatted
;
42 formatter
.format(static_cast<UDate
>(time
.ToDoubleT() * 1000), formatted
);
43 base::string16
formatted16(formatted
.getBuffer(),
44 static_cast<size_t>(formatted
.length()));
45 return base::UTF16ToUTF8(formatted16
);
48 std::string
DateAndTimeWithMicroseconds(const base::Time
& time
) {
49 std::string formatted
= IcuFormattedString(time
, "yyMMddHHmmss");
50 // icu only supports milliseconds, but sometimes we need microseconds, so
51 // append '.' + usecs to the end of the formatted string.
52 int usecs
= static_cast<int>(fmod(time
.ToDoubleT() * 1000000, 1000000));
53 return base::StringPrintf("%s.%06d", formatted
.c_str(), usecs
);
56 std::string
TimeWithSeconds(const base::Time
& time
) {
57 return IcuFormattedString(time
, "HHmmss");
60 class NetworkEventLog
;
61 NetworkEventLog
* g_network_event_log
= NULL
;
62 size_t g_max_network_event_log_entries
= 4000;
63 const char* kLogLevelName
[] = {"Error", "User", "Event", "Debug"};
66 LogEntry(const std::string
& file
,
69 const std::string
& event
,
70 const std::string
& description
);
72 std::string
ToString(bool show_time
,
75 bool format_html
) const;
76 void ToDictionary(base::DictionaryValue
*) const;
78 std::string
GetNormalText(bool show_desc
) const;
79 std::string
GetHtmlText(bool show_desc
) const;
80 std::string
GetAsJSON() const;
82 void SendToVLogOrErrorLog() const;
84 bool ContentEquals(const LogEntry
& other
) const;
90 std::string description
;
95 LogEntry::LogEntry(const std::string
& file
,
98 const std::string
& event
,
99 const std::string
& description
)
101 file_line(file_line
),
102 log_level(log_level
),
104 description(description
),
105 time(base::Time::Now()),
108 std::string
LogEntry::ToString(bool show_time
,
111 bool format_html
) const {
114 line
+= "[" + base::UTF16ToUTF8(base::TimeFormatTimeOfDay(time
)) + "] ";
116 std::string filestr
= format_html
? net::EscapeForHTML(file
) : file
;
117 line
+= base::StringPrintf("%s:%d ", file
.c_str(), file_line
);
119 line
+= format_html
? GetHtmlText(show_desc
) : GetNormalText(show_desc
);
121 line
+= base::StringPrintf(" (%d)", count
);
125 void LogEntry::ToDictionary(base::DictionaryValue
* output
) const {
126 output
->SetString("timestamp", DateAndTimeWithMicroseconds(time
));
127 output
->SetString("timestampshort", TimeWithSeconds(time
));
128 output
->SetString("level", kLogLevelName
[log_level
]);
129 output
->SetString("file",
130 base::StringPrintf("%s:%d ", file
.c_str(), file_line
));
131 output
->SetString("event", event
);
132 output
->SetString("description", description
);
135 std::string
LogEntry::GetAsJSON() const {
136 base::DictionaryValue entry
;
137 ToDictionary(&entry
);
139 JSONStringValueSerializer
serializer(&json
);
140 if (!serializer
.Serialize(entry
)) {
141 LOG(ERROR
) << "Failed to serialize to JSON";
146 std::string
LogEntry::GetNormalText(bool show_desc
) const {
147 std::string text
= event
;
148 if (show_desc
&& !description
.empty())
149 text
+= ": " + description
;
153 std::string
LogEntry::GetHtmlText(bool show_desc
) const {
155 if (log_level
== LOG_LEVEL_DEBUG
)
157 else if (log_level
== LOG_LEVEL_USER
)
159 else if (log_level
== LOG_LEVEL_ERROR
)
163 if (show_desc
&& !description
.empty())
164 text
+= ": " + net::EscapeForHTML(description
);
166 if (log_level
== LOG_LEVEL_DEBUG
)
168 else if (log_level
== LOG_LEVEL_USER
)
170 else if (log_level
== LOG_LEVEL_ERROR
)
175 void LogEntry::SendToVLogOrErrorLog() const {
176 const bool show_time
= true;
177 const bool show_file
= true;
178 const bool show_desc
= true;
179 const bool format_html
= false;
180 std::string output
= ToString(show_time
, show_file
, show_desc
, format_html
);
181 if (log_level
== LOG_LEVEL_ERROR
)
182 LOG(ERROR
) << output
;
187 bool LogEntry::ContentEquals(const LogEntry
& other
) const {
188 return file
== other
.file
&&
189 file_line
== other
.file_line
&&
190 event
== other
.event
&&
191 description
== other
.description
;
194 void GetFormat(const std::string
& format_string
,
200 base::StringTokenizer
tokens(format_string
, ",");
204 *format_html
= false;
205 *format_json
= false;
206 while (tokens
.GetNext()) {
207 std::string
tok(tokens
.token());
221 typedef std::list
<LogEntry
> LogEntryList
;
223 class NetworkEventLog
{
226 ~NetworkEventLog() {}
228 void AddLogEntry(const LogEntry
& entry
);
230 std::string
GetAsString(StringOrder order
,
231 const std::string
& format
,
235 LogEntryList
& entries() { return entries_
; }
238 LogEntryList entries_
;
240 DISALLOW_COPY_AND_ASSIGN(NetworkEventLog
);
243 void NetworkEventLog::AddLogEntry(const LogEntry
& entry
) {
244 if (!entries_
.empty()) {
245 LogEntry
& last
= entries_
.back();
246 if (last
.ContentEquals(entry
)) {
247 // Update count and time for identical events to avoid log spam.
249 last
.log_level
= std::min(last
.log_level
, entry
.log_level
);
250 last
.time
= base::Time::Now();
254 if (entries_
.size() >= g_max_network_event_log_entries
) {
255 const size_t max_error_entries
= g_max_network_event_log_entries
/ 2;
256 // Remove the first (oldest) non-error entry, or the oldest entry if more
257 // than half the entries are errors.
258 size_t error_count
= 0;
259 for (LogEntryList::iterator iter
= entries_
.begin(); iter
!= entries_
.end();
261 if (iter
->log_level
!= LOG_LEVEL_ERROR
) {
262 entries_
.erase(iter
);
265 if (++error_count
> max_error_entries
) {
266 // Too many error entries, remove the oldest entry.
267 entries_
.pop_front();
272 entries_
.push_back(entry
);
273 entry
.SendToVLogOrErrorLog();
276 std::string
NetworkEventLog::GetAsString(StringOrder order
,
277 const std::string
& format
,
280 if (entries_
.empty())
281 return "No Log Entries.";
283 bool show_time
, show_file
, show_desc
, format_html
, format_json
;
285 format
, &show_time
, &show_file
, &show_desc
, &format_html
, &format_json
);
288 base::ListValue log_entries
;
289 if (order
== OLDEST_FIRST
) {
291 if (max_events
> 0 && max_events
< entries_
.size()) {
292 // Iterate backwards through the list skipping uninteresting entries to
293 // determine the first entry to include.
294 size_t shown_events
= 0;
295 size_t num_entries
= 0;
296 for (LogEntryList::const_reverse_iterator riter
= entries_
.rbegin();
297 riter
!= entries_
.rend();
300 if (riter
->log_level
> max_level
)
302 if (++shown_events
>= max_events
)
305 offset
= entries_
.size() - num_entries
;
307 for (LogEntryList::const_iterator iter
= entries_
.begin();
308 iter
!= entries_
.end();
314 if (iter
->log_level
> max_level
)
317 log_entries
.AppendString((*iter
).GetAsJSON());
320 (*iter
).ToString(show_time
, show_file
, show_desc
, format_html
);
326 // Iterate backwards through the list to show the most recent entries first.
327 for (LogEntryList::const_reverse_iterator riter
= entries_
.rbegin();
328 riter
!= entries_
.rend();
330 if (riter
->log_level
> max_level
)
333 log_entries
.AppendString((*riter
).GetAsJSON());
336 (*riter
).ToString(show_time
, show_file
, show_desc
, format_html
);
339 if (max_events
> 0 && ++nlines
>= max_events
)
344 JSONStringValueSerializer
serializer(&result
);
345 serializer
.Serialize(log_entries
);
353 const LogLevel kDefaultLogLevel
= LOG_LEVEL_EVENT
;
356 if (g_network_event_log
)
357 delete g_network_event_log
; // reset log
358 g_network_event_log
= new NetworkEventLog();
362 delete g_network_event_log
;
363 g_network_event_log
= NULL
;
366 bool IsInitialized() { return g_network_event_log
!= NULL
; }
370 size_t GetMaxLogEntries() { return g_max_network_event_log_entries
; }
372 void SetMaxLogEntries(size_t max_entries
) {
373 g_max_network_event_log_entries
= max_entries
;
374 if (!g_network_event_log
)
376 while (g_network_event_log
->entries().size() > max_entries
)
377 g_network_event_log
->entries().pop_front();
380 void AddEntry(const char* file
,
383 const std::string
& event
,
384 const std::string
& description
) {
387 filestr
= base::FilePath(std::string(file
)).BaseName().value();
388 LogEntry
entry(filestr
, file_line
, log_level
, event
, description
);
389 if (!g_network_event_log
) {
390 entry
.SendToVLogOrErrorLog();
393 g_network_event_log
->AddLogEntry(entry
);
396 } // namespace internal
398 std::string
GetAsString(StringOrder order
,
399 const std::string
& format
,
402 if (!g_network_event_log
)
403 return "NetworkEventLog not initialized.";
404 return g_network_event_log
->GetAsString(order
, format
, max_level
, max_events
);
407 std::string
ValueAsString(const base::Value
& value
) {
409 base::JSONWriter::WriteWithOptions(
410 &value
, base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES
, &vstr
);
411 return vstr
.empty() ? "''" : vstr
;
414 } // namespace network_event_log
415 } // namespace chromeos