Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/javascript/webdriver/logging.js
2867 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License. You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied. See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
/**
19
* @fileoverview Defines WebDriver's logging system. The logging system is
20
* broken into major components: local and remote logging.
21
*
22
* The local logging API, which is anchored by the
23
* {@link webdriver.logging.Logger Logger} class, is similar to Java's
24
* logging API. Loggers, retrieved by {@link webdriver.logging.getLogger}, use
25
* hierarchical, dot-delimited namespaces
26
* (e.g. "" > "webdriver" > "webdriver.logging"). Recorded log messages are
27
* represented by the {@link webdriver.logging.LogRecord LogRecord} class. You
28
* can capture log records by
29
* {@linkplain webdriver.logging.Logger#addHandler attaching} a handler function
30
* to the desired logger. For convenience, you can quickly enable logging to
31
* the console by simply calling
32
* {@link webdriver.logging.installConsoleHandler()}.
33
*
34
* The [remote logging API](https://github.com/SeleniumHQ/selenium/wiki/Logging)
35
* allows you to retrieve logs from a remote WebDriver server. This API uses the
36
* {@link Preferences} class to define desired log levels prior to create a
37
* WebDriver session:
38
*
39
* var prefs = new webdriver.logging.Preferences();
40
* prefs.setLevel(webdriver.logging.Type.BROWSER,
41
* webdriver.logging.Level.DEBUG);
42
*
43
* var caps = webdriver.Capabilities.chrome();
44
* caps.setLoggingPrefs(prefs);
45
* // ...
46
*
47
* Remote log entries are represented by the {@link Entry} class and may be
48
* retrieved via {@link webdriver.WebDriver.Logs}:
49
*
50
* driver.manage().logs().get(webdriver.logging.Type.BROWSER)
51
* .then(function(entries) {
52
* entries.forEach(function(entry) {
53
* console.log('[%s] %s', entry.level.name, entry.message);
54
* });
55
* });
56
*
57
* **NOTE:** Only a few browsers support the remote logging API (notably
58
* Firefox and Chrome). Firefox supports basic logging functionality, while
59
* Chrome exposes robust
60
* [performance logging](https://chromedriver.chromium.org/logging)
61
* options. Remote logging is still considered a non-standard feature, and the
62
* APIs exposed by this module for it are non-frozen. Once logging is officially
63
* defined by the [W3C WebDriver spec](http://www.w3.org/TR/webdriver/), this
64
* module will be updated to use a consistent API for local and remote logging.
65
*/
66
67
goog.module('webdriver.logging');
68
goog.module.declareLegacyNamespace();
69
70
var LogManager = goog.require('goog.debug.LogManager');
71
var LogRecord = goog.require('goog.debug.LogRecord');
72
var Logger = goog.require('goog.debug.Logger');
73
var googString = goog.require('goog.string');
74
75
var padNumber = googString.padNumber;
76
77
/** @const */
78
exports.LogRecord = LogRecord;
79
80
81
/** @const */
82
exports.Logger = Logger;
83
84
85
/** @const */
86
exports.Level = Logger.Level;
87
88
89
/**
90
* DEBUG is a message level for debugging messages and has the same log level
91
* as the {@link Logger.Level.CONFIG} message level.
92
* @const {!Logger.Level}
93
*/
94
Logger.Level.DEBUG = new Logger.Level('DEBUG', Logger.Level.CONFIG.value);
95
96
97
/**
98
* Finds a named logger.
99
*
100
* @param {string=} opt_name The dot-delimited logger name, such as
101
* "webdriver.logging.Logger". Defaults to the name of the root logger.
102
* @return {!Logger} The named logger.
103
*/
104
function getLogger(opt_name) {
105
return LogManager.getLogger(opt_name || Logger.ROOT_LOGGER_NAME);
106
}
107
exports.getLogger = getLogger;
108
109
110
/**
111
* Logs all messages to the Console API.
112
*/
113
function consoleHandler(record) {
114
if (typeof console === 'undefined' || !console) {
115
return;
116
}
117
record = /** @type {!LogRecord} */(record);
118
var timestamp = new Date(record.getMillis());
119
var msg =
120
'[' + timestamp.getUTCFullYear() + '-' +
121
padNumber(timestamp.getUTCMonth() + 1, 2) + '-' +
122
padNumber(timestamp.getUTCDate(), 2) + 'T' +
123
padNumber(timestamp.getUTCHours(), 2) + ':' +
124
padNumber(timestamp.getUTCMinutes(), 2) + ':' +
125
padNumber(timestamp.getUTCSeconds(), 2) + 'Z]' +
126
'[' + record.getLevel().name + ']' +
127
'[' + record.getLoggerName() + '] ' +
128
record.getMessage();
129
130
var level = record.getLevel().value;
131
if (level >= Logger.Level.SEVERE.value) {
132
console.error(msg);
133
} else if (level >= Logger.Level.WARNING.value) {
134
console.warn(msg);
135
} else {
136
console.log(msg);
137
}
138
}
139
140
141
/**
142
* Adds the console handler to the given logger. The console handler will log
143
* all messages using the JavaScript Console API.
144
*
145
* @param {Logger=} opt_logger The logger to add the handler to; defaults
146
* to the root logger.
147
* @see exports.removeConsoleHandler
148
*/
149
exports.addConsoleHandler = function(opt_logger) {
150
var logger = opt_logger || getLogger();
151
logger.addHandler(consoleHandler);
152
};
153
154
155
/**
156
* Installs the console log handler on the root logger.
157
* @see exports.addConsoleHandler
158
*/
159
exports.installConsoleHandler = function() {
160
exports.addConsoleHandler();
161
};
162
163
164
/**
165
* Removes the console log handler from the given logger.
166
*
167
* @param {Logger=} opt_logger The logger to remove the handler from; defaults
168
* to the root logger.
169
* @see exports.addConsoleHandler
170
*/
171
exports.removeConsoleHandler = function(opt_logger) {
172
var logger = opt_logger || getLogger();
173
logger.removeHandler(consoleHandler);
174
};
175
176
177
/**
178
* Converts a level name or value to a {@link webdriver.logging.Level} value.
179
* If the name/value is not recognized, {@link webdriver.logging.Level.ALL}
180
* will be returned.
181
* @param {(number|string)} nameOrValue The log level name, or value, to
182
* convert .
183
* @return {!Logger.Level} The converted level.
184
*/
185
function getLevel(nameOrValue) {
186
// DEBUG is not a predefined Closure log level, but maps to CONFIG. Since
187
// DEBUG is a predefined level for the WebDriver protocol, we prefer it over
188
// CONFIG.
189
if ('DEBUG' === nameOrValue || Logger.Level.DEBUG.value === nameOrValue) {
190
return Logger.Level.DEBUG;
191
} else if (goog.isString(nameOrValue)) {
192
return Logger.Level.getPredefinedLevel(/** @type {string} */(nameOrValue))
193
|| Logger.Level.ALL;
194
} else {
195
return Logger.Level.getPredefinedLevelByValue(
196
/** @type {number} */(nameOrValue)) || Logger.Level.ALL;
197
}
198
}
199
exports.getLevel = getLevel;
200
201
202
/**
203
* Normalizes a {@link Logger.Level} to one of the distinct values recognized
204
* by WebDriver's wire protocol.
205
* @param {!Logger.Level} level The input level.
206
* @return {!Logger.Level} The normalized level.
207
*/
208
function normalizeLevel(level) {
209
if (level.value <= Logger.Level.ALL.value) { // ALL is 0.
210
return Logger.Level.ALL;
211
212
} else if (level.value === Logger.Level.OFF.value) { // OFF is Infinity
213
return Logger.Level.OFF;
214
215
} else if (level.value < Logger.Level.INFO.value) {
216
return Logger.Level.DEBUG;
217
218
} else if (level.value < Logger.Level.WARNING.value) {
219
return Logger.Level.INFO;
220
221
} else if (level.value < Logger.Level.SEVERE.value) {
222
return Logger.Level.WARNING;
223
224
} else {
225
return Logger.Level.SEVERE;
226
}
227
}
228
229
230
/**
231
* Common log types.
232
* @enum {string}
233
*/
234
var Type = {
235
/** Logs originating from the browser. */
236
BROWSER: 'browser',
237
/** Logs from a WebDriver client. */
238
CLIENT: 'client',
239
/** Logs from a WebDriver implementation. */
240
DRIVER: 'driver',
241
/** Logs related to performance. */
242
PERFORMANCE: 'performance',
243
/** Logs from the remote server. */
244
SERVER: 'server'
245
};
246
exports.Type = Type;
247
248
249
/**
250
* Describes the log preferences for a WebDriver session.
251
* @final
252
*/
253
var Preferences = goog.defineClass(null, {
254
/** @constructor */
255
constructor: function() {
256
/** @private {!Object.<string, Logger.Level>} */
257
this.prefs_ = {};
258
},
259
260
/**
261
* Sets the desired logging level for a particular log type.
262
* @param {(string|Type)} type The log type.
263
* @param {!Logger.Level} level The desired log level.
264
*/
265
setLevel: function(type, level) {
266
this.prefs_[type] = normalizeLevel(level);
267
},
268
269
/**
270
* Converts this instance to its JSON representation.
271
* @return {!Object.<string, string>} The JSON representation of this set of
272
* preferences.
273
*/
274
toJSON: function() {
275
var obj = {};
276
for (var type in this.prefs_) {
277
if (this.prefs_.hasOwnProperty(type)) {
278
obj[type] = this.prefs_[type].name;
279
}
280
}
281
return obj;
282
}
283
});
284
exports.Preferences = Preferences;
285
286
287
/**
288
* A single log entry recorded by a WebDriver component, such as a remote
289
* WebDriver server.
290
* @final
291
*/
292
var Entry = goog.defineClass(null, {
293
/**
294
* @param {(!Logger.Level|string)} level The entry level.
295
* @param {string} message The log message.
296
* @param {number=} opt_timestamp The time this entry was generated, in
297
* milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the
298
* current time will be used.
299
* @param {string=} opt_type The log type, if known.
300
* @constructor
301
*/
302
constructor: function(level, message, opt_timestamp, opt_type) {
303
304
/** @type {!Logger.Level} */
305
this.level = goog.isString(level) ? getLevel(level) : level;
306
307
/** @type {string} */
308
this.message = message;
309
310
/** @type {number} */
311
this.timestamp = goog.isNumber(opt_timestamp) ? opt_timestamp : goog.now();
312
313
/** @type {string} */
314
this.type = opt_type || '';
315
},
316
317
statics: {
318
/**
319
* Converts a {@link goog.debug.LogRecord} into a
320
* {@link webdriver.logging.Entry}.
321
* @param {!LogRecord} logRecord The record to convert.
322
* @param {string=} opt_type The log type.
323
* @return {!Entry} The converted entry.
324
*/
325
fromClosureLogRecord: function(logRecord, opt_type) {
326
return new Entry(
327
normalizeLevel(/** @type {!Logger.Level} */(logRecord.getLevel())),
328
'[' + logRecord.getLoggerName() + '] ' + logRecord.getMessage(),
329
logRecord.getMillis(),
330
opt_type);
331
}
332
},
333
334
/**
335
* @return {{level: string, message: string, timestamp: number,
336
* type: string}} The JSON representation of this entry.
337
*/
338
toJSON: function() {
339
return {
340
'level': this.level.name,
341
'message': this.message,
342
'timestamp': this.timestamp,
343
'type': this.type
344
};
345
}
346
});
347
exports.Entry = Entry;
348
349