UNPKG

enhanced-adot-node-autoinstrumentation

Version:

This package provides Amazon Web Services distribution of the OpenTelemetry Node Instrumentation, which allows for auto-instrumentation of NodeJS applications.

132 lines 5.52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AwsBatchLogRecordProcessor = exports.MAX_LOG_REQUEST_BYTE_SIZE = exports.BASE_LOG_BUFFER_BYTE_SIZE = void 0; // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 const sdk_logs_1 = require("@opentelemetry/sdk-logs"); const core_1 = require("@opentelemetry/core"); exports.BASE_LOG_BUFFER_BYTE_SIZE = 2000; exports.MAX_LOG_REQUEST_BYTE_SIZE = 1048576; class AwsBatchLogRecordProcessor extends sdk_logs_1.BatchLogRecordProcessor { constructor(exporter, config) { super(exporter, config); this._flushOneBatch = () => this._flushOneBatchIntermediary(); } /** * Custom implementation of BatchLogRecordProcessor that manages log record batching * with size-based constraints to prevent exceeding AWS request size limits. * * This processor still exports all logs up to maxExportBatchSize but rather than doing exactly * one export promise, we do an array of export Promises where each exported batch will have an additonal constraint: * * If the batch to be exported will have a data size of > 1 MB: * The batch will be split into multiple exports of sub-batches of data size <= 1 MB. * * A unique case is if the sub-batch is of data size > 1 MB, then the sub-batch will have exactly 1 log in it. * */ _flushOneBatchIntermediary() { const processor = this; processor._clearTimer(); if (processor._finishedLogRecords.length === 0) { return Promise.resolve(); } const logsToExport = processor._finishedLogRecords.splice(0, processor._maxExportBatchSize); let batch = []; let batchDataSize = 0; const exportPromises = []; for (let i = 0; i < logsToExport.length; i += 1) { const logData = logsToExport[i]; const logSize = AwsBatchLogRecordProcessor.getSizeOfLog(logData); if (batch.length > 0 && batchDataSize + logSize > exports.MAX_LOG_REQUEST_BYTE_SIZE) { // if batchDataSize > MAX_LOG_REQUEST_BYTE_SIZE then batch.length == 1 if (batchDataSize > exports.MAX_LOG_REQUEST_BYTE_SIZE) { processor._exporter.setGenAIFlag(); } exportPromises.push((0, core_1.callWithTimeout)(processor._export(batch), processor._exportTimeoutMillis)); batchDataSize = 0; batch = []; } batchDataSize += logSize; batch.push(logData); } if (batch.length > 0) { // if batchDataSize > MAX_LOG_REQUEST_BYTE_SIZE then batch.length == 1 if (batchDataSize > exports.MAX_LOG_REQUEST_BYTE_SIZE) { processor._exporter.setGenAIFlag(); } exportPromises.push((0, core_1.callWithTimeout)(processor._export(batch), processor._exportTimeoutMillis)); } return new Promise((resolve, reject) => { Promise.all(exportPromises) .then(() => resolve()) .catch(reject); }); } /** * Calculates the estimated byte size of a log record. * * @param log - The LogRecord to calculate the size for * @returns The estimated size in bytes, including a base buffer size plus the size of the log body */ static getSizeOfLog(log) { if (!log.body) { return exports.BASE_LOG_BUFFER_BYTE_SIZE; } return exports.BASE_LOG_BUFFER_BYTE_SIZE + AwsBatchLogRecordProcessor.getSizeOfAnyValue(log.body); } /** * Calculates the size of an AnyValue type. If AnyValue is an instance of a Map or Array, calculation is truncated to one layer. * * @param val - The AnyValue to calculate the size for * @returns The size in bytes */ static getSizeOfAnyValue(val) { // Use a stack to prevent excessive recursive calls const stack = [val]; let size = 0; let depth = 0; while (stack.length > 0) { const nextVal = stack.pop(); if (!nextVal) { continue; } if (typeof nextVal === 'string') { size += nextVal.length; continue; } if (typeof nextVal === 'boolean') { size += nextVal ? 4 : 5; // 'true' or 'false' continue; } if (typeof nextVal === 'number') { size += nextVal.toString().length; continue; } if (nextVal instanceof Uint8Array) { size += nextVal.byteLength; continue; } if (depth < 1) { if (Array.isArray(nextVal)) { for (const item of nextVal) { stack.push(item); } // By process of elimination, nextVal has to be a Map } else { const map = nextVal; for (const key in map) { size += key.length; stack.push(map[key]); } } depth += 1; continue; } } return size; } } exports.AwsBatchLogRecordProcessor = AwsBatchLogRecordProcessor; //# sourceMappingURL=aws-batch-log-record-processor.js.map