gcp-structured-logger
Advanced tools
Comparing version
@@ -7,2 +7,3 @@ /// <reference types="express-serve-static-core" /> | ||
import { requestToHttpRequest } from "./src/request-transformers"; | ||
import { extractTraceContext } from "./src/trace-context"; | ||
import { NextRequest as _NextRequest } from 'next/server' | ||
@@ -95,2 +96,2 @@ | ||
export { requestToHttpRequest, LogSeverity }; | ||
export { requestToHttpRequest, extractTraceContext, LogSeverity }; |
26
index.js
@@ -76,33 +76,14 @@ const { finished } = require('stream') | ||
const uncaughtExceptionType = Logging._resolveUncaughtExceptionType() | ||
process.on('unhandledRejection', onUnhandledRejection) | ||
process.on(uncaughtExceptionType, onUncaughtException) | ||
process.on('uncaughtExceptionMonitor', onUncaughtException) | ||
return function detachFromProcess() { | ||
process.off('unhandledRejection', onUnhandledRejection) | ||
process.off(uncaughtExceptionType, onUncaughtException) | ||
process.off('uncaughtExceptionMonitor', onUncaughtException) | ||
} | ||
} | ||
/** | ||
* @private | ||
* @see https://nodejs.org/docs/latest-v12.x/api/process.html#process_event_uncaughtexceptionmonitor | ||
* @param {string} version | ||
*/ | ||
static _resolveUncaughtExceptionType(version = process.version) { | ||
// vX.Y.Z | ||
const versionArgs = /^v?(\d+)\.(\d+)(?:\.\d+)?$/.exec(version) | ||
if (versionArgs) { | ||
const [major, minor] = [parseInt(versionArgs[1]), parseInt(versionArgs[2])] | ||
if (major > 13 || (major === 13 && minor >= 7) || (major === 12 && minor >= 17)) { | ||
// Added in v12.17.0 and v13.7.0 | ||
return 'uncaughtExceptionMonitor' | ||
} | ||
} | ||
return 'uncaughtException' | ||
} | ||
} | ||
const { requestToHttpRequest } = require('./src/request-transformers') | ||
const { extractTraceContext } = require('./src/trace-context') | ||
@@ -113,2 +94,3 @@ module.exports = { | ||
requestToHttpRequest, | ||
extractTraceContext, | ||
} |
{ | ||
"name": "gcp-structured-logger", | ||
"version": "1.4.7", | ||
"version": "1.4.8", | ||
"description": "Structured logger for GCP logging", | ||
@@ -37,3 +37,3 @@ "main": "index.js", | ||
"@types/express-serve-static-core": "^4.17.33", | ||
"c8": "^7.13.0", | ||
"c8": "^9.1.0", | ||
"chai": "^4.3.7", | ||
@@ -45,4 +45,4 @@ "eslint": "^8.38.0", | ||
"next": "^12.3.4", | ||
"sinon": "^15.0.3" | ||
"sinon": "^17.0.2" | ||
} | ||
} |
@@ -1,5 +0,5 @@ | ||
const { format, formatWithOptions, inspect, types: { isDate } } = require('util') | ||
const { format, formatWithOptions, inspect } = require('util') | ||
const { LogSeverity, CONSOLE_SEVERITY } = require('./severity') | ||
const cleanupForJSON = require('./cleanup-for-json') | ||
const getTraceContext = require('./trace-context') | ||
const { extractTraceContext } = require('./trace-context') | ||
const { requestToErrorReportingHttpRequest } = require('./request-transformers') | ||
@@ -486,3 +486,3 @@ const { now: nowNS, hrToTimestamp, timestampToISOString, NS_MICROSECOND, NS_MILLISECOND, NS_SECOND, NS_MINUTE, NS_HOUR } = require('./hr-time') | ||
/** @readonly @private */ | ||
this._trace = getTraceContext(projectId, request) | ||
this._trace = extractTraceContext(projectId, request) | ||
} | ||
@@ -489,0 +489,0 @@ |
const { getHeader } = require('./request-properties') | ||
/** Header that carries span context across Google infrastructure. */ | ||
const TRACE_CONTEXT_HEADER_NAME = 'x-cloud-trace-context' | ||
const TRACE_CONTEXT_HEADER_FORMAT = /^([0-9a-fA-F]+)(?:\/([0-9]+))(?:;o=(.*))?/ | ||
const TRACEPARENT_HEADER_NAME = 'traceparent' | ||
/** @see https://www.w3.org/TR/trace-context/#traceparent-header-field-values */ | ||
const TRACEPARENT_HEADER_FORMAT_V0 = /^00-(?<traceId>[a-f0-9]{32})-(?<spanId>[a-f0-9]{16})-(?<flags>[a-f0-9]{2})$/i | ||
const CLOUD_TRACE_CONTEXT_HEADER_NAME = 'x-cloud-trace-context' | ||
const CLOUD_TRACE_CONTEXT_HEADER_FORMAT = /^(?<traceId>[a-f0-9]+)\/(?<spanId>[0-9]+)(?:;o=(?<sampled>[01]))?$/i | ||
const TRACE_PARENT_FLAGS = { | ||
sampled: 0b00000001, | ||
} | ||
/** | ||
* @typedef {{ trace: string, spanId: string, traceSampled: boolean }} TraceContext | ||
* @param {string} projectId | ||
* @param {import('./StructuredLogger').Request} req | ||
* @returns {{} | { trace: string, spanId: string }} | ||
* @returns {{} | TraceContext} | ||
*/ | ||
module.exports = function extractTraceContext(projectId, req) { | ||
const traceContextHeader = getHeader(req, TRACE_CONTEXT_HEADER_NAME) | ||
if (traceContextHeader !== null) { | ||
const matches = TRACE_CONTEXT_HEADER_FORMAT.exec(traceContextHeader) | ||
if (matches && matches.length === 4 && matches[0] === traceContextHeader) { | ||
try { | ||
return { | ||
/** `projects/<PROJECT-ID>/traces/<TRACE-ID>` */ | ||
trace: `projects/${projectId}/traces/${matches[1]}`, | ||
// Convert spanId to hex and ensure its always a length-16 hex string | ||
spanId: BigInt(matches[2]).toString(16).padStart(16, '0'), | ||
} | ||
} /* c8 ignore next */ catch { /* Bad span number */ } | ||
} | ||
function extractTraceContext(projectId, req) { | ||
/** @type {string | undefined} */ | ||
let traceId | ||
/** @type {string | undefined | bigint} */ | ||
let spanId | ||
/** @type {number} */ | ||
let flagsValue = 0 | ||
const traceparent = /** @type {{ traceId: string, spanId: string, flags: string } | undefined} */(TRACEPARENT_HEADER_FORMAT_V0.exec(getHeader(req, TRACEPARENT_HEADER_NAME))?.groups) | ||
if (traceparent) { | ||
traceId = traceparent.traceId | ||
spanId = `0x${traceparent.spanId}` | ||
flagsValue = Number.parseInt(traceparent.flags, 16) | ||
} | ||
return {} | ||
const cloudTraceContext = /** @type {{ traceId: string, spanId: string, sampled?: '0' | '1' }} */(CLOUD_TRACE_CONTEXT_HEADER_FORMAT.exec(getHeader(req, CLOUD_TRACE_CONTEXT_HEADER_NAME))?.groups) | ||
if (cloudTraceContext) { | ||
({ traceId, spanId } = cloudTraceContext) | ||
if (cloudTraceContext.sampled !== '0') flagsValue = 0b00000001 // Convert to newer flags format | ||
} | ||
try { | ||
if (traceId === '00000000000000000000000000000000') throw Error('Null traceId') | ||
if (!spanId || BigInt(spanId) === 0n) throw Error('Null spanId') | ||
spanId = BigInt(spanId) | ||
} catch { | ||
// Can ignore - invalid values | ||
return {} | ||
} | ||
return { trace: `projects/${projectId}/traces/${traceId}`, spanId: spanId.toString(16).padStart(16, '0'), traceSampled: Boolean(flagsValue & TRACE_PARENT_FLAGS.sampled) } | ||
} | ||
module.exports = { | ||
extractTraceContext, | ||
} |
42728
0.98%971
0.52%