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 std::string ConvertForDisplayInternal(const std::string& input) { |
46 CHECK_LT(static_cast<size_t>(index), arraysize(kWebDriverLevelNames)); | 85 size_t left = input.find("{"); |
47 return kWebDriverLevelNames[index]; | 86 size_t right = input.rfind("}"); |
87 if (left == std::string::npos || right == std::string::npos) | |
88 return input.substr(0, 10 << 10); | |
89 | |
90 scoped_ptr<base::Value> value( | |
91 base::JSONReader::Read(input.substr(left, right - left + 1))); | |
92 if (!value) | |
93 return input.substr(0, 10 << 10); | |
94 std::string display = input.substr(0, left) + | |
95 PrettyPrintValue(TruncateContainedStrings(value.get())); | |
96 if (input.length() > right) | |
97 display += input.substr(right + 1); | |
98 return display; | |
99 } | |
100 | |
101 // Pretty prints encapsulated JSON and truncates long strings for display. | |
102 std::string ConvertForDisplay(const std::string& input) { | |
103 std::string display = ConvertForDisplayInternal(input); | |
104 return display; | |
105 } | |
106 | |
107 bool HandleLogMessage(int severity, | |
108 const char* file, | |
109 int line, | |
110 size_t message_start, | |
111 const std::string& str) { | |
112 Log::Level level = GetLevelFromSeverity(severity); | |
113 if (level < g_log_level) | |
114 return true; | |
115 | |
116 std::string message = str.substr(message_start); | |
117 const char* level_name = "UNKNOWN"; | |
118 switch (level) { | |
119 case Log::kDebug: | |
120 level_name = "DEBUG"; | |
121 break; | |
122 case Log::kInfo: | |
123 level_name = "INFO"; | |
124 break; | |
125 case Log::kWarning: | |
126 level_name = "WARNING"; | |
127 break; | |
128 case Log::kError: | |
129 level_name = "ERROR"; | |
130 break; | |
131 default: | |
132 break; | |
133 } | |
134 std::string entry = base::StringPrintf( | |
135 "[%.3lf][%s]: %s", | |
136 base::TimeDelta(base::TimeTicks::Now() - | |
137 base::TimeTicks::FromInternalValue(g_start_time)) | |
138 .InSecondsF(), | |
139 level_name, | |
140 ConvertForDisplay(message).c_str()); | |
141 const char* format = "%s\n"; | |
142 if (entry[entry.length() - 1] == '\n') | |
143 format = "%s"; | |
144 | |
145 fprintf(stderr, format, entry.c_str()); | |
146 fflush(stderr); | |
147 return true; | |
48 } | 148 } |
49 | 149 |
50 } // namespace | 150 } // namespace |
51 | 151 |
52 bool WebDriverLog::NameToLevel( | 152 bool WebDriverLog::NameToLevel(const std::string& name, Log::Level* out_level) { |
53 const std::string& name, WebDriverLog::WebDriverLevel* out_level) { | 153 for (size_t i = 0; i < arraysize(kNameToLevel); ++i) { |
54 for (size_t i = 0; i < arraysize(kWebDriverLevelNames); ++i) { | 154 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.
| |
55 if (name == kWebDriverLevelNames[i]) { | 155 *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; | 156 return true; |
61 } | 157 } |
62 } | 158 } |
63 return false; | 159 return false; |
64 } | 160 } |
65 | 161 |
66 WebDriverLog::WebDriverLog( | 162 WebDriverLog::WebDriverLog(const std::string& type, Log::Level min_level) |
67 const std::string& type, WebDriverLog::WebDriverLevel min_wd_level) | 163 : 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 } | 164 } |
73 | 165 |
74 WebDriverLog::~WebDriverLog() { | 166 WebDriverLog::~WebDriverLog() { |
75 VLOG(1) << "Log type '" << type_ << "' lost " | 167 VLOG(1) << "Log type '" << type_ << "' lost " |
76 << entries_->GetSize() << " entries on destruction"; | 168 << entries_->GetSize() << " entries on destruction"; |
77 } | 169 } |
78 | 170 |
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() { | 171 scoped_ptr<base::ListValue> WebDriverLog::GetAndClearEntries() { |
98 scoped_ptr<base::ListValue> ret(entries_.release()); | 172 scoped_ptr<base::ListValue> ret(entries_.release()); |
99 entries_.reset(new base::ListValue()); | 173 entries_.reset(new base::ListValue()); |
100 return ret.Pass(); | 174 return ret.Pass(); |
101 } | 175 } |
102 | 176 |
177 void WebDriverLog::AddEntryTimestamped(const base::Time& timestamp, | |
178 Log::Level level, | |
179 const std::string& message) { | |
180 if (level < min_level_) | |
181 return; | |
182 | |
183 scoped_ptr<base::DictionaryValue> log_entry_dict(new base::DictionaryValue()); | |
184 log_entry_dict->SetDouble("timestamp", | |
185 static_cast<int64>(timestamp.ToJsTime())); | |
186 log_entry_dict->SetString("level", LevelToName(level)); | |
187 log_entry_dict->SetString("message", message); | |
188 entries_->Append(log_entry_dict.release()); | |
189 } | |
190 | |
191 const std::string& WebDriverLog::type() const { | |
192 return type_; | |
193 } | |
194 | |
195 Log::Level WebDriverLog::min_level() const { | |
196 return min_level_; | |
197 } | |
198 | |
199 bool InitLogging() { | |
200 InitLogging(&InternalIsVLogOn); | |
201 g_start_time = base::TimeTicks::Now().ToInternalValue(); | |
202 | |
203 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | |
204 if (cmd_line->HasSwitch("log-path")) { | |
205 g_log_level = Log::kInfo; | |
206 base::FilePath log_path = cmd_line->GetSwitchValuePath("log-path"); | |
207 #if defined(OS_WIN) | |
208 FILE* redir_stderr = _wfreopen(log_path.value().c_str(), L"w", stderr); | |
209 #else | |
210 FILE* redir_stderr = freopen(log_path.value().c_str(), "w", stderr); | |
211 #endif | |
212 if (!redir_stderr) { | |
213 printf("Failed to redirect stderr to log file.\n"); | |
214 return false; | |
215 } | |
216 } | |
217 if (cmd_line->HasSwitch("silent")) | |
218 g_log_level = Log::kOff; | |
219 | |
220 if (cmd_line->HasSwitch("verbose")) { | |
221 g_log_level = Log::kAll; | |
222 } else { | |
223 #if defined(OS_POSIX) | |
224 // Close stderr on exec, so that Chrome log spew doesn't confuse users. | |
225 fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC); | |
226 #endif | |
227 } | |
228 | |
229 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.
| |
230 | |
231 logging::SetMinLogLevel(logging::LOG_WARNING); | |
232 logging::SetLogItems(false, // enable_process_id | |
233 false, // enable_thread_id | |
234 false, // enable_timestamp | |
235 false); // enable_tickcount | |
236 logging::SetLogMessageHandler(&HandleLogMessage); | |
237 | |
238 logging::LoggingSettings logging_settings; | |
239 logging_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; | |
240 return logging::InitLogging(logging_settings); | |
241 } | |
242 | |
103 Status CreateLogs(const Capabilities& capabilities, | 243 Status CreateLogs(const Capabilities& capabilities, |
104 ScopedVector<WebDriverLog>* out_devtools_logs, | 244 ScopedVector<WebDriverLog>* out_logs, |
245 scoped_ptr<WebDriverLog>* out_driver_log, | |
105 ScopedVector<DevToolsEventListener>* out_listeners) { | 246 ScopedVector<DevToolsEventListener>* out_listeners) { |
106 ScopedVector<WebDriverLog> devtools_logs; | 247 ScopedVector<WebDriverLog> logs; |
107 ScopedVector<DevToolsEventListener> listeners; | 248 ScopedVector<DevToolsEventListener> listeners; |
108 WebDriverLog::WebDriverLevel browser_log_level = WebDriverLog::kWdInfo; | 249 Log::Level browser_log_level = Log::kOff; |
250 Log::Level driver_log_level = Log::kWarning; | |
251 const LoggingPrefs& prefs = capabilities.logging_prefs; | |
109 | 252 |
110 if (capabilities.logging_prefs) { | 253 const char kBrowserLogType[] = "browser"; |
111 for (DictionaryValue::Iterator pref(*capabilities.logging_prefs); | 254 const char kDriverLogType[] = "driver"; |
112 !pref.IsAtEnd(); pref.Advance()) { | 255 for (LoggingPrefs::const_iterator iter = prefs.begin(); |
113 const std::string type = pref.key(); | 256 iter != prefs.end(); |
114 std::string level_name; | 257 ++iter) { |
115 if (!pref.value().GetAsString(&level_name)) { | 258 std::string type = iter->first; |
116 return Status(kUnknownError, | 259 Log::Level level = iter->second; |
117 "logging level must be a string for log type: " + type); | 260 if (type == "performance") { |
261 if (level != Log::kOff) { | |
262 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
| |
263 logs.push_back(log); | |
264 listeners.push_back(new PerformanceLogger(log)); | |
118 } | 265 } |
119 WebDriverLog::WebDriverLevel level = WebDriverLog::kWdOff; | 266 } else if (type == kBrowserLogType) { |
120 if (!WebDriverLog::NameToLevel(level_name, &level)) { | 267 browser_log_level = level; |
121 return Status(kUnknownError, | 268 } else if (type == kDriverLogType) { |
122 "invalid log level \"" + level_name + | 269 driver_log_level = level; |
123 "\" for type: " + type); | 270 } else { |
124 } | 271 // Driver "should" ignore unrecognized log types, per Selenium tests. |
125 if ("performance" == type) { | 272 // For example the Java client passes the "client" log type in the caps, |
126 if (WebDriverLog::kWdOff != level) { | 273 // which the server should never provide. |
127 WebDriverLog* log = new WebDriverLog(type, WebDriverLog::kWdAll); | 274 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 } | 275 } |
140 } | 276 } |
141 // Create "browser" log -- should always exist. | 277 // Create "browser" log -- should always exist. |
142 WebDriverLog* browser_log = new WebDriverLog("browser", browser_log_level); | 278 WebDriverLog* browser_log = |
143 devtools_logs.push_back(browser_log); | 279 new WebDriverLog(kBrowserLogType, browser_log_level); |
280 logs.push_back(browser_log); | |
144 // If the level is OFF, don't even bother listening for DevTools events. | 281 // If the level is OFF, don't even bother listening for DevTools events. |
145 if (browser_log_level != WebDriverLog::kWdOff) | 282 if (browser_log_level != Log::kOff) |
146 listeners.push_back(new ConsoleLogger(browser_log)); | 283 listeners.push_back(new ConsoleLogger(browser_log)); |
147 | 284 |
148 out_devtools_logs->swap(devtools_logs); | 285 // Create "driver" log -- should always exist. |
286 scoped_ptr<WebDriverLog> driver_log( | |
287 new WebDriverLog(kDriverLogType, driver_log_level)); | |
288 | |
289 out_logs->swap(logs); | |
290 *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.
| |
149 out_listeners->swap(listeners); | 291 out_listeners->swap(listeners); |
150 return Status(kOk); | 292 return Status(kOk); |
151 } | 293 } |
OLD | NEW |