gcp-structured-logger
Advanced tools
Comparing version
26
index.js
const { finished } = require('stream') | ||
const { ErrorReporting } = require('@google-cloud/error-reporting') | ||
const { StructuredLogger } = require('./src/StructuredLogger') | ||
const { LogSeverity } = require('./src/severity') | ||
const ERROR_REPORTING_PROP = '__errorReporter' | ||
class Logging { | ||
@@ -19,3 +16,3 @@ | ||
/** @readonly */ | ||
this.logger = new StructuredLogger(projectId, logName, () => this._errorReporter, productionTransport, extraLabels) | ||
this.logger = new StructuredLogger(projectId, logName, serviceContext, productionTransport, extraLabels) | ||
} | ||
@@ -25,23 +22,2 @@ | ||
* @private | ||
* Used for all child logs of this Logging instance, lazily loaded. | ||
*/ | ||
get _errorReporter() { | ||
/** @type {import('@google-cloud/error-reporting').ErrorReporting} */ | ||
let e | ||
if (ERROR_REPORTING_PROP in this) { | ||
e = this[ERROR_REPORTING_PROP] | ||
} else { | ||
e = new ErrorReporting({ | ||
serviceContext: this._serviceContext, | ||
reportUnhandledRejections: false, | ||
// Don't report the errors - we'll manually log errors to include other info | ||
reportMode: 'never', | ||
}) | ||
Object.defineProperty(this, ERROR_REPORTING_PROP, { value: e, enumerable: false, configurable: false }) | ||
} | ||
return e | ||
} | ||
/** | ||
* @private | ||
* @param {import('express-serve-static-core').Request} req | ||
@@ -48,0 +24,0 @@ */ |
{ | ||
"name": "gcp-structured-logger", | ||
"version": "1.4.1", | ||
"version": "1.4.2", | ||
"description": "Structured logger for GCP logging", | ||
@@ -34,11 +34,9 @@ "main": "index.js", | ||
}, | ||
"dependencies": { | ||
"@google-cloud/error-reporting": "^2.0.5" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"@types/express-serve-static-core": "^4.17.28", | ||
"@types/express-serve-static-core": "^4.17.29", | ||
"c8": "^7.11.3", | ||
"chai": "^4.3.6", | ||
"eslint": "^8.16.0", | ||
"eslint-plugin-mocha": "^10.0.3", | ||
"eslint": "^8.19.0", | ||
"eslint-plugin-mocha": "^10.0.5", | ||
"eslint-plugin-node": "^11.1.0", | ||
@@ -45,0 +43,0 @@ "mocha": "^10.0.0", |
/** | ||
* Converts a request (and its attached response) to a HttpRequest for Stackdriver LogEntry. | ||
* Converts a request (and its attached response) to a `HttpRequest` for Stackdriver `LogEntry`. | ||
* | ||
@@ -15,6 +15,6 @@ * @param {import('express-serve-static-core').Request} req | ||
} | ||
if ('remoteAddress' in reportingReq) httpReq.remoteIp = reportingReq.remoteAddress | ||
if ('remoteIp' in reportingReq) httpReq.remoteIp = reportingReq.remoteIp | ||
if ('referrer' in reportingReq) httpReq.referer = reportingReq.referrer | ||
if ('userAgent' in reportingReq) httpReq.userAgent = reportingReq.userAgent | ||
if ('statusCode' in reportingReq) httpReq.status = reportingReq.statusCode | ||
if ('responseStatusCode' in reportingReq) httpReq.status = reportingReq.responseStatusCode | ||
@@ -36,4 +36,11 @@ // Add in extra request info | ||
/** | ||
* Converts a request (and its attached response) to a HttpRequestContext for Stackdriver error reporting. | ||
* Converts a request (and its attached response) to a `HttpRequestContext` for Stackdriver error reporting. | ||
* See https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorContext#httprequestcontext | ||
* @typedef {object} HttpRequestContext | ||
* @prop {string} method | ||
* @prop {string} url | ||
* @prop {string} [userAgent] | ||
* @prop {string} [referrer] | ||
* @prop {number} [responseStatusCode] | ||
* @prop {string} [remoteIp] | ||
* | ||
@@ -43,15 +50,15 @@ * @param {import('express-serve-static-core').Request} req | ||
function requestToErrorReportingHttpRequest(req) { | ||
/** @type {import('@google-cloud/error-reporting/build/src/request-extractors/manual').Request} */ | ||
/** @type {HttpRequestContext} */ | ||
const httpReq = { | ||
url: req.originalUrl, | ||
method: req.method, | ||
remoteAddress: req.ip || (Array.isArray(req.ips) ? req.ips[0] : null) | ||
remoteIp: req.ip || (Array.isArray(req.ips) ? req.ips[0] : null) | ||
} | ||
if (!httpReq.remoteAddress) { | ||
if (!httpReq.remoteIp) { | ||
const headerIps = req.get('x-forwarded-for') | ||
if (headerIps && (!Array.isArray(headerIps) || headerIps.length > 0)) { | ||
httpReq.remoteAddress = Array.isArray(headerIps) ? headerIps[0] : headerIps | ||
httpReq.remoteIp = Array.isArray(headerIps) ? headerIps[0] : headerIps | ||
} else { | ||
delete httpReq.remoteAddress | ||
delete httpReq.remoteIp | ||
} | ||
@@ -66,3 +73,3 @@ } | ||
if (req.res) httpReq.statusCode = req.res.statusCode | ||
if (req.res) httpReq.responseStatusCode = req.res.statusCode | ||
@@ -69,0 +76,0 @@ return httpReq |
@@ -1,2 +0,2 @@ | ||
const { format, formatWithOptions } = require('util') | ||
const { format, formatWithOptions, inspect } = require('util') | ||
const { LogSeverity, CONSOLE_SEVERITY } = require('./severity') | ||
@@ -40,7 +40,7 @@ const cleanupForJSON = require('./cleanup-for-json') | ||
* @param {string} logName | ||
* @param {() => import('@google-cloud/error-reporting').ErrorReporting} errorReporter | ||
* @param {import('../').ServiceContext} serviceContext | ||
* @param {?import('../').Transport} productionTransport | ||
* @param {{ [key: string]: string }} labels | ||
*/ | ||
constructor(projectId, logName, errorReporter, productionTransport, labels) { | ||
constructor(projectId, logName, serviceContext, productionTransport, labels) { | ||
/** @readonly @private */ | ||
@@ -51,3 +51,3 @@ this._projectId = projectId | ||
/** @readonly @private */ | ||
this._errorReporter = errorReporter | ||
this._serviceContext = serviceContext | ||
/** @readonly @private */ | ||
@@ -66,3 +66,3 @@ this._productionTransport = productionTransport | ||
child(type) { | ||
return new StructuredLogger(this._projectId, this._logName, this._errorReporter, this._productionTransport, { ...this._labels, type }) | ||
return new StructuredLogger(this._projectId, this._logName, this._serviceContext, this._productionTransport, { ...this._labels, type }) | ||
} | ||
@@ -76,3 +76,3 @@ | ||
_requestChild(request, extractUser) { | ||
return new StructuredRequestLogger(this._projectId, this._logName, this._errorReporter, this._productionTransport, { ...this._labels, type: 'request' }, request, extractUser) | ||
return new StructuredRequestLogger(this._projectId, this._logName, this._serviceContext, this._productionTransport, { ...this._labels, type: 'request' }, request, extractUser) | ||
} | ||
@@ -135,5 +135,3 @@ | ||
} | ||
const event = this._makeReportableError(this._errorReporter(), err) | ||
// Remove report location as the stack trace is used | ||
delete event.context.reportLocation | ||
const event = { eventTime: timestamp.toISOString(), ...this._makeReportableError(err) } | ||
if (!event.context.user) delete event.context.user | ||
@@ -260,9 +258,43 @@ if (!event.context.user && !event.context.httpRequest) delete event.context | ||
/** | ||
* @typedef {object} ReportedErrorEvent | ||
* @prop {import('../').ServiceContext} serviceContext | ||
* @prop {string} message | ||
* @prop {{ httpRequest?: import('./request-transformers').HttpRequestContext, user?: string }} context | ||
* | ||
* @protected | ||
* @param {import('@google-cloud/error-reporting').ErrorReporting} errorReporter | ||
* @param {any} err | ||
*/ | ||
_makeReportableError(errorReporter, err) { | ||
const event = errorReporter.report(err) | ||
delete event.context.httpRequest | ||
_makeReportableError(err) { | ||
/** @type {string?} */ | ||
let message | ||
{ | ||
let hasStack = false | ||
if (err && typeof err === 'object') { | ||
if (err.stack) { | ||
message = err.stack | ||
hasStack = true | ||
} else { | ||
message = (typeof err.toString === 'function' && err.toString() !== String({})) ? err.toString() : err.message | ||
} | ||
} else { | ||
message = typeof err === 'string' ? err : String(err) | ||
} | ||
if (!message) message = inspect(err) | ||
if (!hasStack) { | ||
message += '\n' | ||
const trace = {} | ||
Error.captureStackTrace(trace, this._makeReportableError) | ||
// Remove first line | ||
message += trace.stack.slice(trace.stack.indexOf('\n') + 1) | ||
} | ||
} | ||
/** @type {ReportedErrorEvent} */ | ||
const event = { | ||
message, | ||
serviceContext: { ...this._serviceContext }, | ||
context: {}, | ||
} | ||
if (typeof err === 'object' && err && err.user) event.context.user = String(err.user) | ||
return event | ||
@@ -439,3 +471,3 @@ } | ||
* @param {string} logName | ||
* @param {() => import('@google-cloud/error-reporting').ErrorReporting} errorReporter | ||
* @param {import('../').ServiceContext} serviceContext | ||
* @param {import('../').Transport} productionTransport | ||
@@ -446,4 +478,4 @@ * @param {{ [key: string]: string }} labels | ||
*/ | ||
constructor(projectId, logName, errorReporter, productionTransport, labels, request, extractUser) { | ||
super(projectId, logName, errorReporter, productionTransport, labels) | ||
constructor(projectId, logName, serviceContext, productionTransport, labels, request, extractUser) { | ||
super(projectId, logName, serviceContext, productionTransport, labels) | ||
/** @readonly @private */ | ||
@@ -468,11 +500,11 @@ this._request = request | ||
/** | ||
* @param {import('@google-cloud/error-reporting').ErrorReporting} errorReporter | ||
* @param {any} err | ||
*/ | ||
_makeReportableError(errorReporter, err) { | ||
_makeReportableError(err) { | ||
const event = super._makeReportableError(err) | ||
// Add in request and user info | ||
const event = errorReporter.report(err, requestToErrorReportingHttpRequest(this._request)) | ||
event.context = { httpRequest: requestToErrorReportingHttpRequest(this._request) } | ||
if (typeof this._extractUser === 'function') { | ||
const user = this._extractUser(this._request) | ||
if (user) event.setUser(user) | ||
if (user) event.context.user = user | ||
} | ||
@@ -479,0 +511,0 @@ return event |
37799
0.54%0
-100%850
1.8%- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed