mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-24 07:46:41 +00:00
e148ce0529
We add a database table, which holds information about the last timestamp of a log successfully reported to a backend (local or remote). We then use this value to calculate from which point in time to start reporting logs from the container. If this is the first time we've seen a container, we get all logs, and for every log reported we save the timestamp. If it is not the first time we've seen a container, we request all logs since the last reported time, ensuring no interruption of service. Change-type: minor Closes: #937 Signed-off-by: Cameron Diver <cameron@balena.io>
119 lines
3.3 KiB
CoffeeScript
119 lines
3.3 KiB
CoffeeScript
https = require 'https'
|
|
stream = require 'stream'
|
|
zlib = require 'zlib'
|
|
|
|
Promise = require 'bluebird'
|
|
m = require 'mochainon'
|
|
{ expect } = m.chai
|
|
{ stub } = m.sinon
|
|
|
|
{ Logger } = require '../src/logger'
|
|
{ ContainerLogs } = require '../src/logging/container'
|
|
describe 'Logger', ->
|
|
beforeEach ->
|
|
@_req = new stream.PassThrough()
|
|
@_req.flushHeaders = m.sinon.spy()
|
|
@_req.end = m.sinon.spy()
|
|
|
|
@_req.body = ''
|
|
@_req
|
|
.pipe(zlib.createGunzip())
|
|
.on 'data', (chunk) =>
|
|
@_req.body += chunk
|
|
|
|
stub(https, 'request').returns(@_req)
|
|
|
|
@fakeEventTracker = {
|
|
track: m.sinon.spy()
|
|
}
|
|
|
|
@logger = new Logger({ eventTracker: @fakeEventTracker })
|
|
@logger.init({
|
|
apiEndpoint: 'https://example.com'
|
|
uuid: 'deadbeef'
|
|
deviceApiKey: 'secretkey'
|
|
unmanaged: false
|
|
enableLogs: true
|
|
localMode: false
|
|
})
|
|
|
|
afterEach ->
|
|
https.request.restore()
|
|
|
|
it 'waits the grace period before sending any logs', ->
|
|
clock = m.sinon.useFakeTimers()
|
|
@logger.log({ message: 'foobar', serviceId: 15 })
|
|
clock.tick(4999)
|
|
clock.restore()
|
|
|
|
Promise.delay(100)
|
|
.then =>
|
|
expect(@_req.body).to.equal('')
|
|
|
|
it 'tears down the connection after inactivity', ->
|
|
clock = m.sinon.useFakeTimers()
|
|
@logger.log({ message: 'foobar', serviceId: 15 })
|
|
clock.tick(61000)
|
|
clock.restore()
|
|
|
|
Promise.delay(100)
|
|
.then =>
|
|
expect(@_req.end.calledOnce).to.be.true
|
|
|
|
|
|
it 'sends logs as gzipped ndjson', ->
|
|
clock = m.sinon.useFakeTimers()
|
|
@logger.log({ message: 'foobar', serviceId: 15 })
|
|
@logger.log({ timestamp: 1337, message: 'foobar', serviceId: 15 })
|
|
@logger.log({ message: 'foobar' }) # shold be ignored
|
|
clock.tick(10000)
|
|
clock.restore()
|
|
|
|
expect(https.request.calledOnce).to.be.true
|
|
opts = https.request.firstCall.args[0]
|
|
|
|
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'
|
|
})
|
|
|
|
# small delay for the streams to propagate data
|
|
Promise.delay(100)
|
|
.then =>
|
|
lines = @_req.body.split('\n')
|
|
expect(lines.length).to.equal(3)
|
|
expect(lines[2]).to.equal('')
|
|
|
|
msg = JSON.parse(lines[0])
|
|
expect(msg).to.deep.equal({ timestamp: 0, message: 'foobar', serviceId: 15 })
|
|
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', ->
|
|
clock = m.sinon.useFakeTimers()
|
|
@logger.logSystemMessage('Hello there!', { someProp: 'someVal' }, 'Some event name')
|
|
clock.tick(10000)
|
|
clock.restore()
|
|
|
|
Promise.delay(100)
|
|
.then =>
|
|
expect(@fakeEventTracker.track).to.be.calledWith('Some event name', { someProp: 'someVal' })
|
|
lines = @_req.body.split('\n')
|
|
expect(lines.length).to.equal(2)
|
|
expect(lines[1]).to.equal('')
|
|
|
|
msg = JSON.parse(lines[0])
|
|
expect(msg).to.deep.equal({ message: 'Hello there!', timestamp: 0, isSystem: true })
|
|
|
|
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)
|
|
|
|
expect(ContainerLogs.extractMessage(buffer)).to.deep.equal({
|
|
message: 'this is the message',
|
|
timestamp: 1537533429819
|
|
})
|