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 std::string
TimeWithMillieconds(const base::Time
& time
) {
61 std::string formatted
= IcuFormattedString(time
, "HHmmss");
62 // icu doesn't support milliseconds combined with other formatting.
63 int msecs
= static_cast<int>(fmod(time
.ToDoubleT() * 1000, 1000));
64 return base::StringPrintf("%s.%03d", formatted
.c_str(), msecs
);
67 class NetworkEventLog
;
68 NetworkEventLog
* g_network_event_log
= NULL
;
69 size_t g_max_network_event_log_entries
= 4000;
70 const char* kLogLevelName
[] = {"Error", "User", "Event", "Debug"};
73 LogEntry(const std::string
& file
,
76 const std::string
& event
,
77 const std::string
& description
);
79 std::string
ToString(bool show_time
,
83 bool format_html
) const;
84 void ToDictionary(base::DictionaryValue
*) const;
86 std::string
GetNormalText(bool show_desc
) const;
87 std::string
GetHtmlText(bool show_desc
) const;
88 std::string
GetAsJSON() const;
90 void SendToVLogOrErrorLog() const;
92 bool ContentEquals(const LogEntry
& other
) const;
98 std::string description
;
103 LogEntry::LogEntry(const std::string
& file
,
106 const std::string
& event
,
107 const std::string
& description
)
109 file_line(file_line
),
110 log_level(log_level
),
112 description(description
),
113 time(base::Time::Now()),
116 std::string
LogEntry::ToString(bool show_time
,
120 bool format_html
) const {
123 line
+= "[" + TimeWithMillieconds(time
) + "] ";
125 const char* kLevelDesc
[] = {
131 line
+= base::StringPrintf("%s: ", kLevelDesc
[log_level
]);
134 std::string filestr
= format_html
? net::EscapeForHTML(file
) : file
;
135 line
+= base::StringPrintf("%s:%d ", file
.c_str(), file_line
);
137 line
+= format_html
? GetHtmlText(show_desc
) : GetNormalText(show_desc
);
139 line
+= base::StringPrintf(" (%d)", count
);
143 void LogEntry::ToDictionary(base::DictionaryValue
* output
) const {
144 output
->SetString("timestamp", DateAndTimeWithMicroseconds(time
));
145 output
->SetString("timestampshort", TimeWithSeconds(time
));
146 output
->SetString("level", kLogLevelName
[log_level
]);
147 output
->SetString("file",
148 base::StringPrintf("%s:%d ", file
.c_str(), file_line
));
149 output
->SetString("event", event
);
150 output
->SetString("description", description
);
153 std::string
LogEntry::GetAsJSON() const {
154 base::DictionaryValue entry
;
155 ToDictionary(&entry
);
157 JSONStringValueSerializer
serializer(&json
);
158 if (!serializer
.Serialize(entry
)) {
159 LOG(ERROR
) << "Failed to serialize to JSON";
164 std::string
LogEntry::GetNormalText(bool show_desc
) const {
165 std::string text
= event
;
166 if (show_desc
&& !description
.empty())
167 text
+= ": " + description
;
171 std::string
LogEntry::GetHtmlText(bool show_desc
) const {
173 if (log_level
== LOG_LEVEL_DEBUG
)
175 else if (log_level
== LOG_LEVEL_USER
)
177 else if (log_level
== LOG_LEVEL_ERROR
)
181 if (show_desc
&& !description
.empty())
182 text
+= ": " + net::EscapeForHTML(description
);
184 if (log_level
== LOG_LEVEL_DEBUG
)
186 else if (log_level
== LOG_LEVEL_USER
)
188 else if (log_level
== LOG_LEVEL_ERROR
)
193 void LogEntry::SendToVLogOrErrorLog() const {
194 const bool show_time
= true;
195 const bool show_file
= true;
196 const bool show_level
= false;
197 const bool show_desc
= true;
198 const bool format_html
= false;
200 ToString(show_time
, show_file
, show_level
, show_desc
, format_html
);
201 if (log_level
== LOG_LEVEL_ERROR
)
202 LOG(ERROR
) << output
;
207 bool LogEntry::ContentEquals(const LogEntry
& other
) const {
208 return file
== other
.file
&&
209 file_line
== other
.file_line
&&
210 event
== other
.event
&&
211 description
== other
.description
;
214 void GetFormat(const std::string
& format_string
,
221 base::StringTokenizer
tokens(format_string
, ",");
226 *format_html
= false;
227 *format_json
= false;
228 while (tokens
.GetNext()) {
229 std::string
tok(tokens
.token());
245 typedef std::list
<LogEntry
> LogEntryList
;
247 class NetworkEventLog
{
250 ~NetworkEventLog() {}
252 void AddLogEntry(const LogEntry
& entry
);
254 std::string
GetAsString(StringOrder order
,
255 const std::string
& format
,
259 LogEntryList
& entries() { return entries_
; }
262 LogEntryList entries_
;
264 DISALLOW_COPY_AND_ASSIGN(NetworkEventLog
);
267 void NetworkEventLog::AddLogEntry(const LogEntry
& entry
) {
268 if (!entries_
.empty()) {
269 LogEntry
& last
= entries_
.back();
270 if (last
.ContentEquals(entry
)) {
271 // Update count and time for identical events to avoid log spam.
273 last
.log_level
= std::min(last
.log_level
, entry
.log_level
);
274 last
.time
= base::Time::Now();
278 if (entries_
.size() >= g_max_network_event_log_entries
) {
279 const size_t max_error_entries
= g_max_network_event_log_entries
/ 2;
280 // Remove the first (oldest) non-error entry, or the oldest entry if more
281 // than half the entries are errors.
282 size_t error_count
= 0;
283 for (LogEntryList::iterator iter
= entries_
.begin(); iter
!= entries_
.end();
285 if (iter
->log_level
!= LOG_LEVEL_ERROR
) {
286 entries_
.erase(iter
);
289 if (++error_count
> max_error_entries
) {
290 // Too many error entries, remove the oldest entry.
291 entries_
.pop_front();
296 entries_
.push_back(entry
);
297 entry
.SendToVLogOrErrorLog();
300 std::string
NetworkEventLog::GetAsString(StringOrder order
,
301 const std::string
& format
,
304 if (entries_
.empty())
305 return "No Log Entries.";
307 bool show_time
, show_file
, show_level
, show_desc
, format_html
, format_json
;
317 base::ListValue log_entries
;
318 if (order
== OLDEST_FIRST
) {
320 if (max_events
> 0 && max_events
< entries_
.size()) {
321 // Iterate backwards through the list skipping uninteresting entries to
322 // determine the first entry to include.
323 size_t shown_events
= 0;
324 size_t num_entries
= 0;
325 for (LogEntryList::const_reverse_iterator riter
= entries_
.rbegin();
326 riter
!= entries_
.rend();
329 if (riter
->log_level
> max_level
)
331 if (++shown_events
>= max_events
)
334 offset
= entries_
.size() - num_entries
;
336 for (LogEntryList::const_iterator iter
= entries_
.begin();
337 iter
!= entries_
.end();
343 if (iter
->log_level
> max_level
)
346 log_entries
.AppendString((*iter
).GetAsJSON());
348 result
+= (*iter
).ToString(
349 show_time
, show_file
, show_level
, show_desc
, format_html
);
355 // Iterate backwards through the list to show the most recent entries first.
356 for (LogEntryList::const_reverse_iterator riter
= entries_
.rbegin();
357 riter
!= entries_
.rend();
359 if (riter
->log_level
> max_level
)
362 log_entries
.AppendString((*riter
).GetAsJSON());
364 result
+= (*riter
).ToString(
365 show_time
, show_file
, show_level
, show_desc
, format_html
);
368 if (max_events
> 0 && ++nlines
>= max_events
)
373 JSONStringValueSerializer
serializer(&result
);
374 serializer
.Serialize(log_entries
);
382 const LogLevel kDefaultLogLevel
= LOG_LEVEL_EVENT
;
385 if (g_network_event_log
)
386 delete g_network_event_log
; // reset log
387 g_network_event_log
= new NetworkEventLog();
391 delete g_network_event_log
;
392 g_network_event_log
= NULL
;
395 bool IsInitialized() { return g_network_event_log
!= NULL
; }
399 size_t GetMaxLogEntries() { return g_max_network_event_log_entries
; }
401 void SetMaxLogEntries(size_t max_entries
) {
402 g_max_network_event_log_entries
= max_entries
;
403 if (!g_network_event_log
)
405 while (g_network_event_log
->entries().size() > max_entries
)
406 g_network_event_log
->entries().pop_front();
409 void AddEntry(const char* file
,
412 const std::string
& event
,
413 const std::string
& description
) {
416 filestr
= base::FilePath(std::string(file
)).BaseName().value();
417 LogEntry
entry(filestr
, file_line
, log_level
, event
, description
);
418 if (!g_network_event_log
) {
419 entry
.SendToVLogOrErrorLog();
422 g_network_event_log
->AddLogEntry(entry
);
425 } // namespace internal
427 std::string
GetAsString(StringOrder order
,
428 const std::string
& format
,
431 if (!g_network_event_log
)
432 return "NetworkEventLog not initialized.";
433 return g_network_event_log
->GetAsString(order
, format
, max_level
, max_events
);
436 std::string
ValueAsString(const base::Value
& value
) {
438 base::JSONWriter::WriteWithOptions(
439 &value
, base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES
, &vstr
);
440 return vstr
.empty() ? "''" : vstr
;
443 } // namespace network_event_log
444 } // namespace chromeos