UNPKG

bunyan-gcp-logging

Version:

Bunyan Transport for GCP (Google Cloud Platform) using Cloud Logging

119 lines (118 loc) 4.37 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createStream = exports.GCPCloudLoggingTransformer = void 0; const util_1 = require("util"); const bunyan_1 = __importDefault(require("bunyan")); const stream_1 = require("stream"); // Map of Cloud Logging levels. const BUNYAN_TO_GCP_CLOUD_LOGGING = new Map([ [60, 'CRITICAL'], [50, 'ERROR'], [40, 'WARNING'], [30, 'INFO'], [20, 'DEBUG'], [10, 'DEBUG'], ]); /** * TAKEN from bunyan source code. (but modified) * * A fast JSON.stringify that handles cycles and getter exceptions (when * safeJsonStringify is installed). * * This function attempts to use the regular JSON.stringify for speed, but on * error (e.g. JSON cycle detection exception) it falls back to safe stringify * handlers that can deal with cycles and/or getter exceptions. */ function fastAndSafeJsonStringify(rec) { try { return JSON.stringify(rec); } catch (ex) { try { return JSON.stringify(rec, bunyan_1.default.safeCycles()); } catch (e) { return util_1.format('(Exception in JSON.stringify(rec): %j. ', e.message); } } } class GCPCloudLoggingTransformer extends stream_1.Transform { constructor() { super({ writableObjectMode: true, }); } _transform(chunk, encoding, callback) { if (typeof chunk === 'string') { callback(new Error('Bad configuration. Use raw stream type on bunyan')); return; } let entry; try { entry = this.formatEntry(chunk); } catch (err) { callback(err); return; } callback(undefined, fastAndSafeJsonStringify(entry) + '\n'); } /** * Format a bunyan record into a Stackdriver log entry. */ formatEntry(record) { var _a, _b; // extract field we want to transform or discard const { name: logName, msg, level, err, v, hostname, pid, time: timestamp, ...others } = record; const entry = { logName, timestamp, message: (_a = record === null || record === void 0 ? void 0 : record.message) !== null && _a !== void 0 ? _a : msg, severity: BUNYAN_TO_GCP_CLOUD_LOGGING.get(Number(level)), ...others, }; /** * If this is an error, report the full stack trace. This allows * Cloud Logging Error Reporting to pick up errors automatically (for * severity 'error' or higher). In this case we leave the 'msg' property * intact. * * @see https://cloud.google.com/error-reporting/docs/formatting-error-messages */ if (err && (err === null || err === void 0 ? void 0 : err.stack)) { entry.message = err.stack; entry.serviceContext = { service: logName }; } /** * Try to get HTTP Request from log data to make an log on the GCP Cloud Logggin format. */ const req = (_b = record === null || record === void 0 ? void 0 : record.httpRequest) !== null && _b !== void 0 ? _b : record === null || record === void 0 ? void 0 : record.req; if (req) entry.httpRequest = this.formatHttpEntry(req); return entry; } formatHttpEntry(request) { var _a, _b, _c; const httpRequest = { requestMethod: (_a = request === null || request === void 0 ? void 0 : request.method) !== null && _a !== void 0 ? _a : '', requestUrl: (_b = request === null || request === void 0 ? void 0 : request.url) !== null && _b !== void 0 ? _b : '', remoteIp: (_c = request === null || request === void 0 ? void 0 : request.remoteAddress) !== null && _c !== void 0 ? _c : '', ...request, }; return httpRequest; } } exports.GCPCloudLoggingTransformer = GCPCloudLoggingTransformer; function createStream(level, out) { const transformer = new GCPCloudLoggingTransformer(); transformer.pipe(out || process.stdout); return { level, type: 'raw', stream: transformer, }; } exports.createStream = createStream;