Chromium Code Reviews| Index: chrome/test/chromedriver/logging.cc |
| diff --git a/chrome/test/chromedriver/logging.cc b/chrome/test/chromedriver/logging.cc |
| index bf0a50a69abbd07b3229c181e3e1c076b0492d95..53e61b49eeaaeb802a80e2b6c55f666f00a894d2 100644 |
| --- a/chrome/test/chromedriver/logging.cc |
| +++ b/chrome/test/chromedriver/logging.cc |
| @@ -4,71 +4,163 @@ |
| #include "chrome/test/chromedriver/logging.h" |
| +#include <stdio.h> |
| + |
| #include "base/basictypes.h" |
| +#include "base/command_line.h" |
| +#include "base/json/json_reader.h" |
| #include "base/logging.h" |
| +#include "base/strings/stringprintf.h" |
| #include "base/time/time.h" |
| #include "chrome/test/chromedriver/capabilities.h" |
| #include "chrome/test/chromedriver/chrome/console_logger.h" |
| #include "chrome/test/chromedriver/chrome/performance_logger.h" |
| #include "chrome/test/chromedriver/chrome/status.h" |
| +#include "chrome/test/chromedriver/session.h" |
| + |
| +#if defined(OS_POSIX) |
| +#include <fcntl.h> |
| +#include <unistd.h> |
| +#endif |
| namespace { |
| -// Map between WebDriverLog::WebDriverLevel and its name in WD wire protocol. |
| -// Array indices are the WebDriverLog::WebDriverLevel enum values. |
| -const char* const kWebDriverLevelNames[] = { |
| - "ALL", "DEBUG", "INFO", "WARNING", "SEVERE", "OFF" |
| -}; |
| +Log::Level g_log_level = Log::kWarning; |
| + |
| +int64 g_start_time = 0; |
| -// Map between Log::Level and WebDriverLog::WebDriverLevel. |
| // Array indices are the Log::Level enum values. |
| -WebDriverLog::WebDriverLevel kLogLevelToWebDriverLevels[] = { |
| - WebDriverLog::kWdDebug, // kDebug |
| - WebDriverLog::kWdInfo, // kLog |
| - WebDriverLog::kWdWarning, // kWarning |
| - WebDriverLog::kWdSevere // kError |
| +const char* kLevelToName[] = { |
| + "ALL", // kAll |
| + "DEBUG", // kDebug |
| + "INFO", // kInfo |
| + "WARNING", // kWarning |
| + "SEVERE", // kError |
| + "OFF", // kOff |
| }; |
| -// Translates Log::Level to WebDriverLog::WebDriverLevel. |
| -WebDriverLog::WebDriverLevel LogLevelToWebDriverLevel(Log::Level level) { |
| - const int index = level - Log::kDebug; |
| +const char* LevelToName(Log::Level level) { |
| + const int index = level - Log::kAll; |
| CHECK_GE(index, 0); |
| - CHECK_LT(static_cast<size_t>(index), arraysize(kLogLevelToWebDriverLevels)); |
| - return kLogLevelToWebDriverLevels[index]; |
| + CHECK_LT(static_cast<size_t>(index), arraysize(kLevelToName)); |
| + return kLevelToName[index]; |
| } |
| -// Returns WD wire protocol level name for a WebDriverLog::WebDriverLevel. |
| -std::string GetWebDriverLevelName( |
| - const WebDriverLog::WebDriverLevel level) { |
| - const int index = level - WebDriverLog::kWdAll; |
| - CHECK_GE(index, 0); |
| - CHECK_LT(static_cast<size_t>(index), arraysize(kWebDriverLevelNames)); |
| - return kWebDriverLevelNames[index]; |
| +struct LevelPair { |
| + const char* name; |
| + Log::Level level; |
| +}; |
| + |
| +const LevelPair kNameToLevel[] = { |
| + {"ALL", Log::kAll}, |
| + {"DEBUG", Log::kDebug}, |
| + {"INFO", Log::kInfo}, |
| + {"WARNING", Log::kWarning}, |
| + {"SEVERE", Log::kError}, |
| + {"OFF", Log::kOff}, |
| +}; |
| + |
| +Log::Level GetLevelFromSeverity(int severity) { |
| + switch (severity) { |
| + case logging::LOG_FATAL: |
| + case logging::LOG_ERROR_REPORT: |
| + case logging::LOG_ERROR: |
| + return Log::kError; |
| + case logging::LOG_WARNING: |
| + return Log::kWarning; |
| + case logging::LOG_INFO: |
| + return Log::kInfo; |
| + case logging::LOG_VERBOSE: |
| + default: |
| + return Log::kDebug; |
| + } |
| +} |
| + |
| +bool InternalIsVLogOn(int vlog_level) { |
| + return GetLevelFromSeverity(vlog_level * -1) >= g_log_level; |
| +} |
| + |
| +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); |
| + std::string display = input.substr(0, left) + |
| + PrettyPrintValue(TruncateContainedStrings(value.get())); |
| + 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); |
| + return display; |
| +} |
| + |
| +bool HandleLogMessage(int severity, |
| + const char* file, |
| + int line, |
| + size_t message_start, |
| + const std::string& str) { |
| + Log::Level level = GetLevelFromSeverity(severity); |
| + if (level < g_log_level) |
| + return true; |
| + |
| + std::string message = str.substr(message_start); |
| + const char* level_name = "UNKNOWN"; |
| + switch (level) { |
| + case Log::kDebug: |
| + level_name = "DEBUG"; |
| + break; |
| + case Log::kInfo: |
| + level_name = "INFO"; |
| + break; |
| + case Log::kWarning: |
| + level_name = "WARNING"; |
| + break; |
| + case Log::kError: |
| + level_name = "ERROR"; |
| + break; |
| + default: |
| + break; |
| + } |
| + std::string entry = base::StringPrintf( |
| + "[%.3lf][%s]: %s", |
| + base::TimeDelta(base::TimeTicks::Now() - |
| + base::TimeTicks::FromInternalValue(g_start_time)) |
| + .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); |
| + return true; |
| } |
| } // namespace |
| -bool WebDriverLog::NameToLevel( |
| - const std::string& name, WebDriverLog::WebDriverLevel* out_level) { |
| - for (size_t i = 0; i < arraysize(kWebDriverLevelNames); ++i) { |
| - if (name == kWebDriverLevelNames[i]) { |
| - CHECK_LE(WebDriverLog::kWdAll + i, |
| - static_cast<size_t>(WebDriverLog::kWdOff)); |
| - *out_level = |
| - static_cast<WebDriverLog::WebDriverLevel>(WebDriverLog::kWdAll + i); |
| +bool WebDriverLog::NameToLevel(const std::string& name, Log::Level* out_level) { |
| + for (size_t i = 0; i < arraysize(kNameToLevel); ++i) { |
| + if (name == kNameToLevel[i].name) { |
|
chrisgao (Use stgao instead)
2013/09/05 01:20:46
Can it be case insensitive?
kkania
2013/09/05 15:15:11
the spec isn't clear; i'll leave as is.
|
| + *out_level = kNameToLevel[i].level; |
| return true; |
| } |
| } |
| return false; |
| } |
| -WebDriverLog::WebDriverLog( |
| - const std::string& type, WebDriverLog::WebDriverLevel min_wd_level) |
| - : type_(type), |
| - min_wd_level_(min_wd_level), |
| - entries_(new base::ListValue()) { |
| - VLOG(1) << "Log(" << type_ << ", " << min_wd_level_ << ")"; |
| +WebDriverLog::WebDriverLog(const std::string& type, Log::Level min_level) |
| + : type_(type), min_level_(min_level), entries_(new base::ListValue()) { |
| } |
| WebDriverLog::~WebDriverLog() { |
| @@ -76,76 +168,126 @@ WebDriverLog::~WebDriverLog() { |
| << entries_->GetSize() << " entries on destruction"; |
| } |
| -const std::string& WebDriverLog::GetType() { |
| - return type_; |
| +scoped_ptr<base::ListValue> WebDriverLog::GetAndClearEntries() { |
| + scoped_ptr<base::ListValue> ret(entries_.release()); |
| + entries_.reset(new base::ListValue()); |
| + return ret.Pass(); |
| } |
| void WebDriverLog::AddEntryTimestamped(const base::Time& timestamp, |
| Log::Level level, |
| const std::string& message) { |
| - const WebDriverLog::WebDriverLevel wd_level = LogLevelToWebDriverLevel(level); |
| - if (wd_level < min_wd_level_) |
| + if (level < min_level_) |
| return; |
| + |
| scoped_ptr<base::DictionaryValue> log_entry_dict(new base::DictionaryValue()); |
| log_entry_dict->SetDouble("timestamp", |
| static_cast<int64>(timestamp.ToJsTime())); |
| - log_entry_dict->SetString("level", GetWebDriverLevelName(wd_level)); |
| + log_entry_dict->SetString("level", LevelToName(level)); |
| log_entry_dict->SetString("message", message); |
| entries_->Append(log_entry_dict.release()); |
| } |
| -scoped_ptr<base::ListValue> WebDriverLog::GetAndClearEntries() { |
| - scoped_ptr<base::ListValue> ret(entries_.release()); |
| - entries_.reset(new base::ListValue()); |
| - return ret.Pass(); |
| +const std::string& WebDriverLog::type() const { |
| + return type_; |
| +} |
| + |
| +Log::Level WebDriverLog::min_level() const { |
| + return min_level_; |
| +} |
| + |
| +bool InitLogging() { |
| + InitLogging(&InternalIsVLogOn); |
| + g_start_time = base::TimeTicks::Now().ToInternalValue(); |
| + |
| + CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| + if (cmd_line->HasSwitch("log-path")) { |
| + g_log_level = Log::kInfo; |
| + base::FilePath log_path = cmd_line->GetSwitchValuePath("log-path"); |
| +#if defined(OS_WIN) |
| + FILE* redir_stderr = _wfreopen(log_path.value().c_str(), L"w", stderr); |
| +#else |
| + FILE* redir_stderr = freopen(log_path.value().c_str(), "w", stderr); |
| +#endif |
| + if (!redir_stderr) { |
| + printf("Failed to redirect stderr to log file.\n"); |
| + return false; |
| + } |
| + } |
| + if (cmd_line->HasSwitch("silent")) |
| + g_log_level = Log::kOff; |
| + |
| + if (cmd_line->HasSwitch("verbose")) { |
| + g_log_level = Log::kAll; |
| + } else { |
| +#if defined(OS_POSIX) |
| + // Close stderr on exec, so that Chrome log spew doesn't confuse users. |
| + fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC); |
| +#endif |
| + } |
| + |
| + cmd_line->AppendSwitchASCII("vmodule", "*/chrome/test/chromedriver/*=3"); |
|
chrisgao (Use stgao instead)
2013/09/05 01:20:46
How about adding a comment for this one?
kkania
2013/09/05 15:15:11
Done.
|
| + |
| + logging::SetMinLogLevel(logging::LOG_WARNING); |
| + logging::SetLogItems(false, // enable_process_id |
| + false, // enable_thread_id |
| + false, // enable_timestamp |
| + false); // enable_tickcount |
| + logging::SetLogMessageHandler(&HandleLogMessage); |
| + |
| + logging::LoggingSettings logging_settings; |
| + logging_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; |
| + return logging::InitLogging(logging_settings); |
| } |
| Status CreateLogs(const Capabilities& capabilities, |
| - ScopedVector<WebDriverLog>* out_devtools_logs, |
| + ScopedVector<WebDriverLog>* out_logs, |
| + scoped_ptr<WebDriverLog>* out_driver_log, |
| ScopedVector<DevToolsEventListener>* out_listeners) { |
| - ScopedVector<WebDriverLog> devtools_logs; |
| + ScopedVector<WebDriverLog> logs; |
| ScopedVector<DevToolsEventListener> listeners; |
| - WebDriverLog::WebDriverLevel browser_log_level = WebDriverLog::kWdInfo; |
| - |
| - if (capabilities.logging_prefs) { |
| - for (DictionaryValue::Iterator pref(*capabilities.logging_prefs); |
| - !pref.IsAtEnd(); pref.Advance()) { |
| - const std::string type = pref.key(); |
| - std::string level_name; |
| - if (!pref.value().GetAsString(&level_name)) { |
| - return Status(kUnknownError, |
| - "logging level must be a string for log type: " + type); |
| - } |
| - WebDriverLog::WebDriverLevel level = WebDriverLog::kWdOff; |
| - if (!WebDriverLog::NameToLevel(level_name, &level)) { |
| - return Status(kUnknownError, |
| - "invalid log level \"" + level_name + |
| - "\" for type: " + type); |
| - } |
| - if ("performance" == type) { |
| - if (WebDriverLog::kWdOff != level) { |
| - WebDriverLog* log = new WebDriverLog(type, WebDriverLog::kWdAll); |
| - devtools_logs.push_back(log); |
| - listeners.push_back(new PerformanceLogger(log)); |
| - } |
| - } else if ("browser" == type) { |
| - browser_log_level = level; |
| - } else { |
| - // Driver "should" ignore unrecognized log types, per Selenium tests. |
| - // For example the Java client passes the "client" log type in the caps, |
| - // which the server should never provide. |
| - LOG(WARNING) << "Ignoring unrecognized log type: LoggingPrefs." << type; |
| + Log::Level browser_log_level = Log::kOff; |
| + Log::Level driver_log_level = Log::kWarning; |
| + const LoggingPrefs& prefs = capabilities.logging_prefs; |
| + |
| + const char kBrowserLogType[] = "browser"; |
| + const char kDriverLogType[] = "driver"; |
| + for (LoggingPrefs::const_iterator iter = prefs.begin(); |
| + iter != prefs.end(); |
| + ++iter) { |
| + std::string type = iter->first; |
| + Log::Level level = iter->second; |
| + if (type == "performance") { |
| + if (level != Log::kOff) { |
| + WebDriverLog* log = new WebDriverLog(type, Log::kDebug); |
|
chrisgao (Use stgao instead)
2013/09/05 01:20:46
Why not using |level| but Log::kDebug?
kkania
2013/09/05 15:15:11
the thinking was that perf logging doesn't really
|
| + logs.push_back(log); |
| + listeners.push_back(new PerformanceLogger(log)); |
| } |
| + } else if (type == kBrowserLogType) { |
| + browser_log_level = level; |
| + } else if (type == kDriverLogType) { |
| + driver_log_level = level; |
| + } else { |
| + // Driver "should" ignore unrecognized log types, per Selenium tests. |
| + // For example the Java client passes the "client" log type in the caps, |
| + // which the server should never provide. |
| + LOG(WARNING) << "Ignoring unrecognized log type: " << type; |
| } |
| } |
| // Create "browser" log -- should always exist. |
| - WebDriverLog* browser_log = new WebDriverLog("browser", browser_log_level); |
| - devtools_logs.push_back(browser_log); |
| + WebDriverLog* browser_log = |
| + new WebDriverLog(kBrowserLogType, browser_log_level); |
| + logs.push_back(browser_log); |
| // If the level is OFF, don't even bother listening for DevTools events. |
| - if (browser_log_level != WebDriverLog::kWdOff) |
| + if (browser_log_level != Log::kOff) |
| listeners.push_back(new ConsoleLogger(browser_log)); |
| - out_devtools_logs->swap(devtools_logs); |
| + // Create "driver" log -- should always exist. |
| + scoped_ptr<WebDriverLog> driver_log( |
| + new WebDriverLog(kDriverLogType, driver_log_level)); |
| + |
| + out_logs->swap(logs); |
| + *out_driver_log = driver_log.Pass(); |
|
chrisgao (Use stgao instead)
2013/09/05 01:20:46
|out_driver_log| is not contained in/owned by |out
kkania
2013/09/05 15:15:11
Updated comment.
|
| out_listeners->swap(listeners); |
| return Status(kOk); |
| } |