aboutsummaryrefslogtreecommitdiff
path: root/node_modules/selenium-webdriver/lib/logging.js
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-11-03 01:33:53 +0100
committerFlorian Dold <florian.dold@gmail.com>2016-11-03 01:33:53 +0100
commitd1291f67551c58168af43698a359cb5ddfd266b0 (patch)
tree55a13ed29fe1915e3f42f1b1b7038dafa2e975a7 /node_modules/selenium-webdriver/lib/logging.js
parentd0a0695fb5d34996850723f7d4b1b59c3df909c2 (diff)
node_modules
Diffstat (limited to 'node_modules/selenium-webdriver/lib/logging.js')
-rw-r--r--node_modules/selenium-webdriver/lib/logging.js670
1 files changed, 670 insertions, 0 deletions
diff --git a/node_modules/selenium-webdriver/lib/logging.js b/node_modules/selenium-webdriver/lib/logging.js
new file mode 100644
index 000000000..97f54924c
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/logging.js
@@ -0,0 +1,670 @@
+// Licensed to the Software Freedom Conservancy (SFC) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The SFC licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+'use strict';
+
+/**
+ * @fileoverview Defines WebDriver's logging system. The logging system is
+ * broken into major components: local and remote logging.
+ *
+ * The local logging API, which is anchored by the {@linkplain Logger} class is
+ * similar to Java's logging API. Loggers, retrieved by
+ * {@linkplain #getLogger getLogger(name)}, use hierarchical, dot-delimited
+ * namespaces (e.g. "" > "webdriver" > "webdriver.logging"). Recorded log
+ * messages are represented by the {@linkplain Entry} class. You can capture log
+ * records by {@linkplain Logger#addHandler attaching} a handler function to the
+ * desired logger. For convenience, you can quickly enable logging to the
+ * console by simply calling {@linkplain #installConsoleHandler
+ * installConsoleHandler}.
+ *
+ * The [remote logging API](https://github.com/SeleniumHQ/selenium/wiki/Logging)
+ * allows you to retrieve logs from a remote WebDriver server. This API uses the
+ * {@link Preferences} class to define desired log levels prior to creating
+ * a WebDriver session:
+ *
+ * var prefs = new logging.Preferences();
+ * prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
+ *
+ * var caps = Capabilities.chrome();
+ * caps.setLoggingPrefs(prefs);
+ * // ...
+ *
+ * Remote log entries, also represented by the {@link Entry} class, may be
+ * retrieved via {@link webdriver.WebDriver.Logs}:
+ *
+ * driver.manage().logs().get(logging.Type.BROWSER)
+ * .then(function(entries) {
+ * entries.forEach(function(entry) {
+ * console.log('[%s] %s', entry.level.name, entry.message);
+ * });
+ * });
+ *
+ * **NOTE:** Only a few browsers support the remote logging API (notably
+ * Firefox and Chrome). Firefox supports basic logging functionality, while
+ * Chrome exposes robust
+ * [performance logging](https://sites.google.com/a/chromium.org/chromedriver/logging)
+ * options. Remote logging is still considered a non-standard feature, and the
+ * APIs exposed by this module for it are non-frozen. This module will be
+ * updated, possibly breaking backwards-compatibility, once logging is
+ * officially defined by the
+ * [W3C WebDriver spec](http://www.w3.org/TR/webdriver/).
+ */
+
+/**
+ * Defines a message level that may be used to control logging output.
+ *
+ * @final
+ */
+class Level {
+ /**
+ * @param {string} name the level's name.
+ * @param {number} level the level's numeric value.
+ */
+ constructor(name, level) {
+ if (level < 0) {
+ throw new TypeError('Level must be >= 0');
+ }
+
+ /** @private {string} */
+ this.name_ = name;
+
+ /** @private {number} */
+ this.value_ = level;
+ }
+
+ /** This logger's name. */
+ get name() {
+ return this.name_;
+ }
+
+ /** The numeric log level. */
+ get value() {
+ return this.value_;
+ }
+
+ /** @override */
+ toString() {
+ return this.name;
+ }
+}
+
+/**
+ * Indicates no log messages should be recorded.
+ * @const
+ */
+Level.OFF = new Level('OFF', Infinity);
+
+
+/**
+ * Log messages with a level of `1000` or higher.
+ * @const
+ */
+Level.SEVERE = new Level('SEVERE', 1000);
+
+
+/**
+ * Log messages with a level of `900` or higher.
+ * @const
+ */
+Level.WARNING = new Level('WARNING', 900);
+
+
+/**
+ * Log messages with a level of `800` or higher.
+ * @const
+ */
+Level.INFO = new Level('INFO', 800);
+
+
+/**
+ * Log messages with a level of `700` or higher.
+ * @const
+ */
+Level.DEBUG = new Level('DEBUG', 700);
+
+
+/**
+ * Log messages with a level of `500` or higher.
+ * @const
+ */
+Level.FINE = new Level('FINE', 500);
+
+
+/**
+ * Log messages with a level of `400` or higher.
+ * @const
+ */
+Level.FINER = new Level('FINER', 400);
+
+
+/**
+ * Log messages with a level of `300` or higher.
+ * @const
+ */
+Level.FINEST = new Level('FINEST', 300);
+
+
+/**
+ * Indicates all log messages should be recorded.
+ * @const
+ */
+Level.ALL = new Level('ALL', 0);
+
+
+const ALL_LEVELS = /** !Set<Level> */new Set([
+ Level.OFF,
+ Level.SEVERE,
+ Level.WARNING,
+ Level.INFO,
+ Level.DEBUG,
+ Level.FINE,
+ Level.FINER,
+ Level.FINEST,
+ Level.ALL
+]);
+
+
+const LEVELS_BY_NAME = /** !Map<string, !Level> */ new Map([
+ [Level.OFF.name, Level.OFF],
+ [Level.SEVERE.name, Level.SEVERE],
+ [Level.WARNING.name, Level.WARNING],
+ [Level.INFO.name, Level.INFO],
+ [Level.DEBUG.name, Level.DEBUG],
+ [Level.FINE.name, Level.FINE],
+ [Level.FINER.name, Level.FINER],
+ [Level.FINEST.name, Level.FINEST],
+ [Level.ALL.name, Level.ALL]
+]);
+
+
+/**
+ * Converts a level name or value to a {@link Level} value. If the name/value
+ * is not recognized, {@link Level.ALL} will be returned.
+ *
+ * @param {(number|string)} nameOrValue The log level name, or value, to
+ * convert.
+ * @return {!Level} The converted level.
+ */
+function getLevel(nameOrValue) {
+ if (typeof nameOrValue === 'string') {
+ return LEVELS_BY_NAME.get(nameOrValue) || Level.ALL;
+ }
+ if (typeof nameOrValue !== 'number') {
+ throw new TypeError('not a string or number');
+ }
+ for (let level of ALL_LEVELS) {
+ if (nameOrValue >= level.value) {
+ return level;
+ }
+ }
+ return Level.ALL;
+}
+
+
+/**
+ * Describes a single log entry.
+ *
+ * @final
+ */
+class Entry {
+ /**
+ * @param {(!Level|string|number)} level The entry level.
+ * @param {string} message The log message.
+ * @param {number=} opt_timestamp The time this entry was generated, in
+ * milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the
+ * current time will be used.
+ * @param {string=} opt_type The log type, if known.
+ */
+ constructor(level, message, opt_timestamp, opt_type) {
+ this.level = level instanceof Level ? level : getLevel(level);
+ this.message = message;
+ this.timestamp =
+ typeof opt_timestamp === 'number' ? opt_timestamp : Date.now();
+ this.type = opt_type || '';
+ }
+
+ /**
+ * @return {{level: string, message: string, timestamp: number,
+ * type: string}} The JSON representation of this entry.
+ */
+ toJSON() {
+ return {
+ 'level': this.level.name,
+ 'message': this.message,
+ 'timestamp': this.timestamp,
+ 'type': this.type
+ };
+ }
+}
+
+
+/** @typedef {(string|function(): string)} */
+let Loggable;
+
+
+/**
+ * An object used to log debugging messages. Loggers use a hierarchical,
+ * dot-separated naming scheme. For instance, "foo" is considered the parent of
+ * the "foo.bar" and an ancestor of "foo.bar.baz".
+ *
+ * Each logger may be assigned a {@linkplain #setLevel log level}, which
+ * controls which level of messages will be reported to the
+ * {@linkplain #addHandler handlers} attached to this instance. If a log level
+ * is not explicitly set on a logger, it will inherit its parent.
+ *
+ * This class should never be directly instantiated. Instead, users should
+ * obtain logger references using the {@linkplain ./logging.getLogger()
+ * getLogger()} function.
+ *
+ * @final
+ */
+class Logger {
+ /**
+ * @param {string} name the name of this logger.
+ * @param {Level=} opt_level the initial level for this logger.
+ */
+ constructor(name, opt_level) {
+ /** @private {string} */
+ this.name_ = name;
+
+ /** @private {Level} */
+ this.level_ = opt_level || null;
+
+ /** @private {Logger} */
+ this.parent_ = null;
+
+ /** @private {Set<function(!Entry)>} */
+ this.handlers_ = null;
+ }
+
+ /** @return {string} the name of this logger. */
+ getName() {
+ return this.name_;
+ }
+
+ /**
+ * @param {Level} level the new level for this logger, or `null` if the logger
+ * should inherit its level from its parent logger.
+ */
+ setLevel(level) {
+ this.level_ = level;
+ }
+
+ /** @return {Level} the log level for this logger. */
+ getLevel() {
+ return this.level_;
+ }
+
+ /**
+ * @return {!Level} the effective level for this logger.
+ */
+ getEffectiveLevel() {
+ let logger = this;
+ let level;
+ do {
+ level = logger.level_;
+ logger = logger.parent_;
+ } while (logger && !level);
+ return level || Level.OFF;
+ }
+
+ /**
+ * @param {!Level} level the level to check.
+ * @return {boolean} whether messages recorded at the given level are loggable
+ * by this instance.
+ */
+ isLoggable(level) {
+ return level.value !== Level.OFF.value
+ && level.value >= this.getEffectiveLevel().value;
+ }
+
+ /**
+ * Adds a handler to this logger. The handler will be invoked for each message
+ * logged with this instance, or any of its descendants.
+ *
+ * @param {function(!Entry)} handler the handler to add.
+ */
+ addHandler(handler) {
+ if (!this.handlers_) {
+ this.handlers_ = new Set;
+ }
+ this.handlers_.add(handler);
+ }
+
+ /**
+ * Removes a handler from this logger.
+ *
+ * @param {function(!Entry)} handler the handler to remove.
+ * @return {boolean} whether a handler was successfully removed.
+ */
+ removeHandler(handler) {
+ if (!this.handlers_) {
+ return false;
+ }
+ return this.handlers_.delete(handler);
+ }
+
+ /**
+ * Logs a message at the given level. The message may be defined as a string
+ * or as a function that will return the message. If a function is provided,
+ * it will only be invoked if this logger's
+ * {@linkplain #getEffectiveLevel() effective log level} includes the given
+ * `level`.
+ *
+ * @param {!Level} level the level at which to log the message.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ log(level, loggable) {
+ if (!this.isLoggable(level)) {
+ return;
+ }
+ let message = '[' + this.name_ + '] '
+ + (typeof loggable === 'function' ? loggable() : loggable);
+ let entry = new Entry(level, message, Date.now());
+ for (let logger = this; !!logger; logger = logger.parent_) {
+ if (logger.handlers_) {
+ for (let handler of logger.handlers_) {
+ handler(entry);
+ }
+ }
+ }
+ }
+
+ /**
+ * Logs a message at the {@link Level.SEVERE} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ severe(loggable) {
+ this.log(Level.SEVERE, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.WARNING} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ warning(loggable) {
+ this.log(Level.WARNING, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.INFO} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ info(loggable) {
+ this.log(Level.INFO, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.DEBUG} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ debug(loggable) {
+ this.log(Level.DEBUG, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.FINE} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ fine(loggable) {
+ this.log(Level.FINE, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.FINER} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ finer(loggable) {
+ this.log(Level.FINER, loggable);
+ }
+
+ /**
+ * Logs a message at the {@link Level.FINEST} log level.
+ * @param {(string|function(): string)} loggable the message to log, or a
+ * function that will return the message.
+ */
+ finest(loggable) {
+ this.log(Level.FINEST, loggable);
+ }
+}
+
+
+/**
+ * Maintains a collection of loggers.
+ *
+ * @final
+ */
+class LogManager {
+ constructor() {
+ /** @private {!Map<string, !Logger>} */
+ this.loggers_ = new Map;
+ this.root_ = new Logger('', Level.OFF);
+ }
+
+ /**
+ * Retrieves a named logger, creating it in the process. This function will
+ * implicitly create the requested logger, and any of its parents, if they
+ * do not yet exist.
+ *
+ * @param {string} name the logger's name.
+ * @return {!Logger} the requested logger.
+ */
+ getLogger(name) {
+ if (!name) {
+ return this.root_;
+ }
+ let parent = this.root_;
+ for (let i = name.indexOf('.'); i != -1; i = name.indexOf('.', i + 1)) {
+ let parentName = name.substr(0, i);
+ parent = this.createLogger_(parentName, parent);
+ }
+ return this.createLogger_(name, parent);
+ }
+
+ /**
+ * Creates a new logger.
+ *
+ * @param {string} name the logger's name.
+ * @param {!Logger} parent the logger's parent.
+ * @return {!Logger} the new logger.
+ * @private
+ */
+ createLogger_(name, parent) {
+ if (this.loggers_.has(name)) {
+ return /** @type {!Logger} */(this.loggers_.get(name));
+ }
+ let logger = new Logger(name, null);
+ logger.parent_ = parent;
+ this.loggers_.set(name, logger);
+ return logger;
+ }
+}
+
+
+const logManager = new LogManager;
+
+
+/**
+ * Retrieves a named logger, creating it in the process. This function will
+ * implicitly create the requested logger, and any of its parents, if they
+ * do not yet exist.
+ *
+ * The log level will be unspecified for newly created loggers. Use
+ * {@link Logger#setLevel(level)} to explicitly set a level.
+ *
+ * @param {string} name the logger's name.
+ * @return {!Logger} the requested logger.
+ */
+function getLogger(name) {
+ return logManager.getLogger(name);
+}
+
+
+function pad(n) {
+ if (n > 10) {
+ return '' + n;
+ } else {
+ return '0' + n;
+ }
+}
+
+
+/**
+ * Logs all messages to the Console API.
+ * @param {!Entry} entry the entry to log.
+ */
+function consoleHandler(entry) {
+ if (typeof console === 'undefined' || !console) {
+ return;
+ }
+
+ var timestamp = new Date(entry.timestamp);
+ var msg =
+ '[' + timestamp.getUTCFullYear() + '-' +
+ pad(timestamp.getUTCMonth() + 1) + '-' +
+ pad(timestamp.getUTCDate()) + 'T' +
+ pad(timestamp.getUTCHours()) + ':' +
+ pad(timestamp.getUTCMinutes()) + ':' +
+ pad(timestamp.getUTCSeconds()) + 'Z] ' +
+ '[' + entry.level.name + '] ' +
+ entry.message;
+
+ var level = entry.level.value;
+ if (level >= Level.SEVERE.value) {
+ console.error(msg);
+ } else if (level >= Level.WARNING.value) {
+ console.warn(msg);
+ } else {
+ console.log(msg);
+ }
+}
+
+
+/**
+ * Adds the console handler to the given logger. The console handler will log
+ * all messages using the JavaScript Console API.
+ *
+ * @param {Logger=} opt_logger The logger to add the handler to; defaults
+ * to the root logger.
+ */
+function addConsoleHandler(opt_logger) {
+ let logger = opt_logger || logManager.root_;
+ logger.addHandler(consoleHandler);
+}
+
+
+/**
+ * Removes the console log handler from the given logger.
+ *
+ * @param {Logger=} opt_logger The logger to remove the handler from; defaults
+ * to the root logger.
+ * @see exports.addConsoleHandler
+ */
+function removeConsoleHandler(opt_logger) {
+ let logger = opt_logger || logManager.root_;
+ logger.removeHandler(consoleHandler);
+}
+
+
+/**
+ * Installs the console log handler on the root logger.
+ */
+function installConsoleHandler() {
+ addConsoleHandler(logManager.root_);
+}
+
+
+/**
+ * Common log types.
+ * @enum {string}
+ */
+const Type = {
+ /** Logs originating from the browser. */
+ BROWSER: 'browser',
+ /** Logs from a WebDriver client. */
+ CLIENT: 'client',
+ /** Logs from a WebDriver implementation. */
+ DRIVER: 'driver',
+ /** Logs related to performance. */
+ PERFORMANCE: 'performance',
+ /** Logs from the remote server. */
+ SERVER: 'server'
+};
+
+
+/**
+ * Describes the log preferences for a WebDriver session.
+ *
+ * @final
+ */
+class Preferences {
+ constructor() {
+ /** @private {!Map<string, !Level>} */
+ this.prefs_ = new Map;
+ }
+
+ /**
+ * Sets the desired logging level for a particular log type.
+ * @param {(string|Type)} type The log type.
+ * @param {(!Level|string|number)} level The desired log level.
+ * @throws {TypeError} if `type` is not a `string`.
+ */
+ setLevel(type, level) {
+ if (typeof type !== 'string') {
+ throw TypeError('specified log type is not a string: ' + typeof type);
+ }
+ this.prefs_.set(type, level instanceof Level ? level : getLevel(level));
+ }
+
+ /**
+ * Converts this instance to its JSON representation.
+ * @return {!Object<string, string>} The JSON representation of this set of
+ * preferences.
+ */
+ toJSON() {
+ let json = {};
+ for (let key of this.prefs_.keys()) {
+ json[key] = this.prefs_.get(key).name;
+ }
+ return json;
+ }
+}
+
+
+// PUBLIC API
+
+
+module.exports = {
+ Entry: Entry,
+ Level: Level,
+ LogManager: LogManager,
+ Logger: Logger,
+ Preferences: Preferences,
+ Type: Type,
+ addConsoleHandler: addConsoleHandler,
+ getLevel: getLevel,
+ getLogger: getLogger,
+ installConsoleHandler: installConsoleHandler,
+ removeConsoleHandler: removeConsoleHandler
+};