2018-07-18 00:40:09 +00:00
|
|
|
https = require 'https'
|
|
|
|
stream = require 'stream'
|
|
|
|
zlib = require 'zlib'
|
|
|
|
|
|
|
|
Promise = require 'bluebird'
|
2019-06-19 17:23:19 +00:00
|
|
|
{ expect } = require './lib/chai-config'
|
|
|
|
sinon = require 'sinon'
|
|
|
|
{ stub } = sinon
|
2017-11-01 06:47:48 +00:00
|
|
|
|
2018-08-27 00:17:11 +00:00
|
|
|
{ Logger } = require '../src/logger'
|
2019-04-01 14:55:15 +00:00
|
|
|
{ ContainerLogs } = require '../src/logging/container'
|
2017-11-01 06:47:48 +00:00
|
|
|
describe 'Logger', ->
|
2018-07-18 00:40:09 +00:00
|
|
|
beforeEach ->
|
|
|
|
@_req = new stream.PassThrough()
|
2019-06-19 17:23:19 +00:00
|
|
|
@_req.flushHeaders = sinon.spy()
|
|
|
|
@_req.end = sinon.spy()
|
2018-07-18 00:40:09 +00:00
|
|
|
|
|
|
|
@_req.body = ''
|
|
|
|
@_req
|
|
|
|
.pipe(zlib.createGunzip())
|
|
|
|
.on 'data', (chunk) =>
|
|
|
|
@_req.body += chunk
|
|
|
|
|
|
|
|
stub(https, 'request').returns(@_req)
|
|
|
|
|
2017-11-01 06:47:48 +00:00
|
|
|
@fakeEventTracker = {
|
2019-06-19 17:23:19 +00:00
|
|
|
track: sinon.spy()
|
2017-11-01 06:47:48 +00:00
|
|
|
}
|
2018-07-18 00:40:09 +00:00
|
|
|
|
2018-11-02 14:15:01 +00:00
|
|
|
@logger = new Logger({ eventTracker: @fakeEventTracker })
|
2018-07-18 00:40:09 +00:00
|
|
|
@logger.init({
|
|
|
|
apiEndpoint: 'https://example.com'
|
|
|
|
uuid: 'deadbeef'
|
|
|
|
deviceApiKey: 'secretkey'
|
2018-12-13 14:14:15 +00:00
|
|
|
unmanaged: false
|
2018-08-27 00:17:11 +00:00
|
|
|
enableLogs: true
|
2018-10-09 11:02:38 +00:00
|
|
|
localMode: false
|
2018-07-18 00:40:09 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
afterEach ->
|
|
|
|
https.request.restore()
|
|
|
|
|
|
|
|
it 'waits the grace period before sending any logs', ->
|
2019-06-19 17:23:19 +00:00
|
|
|
clock = sinon.useFakeTimers()
|
2018-11-02 14:15:01 +00:00
|
|
|
@logger.log({ message: 'foobar', serviceId: 15 })
|
2018-07-18 00:40:09 +00:00
|
|
|
clock.tick(4999)
|
|
|
|
clock.restore()
|
|
|
|
|
2018-10-25 20:36:53 +00:00
|
|
|
Promise.delay(100)
|
2018-07-18 00:40:09 +00:00
|
|
|
.then =>
|
|
|
|
expect(@_req.body).to.equal('')
|
|
|
|
|
|
|
|
it 'tears down the connection after inactivity', ->
|
2019-06-19 17:23:19 +00:00
|
|
|
clock = sinon.useFakeTimers()
|
2018-11-02 14:15:01 +00:00
|
|
|
@logger.log({ message: 'foobar', serviceId: 15 })
|
2018-07-18 00:40:09 +00:00
|
|
|
clock.tick(61000)
|
|
|
|
clock.restore()
|
|
|
|
|
2018-10-25 20:36:53 +00:00
|
|
|
Promise.delay(100)
|
2018-07-18 00:40:09 +00:00
|
|
|
.then =>
|
|
|
|
expect(@_req.end.calledOnce).to.be.true
|
|
|
|
|
|
|
|
|
|
|
|
it 'sends logs as gzipped ndjson', ->
|
2019-05-03 13:58:57 +00:00
|
|
|
timestamp = Date.now()
|
2018-07-18 00:40:09 +00:00
|
|
|
@logger.log({ message: 'foobar', serviceId: 15 })
|
|
|
|
@logger.log({ timestamp: 1337, message: 'foobar', serviceId: 15 })
|
|
|
|
@logger.log({ message: 'foobar' }) # shold be ignored
|
|
|
|
|
2019-05-03 13:58:57 +00:00
|
|
|
Promise.delay(5500).then =>
|
|
|
|
expect(https.request.calledOnce).to.be.true
|
|
|
|
opts = https.request.firstCall.args[0]
|
2018-07-18 00:40:09 +00:00
|
|
|
|
2019-05-03 13:58:57 +00:00
|
|
|
expect(opts.href).to.equal('https://example.com/device/v2/deadbeef/log-stream')
|
|
|
|
expect(opts.method).to.equal('POST')
|
|
|
|
expect(opts.headers).to.deep.equal({
|
|
|
|
'Authorization': 'Bearer secretkey'
|
|
|
|
'Content-Type': 'application/x-ndjson'
|
|
|
|
'Content-Encoding': 'gzip'
|
|
|
|
})
|
2018-07-18 00:40:09 +00:00
|
|
|
|
|
|
|
lines = @_req.body.split('\n')
|
|
|
|
expect(lines.length).to.equal(3)
|
|
|
|
expect(lines[2]).to.equal('')
|
|
|
|
|
|
|
|
msg = JSON.parse(lines[0])
|
2019-05-03 13:58:57 +00:00
|
|
|
expect(msg).to.have.property('message').that.equals('foobar')
|
|
|
|
expect(msg).to.have.property('serviceId').that.equals(15)
|
|
|
|
expect(msg).to.have.property('timestamp').that.is.at.least(timestamp)
|
2018-07-18 00:40:09 +00:00
|
|
|
msg = JSON.parse(lines[1])
|
|
|
|
expect(msg).to.deep.equal({ timestamp: 1337, message: 'foobar', serviceId: 15 })
|
|
|
|
|
|
|
|
it 'allows logging system messages which are also reported to the eventTracker', ->
|
2019-05-03 13:58:57 +00:00
|
|
|
timestamp = Date.now()
|
2017-11-01 06:47:48 +00:00
|
|
|
@logger.logSystemMessage('Hello there!', { someProp: 'someVal' }, 'Some event name')
|
2018-07-18 00:40:09 +00:00
|
|
|
|
2019-05-03 13:58:57 +00:00
|
|
|
Promise.delay(5500)
|
2018-07-18 00:40:09 +00:00
|
|
|
.then =>
|
2017-11-01 06:47:48 +00:00
|
|
|
expect(@fakeEventTracker.track).to.be.calledWith('Some event name', { someProp: 'someVal' })
|
2018-07-18 00:40:09 +00:00
|
|
|
lines = @_req.body.split('\n')
|
|
|
|
expect(lines.length).to.equal(2)
|
|
|
|
expect(lines[1]).to.equal('')
|
|
|
|
|
|
|
|
msg = JSON.parse(lines[0])
|
2019-05-03 13:58:57 +00:00
|
|
|
expect(msg).to.have.property('message').that.equals('Hello there!')
|
|
|
|
expect(msg).to.have.property('isSystem').that.equals(true)
|
|
|
|
expect(msg).to.have.property('timestamp').that.is.at.least(timestamp)
|
2018-09-21 14:49:43 +00:00
|
|
|
|
|
|
|
it 'should support non-tty log lines', ->
|
|
|
|
message = '\u0001\u0000\u0000\u0000\u0000\u0000\u0000?2018-09-21T12:37:09.819134000Z this is the message'
|
|
|
|
buffer = Buffer.from(message)
|
|
|
|
|
2019-04-01 14:55:15 +00:00
|
|
|
expect(ContainerLogs.extractMessage(buffer)).to.deep.equal({
|
2018-09-21 14:49:43 +00:00
|
|
|
message: 'this is the message',
|
|
|
|
timestamp: 1537533429819
|
|
|
|
})
|