1 // Copyright 2014 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/device_event_log_impl.h"
11 #include "base/files/file_path.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/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h"
21 #include "net/base/escape.h"
25 namespace device_event_log
{
29 const char* kLogLevelName
[] = {"Error", "User", "Event", "Debug"};
31 const char* kLogTypeNetworkDesc
= "Network";
32 const char* kLogTypePowerDesc
= "Power";
33 const char* kLogTypeLoginDesc
= "Login";
35 std::string
GetLogTypeString(LogType type
) {
36 if (type
== LOG_TYPE_NETWORK
)
37 return kLogTypeNetworkDesc
;
38 if (type
== LOG_TYPE_POWER
)
39 return kLogTypePowerDesc
;
40 if (type
== LOG_TYPE_LOGIN
)
41 return kLogTypeLoginDesc
;
46 std::string
DateAndTimeWithMicroseconds(const base::Time
& time
) {
47 base::Time::Exploded exploded
;
48 time
.LocalExplode(&exploded
);
49 // base::Time::Exploded does not include microseconds, but sometimes we need
50 // microseconds, so append '.' + usecs to the end of the formatted string.
51 int usecs
= static_cast<int>(fmod(time
.ToDoubleT() * 1000000, 1000000));
52 return base::StringPrintf("%04d/%02d/%02d %02d:%02d:%02d.%06d", exploded
.year
,
53 exploded
.month
, exploded
.day_of_month
,
54 exploded
.hour
, exploded
.minute
, exploded
.second
,
58 std::string
TimeWithSeconds(const base::Time
& time
) {
59 base::Time::Exploded exploded
;
60 time
.LocalExplode(&exploded
);
61 return base::StringPrintf("%02d:%02d:%02d", exploded
.hour
, exploded
.minute
,
65 std::string
TimeWithMillieconds(const base::Time
& time
) {
66 base::Time::Exploded exploded
;
67 time
.LocalExplode(&exploded
);
68 return base::StringPrintf("%02d:%02d:%02d.%03d", exploded
.hour
,
69 exploded
.minute
, exploded
.second
,
70 exploded
.millisecond
);
73 // Defined below for easier review. TODO(stevenjb): Move implementation here.
74 std::string
GetHtmlText(LogLevel log_level
, const std::string
& event
);
76 std::string
LogEntryToString(const DeviceEventLogImpl::LogEntry
& log_entry
,
84 line
+= "[" + TimeWithMillieconds(log_entry
.time
) + "] ";
86 line
+= GetLogTypeString(log_entry
.log_type
) + ": ";
88 const char* kLevelDesc
[] = {"ERROR", "USER", "EVENT", "DEBUG"};
89 line
+= base::StringPrintf("%s: ", kLevelDesc
[log_entry
.log_level
]);
93 format_html
? net::EscapeForHTML(log_entry
.file
) : log_entry
.file
;
94 line
+= base::StringPrintf("%s:%d ", log_entry
.file
.c_str(),
97 line
+= format_html
? GetHtmlText(log_entry
.log_level
, log_entry
.event
)
99 if (log_entry
.count
> 1)
100 line
+= base::StringPrintf(" (%d)", log_entry
.count
);
104 void LogEntryToDictionary(const DeviceEventLogImpl::LogEntry
& log_entry
,
105 base::DictionaryValue
* output
) {
106 output
->SetString("timestamp", DateAndTimeWithMicroseconds(log_entry
.time
));
107 output
->SetString("timestampshort", TimeWithSeconds(log_entry
.time
));
108 output
->SetString("level", kLogLevelName
[log_entry
.log_level
]);
109 output
->SetString("type", GetLogTypeString(log_entry
.log_type
));
110 output
->SetString("file", base::StringPrintf("%s:%d ", log_entry
.file
.c_str(),
111 log_entry
.file_line
));
112 output
->SetString("event", log_entry
.event
);
115 std::string
LogEntryAsJSON(const DeviceEventLogImpl::LogEntry
& log_entry
) {
116 base::DictionaryValue entry_dict
;
117 LogEntryToDictionary(log_entry
, &entry_dict
);
119 JSONStringValueSerializer
serializer(&json
);
120 if (!serializer
.Serialize(entry_dict
)) {
121 LOG(ERROR
) << "Failed to serialize to JSON";
126 std::string
GetHtmlText(LogLevel log_level
, const std::string
& event
) {
128 if (log_level
== LOG_LEVEL_DEBUG
)
130 else if (log_level
== LOG_LEVEL_USER
)
132 else if (log_level
== LOG_LEVEL_ERROR
)
135 text
+= net::EscapeForHTML(event
);
137 if (log_level
== LOG_LEVEL_DEBUG
)
139 else if (log_level
== LOG_LEVEL_USER
)
141 else if (log_level
== LOG_LEVEL_ERROR
)
146 void SendLogEntryToVLogOrErrorLog(
147 const DeviceEventLogImpl::LogEntry
& log_entry
) {
148 if (log_entry
.log_level
!= LOG_LEVEL_ERROR
&& !VLOG_IS_ON(1))
150 const bool show_time
= true;
151 const bool show_file
= true;
152 const bool show_type
= true;
153 const bool show_level
= false;
154 const bool format_html
= false;
155 std::string output
= LogEntryToString(log_entry
, show_time
, show_file
,
156 show_type
, show_level
, format_html
);
157 if (log_entry
.log_level
== LOG_LEVEL_ERROR
)
158 LOG(ERROR
) << output
;
163 bool LogEntryMatches(const DeviceEventLogImpl::LogEntry
& first
,
164 const DeviceEventLogImpl::LogEntry
& second
) {
165 return first
.file
== second
.file
&& first
.file_line
== second
.file_line
&&
166 first
.log_level
== second
.log_level
&&
167 first
.log_type
== second
.log_type
&& first
.event
== second
.event
;
170 bool LogEntryMatchesTypes(const DeviceEventLogImpl::LogEntry
& entry
,
171 const std::set
<LogType
>& include_types
,
172 const std::set
<LogType
>& exclude_types
) {
173 if (include_types
.empty() && exclude_types
.empty())
175 if (!include_types
.empty() && include_types
.count(entry
.log_type
))
177 if (!exclude_types
.empty() && !exclude_types
.count(entry
.log_type
))
182 void GetFormat(const std::string
& format_string
,
189 base::StringTokenizer
tokens(format_string
, ",");
194 *format_html
= false;
195 *format_json
= false;
196 while (tokens
.GetNext()) {
197 std::string
tok(tokens
.token());
213 LogType
LogTypeFromString(const std::string
& desc
) {
214 std::string desc_lc
= base::StringToLowerASCII(desc
);
215 if (desc_lc
== "network")
216 return LOG_TYPE_NETWORK
;
217 if (desc_lc
== "power")
218 return LOG_TYPE_POWER
;
219 if (desc_lc
== "login")
220 return LOG_TYPE_LOGIN
;
221 NOTREACHED() << "Unrecogized LogType: " << desc
;
222 return LOG_TYPE_UNKNOWN
;
225 void GetLogTypes(const std::string
& types
,
226 std::set
<LogType
>* include_types
,
227 std::set
<LogType
>* exclude_types
) {
228 base::StringTokenizer
tokens(types
, ",");
229 while (tokens
.GetNext()) {
230 std::string
tok(tokens
.token());
231 if (tok
.substr(0, 4) == "non-") {
232 LogType type
= LogTypeFromString(tok
.substr(4));
233 if (type
!= LOG_TYPE_UNKNOWN
)
234 exclude_types
->insert(type
);
236 LogType type
= LogTypeFromString(tok
);
237 if (type
!= LOG_TYPE_UNKNOWN
)
238 include_types
->insert(type
);
246 void DeviceEventLogImpl::SendToVLogOrErrorLog(const char* file
,
250 const std::string
& event
) {
251 LogEntry
entry(file
, file_line
, log_type
, log_level
, event
);
252 SendLogEntryToVLogOrErrorLog(entry
);
255 DeviceEventLogImpl::DeviceEventLogImpl(size_t max_entries
)
256 : max_entries_(max_entries
) {
259 DeviceEventLogImpl::~DeviceEventLogImpl() {
262 void DeviceEventLogImpl::AddEntry(const char* file
,
266 const std::string
& event
) {
267 LogEntry
entry(file
, file_line
, log_type
, log_level
, event
);
271 void DeviceEventLogImpl::AddLogEntry(const LogEntry
& entry
) {
272 if (!entries_
.empty()) {
273 LogEntry
& last
= entries_
.back();
274 if (LogEntryMatches(last
, entry
)) {
275 // Update count and time for identical events to avoid log spam.
277 last
.log_level
= std::min(last
.log_level
, entry
.log_level
);
278 last
.time
= base::Time::Now();
282 if (entries_
.size() >= max_entries_
) {
283 const size_t max_error_entries
= max_entries_
/ 2;
284 // Remove the first (oldest) non-error entry, or the oldest entry if more
285 // than half the entries are errors.
286 size_t error_count
= 0;
287 for (LogEntryList::iterator iter
= entries_
.begin(); iter
!= entries_
.end();
289 if (iter
->log_level
!= LOG_LEVEL_ERROR
) {
290 entries_
.erase(iter
);
293 if (++error_count
> max_error_entries
) {
294 // Too many error entries, remove the oldest entry.
295 entries_
.pop_front();
300 entries_
.push_back(entry
);
301 SendLogEntryToVLogOrErrorLog(entry
);
304 std::string
DeviceEventLogImpl::GetAsString(StringOrder order
,
305 const std::string
& format
,
306 const std::string
& types
,
309 if (entries_
.empty())
310 return "No Log Entries.";
312 bool show_time
, show_file
, show_type
, show_level
, format_html
, format_json
;
313 GetFormat(format
, &show_time
, &show_file
, &show_type
, &show_level
,
314 &format_html
, &format_json
);
316 std::set
<LogType
> include_types
, exclude_types
;
317 GetLogTypes(types
, &include_types
, &exclude_types
);
320 base::ListValue log_entries
;
321 if (order
== OLDEST_FIRST
) {
323 if (max_events
> 0 && max_events
< entries_
.size()) {
324 // Iterate backwards through the list skipping uninteresting entries to
325 // determine the first entry to include.
326 size_t shown_events
= 0;
327 size_t num_entries
= 0;
328 for (LogEntryList::const_reverse_iterator riter
= entries_
.rbegin();
329 riter
!= entries_
.rend(); ++riter
) {
331 if (!LogEntryMatchesTypes(*riter
, include_types
, exclude_types
))
333 if (riter
->log_level
> max_level
)
335 if (++shown_events
>= max_events
)
338 offset
= entries_
.size() - num_entries
;
340 for (LogEntryList::const_iterator iter
= entries_
.begin();
341 iter
!= entries_
.end(); ++iter
) {
346 if (!LogEntryMatchesTypes(*iter
, include_types
, exclude_types
))
348 if (iter
->log_level
> max_level
)
351 log_entries
.AppendString(LogEntryAsJSON(*iter
));
353 result
+= LogEntryToString(*iter
, show_time
, show_file
, show_type
,
354 show_level
, format_html
);
360 // Iterate backwards through the list to show the most recent entries first.
361 for (LogEntryList::const_reverse_iterator riter
= entries_
.rbegin();
362 riter
!= entries_
.rend(); ++riter
) {
363 if (!LogEntryMatchesTypes(*riter
, include_types
, exclude_types
))
365 if (riter
->log_level
> max_level
)
368 log_entries
.AppendString(LogEntryAsJSON(*riter
));
370 result
+= LogEntryToString(*riter
, show_time
, show_file
, show_type
,
371 show_level
, format_html
);
374 if (max_events
> 0 && ++nlines
>= max_events
)
379 JSONStringValueSerializer
serializer(&result
);
380 serializer
.Serialize(log_entries
);
386 DeviceEventLogImpl::LogEntry::LogEntry(const char* filedesc
,
390 const std::string
& event
)
391 : file_line(file_line
),
393 log_level(log_level
),
395 time(base::Time::Now()),
398 file
= base::FilePath(std::string(filedesc
)).BaseName().value();
401 } // namespace device_event_log
403 } // namespace chromeos