| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/test/chromedriver/chrome/log.h" |
| |
| #include <stdio.h> |
| |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/values.h" |
| |
| namespace { |
| |
| // Truncates the given string to 200 characters, adding an ellipsis if |
| // truncation was necessary. |
| void TruncateString(std::string* data) { |
| const size_t kMaxLength = 200; |
| if (data->length() > kMaxLength) { |
| data->resize(kMaxLength); |
| data->replace(kMaxLength - 3, 3, "..."); |
| } |
| } |
| |
| // Truncates all strings contained in the given value. |
| void TruncateContainedStrings(base::Value* value) { |
| base::ListValue* list = NULL; |
| base::DictionaryValue* dict = NULL; |
| if (value->GetAsDictionary(&dict)) { |
| for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); |
| it.Advance()) { |
| std::string data; |
| if (it.value().GetAsString(&data)) { |
| TruncateString(&data); |
| dict->SetWithoutPathExpansion(it.key(), new base::StringValue(data)); |
| } else { |
| base::Value* child = NULL; |
| dict->GetWithoutPathExpansion(it.key(), &child); |
| TruncateContainedStrings(child); |
| } |
| } |
| } else if (value->GetAsList(&list)) { |
| for (size_t i = 0; i < list->GetSize(); ++i) { |
| base::Value* child = NULL; |
| if (!list->Get(i, &child)) |
| continue; |
| std::string data; |
| if (child->GetAsString(&data)) { |
| TruncateString(&data); |
| list->Set(i, new base::StringValue(data)); |
| } else { |
| TruncateContainedStrings(child); |
| } |
| } |
| } |
| } |
| |
| std::string ConvertForDisplayInternal(const std::string& input) { |
| size_t left = input.find("{"); |
| size_t right = input.rfind("}"); |
| if (left == std::string::npos || right == std::string::npos) |
| return input.substr(0, 10 << 10); |
| |
| scoped_ptr<base::Value> value( |
| base::JSONReader::Read(input.substr(left, right - left + 1))); |
| if (!value) |
| return input.substr(0, 10 << 10); |
| TruncateContainedStrings(value.get()); |
| std::string json; |
| base::JSONWriter::WriteWithOptions( |
| value.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &json); |
| std::string display = input.substr(0, left) + json; |
| if (input.length() > right) |
| display += input.substr(right + 1); |
| return display; |
| } |
| |
| // Pretty prints encapsulated JSON and truncates long strings for display. |
| std::string ConvertForDisplay(const std::string& input) { |
| std::string display = ConvertForDisplayInternal(input); |
| char remove_chars[] = {'\r', '\0'}; |
| RemoveChars(display, remove_chars, &display); |
| return display; |
| } |
| |
| } // namespace |
| |
| void Log::AddEntry(Level level, const std::string& message) { |
| AddEntryTimestamped(base::Time::Now(), level, message); |
| } |
| |
| Logger::Logger() : min_log_level_(kLog), start_(base::Time::Now()) {} |
| |
| Logger::Logger(Level min_log_level) |
| : min_log_level_(min_log_level), start_(base::Time::Now()) {} |
| |
| Logger::~Logger() {} |
| |
| void Logger::AddEntryTimestamped(const base::Time& timestamp, |
| Level level, |
| const std::string& message) { |
| if (level < min_log_level_) |
| return; |
| |
| const char* level_name = "UNKNOWN"; |
| switch (level) { |
| case kDebug: |
| level_name = "DEBUG"; |
| break; |
| case kLog: |
| level_name = "INFO"; |
| break; |
| case kWarning: |
| level_name = "WARNING"; |
| break; |
| case kError: |
| level_name = "ERROR"; |
| break; |
| default: |
| break; |
| } |
| std::string entry = |
| base::StringPrintf("[%.3lf][%s]: %s", |
| base::TimeDelta(timestamp - start_).InSecondsF(), |
| level_name, |
| ConvertForDisplay(message).c_str()); |
| const char* format = "%s\n"; |
| if (entry[entry.length() - 1] == '\n') |
| format = "%s"; |
| fprintf(stderr, format, entry.c_str()); |
| fflush(stderr); |
| } |