Skip to content

Commit

Permalink
feat: Log Levels (#3853)
Browse files Browse the repository at this point in the history
Add a log levels and history api: `videojs.log.level()` and `videojs.log.history()`.
`.level()` will return the current level and you can also set it to be one of: `all`, `error`, `off`, or `warn`.
`.history()` will return a list of all things logged since videojs loaded. It can be disabled via `videojs.log.history.disable()` (and re-enabled with `enable()`) as well as cleared with `videojs.log.history.clear()`.
  • Loading branch information
misteroneill authored and gkatsev committed Jan 18, 2017
1 parent b07143d commit 844e4f0
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 19 deletions.
13 changes: 13 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* [Q: How can I autoplay a video on a mobile device?](#q-how-can-i-autoplay-a-video-on-a-mobile-device)
* [Q: How can I play RTMP video in video.js?](#q-how-can-i-play-rtmp-video-in-videojs)
* [Q: How can I hide the links to my video/subtitles/audio/tracks?](#q-how-can-i-hide-the-links-to-my-videosubtitlesaudiotracks)
* [Q: Can I turn off video.js logging?](#q-can-i-turn-off-videojs-logging)
* [Q: What is a plugin?](#q-what-is-a-plugin)
* [Q: How do I make a plugin for video.js?](#q-how-do-i-make-a-plugin-for-videojs)
* [Q: Where can I find a list of video.js plugins?](#q-where-can-i-find-a-list-of-videojs-plugins)
Expand Down Expand Up @@ -169,6 +170,16 @@ help but are outside of the scope of video.js.

For content that must be highly secure [videojs-contrib-eme][eme] adds DRM support.

## Q: Can I turn off video.js logging?

Yes! This can be achieved by adding the following code _after_ including Video.js, but _before_ creating any player(s):

```js
videojs.log.level('off');
```

For more information, including which logging levels are available, check out the [debugging guide][debug-guide].

## Q: What is a plugin?

A plugin is a group of reusable functionality that can be re-used by others. For instance a plugin could add
Expand Down Expand Up @@ -307,3 +318,5 @@ Yes! Please [submit an issue or open a pull request][pr-issue-question] if this
[semver]: http://semver.org/

[starter-example]: http://jsbin.com/axedog/edit?html,output

[debug-guide]: ./guides/debug.md
112 changes: 112 additions & 0 deletions docs/guides/debugging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Debugging

## Table of Contents

* [Logging](#logging)
* [API Overview](#api-overview)
* [Log Safely](#log-safely)
* [Log Objects Usefully](#log-objects-usefully)
* [Log Levels](#log-levels)
* [Available Log Levels](#available-log-levels)
* [History](#history)

## Logging

Video.js includes a lightweight wrapper - `videojs.log` - around a subset of [the `console` API][console]. The available methods are `videojs.log`, `videojs.log.warn`, and `videojs.log.error`.

### API Overview

Most of these methods should be fairly self-explanatory, but for complete details, see [the API docs][api].

| Method | Alias Of | Matching Level(s) |
| ------------------------------- | --------------- | ----------------- |
| `videojs.log()` | `console.log` | all |
| `videojs.log.warn()` | `console.warn` | all, warn |
| `videojs.log.error()` | `console.error` | all, warn, error |
| `videojs.log.level()` | n/a | n/a |
| `videojs.log.history()` | n/a | n/a |
| `videojs.log.history.clear()` | n/a | n/a |
| `videojs.log.history.disable()` | n/a | n/a |
| `videojs.log.history.enable()` | n/a | n/a |

For descriptions of these features, please refer to the sections below.

### Log Safely

Unlike the `console`, it's safe to leave `videojs.log` calls in your code. They won't throw errors when the `console` doesn't exist.

### Log Objects Usefully

Similar to the `console`, any number of mixed-type values can be passed to `videojs.log` methods:

```js
videojs.log('this is a string', {butThis: 'is an object'});
```

However, certain browser consoles (namely, IE10 and lower) do not support non-string values. Video.js improves on this situation by passing objects through `JSON.stringify` before logging them in IE10 and below. In other words, instead of the above producing this:

```txt
VIDEOJS: this is a string [object Object]
```

it will produce this:

```txt
VIDEOJS: this is a string {"butThis": "is an object"}
```

### Log Levels

Unlike the `console`, `videojs.log` includes the concept of logging levels. These levels toggle logging methods on or off.

Levels are exposed through the `videojs.log.level` method. This method acts as both a getter and setter for the current logging level. With no arguments, it returns the current logging level:

```js
videojs.log.level(); // "all"
```

By passing a string, the logging level can be changed to one of the available logging levels:

```js
videojs.log.level('error'); // show only error messages and suppress others
videojs.log('foo'); // does nothing
videojs.log.warn('foo'); // does nothing
videojs.log.error('foo'); // logs "foo" as an error
```

### Available Log Levels

* **all** (default): enables all logging methods
* **error**: only show `log.error` messages
* **off**: disable all logging methods
* **warn**: only show `log.warn` _and_ `log.error` messages

### History

> **Note:** In Video.js 5, `videojs.log.history` was an array. As of Video.js 6, it is a function which returns an array. This change was made to provide a richer, safer logging history API.
By default, the `videojs.log` module tracks a history of _everything_ passed to it regardless of logging level:

```js
videojs.log.history(); // an array of everything that's been logged up to now
```

This will work even when logging is set to **off**.

This can be useful, but it can also be a source of memory leaks. For example, logged objects will be retained in history even if references are removed everywhere else!

To avoid this problem, history can be disabled or enabled via method calls (using the `disable` and `enable` methods respectively). Disabling history is as easy as:

```js
videojs.log.history.disable();
```

Finally, the history (if enabled) can be cleared at any time via:

```js
videojs.log.history.clear();
```

[api]: http://docs.videojs.com/docs/api/index.html

[console]: https://developer.mozilla.org/en-US/docs/Web/API/Console
122 changes: 107 additions & 15 deletions src/js/utils/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ import {isObject} from './obj';

let log;

// This is the private tracking variable for logging level.
let level = 'all';

// This is the private tracking variable for the logging history.
let history = [];

/**
* Log messages to the console and history based on the type of message
*
* @private
* @param {string} type
* The name of the console method to use.
*
Expand All @@ -22,29 +29,34 @@ let log;
* but this is exposed as a parameter to facilitate testing.
*/
export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 11) => {
const lvl = log.levels[level];
const lvlRegExp = new RegExp(`^(${lvl})$`);

if (type !== 'log') {

// add the type to the front of the message when it's not "log"
// Add the type to the front of the message when it's not "log".
args.unshift(type.toUpperCase() + ':');
}

// add to history
log.history.push(args);
// Add a clone of the args at this point to history.
if (history) {
history.push([].concat(args));
}

// add console prefix after adding to history
// Add console prefix after adding to history.
args.unshift('VIDEOJS:');

// If there's no console then don't try to output messages, but they will
// still be stored in `log.history`.
// still be stored in history.
//
// Was setting these once outside of this function, but containing them
// in the function makes it easier to test cases where console doesn't exist
// when the module is executed.
const fn = window.console && window.console[type];

// Bail out if there's no console.
if (!fn) {
// Bail out if there's no console or if this type is not allowed by the
// current logging level.
if (!fn || !lvl || !lvlRegExp.test(type)) {
return;
}

Expand Down Expand Up @@ -76,32 +88,112 @@ export const logByType = (type, args, stringify = !!IE_VERSION && IE_VERSION < 1
};

/**
* Log plain debug messages
* Logs plain debug messages. Similar to `console.log`.
*
* @param {Mixed[]} args
* One or more messages or objects that should be logged.
* @class
* @param {Mixed[]} args
* One or more messages or objects that should be logged.
*/
log = function(...args) {
logByType('log', args);
};

/**
* Keep a history of log messages
* Enumeration of available logging levels, where the keys are the level names
* and the values are `|`-separated strings containing logging methods allowed
* in that logging level. These strings are used to create a regular expression
* matching the function name being called.
*
* Levels provided by video.js are:
*
* - `off`: Matches no calls. Any value that can be cast to `false` will have
* this effect. The most restrictive.
* - `all` (default): Matches only Video.js-provided functions (`log`,
* `log.warn`, and `log.error`).
* - `warn`: Matches `log.warn` and `log.error` calls.
* - `error`: Matches only `log.error` calls.
*
* @type {Object}
*/
log.levels = {
all: 'log|warn|error',
error: 'error',
off: '',
warn: 'warn|error',
DEFAULT: level
};

/**
* Get or set the current logging level. If a string matching a key from
* {@link log.levels} is provided, acts as a setter. Regardless of argument,
* returns the current logging level.
*
* @param {string} [lvl]
* Pass to set a new logging level.
*
* @return {string}
* The current logging level.
*/
log.level = (lvl) => {
if (typeof lvl === 'string') {
if (!log.levels.hasOwnProperty(lvl)) {
throw new Error(`"${lvl}" in not a valid log level`);
}
level = lvl;
}
return level;
};

/**
* Returns an array containing everything that has been logged to the history.
*
* This array is a shallow clone of the internal history record. However, its
* contents are _not_ cloned; so, mutating objects inside this array will
* mutate them in history.
*
* @type {Array}
* @return {Array}
*/
log.history = () => history ? [].concat(history) : [];

/**
* Clears the internal history tracking, but does not prevent further history
* tracking.
*/
log.history = [];
log.history.clear = () => {
if (history) {
history.length = 0;
}
};

/**
* Disable history tracking if it is currently enabled.
*/
log.history.disable = () => {
if (history !== null) {
history.length = 0;
history = null;
}
};

/**
* Enable history tracking if it is currently disabled.
*/
log.history.enable = () => {
if (history === null) {
history = [];
}
};

/**
* Log error messages
* Logs error messages. Similar to `console.error`.
*
* @param {Mixed[]} args
* One or more messages or objects that should be logged as an error
*/
log.error = (...args) => logByType('error', args);

/**
* Log warning messages
* Logs warning messages. Similar to `console.warn`.
*
* @param {Mixed[]} args
* One or more messages or objects that should be logged as a warning.
Expand Down
Loading

0 comments on commit 844e4f0

Please sign in to comment.