Skip to content

Commit

Permalink
Allow custom logger overrides
Browse files Browse the repository at this point in the history
This change adds the ability to override ShareDB's logging behaviour.

By default, ShareDB will still log to `console`. However, this default
can be overridden with custom methods on both the backend and in the
client.

## Supported methods

The ShareDB logger only supports the following methods:

  - `info`
  - `warn`
  - `error`

Any method that is not overridden will default to `console`.

## Backend

The backend methods can be overridden when instantiating `Backend`:

```javascript
var share = new Backend({
  logger: {
    info: () => {},                         // Silence info
    warn: () => alerts.warn(arguments),     // Forward warnings
    error: () => alerts.critical(arguments) // Map errors to critical
  }
});
```

## Client

Client methods can also be overridden:

```javascript
var ShareDB = require('sharedb/lib/client');
ShareDB.logger.setMethods({
  info: () => {},
  // etc.
});
```
  • Loading branch information
Alec Gibson committed Nov 15, 2018
1 parent a67ad42 commit 002c4c2
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 11 deletions.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,28 @@ share.addProjection('users_limited', 'users', { name:true, profileUrl:true });

Note that only the [JSON0 OT type](https://github.com/ottypes/json0) is supported for projections.

### Logging

By default, ShareDB logs to `console`. This can be overridden if you wish to silence logs, or to log to your own logging driver or alert service.

Methods can be overridden by passing a [`console`-like object](https://developer.mozilla.org/en-US/docs/Web/API/console) to `Backend`:

```javascript
var share = new Backend({
logger: {
info: () => {}, // Silence info
warn: () => alerts.warn(arguments), // Forward warnings to alerting service
error: () => alerts.critical(arguments) // Remap errors to critical alerts
}
});
```

ShareDB only supports the following logger methods:

- `info`
- `warn`
- `error`

### Shutdown

`share.close(callback)`
Expand Down Expand Up @@ -358,6 +380,27 @@ after a sequence of diffs are handled.
`query.on('extra', function() {...}))`
(Only fires on subscription queries) `query.extra` changed.

### Logging

By default, ShareDB logs to `console`. This can be overridden if you wish to silence logs, or to log to your own logging driver or alert service.

Methods can be overridden by passing a [`console`-like object](https://developer.mozilla.org/en-US/docs/Web/API/console) to `logger.setMethods`

```javascript
var ShareDB = require('sharedb/lib/client');
ShareDB.logger.setMethods({
info: () => {}, // Silence info
warn: () => alerts.warn(arguments), // Forward warnings to alerting service
error: () => alerts.critical(arguments) // Remap errors to critical alerts
});
```

ShareDB only supports the following logger methods:

- `info`
- `warn`
- `error`


## Error codes

Expand Down
9 changes: 5 additions & 4 deletions lib/agent.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var hat = require('hat');
var util = require('./util');
var types = require('./types');
var logger = require('./logger');

/**
* Agent deserializes the wire protocol messages received from the stream and
Expand Down Expand Up @@ -47,7 +48,7 @@ module.exports = Agent;
// Close the agent with the client.
Agent.prototype.close = function(err) {
if (err) {
console.warn('Agent closed due to error', this.clientId, err.stack || err);
logger.warn('Agent closed due to error', this.clientId, err.stack || err);
}
if (this.closed) return;
// This will end the writable stream and emit 'finish'
Expand Down Expand Up @@ -95,7 +96,7 @@ Agent.prototype._subscribeToStream = function(collection, id, stream) {
// Log then silently ignore errors in a subscription stream, since these
// may not be the client's fault, and they were not the result of a
// direct request by the client
console.error('Doc subscription stream error', collection, id, data.error);
logger.error('Doc subscription stream error', collection, id, data.error);
return;
}
if (agent._isOwnOp(collection, data)) return;
Expand Down Expand Up @@ -137,7 +138,7 @@ Agent.prototype._subscribeToQuery = function(emitter, queryId, collection, query
// Log then silently ignore errors in a subscription stream, since these
// may not be the client's fault, and they were not the result of a
// direct request by the client
console.error('Query subscription stream error', collection, query, err);
logger.error('Query subscription stream error', collection, query, err);
};

emitter.onOp = function(op) {
Expand Down Expand Up @@ -195,7 +196,7 @@ Agent.prototype._reply = function(request, err, message) {
};
} else {
if (err.stack) {
console.warn(err.stack);
logger.warn(err.stack);
}
request.error = {
code: err.code,
Expand Down
3 changes: 3 additions & 0 deletions lib/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var async = require('async');
var Agent = require('./agent');
var Connection = require('./client/connection');
var emitter = require('./emitter');
var logger = require('./logger');
var MemoryDB = require('./db/memory');
var NoOpMilestoneDB = require('./milestone-db/no-op');
var MemoryPubSub = require('./pubsub/memory');
Expand Down Expand Up @@ -40,6 +41,8 @@ function Backend(options) {
this.agentsCount = 0;
this.remoteAgentsCount = 0;

logger.setMethods(options.logger);

// The below shims are for backwards compatibility. These options will be
// removed in a future major version
if (!options.disableDocAction) {
Expand Down
11 changes: 6 additions & 5 deletions lib/client/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var emitter = require('../emitter');
var ShareDBError = require('../error');
var types = require('../types');
var util = require('../util');
var logger = require('../logger');

function connectionState(socket) {
if (socket.readyState === 0 || socket.readyState === 1) return 'connecting';
Expand Down Expand Up @@ -116,11 +117,11 @@ Connection.prototype.bindToSocket = function(socket) {
var data = (typeof event.data === 'string') ?
JSON.parse(event.data) : event.data;
} catch (err) {
console.warn('Failed to parse message', event);
logger.warn('Failed to parse message', event);
return;
}

if (connection.debug) console.log('RECV', JSON.stringify(data));
if (connection.debug) logger.info('RECV', JSON.stringify(data));

var request = {data: data};
connection.emit('receive', request);
Expand Down Expand Up @@ -252,7 +253,7 @@ Connection.prototype.handleMessage = function(message) {
return;

default:
console.warn('Ignoring unrecognized message', message);
logger.warn('Ignoring unrecognized message', message);
}
};

Expand All @@ -274,7 +275,7 @@ Connection.prototype._handleBulkMessage = function(message, method) {
if (doc) doc[method](message.error);
}
} else {
console.error('Invalid bulk message', message);
logger.error('Invalid bulk message', message);
}
};

Expand Down Expand Up @@ -426,7 +427,7 @@ Connection.prototype.sendOp = function(doc, op) {
* Sends a message down the socket
*/
Connection.prototype.send = function(message) {
if (this.debug) console.log('SEND', JSON.stringify(message));
if (this.debug) logger.info('SEND', JSON.stringify(message));

this.emit('send', message);
this.socket.send(JSON.stringify(message));
Expand Down
3 changes: 2 additions & 1 deletion lib/client/doc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var emitter = require('../emitter');
var logger = require('../logger');
var ShareDBError = require('../error');
var types = require('../types');

Expand Down Expand Up @@ -832,7 +833,7 @@ Doc.prototype._opAcknowledged = function(message) {
} else if (message.v !== this.version) {
// We should already be at the same version, because the server should
// have sent all the ops that have happened before acknowledging our op
console.warn('Invalid version from server. Expected: ' + this.version + ' Received: ' + message.v, message);
logger.warn('Invalid version from server. Expected: ' + this.version + ' Received: ' + message.v, message);

// Fetching should get us back to a working document state
return this.fetch();
Expand Down
1 change: 1 addition & 0 deletions lib/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ exports.Doc = require('./doc');
exports.Error = require('../error');
exports.Query = require('./query');
exports.types = require('../types');
exports.logger = require('../logger');
3 changes: 3 additions & 0 deletions lib/logger/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
var Logger = require('./logger');
var logger = new Logger();
module.exports = logger;
21 changes: 21 additions & 0 deletions lib/logger/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
var SUPPORTED_METHODS = [
'info',
'warn',
'error'
];

function Logger() {
this.setMethods(console);
}
module.exports = Logger;

Logger.prototype.setMethods = function (overrides) {
overrides = overrides || {};
var logger = this;

SUPPORTED_METHODS.forEach(function (method) {
if (typeof overrides[method] === 'function') {
logger[method] = overrides[method];
}
});
};
3 changes: 2 additions & 1 deletion lib/stream-socket.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var Duplex = require('stream').Duplex;
var inherits = require('util').inherits;
var logger = require('./logger');
var util = require('./util');

function StreamSocket() {
Expand Down Expand Up @@ -36,7 +37,7 @@ function ServerStream(socket) {
this.socket = socket;

this.on('error', function(error) {
console.warn('ShareDB client message stream error', error);
logger.warn('ShareDB client message stream error', error);
socket.close('stopped');
});

Expand Down
46 changes: 46 additions & 0 deletions test/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
var Logger = require('../lib/logger/logger');
var expect = require('expect.js');
var sinon = require('sinon');

describe('Logger', function () {
describe('Stubbing console.warn', function () {
beforeEach(function () {
sinon.stub(console, 'warn');
});

afterEach(function () {
sinon.restore();
});

it('logs to console by default', function () {
var logger = new Logger();
logger.warn('warning');
expect(console.warn.calledOnceWithExactly('warning')).to.be(true);
});

it('overrides console', function () {
var customWarn = sinon.stub();
var logger = new Logger();
logger.setMethods({
warn: customWarn
});

logger.warn('warning');

expect(console.warn.notCalled).to.be(true);
expect(customWarn.calledOnceWithExactly('warning')).to.be(true);
});

it('only overrides if provided with a method', function () {
var badWarn = 'not a function';
var logger = new Logger();
logger.setMethods({
warn: badWarn
});

logger.warn('warning');

expect(console.warn.calledOnceWithExactly('warning')).to.be(true);
});
});
});

0 comments on commit 002c4c2

Please sign in to comment.