overleaf/libraries/logger/test/unit/logging-manager-tests.js
Antoine Clausse 7f48c67512 Add prefer-node-protocol ESLint rule (#21532)
* Add `unicorn/prefer-node-protocol`

* Fix `unicorn/prefer-node-protocol` ESLint errors

* Run `npm run format:fix`

* Add sandboxed-module sourceTransformers in mocha setups

Fix `no such file or directory, open 'node:fs'` in `sandboxed-module`

* Remove `node:` in the SandboxedModule requires

* Fix new linting errors with `node:`

GitOrigin-RevId: 68f6e31e2191fcff4cb8058dd0a6914c14f59926
2024-11-11 09:04:51 +00:00

215 lines
6.3 KiB
JavaScript

const SandboxedModule = require('sandboxed-module')
const bunyan = require('bunyan')
const { expect } = require('chai')
const path = require('node:path')
const sinon = require('sinon')
const MODULE_PATH = path.join(__dirname, '../../logging-manager.js')
describe('LoggingManager', function () {
beforeEach(function () {
this.start = Date.now()
this.bunyanLogger = {
addStream: sinon.stub(),
debug: sinon.stub(),
error: sinon.stub(),
fatal: sinon.stub(),
info: sinon.stub(),
level: sinon.stub(),
warn: sinon.stub(),
}
this.Bunyan = {
createLogger: sinon.stub().returns(this.bunyanLogger),
RingBuffer: bunyan.RingBuffer,
}
this.stackdriverStreamConfig = { stream: 'stackdriver' }
this.stackdriverClient = {
stream: sinon.stub().returns(this.stackdriverStreamConfig),
}
this.GCPLogging = {
LoggingBunyan: sinon.stub().returns(this.stackdriverClient),
}
this.FileLogLevelChecker = {
start: sinon.stub(),
stop: sinon.stub(),
}
this.GCEMetadataLogLevelChecker = {
start: sinon.stub(),
stop: sinon.stub(),
}
this.LogLevelChecker = {
FileLogLevelChecker: sinon.stub().returns(this.FileLogLevelChecker),
GCEMetadataLogLevelChecker: sinon
.stub()
.returns(this.GCEMetadataLogLevelChecker),
}
this.SentryManager = {
captureException: sinon.stub(),
captureExceptionRateLimited: sinon.stub(),
}
this.LoggingManager = SandboxedModule.require(MODULE_PATH, {
requires: {
bunyan: this.Bunyan,
'./log-level-checker': this.LogLevelChecker,
'./sentry-manager': sinon.stub().returns(this.SentryManager),
},
})
this.loggerName = 'test'
this.logger = this.LoggingManager.initialize(this.loggerName)
this.logger.initializeErrorReporting('test_dsn')
})
afterEach(function () {
this.LoggingManager.removeWarningHandler()
})
describe('initialize', function () {
beforeEach(function () {
this.Bunyan.createLogger.reset()
})
describe('not in production', function () {
beforeEach(function () {
this.logger = this.LoggingManager.initialize(this.loggerName)
})
it('should default to log level debug', function () {
this.Bunyan.createLogger.firstCall.args[0].streams[0].level.should.equal(
'debug'
)
})
it('should not instantiate a log level checker', function () {
expect(this.LoggingManager.logLevelChecker).not.to.exist
})
})
describe('in production', function () {
beforeEach(function () {
process.env.NODE_ENV = 'production'
this.logger = this.LoggingManager.initialize(this.loggerName)
})
afterEach(function () {
delete process.env.NODE_ENV
})
it('should default to log level info', function () {
this.Bunyan.createLogger.firstCall.args[0].streams[0].level.should.equal(
'info'
)
})
it('should set up a file log level checker', function () {
expect(this.logger.logLevelChecker).to.equal(this.FileLogLevelChecker)
expect(this.FileLogLevelChecker.start).to.have.been.called
})
})
describe('when LOG_LEVEL set in env', function () {
beforeEach(function () {
process.env.LOG_LEVEL = 'trace'
this.LoggingManager.initialize()
})
afterEach(function () {
delete process.env.LOG_LEVEL
})
it('should use custom log level', function () {
this.Bunyan.createLogger.firstCall.args[0].streams[0].level.should.equal(
'trace'
)
})
})
})
describe('bunyan logging', function () {
beforeEach(function () {
this.logArgs = [{ foo: 'bar' }, 'foo', 'bar']
})
it('should log debug', function () {
this.logger.debug(this.logArgs)
this.bunyanLogger.debug.should.have.been.calledWith(this.logArgs)
})
it('should log error', function () {
this.logger.error(this.logArgs)
this.bunyanLogger.error.should.have.been.calledWith(this.logArgs)
})
it('should log fatal', function () {
this.logger.fatal(this.logArgs)
this.bunyanLogger.fatal.should.have.been.calledWith(this.logArgs)
})
it('should log info', function () {
this.logger.info(this.logArgs)
this.bunyanLogger.info.should.have.been.calledWith(this.logArgs)
})
it('should log warn', function () {
this.logger.warn(this.logArgs)
this.bunyanLogger.warn.should.have.been.calledWith(this.logArgs)
})
it('should log err', function () {
this.logger.err(this.logArgs)
this.bunyanLogger.error.should.have.been.calledWith(this.logArgs)
})
})
describe('logger.error', function () {
it('should report errors to Sentry', function () {
this.logger.error({ foo: 'bar' }, 'message')
expect(this.SentryManager.captureExceptionRateLimited).to.have.been.called
})
})
describe('ringbuffer', function () {
beforeEach(function () {
this.logBufferMock = [
{ msg: 'log 1' },
{ msg: 'log 2' },
{ level: 50, msg: 'error' },
]
})
describe('when ring buffer size is positive', function () {
beforeEach(function () {
process.env.LOG_RING_BUFFER_SIZE = '20'
this.logger = this.LoggingManager.initialize(this.loggerName)
this.logger.ringBuffer.records = this.logBufferMock
this.logger.error({}, 'error')
})
afterEach(function () {
process.env.LOG_RING_BUFFER_SIZE = undefined
})
it('should include buffered logs in error log and filter out error logs in buffer', function () {
this.bunyanLogger.error.lastCall.args[0].logBuffer.should.deep.equal([
{ msg: 'log 1' },
{ msg: 'log 2' },
])
})
})
describe('when ring buffer size is zero', function () {
beforeEach(function () {
process.env.LOG_RING_BUFFER_SIZE = '0'
this.logger = this.LoggingManager.initialize(this.loggerName)
this.logger.error({}, 'error')
})
afterEach(function () {
process.env.LOG_RING_BUFFER_SIZE = undefined
})
it('should not include buffered logs in error log', function () {
expect(this.bunyanLogger.error.lastCall.args[0].logBuffer).be.undefined
})
})
})
})