OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/test/chromedriver/logging.h" | 5 #include "chrome/test/chromedriver/logging.h" |
6 | 6 |
| 7 #include <stdio.h> |
| 8 |
7 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
| 10 #include "base/command_line.h" |
| 11 #include "base/json/json_reader.h" |
8 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/strings/stringprintf.h" |
9 #include "base/time/time.h" | 14 #include "base/time/time.h" |
10 #include "chrome/test/chromedriver/capabilities.h" | 15 #include "chrome/test/chromedriver/capabilities.h" |
11 #include "chrome/test/chromedriver/chrome/console_logger.h" | 16 #include "chrome/test/chromedriver/chrome/console_logger.h" |
12 #include "chrome/test/chromedriver/chrome/performance_logger.h" | 17 #include "chrome/test/chromedriver/chrome/performance_logger.h" |
13 #include "chrome/test/chromedriver/chrome/status.h" | 18 #include "chrome/test/chromedriver/chrome/status.h" |
| 19 #include "chrome/test/chromedriver/session.h" |
| 20 |
| 21 #if defined(OS_POSIX) |
| 22 #include <fcntl.h> |
| 23 #include <unistd.h> |
| 24 #endif |
14 | 25 |
15 | 26 |
16 namespace { | 27 namespace { |
17 | 28 |
18 // Map between WebDriverLog::WebDriverLevel and its name in WD wire protocol. | 29 Log::Level g_log_level = Log::kWarning; |
19 // Array indices are the WebDriverLog::WebDriverLevel enum values. | 30 |
20 const char* const kWebDriverLevelNames[] = { | 31 int64 g_start_time = 0; |
21 "ALL", "DEBUG", "INFO", "WARNING", "SEVERE", "OFF" | 32 |
| 33 // Array indices are the Log::Level enum values. |
| 34 const char* kLevelToName[] = { |
| 35 "ALL", // kAll |
| 36 "DEBUG", // kDebug |
| 37 "INFO", // kInfo |
| 38 "WARNING", // kWarning |
| 39 "SEVERE", // kError |
| 40 "OFF", // kOff |
22 }; | 41 }; |
23 | 42 |
24 // Map between Log::Level and WebDriverLog::WebDriverLevel. | 43 const char* LevelToName(Log::Level level) { |
25 // Array indices are the Log::Level enum values. | 44 const int index = level - Log::kAll; |
26 WebDriverLog::WebDriverLevel kLogLevelToWebDriverLevels[] = { | 45 CHECK_GE(index, 0); |
27 WebDriverLog::kWdDebug, // kDebug | 46 CHECK_LT(static_cast<size_t>(index), arraysize(kLevelToName)); |
28 WebDriverLog::kWdInfo, // kLog | 47 return kLevelToName[index]; |
29 WebDriverLog::kWdWarning, // kWarning | 48 } |
30 WebDriverLog::kWdSevere // kError | 49 |
| 50 struct LevelPair { |
| 51 const char* name; |
| 52 Log::Level level; |
31 }; | 53 }; |
32 | 54 |
33 // Translates Log::Level to WebDriverLog::WebDriverLevel. | 55 const LevelPair kNameToLevel[] = { |
34 WebDriverLog::WebDriverLevel LogLevelToWebDriverLevel(Log::Level level) { | 56 {"ALL", Log::kAll}, |
35 const int index = level - Log::kDebug; | 57 {"DEBUG", Log::kDebug}, |
36 CHECK_GE(index, 0); | 58 {"INFO", Log::kInfo}, |
37 CHECK_LT(static_cast<size_t>(index), arraysize(kLogLevelToWebDriverLevels)); | 59 {"WARNING", Log::kWarning}, |
38 return kLogLevelToWebDriverLevels[index]; | 60 {"SEVERE", Log::kError}, |
| 61 {"OFF", Log::kOff}, |
| 62 }; |
| 63 |
| 64 Log::Level GetLevelFromSeverity(int severity) { |
| 65 switch (severity) { |
| 66 case logging::LOG_FATAL: |
| 67 case logging::LOG_ERROR_REPORT: |
| 68 case logging::LOG_ERROR: |
| 69 return Log::kError; |
| 70 case logging::LOG_WARNING: |
| 71 return Log::kWarning; |
| 72 case logging::LOG_INFO: |
| 73 return Log::kInfo; |
| 74 case logging::LOG_VERBOSE: |
| 75 default: |
| 76 return Log::kDebug; |
| 77 } |
39 } | 78 } |
40 | 79 |
41 // Returns WD wire protocol level name for a WebDriverLog::WebDriverLevel. | 80 bool InternalIsVLogOn(int vlog_level) { |
42 std::string GetWebDriverLevelName( | 81 return GetLevelFromSeverity(vlog_level * -1) >= g_log_level; |
43 const WebDriverLog::WebDriverLevel level) { | 82 } |
44 const int index = level - WebDriverLog::kWdAll; | 83 |
45 CHECK_GE(index, 0); | 84 bool HandleLogMessage(int severity, |
46 CHECK_LT(static_cast<size_t>(index), arraysize(kWebDriverLevelNames)); | 85 const char* file, |
47 return kWebDriverLevelNames[index]; | 86 int line, |
| 87 size_t message_start, |
| 88 const std::string& str) { |
| 89 Log::Level level = GetLevelFromSeverity(severity); |
| 90 if (level < g_log_level) |
| 91 return true; |
| 92 |
| 93 std::string message = str.substr(message_start); |
| 94 const char* level_name = "UNKNOWN"; |
| 95 switch (level) { |
| 96 case Log::kDebug: |
| 97 level_name = "DEBUG"; |
| 98 break; |
| 99 case Log::kInfo: |
| 100 level_name = "INFO"; |
| 101 break; |
| 102 case Log::kWarning: |
| 103 level_name = "WARNING"; |
| 104 break; |
| 105 case Log::kError: |
| 106 level_name = "ERROR"; |
| 107 break; |
| 108 default: |
| 109 break; |
| 110 } |
| 111 std::string entry = base::StringPrintf( |
| 112 "[%.3lf][%s]: %s", |
| 113 base::TimeDelta(base::TimeTicks::Now() - |
| 114 base::TimeTicks::FromInternalValue(g_start_time)) |
| 115 .InSecondsF(), |
| 116 level_name, |
| 117 message.c_str()); |
| 118 |
| 119 fprintf(stderr, "%s", entry.c_str()); |
| 120 fflush(stderr); |
| 121 return true; |
48 } | 122 } |
49 | 123 |
50 } // namespace | 124 } // namespace |
51 | 125 |
52 bool WebDriverLog::NameToLevel( | 126 bool WebDriverLog::NameToLevel(const std::string& name, Log::Level* out_level) { |
53 const std::string& name, WebDriverLog::WebDriverLevel* out_level) { | 127 for (size_t i = 0; i < arraysize(kNameToLevel); ++i) { |
54 for (size_t i = 0; i < arraysize(kWebDriverLevelNames); ++i) { | 128 if (name == kNameToLevel[i].name) { |
55 if (name == kWebDriverLevelNames[i]) { | 129 *out_level = kNameToLevel[i].level; |
56 CHECK_LE(WebDriverLog::kWdAll + i, | |
57 static_cast<size_t>(WebDriverLog::kWdOff)); | |
58 *out_level = | |
59 static_cast<WebDriverLog::WebDriverLevel>(WebDriverLog::kWdAll + i); | |
60 return true; | 130 return true; |
61 } | 131 } |
62 } | 132 } |
63 return false; | 133 return false; |
64 } | 134 } |
65 | 135 |
66 WebDriverLog::WebDriverLog( | 136 WebDriverLog::WebDriverLog(const std::string& type, Log::Level min_level) |
67 const std::string& type, WebDriverLog::WebDriverLevel min_wd_level) | 137 : type_(type), min_level_(min_level), entries_(new base::ListValue()) { |
68 : type_(type), | |
69 min_wd_level_(min_wd_level), | |
70 entries_(new base::ListValue()) { | |
71 VLOG(1) << "Log(" << type_ << ", " << min_wd_level_ << ")"; | |
72 } | 138 } |
73 | 139 |
74 WebDriverLog::~WebDriverLog() { | 140 WebDriverLog::~WebDriverLog() { |
75 VLOG(1) << "Log type '" << type_ << "' lost " | 141 VLOG(1) << "Log type '" << type_ << "' lost " |
76 << entries_->GetSize() << " entries on destruction"; | 142 << entries_->GetSize() << " entries on destruction"; |
77 } | 143 } |
78 | 144 |
79 const std::string& WebDriverLog::GetType() { | |
80 return type_; | |
81 } | |
82 | |
83 void WebDriverLog::AddEntryTimestamped(const base::Time& timestamp, | |
84 Log::Level level, | |
85 const std::string& message) { | |
86 const WebDriverLog::WebDriverLevel wd_level = LogLevelToWebDriverLevel(level); | |
87 if (wd_level < min_wd_level_) | |
88 return; | |
89 scoped_ptr<base::DictionaryValue> log_entry_dict(new base::DictionaryValue()); | |
90 log_entry_dict->SetDouble("timestamp", | |
91 static_cast<int64>(timestamp.ToJsTime())); | |
92 log_entry_dict->SetString("level", GetWebDriverLevelName(wd_level)); | |
93 log_entry_dict->SetString("message", message); | |
94 entries_->Append(log_entry_dict.release()); | |
95 } | |
96 | |
97 scoped_ptr<base::ListValue> WebDriverLog::GetAndClearEntries() { | 145 scoped_ptr<base::ListValue> WebDriverLog::GetAndClearEntries() { |
98 scoped_ptr<base::ListValue> ret(entries_.release()); | 146 scoped_ptr<base::ListValue> ret(entries_.release()); |
99 entries_.reset(new base::ListValue()); | 147 entries_.reset(new base::ListValue()); |
100 return ret.Pass(); | 148 return ret.Pass(); |
101 } | 149 } |
102 | 150 |
| 151 void WebDriverLog::AddEntryTimestamped(const base::Time& timestamp, |
| 152 Log::Level level, |
| 153 const std::string& message) { |
| 154 if (level < min_level_) |
| 155 return; |
| 156 |
| 157 scoped_ptr<base::DictionaryValue> log_entry_dict(new base::DictionaryValue()); |
| 158 log_entry_dict->SetDouble("timestamp", |
| 159 static_cast<int64>(timestamp.ToJsTime())); |
| 160 log_entry_dict->SetString("level", LevelToName(level)); |
| 161 log_entry_dict->SetString("message", message); |
| 162 entries_->Append(log_entry_dict.release()); |
| 163 } |
| 164 |
| 165 const std::string& WebDriverLog::type() const { |
| 166 return type_; |
| 167 } |
| 168 |
| 169 Log::Level WebDriverLog::min_level() const { |
| 170 return min_level_; |
| 171 } |
| 172 |
| 173 bool InitLogging() { |
| 174 InitLogging(&InternalIsVLogOn); |
| 175 g_start_time = base::TimeTicks::Now().ToInternalValue(); |
| 176 |
| 177 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| 178 if (cmd_line->HasSwitch("log-path")) { |
| 179 g_log_level = Log::kInfo; |
| 180 base::FilePath log_path = cmd_line->GetSwitchValuePath("log-path"); |
| 181 #if defined(OS_WIN) |
| 182 FILE* redir_stderr = _wfreopen(log_path.value().c_str(), L"w", stderr); |
| 183 #else |
| 184 FILE* redir_stderr = freopen(log_path.value().c_str(), "w", stderr); |
| 185 #endif |
| 186 if (!redir_stderr) { |
| 187 printf("Failed to redirect stderr to log file.\n"); |
| 188 return false; |
| 189 } |
| 190 } |
| 191 if (cmd_line->HasSwitch("silent")) |
| 192 g_log_level = Log::kOff; |
| 193 |
| 194 if (cmd_line->HasSwitch("verbose")) { |
| 195 g_log_level = Log::kAll; |
| 196 } else { |
| 197 #if defined(OS_POSIX) |
| 198 // Close stderr on exec, so that Chrome log spew doesn't confuse users. |
| 199 fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC); |
| 200 #endif |
| 201 } |
| 202 |
| 203 // Turn on VLOG for chromedriver. This is parsed during logging::InitLogging. |
| 204 cmd_line->AppendSwitchASCII("vmodule", "*/chrome/test/chromedriver/*=3"); |
| 205 |
| 206 logging::SetMinLogLevel(logging::LOG_WARNING); |
| 207 logging::SetLogItems(false, // enable_process_id |
| 208 false, // enable_thread_id |
| 209 false, // enable_timestamp |
| 210 false); // enable_tickcount |
| 211 logging::SetLogMessageHandler(&HandleLogMessage); |
| 212 |
| 213 logging::LoggingSettings logging_settings; |
| 214 logging_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; |
| 215 return logging::InitLogging(logging_settings); |
| 216 } |
| 217 |
103 Status CreateLogs(const Capabilities& capabilities, | 218 Status CreateLogs(const Capabilities& capabilities, |
104 ScopedVector<WebDriverLog>* out_devtools_logs, | 219 ScopedVector<WebDriverLog>* out_logs, |
| 220 scoped_ptr<WebDriverLog>* out_driver_log, |
105 ScopedVector<DevToolsEventListener>* out_listeners) { | 221 ScopedVector<DevToolsEventListener>* out_listeners) { |
106 ScopedVector<WebDriverLog> devtools_logs; | 222 ScopedVector<WebDriverLog> logs; |
107 ScopedVector<DevToolsEventListener> listeners; | 223 ScopedVector<DevToolsEventListener> listeners; |
108 WebDriverLog::WebDriverLevel browser_log_level = WebDriverLog::kWdInfo; | 224 Log::Level browser_log_level = Log::kOff; |
| 225 Log::Level driver_log_level = Log::kWarning; |
| 226 const LoggingPrefs& prefs = capabilities.logging_prefs; |
109 | 227 |
110 if (capabilities.logging_prefs) { | 228 const char kBrowserLogType[] = "browser"; |
111 for (DictionaryValue::Iterator pref(*capabilities.logging_prefs); | 229 const char kDriverLogType[] = "driver"; |
112 !pref.IsAtEnd(); pref.Advance()) { | 230 for (LoggingPrefs::const_iterator iter = prefs.begin(); |
113 const std::string type = pref.key(); | 231 iter != prefs.end(); |
114 std::string level_name; | 232 ++iter) { |
115 if (!pref.value().GetAsString(&level_name)) { | 233 std::string type = iter->first; |
116 return Status(kUnknownError, | 234 Log::Level level = iter->second; |
117 "logging level must be a string for log type: " + type); | 235 if (type == "performance") { |
| 236 if (level != Log::kOff) { |
| 237 WebDriverLog* log = new WebDriverLog(type, Log::kAll); |
| 238 logs.push_back(log); |
| 239 listeners.push_back(new PerformanceLogger(log)); |
118 } | 240 } |
119 WebDriverLog::WebDriverLevel level = WebDriverLog::kWdOff; | 241 } else if (type == kBrowserLogType) { |
120 if (!WebDriverLog::NameToLevel(level_name, &level)) { | 242 browser_log_level = level; |
121 return Status(kUnknownError, | 243 } else if (type == kDriverLogType) { |
122 "invalid log level \"" + level_name + | 244 driver_log_level = level; |
123 "\" for type: " + type); | 245 } else { |
124 } | 246 // Driver "should" ignore unrecognized log types, per Selenium tests. |
125 if ("performance" == type) { | 247 // For example the Java client passes the "client" log type in the caps, |
126 if (WebDriverLog::kWdOff != level) { | 248 // which the server should never provide. |
127 WebDriverLog* log = new WebDriverLog(type, WebDriverLog::kWdAll); | 249 LOG(WARNING) << "Ignoring unrecognized log type: " << type; |
128 devtools_logs.push_back(log); | |
129 listeners.push_back(new PerformanceLogger(log)); | |
130 } | |
131 } else if ("browser" == type) { | |
132 browser_log_level = level; | |
133 } else { | |
134 // Driver "should" ignore unrecognized log types, per Selenium tests. | |
135 // For example the Java client passes the "client" log type in the caps, | |
136 // which the server should never provide. | |
137 LOG(WARNING) << "Ignoring unrecognized log type: LoggingPrefs." << type; | |
138 } | |
139 } | 250 } |
140 } | 251 } |
141 // Create "browser" log -- should always exist. | 252 // Create "browser" log -- should always exist. |
142 WebDriverLog* browser_log = new WebDriverLog("browser", browser_log_level); | 253 WebDriverLog* browser_log = |
143 devtools_logs.push_back(browser_log); | 254 new WebDriverLog(kBrowserLogType, browser_log_level); |
| 255 logs.push_back(browser_log); |
144 // If the level is OFF, don't even bother listening for DevTools events. | 256 // If the level is OFF, don't even bother listening for DevTools events. |
145 if (browser_log_level != WebDriverLog::kWdOff) | 257 if (browser_log_level != Log::kOff) |
146 listeners.push_back(new ConsoleLogger(browser_log)); | 258 listeners.push_back(new ConsoleLogger(browser_log)); |
147 | 259 |
148 out_devtools_logs->swap(devtools_logs); | 260 // Create "driver" log -- should always exist. |
| 261 scoped_ptr<WebDriverLog> driver_log( |
| 262 new WebDriverLog(kDriverLogType, driver_log_level)); |
| 263 |
| 264 out_logs->swap(logs); |
| 265 *out_driver_log = driver_log.Pass(); |
149 out_listeners->swap(listeners); | 266 out_listeners->swap(listeners); |
150 return Status(kOk); | 267 return Status(kOk); |
151 } | 268 } |
OLD | NEW |