Skip to content

Commit 252fd03

Browse files
committed
Merge pull request #1 from turbonetix/implementation
READY: Implementation
2 parents 081e874 + 1c4c1b9 commit 252fd03

5 files changed

Lines changed: 325 additions & 14 deletions

File tree

README.md

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,91 @@
22
[![NPM version](https://badge.fury.io/js/socket.io-logger.svg)](http://badge.fury.io/js/socket.io-logger)
33
[![David DM](https://david-dm.org/turbonetix/socket.io-logger.png)](https://david-dm.org/turbonetix/socket.io-logger.png)
44

5-
![Bus.IO](https://raw.github.com/turbonetix/bus.io/master/logo.png)
5+
Simple logger middleware for [socket.io](https://github.com/Automattic/socket.io "socket.io").
66

7-
WIP
7+
`$ npm install socket.io-logger`
88

9-
A socket.io-logger will log all of your `events`.
9+
```javascript
10+
var fs = require('fs');
11+
var io = require('socket.io')(3000);
12+
var options = {stream: fs.createWriteStream('/var/log/events.log',{flags:'a'}) };
13+
var logger = require('socket.io-logger')(options);
14+
io.use(logger);
15+
```
16+
17+
# Features
18+
19+
* Data is flattened with [flat](https://www.npmjs.org/package/flat "flat").
20+
* Customized formatting support.
21+
* Plugable interface to use with your favorite logger packages.
22+
23+
# API
24+
25+
## Logger
26+
27+
```javascript
28+
var Logger = require('socket.io-logger')();
29+
```
30+
31+
### Logger#
32+
33+
```javascript
34+
io.use(Logger());
35+
```
36+
37+
By default the logger middleware will write to `stdout`.
38+
39+
### Logger#(options:Object)
40+
41+
```javascript
42+
var options = {
43+
stream: fs.createWriteStream('/var/log/events.log', {flags:'a'})
44+
format: function (sock, args) { return { sock:sock.id, args: args}; }
45+
};
46+
io.use(Logger(options));
47+
```
48+
49+
You can use anything as long as the `stream` has a `write` method.
50+
51+
```javascript
52+
var options = {
53+
stream: {
54+
write: function (data) {
55+
console.log(data);
56+
}
57+
}
58+
};
59+
io.use(Logger(options));
60+
```
61+
62+
### Logger#stream (stream:Object)
63+
64+
The `stream` must have a `write` method.
65+
66+
```javascript
67+
var stream = fs.createWriteStream('/var/log/events.log', {flags:'a'});
68+
var logger = Logger();
69+
logger.stream(stream);
70+
io.use(logger);
71+
```
72+
73+
### Logger#format (format:Function)
74+
75+
Set a customized function to manipulate the data that will be serialized.
76+
77+
```javascript
78+
var format = function (sock, args) {
79+
return {
80+
sock: sock.id,
81+
name: args.shift(),
82+
data: args
83+
};
84+
};
85+
86+
var logger = Logger();
87+
logger.format(format);
88+
io.use(logger);
89+
```
1090

1191
# Installation and Environment Setup
1292

examples/logger.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
var io = require('socket.io')(3000);
2+
var Logger = require('./..');
3+
var options = { format: Logger.defaultFormat, stream: { write: function (line) { process.stdout.write(line); } } };
4+
var logger = Logger(options);
5+
io.use(logger);
6+
io.on('connection', function (sock) {
7+
sock.emit('done');
8+
});
9+
10+
setTimeout(function () {
11+
var sock = require('socket.io-client').connect('ws://localhost:3000');
12+
sock.on('connect', function () {
13+
sock.emit('this');
14+
sock.emit('is');
15+
sock.emit('a');
16+
sock.emit('test');
17+
setTimeout(function () {
18+
process.exit(0);
19+
}, 1000);
20+
});
21+
},1000);

lib/index.js

Lines changed: 133 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,143 @@
11
var debug = require('debug')('logger');
2+
var pkg = require('./../package.json');
3+
var flat = require('flat');
4+
var Router = require('socket.io-events');
25

36
exports = module.exports = Logger;
4-
exports.version = require('./../package.json').version;
7+
exports.version = pkg.version;
58

6-
function Logger () {
7-
if (!(this instanceof Logger)) return new Logger();
8-
function logger () {
9+
/**
10+
* A very simple middleware for socket.io that will record the events and log
11+
* them to a stream.
12+
*
13+
* @return Logger
14+
*/
915

16+
function Logger (options) {
17+
if (!(this instanceof Logger)) return new Logger(options);
18+
19+
options = options || {};
20+
21+
debug('new logger v%s', pkg.version);
22+
23+
var router = Router();
24+
router.on(function (sock, args, cb) {
25+
debug('logger args.length %s', arguments.length);
26+
try {
27+
// "this" is the "socket"
28+
logger.stream().write(logger.format(sock, args) + "\n");
29+
cb();
30+
}
31+
catch (e) {
32+
debug('caught an error %s', e);
33+
console.trace(e);
34+
cb(e);
35+
}
36+
});
37+
38+
function logger (sock, cb) {
39+
debug('logger sock', sock, cb.toString());
40+
router(sock, cb);
1041
}
42+
1143
logger.__proto__ = Logger.prototype;
44+
45+
if (options.stream) {
46+
logger.stream(options.stream);
47+
}
48+
49+
if (options.format) {
50+
logger.format(options.format);
51+
}
52+
1253
return logger;
1354
}
1455

15-
Logger.prototype = {}
56+
/**
57+
* The default formating function for Logger
58+
*
59+
* @param {Object} sock
60+
* @param {args} args
61+
*/
62+
63+
Logger.defaultFormat = function (sock, args) {
64+
debug('defaultFormat', sock, args);
65+
args = args || [];
66+
var name = args.shift() || 'unkown';
67+
var data;
68+
switch(args.length) {
69+
case 0:
70+
data = {};
71+
break;
72+
case 1:
73+
data = args.shift();
74+
break;
75+
default:
76+
data = args;
77+
break;
78+
};
79+
return {
80+
socket: {
81+
id: sock.id
82+
},
83+
event: {
84+
name: name,
85+
data: data
86+
}
87+
};
88+
};
89+
90+
/**
91+
* Either sets / gets the format function, or invokes the format
92+
* function given the sock, and args.
93+
*
94+
* @api public
95+
* @param mixed
96+
* @return String
97+
*/
98+
99+
Logger.prototype.format = function () {
100+
var args = Array.prototype.slice.call(arguments);
101+
debug('format args', args);
102+
103+
switch(arguments.length) {
104+
case 0:
105+
debug('get format');
106+
if (!this._format) {
107+
debug('no format');
108+
this.format(Logger.defaultFormat);
109+
}
110+
return this._format;
111+
break;
112+
case 1:
113+
debug('setting format')
114+
if ('function' === typeof args[0] && args[0].length >= 2) {
115+
this._format = args[0];
116+
}
117+
return this;
118+
break;
119+
default:
120+
return JSON.stringify(flat.flatten(this.format().apply(this, args)));
121+
break;
122+
}
123+
124+
};
125+
126+
/**
127+
* set / get the stream instance
128+
*
129+
* @api public
130+
* @param {Object} stream
131+
* @return Logger;
132+
*/
133+
134+
Logger.prototype.stream = function (stream) {
135+
if ('undefined' !== typeof stream && 'function' === typeof stream.write) {
136+
this._stream = stream;
137+
return this;
138+
}
139+
if (!this._stream || 'function' !== typeof this._stream.write) {
140+
this.stream(process.stdout);
141+
}
142+
return this._stream;
143+
};

package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "socket.io-logger",
3-
"version": "0.0.0-alpha1",
4-
"description": "A socket.io-logger will log all of your events.",
3+
"version": "0.0.0",
4+
"description": "Simple logger middleware for socket.io.",
55
"main": "index.js",
66
"scripts": {
77
"test": "grunt"
@@ -26,9 +26,13 @@
2626
"coffee-script": "^1.7.1",
2727
"grunt-jasmine-bundle": "^0.2.0",
2828
"grunt-cli": "^0.1.13",
29-
"grunt": "^0.4.5"
29+
"grunt": "^0.4.5",
30+
"socket.io": "^1.0.6",
31+
"socket.io-client": "^1.0.6"
3032
},
3133
"dependencies": {
32-
"debug": "^1.0.2"
34+
"debug": "^1.0.2",
35+
"flat": "^1.2.1",
36+
"socket.io-events": "^0.3.2"
3337
}
3438
}

spec/lib/index-spec.coffee

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,96 @@
1+
EventEmitter = require('events').EventEmitter
2+
Flat = require 'flat'
3+
14
describe 'lib', ->
5+
6+
Given -> @sock = new EventEmitter
7+
Given -> @sock.id = 1
8+
Given -> @args = ['some event', {some: 'data'}]
9+
Given -> @cb = jasmine.createSpy 'cb'
10+
Given -> @line = '{"socket.id":1,"event.name":"some event","event.data.some":"data"}'
211

3-
Given -> @lib = requireSubject 'lib', {
12+
Given -> @lib = requireSubject 'lib',
413
'./../package.json':
514
version: 1
6-
}
15+
flat: Flat
716

817
describe '.version', ->
918

1019
When -> @version = @lib.version
1120
Then -> expect(@version).toEqual 1
1221

22+
describe '#defaultFormat', ->
23+
24+
When -> @res = @lib.defaultFormat @sock, @args
25+
Then -> expect(@res).toEqual
26+
socket:
27+
id: 1
28+
event:
29+
name: 'some event'
30+
data:
31+
some: 'data'
32+
1333
describe '#', ->
1434

1535
When -> @res = @lib()
1636
Then -> expect(typeof @res).toBe 'function'
1737
And -> expect(@res instanceof @lib).toBe true
1838

39+
describe '#(options:Object)', ->
40+
41+
Given -> @options = stream: write: ->
42+
When -> @res = @lib @options
43+
Then -> expect(typeof @res).toBe 'function'
44+
And -> expect(@res instanceof @lib).toBe true
45+
And -> expect(@res.stream()).toEqual @options.stream
46+
47+
describe 'prototype', ->
48+
49+
Given -> @logger = @lib()
50+
51+
describe.only '# (sock:Object, args:Array)', ->
52+
53+
Given -> @packet = data: @args
54+
Given -> @stream = jasmine.createSpyObj 'stream', ['write']
55+
Given -> @logger.stream @stream
56+
Given -> @logger @sock, @cb
57+
When -> @sock.onevent @packet
58+
Then -> expect(@stream.write).toHaveBeenCalled()
59+
And -> expect(@stream.write.argsForCall[0][0]).toEqual @line + '\n'
60+
61+
describe '#format', ->
62+
63+
When -> @format = @logger.format()
64+
Then -> expect(typeof @format).toBe 'function'
65+
And -> expect(@format.length).toBe 2
66+
And -> expect(@format).toEqual @lib.defaultFormat
67+
68+
describe '#format (format:Function)', ->
69+
70+
Given -> @format = (a, b) ->
71+
When -> @logger.format @format
72+
Then -> expect(@logger.format()).toEqual @format
73+
74+
describe '#format (sock:Object, args:Array)', ->
75+
76+
When -> @res = @logger.format @sock, @args
77+
And -> expect(@res).toEqual @line
78+
79+
describe '#stream', ->
80+
81+
When -> @stream = @logger.stream()
82+
Then -> expect(typeof @stream).toBe 'object'
83+
And -> expect(typeof @stream.write).toBe 'function'
84+
85+
describe '#stream (stream:Object)', ->
86+
87+
Given -> @stream = write: ->
88+
When -> @logger.stream @stream
89+
Then -> expect(@logger.stream()).toEqual @stream
90+
91+
describe '#stream (stream:mixed)', ->
92+
93+
Given -> @stream = 'crap'
94+
Given -> @existing = @logger.stream()
95+
When -> @logger.stream @stream
96+
Then -> expect(@logger.stream()).toEqual @existing

0 commit comments

Comments
 (0)