cloud-pine
Version:
Pino Transport abstraction for Google Cloud Logging.
1,941 lines (1,619 loc) • 50.1 kB
JavaScript
const tap = require('tap')
const { CloudLogging } = require('../../lib/cloud-logging')
class BaseLogMock {
constructor (name) {
this.name = name
}
entry () {
return {}
}
write () {}
}
class BaseLoggingMock {
constructor () {
this.detectedResource = null
}
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
return Promise.resolve()
}
parseLine () {}
log (name = 'test') {
return new BaseLogMock(name)
}
}
tap.plan(2)
tap.test('CloudLogging#sync', root => {
root.plan(3)
root.test('#mapSeverity', t => {
const cloudLogging = new CloudLogging({ sync: true })
t.plan(7)
t.equal(cloudLogging.mapSeverity('10'), 'DEBUG')
t.equal(cloudLogging.mapSeverity('20'), 'DEBUG')
t.equal(cloudLogging.mapSeverity('30'), 'INFO')
t.equal(cloudLogging.mapSeverity('40'), 'WARNING')
t.equal(cloudLogging.mapSeverity('50'), 'ERROR')
t.equal(cloudLogging.mapSeverity('60'), 'CRITICAL')
t.equal(cloudLogging.mapSeverity('100'), 'INFO')
})
root.test('#init', nested => {
nested.plan(3)
nested.test(
'Should initialize the log instance with resources',
async t => {
const logName = 'test'
const projectId = 'test-project'
const detectedResource = {
labels: {
projectId,
instanceId: '12345678901234',
zone: 'us-central1-a'
}
}
const defaultOptions = {
googleCloudOptions: {
projectId,
keyFilename: '/path/to/keyfile.json'
},
sync: true
}
class LoggingMock extends BaseLoggingMock {
constructor (options) {
t.same(options, defaultOptions.googleCloudOptions)
super()
}
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
t.equal(name, logName)
return new BaseLogMock(name)
}
}
const expectedDetectedResource = Object.assign(
{ type: 'global' },
detectedResource
)
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(4)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
t.same(instance.resource, expectedDetectedResource)
t.ok(instance.sync)
}
)
nested.test(
'Calles made to #resource should return the fresh object of the parent resource',
async t => {
const logName = 'test'
const projectId = 'test-project'
const detectedResource = {
labels: {
projectId,
instanceId: '12345678901234',
zone: 'us-central1-a'
}
}
const defaultOptions = {
googleCloudOptions: {
projectId,
keyFilename: '/path/to/keyfile.json'
},
sync: true
}
class LoggingMock extends BaseLoggingMock {
constructor (options) {
t.same(options, defaultOptions.googleCloudOptions)
super()
}
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
t.equal(name, logName)
return new BaseLogMock(name)
}
}
const expectedDetectedResource = Object.assign(
{ type: 'global' },
detectedResource
)
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(6)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
t.ok(instance.sync)
t.same(instance.resource, expectedDetectedResource)
t.same(instance.resource, expectedDetectedResource)
t.same(instance.resource, expectedDetectedResource)
}
)
nested.test('Should be skip if skipInit flag is set', async t => {
const logName = 'test'
const projectId = 'test-project'
const detectedResource = {
labels: {
projectId,
instanceId: '12345678901234',
zone: 'us-central1-a'
}
}
const defaultOptions = {
googleCloudOptions: {
projectId,
keyFilename: '/path/to/keyfile.json'
},
sync: true,
skipInit: true
}
class LoggingMock extends BaseLoggingMock {
constructor (options) {
t.same(options, defaultOptions.googleCloudOptions)
super()
}
setProjectId () {
t.fail('setProjectId should not be called')
return Promise.resolve()
}
setDetectedResource () {
t.fail('setDetectedResource should not be called')
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
t.equal(name, logName)
return new BaseLogMock(name)
}
}
const expectedDetectedResource = Object.assign(
{ type: 'global' },
detectedResource
)
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(4)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
t.ok(instance.sync)
t.notSame(instance.resource, expectedDetectedResource)
})
})
root.test('#parseLine', nested => {
const logName = 'test'
const projectId = 'test-project'
const detectedResource = {
labels: {
projectId,
instanceId: '12345678901234',
zone: 'us-central1-a'
}
}
const defaultOptions = {
googleCloudOptions: {
projectId,
keyFilename: '/path/to/keyfile.json'
},
sync: true
}
nested.plan(10)
nested.test(
'Should discard non-object meta and parse a line correctly',
async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: ''
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
{
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
t.same(meta, expectedMeta)
t.same(log, expectedLogEntry)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
}
)
nested.test('Should parse a line correctly', async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: {
trace: 'testTrace-123'
}
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
expectedLogEntry.meta,
{
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLog,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
})
nested.test('Should parse a line correctly (custom severity)', async t => {
let expectedEntry
const expectedLogEntry = {
level: 50,
some: 'log',
being: 'printed',
message: 'being printed',
meta: {
trace: 'testTrace-123'
}
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
expectedLogEntry.meta,
{
severity: CloudLogging.SEVERITY_MAP[50],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
})
nested.test('Should parse a line correctly (custom meta)', async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: {
trace: 'testTrace-123'
}
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
expectedLogEntry.meta,
{
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLog,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
})
nested.test(
'Should discard non-object meta and parse a line correctly',
async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: false
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
{
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
t.same(meta, expectedMeta)
t.same(log, expectedLogEntry)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
}
)
nested.test('Should delete the msg and meta property', async t => {
let expectedEntry
const logEntry = {
level: 50,
some: 'log',
being: 'printed',
msg: 'being printed',
meta: {
trace: 'testTrace-123',
spanId: 'testSpan-123'
}
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
logEntry.meta,
{
severity: CloudLogging.SEVERITY_MAP[50],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const expectedLogEntry = Object.assign({ message: '' }, logEntry)
expectedLogEntry.message = logEntry.msg
delete expectedLogEntry.msg
const { meta: _meta, ...expectedLog } = expectedLogEntry
delete expectedLogEntry.meta
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: log,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.notOk(entry.jsonPayload.msg)
t.notOk(entry.jsonPayload.meta)
t.equal(entry.jsonPayload.message, logEntry.msg)
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(6)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(logEntry))
})
nested.test('Should handle msg property', async t => {
let expectedEntry
const logEntry = {
level: 50,
some: 'log',
being: 'printed',
msg: 'being printed',
meta: {
trace: 'testTrace-123'
}
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
logEntry.meta,
{
severity: CloudLogging.SEVERITY_MAP[50],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const expectedLogEntry = Object.assign({ message: '' }, logEntry)
expectedLogEntry.message = logEntry.msg
delete expectedLogEntry.msg
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: log,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.notOk(entry.jsonPayload.msg)
t.equal(entry.jsonPayload.message, logEntry.msg)
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(5)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(logEntry))
})
nested.test('Should respect default labels when parsing lines', async t => {
let expectedEntry
const logEntry = {
level: 50,
some: 'log',
being: 'printed',
msg: 'being printed'
}
const defaultLabels = {
some: 'label',
another: 'label'
}
const newLogLabels = {
passed: 'when',
parsing: 'line'
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
{
severity: CloudLogging.SEVERITY_MAP[50],
labels: Object.assign(
{
logger: 'pino',
agent: 'cloud_pine'
},
defaultLabels,
newLogLabels
)
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const expectedLogEntry = Object.assign({ message: '' }, logEntry)
expectedLogEntry.message = logEntry.msg
delete expectedLogEntry.msg
t.same(meta, expectedMeta)
t.same(log, expectedLogEntry)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: log,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.notOk(entry.jsonPayload.msg)
t.equal(entry.jsonPayload.message, logEntry.msg)
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(10)
const instance = new CloudLogging(
logName,
Object.assign({}, { defaultLabels }, defaultOptions)
)
await instance.init()
instance.parseLine(
JSON.stringify(
Object.assign({}, logEntry, {
meta: {
labels: newLogLabels
}
})
)
)
instance.parseLine(
JSON.stringify(
Object.assign({}, logEntry, {
meta: {
labels: newLogLabels
}
})
)
)
})
nested.test('Should handle an httpRequest prop being passed', async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: {
trace: 'testTrace-123'
}
}
const httpRequestLog = {
requestMethod: 'POST',
requestUrl: 'some/url',
requestSize: '1234',
status: '201',
responseSize: '1234',
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
remoteIp: '192.132.34.2',
serverIp: '192.132.34.2',
referer: 'https://developer.mozilla.org/es/docs/Web/JavaScript',
latency: '1234',
cacheLookup: false,
cacheHit: true,
cacheValidatedWithOriginServer: false,
cacheFillBytes: false,
protocol: 'HTTPS'
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
expectedLogEntry.meta,
{
httpRequest: httpRequestLog,
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(
JSON.stringify(
Object.assign({ httpRequest: httpRequestLog }, expectedLogEntry)
)
)
})
nested.test(
'Should map into an error log if the line is malformed',
async t => {
let expectedEntry
const expectedLogEntry = {
error: new SyntaxError('Unexpected token { in JSON at position 1'),
message: 'Malformed log entry'
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
{
severity: CloudLogging.SEVERITY_MAP[50],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
t.same(meta, expectedMeta)
t.same(log.message, 'Malformed log entry')
t.type(log.error, SyntaxError)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
logSync (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(4)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine('{{}')
}
)
})
})
tap.test('CloudLogging#async', root => {
root.plan(3)
root.test('#mapSeverity', t => {
const cloudLogging = new CloudLogging({})
t.plan(7)
t.equal(cloudLogging.mapSeverity('10'), 'DEBUG')
t.equal(cloudLogging.mapSeverity('20'), 'DEBUG')
t.equal(cloudLogging.mapSeverity('30'), 'INFO')
t.equal(cloudLogging.mapSeverity('40'), 'WARNING')
t.equal(cloudLogging.mapSeverity('50'), 'ERROR')
t.equal(cloudLogging.mapSeverity('60'), 'CRITICAL')
t.equal(cloudLogging.mapSeverity('100'), 'INFO')
})
root.test('#init', nested => {
nested.plan(3)
nested.test(
'Should initialize the log instance with resources',
async t => {
const logName = 'test'
const projectId = 'test-project'
const detectedResource = {
labels: {
projectId,
instanceId: '12345678901234',
zone: 'us-central1-a'
}
}
const defaultOptions = {
googleCloudOptions: {
projectId,
keyFilename: '/path/to/keyfile.json'
}
}
class LoggingMock extends BaseLoggingMock {
constructor (options) {
t.same(options, defaultOptions.googleCloudOptions)
super()
}
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
t.equal(name, logName)
return new BaseLogMock(name)
}
}
const expectedDetectedResource = Object.assign(
{ type: 'global' },
detectedResource
)
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
t.same(instance.resource, expectedDetectedResource)
}
)
nested.test(
'Calles made to #resource should return the fresh object of the parent resource',
async t => {
const logName = 'test'
const projectId = 'test-project'
const detectedResource = {
labels: {
projectId,
instanceId: '12345678901234',
zone: 'us-central1-a'
}
}
const defaultOptions = {
googleCloudOptions: {
projectId,
keyFilename: '/path/to/keyfile.json'
}
}
class LoggingMock extends BaseLoggingMock {
constructor (options) {
t.same(options, defaultOptions.googleCloudOptions)
super()
}
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
t.equal(name, logName)
return new BaseLogMock(name)
}
}
const expectedDetectedResource = Object.assign(
{ type: 'global' },
detectedResource
)
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(5)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
t.same(instance.resource, expectedDetectedResource)
t.same(instance.resource, expectedDetectedResource)
t.same(instance.resource, expectedDetectedResource)
}
)
nested.test('Should be skip if skipInit flag is set', async t => {
const logName = 'test'
const projectId = 'test-project'
const detectedResource = {
labels: {
projectId,
instanceId: '12345678901234',
zone: 'us-central1-a'
}
}
const defaultOptions = {
googleCloudOptions: {
projectId,
keyFilename: '/path/to/keyfile.json'
},
skipInit: true
}
class LoggingMock extends BaseLoggingMock {
constructor (options) {
t.same(options, defaultOptions.googleCloudOptions)
super()
}
setProjectId () {
t.fail('setProjectId should not be called')
return Promise.resolve()
}
setDetectedResource () {
t.fail('setDetectedResource should not be called')
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
t.equal(name, logName)
return new BaseLogMock(name)
}
}
const expectedDetectedResource = Object.assign(
{ type: 'global' },
detectedResource
)
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(4)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
t.notOk(instance.sync)
t.notSame(instance.resource, expectedDetectedResource)
})
})
root.test('#parseLine', nested => {
const logName = 'test'
const projectId = 'test-project'
const detectedResource = {
labels: {
projectId,
instanceId: '12345678901234',
zone: 'us-central1-a'
}
}
const defaultOptions = {
googleCloudOptions: {
projectId,
keyFilename: '/path/to/keyfile.json'
}
}
nested.plan(9)
nested.test('Should parse a line correctly', async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: {
trace: 'exampleTrace-123'
}
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
expectedLogEntry.meta,
{
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
})
nested.test('Should parse a line correctly (custom severity)', async t => {
let expectedEntry
const expectedLogEntry = {
level: 50,
some: 'log',
being: 'printed',
message: 'being printed',
meta: {
trace: 'testTrace-123'
}
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
expectedLogEntry.meta,
{
severity: CloudLogging.SEVERITY_MAP[50],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
})
nested.test('Should parse a line correctly (custom meta)', async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: {
trace: 'exampleTrace-123'
}
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
expectedLogEntry.meta,
{
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
})
nested.test(
'Should discard non-object meta and parse a line correctly',
async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: false
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
{
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
t.same(meta, expectedMeta)
t.same(log, expectedLogEntry)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
}
)
nested.test(
'Should discard non-object meta and parse a line correctly',
async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: ''
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
{
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
t.same(meta, expectedMeta)
t.same(log, expectedLogEntry)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(expectedLogEntry))
}
)
nested.test('Should handle msg property', async t => {
let expectedEntry
const logEntry = {
level: 50,
some: 'log',
being: 'printed',
msg: 'being printed',
meta: {
trace: 'testTrace-123'
}
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
logEntry.meta,
{
severity: CloudLogging.SEVERITY_MAP[50],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const expectedLogEntry = Object.assign({ message: '' }, logEntry)
expectedLogEntry.message = logEntry.msg
delete expectedLogEntry.msg
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: log,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.notOk(entry.jsonPayload.msg)
t.equal(entry.jsonPayload.message, logEntry.msg)
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(5)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(JSON.stringify(logEntry))
})
nested.test('Should respect default labels when parsing lines', async t => {
let expectedEntry
const logEntry = {
level: 50,
some: 'log',
being: 'printed',
msg: 'being printed'
}
const defaultLabels = {
some: 'label',
another: 'label'
}
const newLogLabels = {
passed: 'when',
parsing: 'line'
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
{
severity: CloudLogging.SEVERITY_MAP[50],
labels: Object.assign(
{
logger: 'pino',
agent: 'cloud_pine'
},
defaultLabels,
newLogLabels
)
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const expectedLogEntry = Object.assign({ message: '' }, logEntry)
expectedLogEntry.message = logEntry.msg
delete expectedLogEntry.msg
t.same(meta, expectedMeta)
t.same(log, expectedLogEntry)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: log,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.notOk(entry.jsonPayload.msg)
t.equal(entry.jsonPayload.message, logEntry.msg)
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(10)
const instance = new CloudLogging(
logName,
Object.assign({}, { defaultLabels }, defaultOptions)
)
await instance.init()
instance.parseLine(
JSON.stringify(
Object.assign({}, logEntry, {
meta: {
labels: newLogLabels
}
})
)
)
instance.parseLine(
JSON.stringify(
Object.assign({}, logEntry, {
meta: {
labels: newLogLabels
}
})
)
)
})
nested.test('Should handle an httpRequest prop being passed', async t => {
let expectedEntry
const expectedLogEntry = {
some: 'log',
being: 'printed',
message: 'being printed',
meta: {
trace: 'testTrace-123'
}
}
const httpRequestLog = {
requestMethod: 'POST',
requestUrl: 'some/url',
requestSize: '1234',
status: '201',
responseSize: '1234',
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36',
remoteIp: '192.132.34.2',
serverIp: '192.132.34.2',
referer: 'https://developer.mozilla.org/es/docs/Web/JavaScript',
latency: '1234',
cacheLookup: false,
cacheHit: true,
cacheValidatedWithOriginServer: false,
cacheFillBytes: false,
protocol: 'HTTPS'
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
expectedLogEntry.meta,
{
httpRequest: httpRequestLog,
severity: CloudLogging.SEVERITY_MAP[30],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
const { meta: _meta, ...expectedLog } = expectedLogEntry
t.same(meta, expectedMeta)
t.same(log, expectedLog)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(3)
const instance = new CloudLogging(logName, defaultOptions)
await instance.init()
instance.parseLine(
JSON.stringify(
Object.assign({ httpRequest: httpRequestLog }, expectedLogEntry)
)
)
})
nested.test(
'Should map into an error log if the line is malformed',
async t => {
let expectedEntry
const expectedLogEntry = {
error: new SyntaxError('Unexpected token { in JSON at position 1'),
message: 'Malformed log entry'
}
class LogMock extends BaseLogMock {
entry (meta, log) {
const expectedMeta = Object.assign(
{
severity: CloudLogging.SEVERITY_MAP[50],
labels: {
logger: 'pino',
agent: 'cloud_pine'
}
},
{ resource: Object.assign({ type: 'global' }, detectedResource) }
)
t.same(meta, expectedMeta)
t.same(log.message, 'Malformed log entry')
t.type(log.error, SyntaxError)
return (
(expectedEntry = Object.assign({}, meta, {
jsonPayload: expectedLogEntry,
logName: this.name
})),
expectedEntry
)
}
write (entry) {
t.same(entry, expectedEntry)
}
}
class LoggingMock extends BaseLoggingMock {
setProjectId () {
return Promise.resolve()
}
setDetectedResource () {
this.detectedResource = detectedResource
return Promise.resolve()
}
log (name) {
return new LogMock(name)
}
}
const { CloudLogging } = t.mock('../../lib/cloud-logging', {
'@google-cloud/logging': { Logging: LoggingMock }
})
t.plan(4)