UNPKG

@google-cloud/logging

Version:
287 lines 11.5 kB
"use strict"; /*! * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Entry = exports.TRACE_SAMPLED_KEY = exports.TRACE_KEY = exports.SPAN_ID_KEY = exports.SOURCE_LOCATION_KEY = exports.OPERATION_KEY = exports.LABELS_KEY = exports.INSERT_ID_KEY = void 0; const extend = require("extend"); const event_id_1 = require("./utils/event-id"); const common_1 = require("./utils/common"); const http_request_1 = require("./utils/http-request"); const context_1 = require("./utils/context"); const eventId = new event_id_1.EventId(); exports.INSERT_ID_KEY = 'logging.googleapis.com/insertId'; exports.LABELS_KEY = 'logging.googleapis.com/labels'; exports.OPERATION_KEY = 'logging.googleapis.com/operation'; exports.SOURCE_LOCATION_KEY = 'logging.googleapis.com/sourceLocation'; exports.SPAN_ID_KEY = 'logging.googleapis.com/spanId'; exports.TRACE_KEY = 'logging.googleapis.com/trace'; exports.TRACE_SAMPLED_KEY = 'logging.googleapis.com/trace_sampled'; /** * Create an entry object to define new data to insert into a meta. * * Note, {@link https://cloud.google.com/logging/quotas|Cloud Logging Quotas and limits} * dictates that the maximum log entry size, including all * {@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry|LogEntry Resource properties}, * cannot exceed approximately 256 KB. * * See {@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry|LogEntry JSON representation} * * @class * * @param {?object} [metadata] See a * [LogEntry * Resource](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry). * @param {object|string} data The data to use as the value for this log * entry. * * If providing an object, these value types are supported: * - `String` * - `Number` * - `Boolean` * - `Buffer` * - `Object` * - `Array` * * Any other types are stringified with `String(value)`. * * @example * ``` * const {Logging} = require('@google-cloud/logging'); * const logging = new Logging(); * const syslog = logging.log('syslog'); * * const metadata = { * resource: { * type: 'gce_instance', * labels: { * zone: 'global', * instance_id: '3' * } * } * }; * * const entry = syslog.entry(metadata, { * delegate: 'my_username' * }); * * syslog.alert(entry, (err, apiResponse) => { * if (!err) { * // Log entry inserted successfully. * } * }); * * //- * // You will also receive `Entry` objects when using * // Logging#getEntries() and Log#getEntries(). * //- * logging.getEntries((err, entries) => { * if (!err) { * // entries[0].data = The data value from the log entry. * } * }); * ``` */ class Entry { constructor(metadata, data) { /** * @name Entry#metadata * @type {object} * @property {Date} timestamp * @property {number} insertId */ this.metadata = extend({ timestamp: new Date(), }, metadata); // JavaScript date has a very coarse granularity (millisecond), which makes // it quite likely that multiple log entries would have the same timestamp. // The Logging API doesn't guarantee to preserve insertion order for entries // with the same timestamp. The service does use `insertId` as a secondary // ordering for entries with the same timestamp. `insertId` needs to be // globally unique (within the project) however. // // We use a globally unique monotonically increasing EventId as the // insertId. this.metadata.insertId = this.metadata.insertId || eventId.new(); /** * @name Entry#data * @type {object} */ this.data = data; } /** * Serialize an entry to the format the API expects. Read more: * https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry * * @param {object} [options] Configuration object. * @param {boolean} [options.removeCircular] Replace circular references in an * object with a string value, `[Circular]`. * @param {string} [projectId] GCP Project ID. */ toJSON(options = {}, projectId = '') { const entry = extend(true, {}, this.metadata); // Format log message if (this.isObject(this.data)) { entry.jsonPayload = (0, common_1.objToStruct)(this.data, { removeCircular: !!options.removeCircular, stringify: true, }); } else if (typeof this.data === 'string') { entry.textPayload = this.data; } // Format log timestamp if (entry.timestamp instanceof Date) { entry.timestamp = (0, common_1.toNanosAndSecondsObj)(entry.timestamp); } else if (typeof entry.timestamp === 'string') { entry.timestamp = (0, common_1.zuluToDateObj)(entry.timestamp); } // Format httpRequest const req = this.metadata.httpRequest; if ((0, http_request_1.isRawHttpRequest)(req)) { entry.httpRequest = (0, http_request_1.makeHttpRequestData)(req); } // Format trace and span const traceContext = this.extractTraceContext(projectId); if (traceContext) { if (!this.metadata.trace && traceContext.trace) entry.trace = traceContext.trace; if (!this.metadata.spanId && traceContext.spanId) entry.spanId = traceContext.spanId; if (this.metadata.traceSampled === undefined) entry.traceSampled = traceContext.traceSampled; } return entry; } /** * Serialize an entry to a standard format for any transports, e.g. agents. * Read more: https://cloud.google.com/logging/docs/structured-logging */ toStructuredJSON(projectId = '', useMessageField = true) { const meta = this.metadata; // Mask out the keys that need to be renamed. /* eslint-disable @typescript-eslint/no-unused-vars */ const { textPayload, jsonPayload, insertId, trace, spanId, traceSampled, operation, sourceLocation, labels, ...validKeys } = meta; /* eslint-enable @typescript-eslint/no-unused-vars */ let entry = extend(true, {}, validKeys); // Re-map keys names. entry[exports.LABELS_KEY] = meta.labels ? Object.assign({}, meta.labels) : undefined; entry[exports.INSERT_ID_KEY] = meta.insertId || undefined; entry[exports.TRACE_KEY] = meta.trace || undefined; entry[exports.SPAN_ID_KEY] = meta.spanId || undefined; entry[exports.TRACE_SAMPLED_KEY] = 'traceSampled' in meta && meta.traceSampled !== null ? meta.traceSampled : undefined; // Format log payload. const data = this.data || meta.textPayload || meta.jsonPayload || meta.protoPayload || undefined; if (useMessageField) { /** If useMessageField is set, we add the payload to {@link StructuredJson#message} field.*/ entry.message = data; } else { /** useMessageField is false, we add the structured payload to {@link StructuredJson} key-value map. * It could be especially useful for serverless environments like Cloud Run/Functions when stdout transport is used. * Note that text still added to {@link StructuredJson#message} field for text payload since it does not have fields within. */ if (data !== undefined && data !== null) { if (this.isObject(data)) { entry = extend(true, {}, entry, data); } else if (typeof data === 'string') { entry.message = data; } else { entry.message = JSON.stringify(data); } } } // Format timestamp if (meta.timestamp instanceof Date) { entry.timestamp = (0, common_1.toNanosAndSecondsObj)(meta.timestamp); } // Format httprequest const req = meta.httpRequest; if ((0, http_request_1.isRawHttpRequest)(req)) { entry.httpRequest = (0, http_request_1.makeHttpRequestData)(req); } // Detected trace context from OpenTelemetry context or http headers if applicable. const traceContext = this.extractTraceContext(projectId); if (traceContext) { if (!entry[exports.TRACE_KEY] && traceContext.trace) entry[exports.TRACE_KEY] = traceContext.trace; if (!entry[exports.SPAN_ID_KEY] && traceContext.spanId) entry[exports.SPAN_ID_KEY] = traceContext.spanId; if (entry[exports.TRACE_SAMPLED_KEY] === undefined) entry[exports.TRACE_SAMPLED_KEY] = traceContext.traceSampled; } return entry; } /** * extractTraceContext extracts trace and span information from OpenTelemetry * span context or raw HTTP request headers. * @private */ extractTraceContext(projectId) { // Extract trace context from OpenTelemetry span context. const otelContext = (0, context_1.getContextFromOtelContext)(projectId); if (otelContext) return otelContext; // Extract trace context from http request headers. const rawReq = this.metadata.httpRequest; if (rawReq && 'headers' in rawReq) { return (0, context_1.getOrInjectContext)(rawReq, projectId, false); } return null; } /** * Create an Entry object from an API response, such as `entries:list`. * * @private * * @param {object} entry An API representation of an entry. See a * {@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry| LogEntry}. * @returns {Entry} */ static fromApiResponse_(entry) { let data = entry[entry.payload]; if (entry.payload === 'jsonPayload') { data = (0, common_1.structToObj)(data); } const serializedEntry = new Entry(entry, data); if (entry.timestamp) { let ms = Number(entry.timestamp.seconds) * 1000; ms += Number(entry.timestamp.nanos) / 1e6; serializedEntry.metadata.timestamp = new Date(ms); } return serializedEntry; } /** * Determines whether `value` is a JavaScript object. * @param value The value to be checked * @returns true if `value` is a JavaScript object, false otherwise */ isObject(value) { return Object.prototype.toString.call(value) === '[object Object]'; } } exports.Entry = Entry; //# sourceMappingURL=entry.js.map