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
JavaScript
;
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