mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #23 from overleaf/csh-metadata-fallback-node-fetch
Support file and GCE metadata as log level sources
This commit is contained in:
commit
f49bc2974d
3 changed files with 194 additions and 45 deletions
|
@ -1,4 +1,5 @@
|
||||||
const bunyan = require('bunyan')
|
const bunyan = require('bunyan')
|
||||||
|
const fetch = require('node-fetch')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const yn = require('yn')
|
const yn = require('yn')
|
||||||
const OError = require('@overleaf/o-error')
|
const OError = require('@overleaf/o-error')
|
||||||
|
@ -21,6 +22,7 @@ const errSerializer = function (err) {
|
||||||
|
|
||||||
const Logger = (module.exports = {
|
const Logger = (module.exports = {
|
||||||
initialize(name) {
|
initialize(name) {
|
||||||
|
this.logLevelSource = (process.env.LOG_LEVEL_SOURCE || 'file').toLowerCase()
|
||||||
this.isProduction =
|
this.isProduction =
|
||||||
(process.env.NODE_ENV || '').toLowerCase() === 'production'
|
(process.env.NODE_ENV || '').toLowerCase() === 'production'
|
||||||
this.defaultLevel =
|
this.defaultLevel =
|
||||||
|
@ -41,18 +43,33 @@ const Logger = (module.exports = {
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
checkLogLevel() {
|
async checkLogLevel() {
|
||||||
fs.readFile('/logging/tracingEndTime', (error, end) => {
|
try {
|
||||||
if (error || !end) {
|
const end = await this.getTracingEndTime()
|
||||||
this.logger.level(this.defaultLevel)
|
if (parseInt(end, 10) > Date.now()) {
|
||||||
return
|
|
||||||
}
|
|
||||||
if (parseInt(end) > Date.now()) {
|
|
||||||
this.logger.level('trace')
|
this.logger.level('trace')
|
||||||
} else {
|
} else {
|
||||||
this.logger.level(this.defaultLevel)
|
this.logger.level(this.defaultLevel)
|
||||||
}
|
}
|
||||||
})
|
} catch (err) {
|
||||||
|
this.logger.level(this.defaultLevel)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async getTracingEndTimeFile() {
|
||||||
|
return fs.promises.readFile('/logging/tracingEndTime')
|
||||||
|
},
|
||||||
|
|
||||||
|
async getTracingEndTimeMetadata() {
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
'Metadata-Flavor': 'Google'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const uri = `http://metadata.google.internal/computeMetadata/v1/project/attributes/${this.loggerName}-setLogLevelEndTime`
|
||||||
|
const res = await fetch(uri, options)
|
||||||
|
if (!res.ok) throw new Error('Metadata not okay')
|
||||||
|
return res.text()
|
||||||
},
|
},
|
||||||
|
|
||||||
initializeErrorReporting(sentryDsn, options) {
|
initializeErrorReporting(sentryDsn, options) {
|
||||||
|
@ -241,6 +258,16 @@ const Logger = (module.exports = {
|
||||||
if (this.checkInterval) {
|
if (this.checkInterval) {
|
||||||
clearInterval(this.checkInterval)
|
clearInterval(this.checkInterval)
|
||||||
}
|
}
|
||||||
|
if (this.logLevelSource === 'file') {
|
||||||
|
this.getTracingEndTime = this.getTracingEndTimeFile
|
||||||
|
} else if (this.logLevelSource === 'gce_metadata') {
|
||||||
|
this.getTracingEndTime = this.getTracingEndTimeMetadata
|
||||||
|
} else if (this.logLevelSource === 'none') {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
console.log('Unrecognised log level source')
|
||||||
|
return
|
||||||
|
}
|
||||||
// check for log level override on startup
|
// check for log level override on startup
|
||||||
this.checkLogLevel()
|
this.checkLogLevel()
|
||||||
// re-check log level every minute
|
// re-check log level every minute
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
"url": "http://github.com/sharelatex/logger-sharelatex.git"
|
"url": "http://github.com/sharelatex/logger-sharelatex.git"
|
||||||
},
|
},
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha test/**/*.js",
|
"test": "mocha --grep=$MOCHA_GREP test/**/*.js",
|
||||||
"format": "prettier-eslint $PWD'/**/*.js' --list-different",
|
"format": "prettier-eslint $PWD'/**/*.js' --list-different",
|
||||||
"format:fix": "prettier-eslint $PWD'/**/*.js' --write",
|
"format:fix": "prettier-eslint $PWD'/**/*.js' --write",
|
||||||
"lint": "eslint -f unix ."
|
"lint": "eslint -f unix ."
|
||||||
|
@ -18,6 +18,7 @@
|
||||||
"@google-cloud/logging-bunyan": "^3.0.0",
|
"@google-cloud/logging-bunyan": "^3.0.0",
|
||||||
"@overleaf/o-error": "^3.0.0",
|
"@overleaf/o-error": "^3.0.0",
|
||||||
"bunyan": "^1.8.14",
|
"bunyan": "^1.8.14",
|
||||||
|
"node-fetch": "^2.6.0",
|
||||||
"raven": "^2.6.4",
|
"raven": "^2.6.4",
|
||||||
"yn": "^4.0.0"
|
"yn": "^4.0.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,6 +29,11 @@ describe('LoggingManager', function () {
|
||||||
captureException: this.captureException,
|
captureException: this.captureException,
|
||||||
once: sinon.stub().yields()
|
once: sinon.stub().yields()
|
||||||
}
|
}
|
||||||
|
this.fetchResponse = {
|
||||||
|
text: sinon.stub().resolves(''),
|
||||||
|
status: 200,
|
||||||
|
ok: true
|
||||||
|
}
|
||||||
this.Bunyan = {
|
this.Bunyan = {
|
||||||
createLogger: sinon.stub().returns(this.bunyanLogger),
|
createLogger: sinon.stub().returns(this.bunyanLogger),
|
||||||
RingBuffer: bunyan.RingBuffer,
|
RingBuffer: bunyan.RingBuffer,
|
||||||
|
@ -40,9 +45,13 @@ describe('LoggingManager', function () {
|
||||||
this.Raven = {
|
this.Raven = {
|
||||||
Client: sinon.stub().returns(this.ravenClient)
|
Client: sinon.stub().returns(this.ravenClient)
|
||||||
}
|
}
|
||||||
|
this.Fetch = sinon.stub().resolves(this.fetchResponse)
|
||||||
this.Fs = {
|
this.Fs = {
|
||||||
|
readFile: sinon.stub(),
|
||||||
|
promises: {
|
||||||
readFile: sinon.stub()
|
readFile: sinon.stub()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.stackdriverStreamConfig = { stream: 'stackdriver' }
|
this.stackdriverStreamConfig = { stream: 'stackdriver' }
|
||||||
this.stackdriverClient = {
|
this.stackdriverClient = {
|
||||||
stream: sinon.stub().returns(this.stackdriverStreamConfig)
|
stream: sinon.stub().returns(this.stackdriverStreamConfig)
|
||||||
|
@ -55,6 +64,7 @@ describe('LoggingManager', function () {
|
||||||
requires: {
|
requires: {
|
||||||
bunyan: this.Bunyan,
|
bunyan: this.Bunyan,
|
||||||
raven: this.Raven,
|
raven: this.Raven,
|
||||||
|
'node-fetch': this.Fetch,
|
||||||
fs: this.Fs,
|
fs: this.Fs,
|
||||||
'@google-cloud/logging-bunyan': this.GCPLogging
|
'@google-cloud/logging-bunyan': this.GCPLogging
|
||||||
}
|
}
|
||||||
|
@ -70,7 +80,9 @@ describe('LoggingManager', function () {
|
||||||
|
|
||||||
describe('initialize', function () {
|
describe('initialize', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.checkLogLevelStub = sinon.stub(this.LoggingManager, 'checkLogLevel')
|
this.checkLogLevelStub = sinon
|
||||||
|
.stub(this.LoggingManager, 'checkLogLevel')
|
||||||
|
.resolves('')
|
||||||
this.Bunyan.createLogger.reset()
|
this.Bunyan.createLogger.reset()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -108,6 +120,7 @@ describe('LoggingManager', function () {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('logLevelSource file', function () {
|
||||||
it('should run checkLogLevel', function () {
|
it('should run checkLogLevel', function () {
|
||||||
this.checkLogLevelStub.should.have.been.calledOnce
|
this.checkLogLevelStub.should.have.been.calledOnce
|
||||||
})
|
})
|
||||||
|
@ -124,6 +137,7 @@ describe('LoggingManager', function () {
|
||||||
this.checkLogLevelStub.should.have.been.calledThrice
|
this.checkLogLevelStub.should.have.been.calledThrice
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('when LOG_LEVEL set in env', function () {
|
describe('when LOG_LEVEL set in env', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
@ -287,18 +301,20 @@ describe('LoggingManager', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('checkLogLevel', function () {
|
describe('checkLogLevelFile', function () {
|
||||||
it('should request log level override from the config map', function () {
|
it('should request log level override from the config map', async function () {
|
||||||
this.logger.checkLogLevel()
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeFile
|
||||||
this.Fs.readFile.should.have.been.calledWithMatch(
|
await this.logger.checkLogLevel()
|
||||||
|
this.Fs.promises.readFile.should.have.been.calledWithMatch(
|
||||||
'/logging/tracingEndTime'
|
'/logging/tracingEndTime'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when read errors', function () {
|
describe('when read errors', function () {
|
||||||
beforeEach(function () {
|
beforeEach(async function () {
|
||||||
this.Fs.readFile.yields(new Error('error'))
|
this.Fs.promises.readFile.throws(new Error('test read error'))
|
||||||
this.logger.checkLogLevel()
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeFile
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should only set default level', function () {
|
it('should only set default level', function () {
|
||||||
|
@ -309,9 +325,10 @@ describe('LoggingManager', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when the file is empty', function () {
|
describe('when the file is empty', function () {
|
||||||
beforeEach(function () {
|
beforeEach(async function () {
|
||||||
this.Fs.readFile.yields(null, '')
|
this.Fs.promises.readFile.returns('')
|
||||||
this.logger.checkLogLevel()
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeFile
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should only set default level', function () {
|
it('should only set default level', function () {
|
||||||
|
@ -322,9 +339,10 @@ describe('LoggingManager', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when time value returned that is less than current time', function () {
|
describe('when time value returned that is less than current time', function () {
|
||||||
beforeEach(function () {
|
beforeEach(async function () {
|
||||||
this.Fs.readFile.yields(null, '1')
|
this.Fs.promises.readFile.returns('1')
|
||||||
this.logger.checkLogLevel()
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeFile
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should only set default level', function () {
|
it('should only set default level', function () {
|
||||||
|
@ -336,10 +354,11 @@ describe('LoggingManager', function () {
|
||||||
|
|
||||||
describe('when time value returned that is more than current time', function () {
|
describe('when time value returned that is more than current time', function () {
|
||||||
describe('when level is already set', function () {
|
describe('when level is already set', function () {
|
||||||
beforeEach(function () {
|
beforeEach(async function () {
|
||||||
this.bunyanLogger.level.returns(10)
|
this.bunyanLogger.level.returns(10)
|
||||||
this.Fs.readFile.yields(null, (this.start + 1000).toString())
|
this.Fs.promises.readFile.returns((this.start + 1000).toString())
|
||||||
this.logger.checkLogLevel()
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeFile
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set trace level', function () {
|
it('should set trace level', function () {
|
||||||
|
@ -350,10 +369,11 @@ describe('LoggingManager', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when level is not already set', function () {
|
describe('when level is not already set', function () {
|
||||||
beforeEach(function () {
|
beforeEach(async function () {
|
||||||
this.bunyanLogger.level.returns(20)
|
this.bunyanLogger.level.returns(20)
|
||||||
this.Fs.readFile.yields(null, (this.start + 1000).toString())
|
this.Fs.promises.readFile.returns((this.start + 1000).toString())
|
||||||
this.logger.checkLogLevel()
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeFile
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set trace level', function () {
|
it('should set trace level', function () {
|
||||||
|
@ -365,6 +385,107 @@ describe('LoggingManager', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('checkLogLevelMetadata', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
this.logger = this.LoggingManager.initialize(this.loggerName)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('checkLogLevel', function () {
|
||||||
|
it('should request log level override from google meta data service', async function () {
|
||||||
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeMetadata
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
'Metadata-Flavor': 'Google'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const uri = `http://metadata.google.internal/computeMetadata/v1/project/attributes/${this.loggerName}-setLogLevelEndTime`
|
||||||
|
this.Fetch.should.have.been.calledWithMatch(uri, options)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when request has error', function () {
|
||||||
|
beforeEach(async function () {
|
||||||
|
this.Fetch = sinon.stub().throws()
|
||||||
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeMetadata
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should only set default level', function () {
|
||||||
|
this.bunyanLogger.level.should.have.been.calledOnce.and.calledWith(
|
||||||
|
'debug'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when statusCode is not 200', function () {
|
||||||
|
beforeEach(async function () {
|
||||||
|
this.fetchResponse.status = 404
|
||||||
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeMetadata
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should only set default level', function () {
|
||||||
|
this.bunyanLogger.level.should.have.been.calledOnce.and.calledWith(
|
||||||
|
'debug'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when time value returned that is less than current time', function () {
|
||||||
|
beforeEach(async function () {
|
||||||
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeMetadata
|
||||||
|
this.fetchResponse.text = sinon.stub().resolves('1')
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should only set default level', function () {
|
||||||
|
this.bunyanLogger.level.should.have.been.calledOnce.and.calledWith(
|
||||||
|
'debug'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when time value returned that is more than current time', function () {
|
||||||
|
describe('when level is already set', function () {
|
||||||
|
beforeEach(async function () {
|
||||||
|
this.bunyanLogger.level.returns(10)
|
||||||
|
this.fetchResponse.text = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves((this.start + 1000).toString())
|
||||||
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeMetadata
|
||||||
|
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should set trace level', function () {
|
||||||
|
this.bunyanLogger.level.should.have.been.calledOnce.and.calledWith(
|
||||||
|
'trace'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when level is not already set', function () {
|
||||||
|
beforeEach(async function () {
|
||||||
|
this.bunyanLogger.level.returns(20)
|
||||||
|
this.fetchResponse.text = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves((this.start + 1000).toString())
|
||||||
|
this.Fetch.fetch = sinon.stub().resolves(this.fetchResponse)
|
||||||
|
this.logger.getTracingEndTime = this.logger.getTracingEndTimeMetadata
|
||||||
|
|
||||||
|
await this.logger.checkLogLevel()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should set trace level', function () {
|
||||||
|
this.bunyanLogger.level.should.have.been.calledOnce.and.calledWith(
|
||||||
|
'trace'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('ringbuffer', function () {
|
describe('ringbuffer', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.logBufferMock = [
|
this.logBufferMock = [
|
||||||
|
|
Loading…
Reference in a new issue