@azure/cosmos
Version:
Microsoft Azure Cosmos DB Service Node.js SDK for NOSQL API
300 lines • 12.8 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { CosmosDiagnosticContext } from "./CosmosDiagnosticsContext.js";
import { ErrorResponse } from "../request/index.js";
import { CosmosDiagnostics, getRootNode } from "../CosmosDiagnostics.js";
import { getCurrentTimestampInMs } from "../utils/time.js";
import { CosmosDbDiagnosticLevel } from "./CosmosDbDiagnosticLevel.js";
import { Constants, prepareURL } from "../common/index.js";
import { allowTracing } from "./diagnosticLevelComparator.js";
import { randomUUID } from "@azure/core-util";
/**
* @hidden
* This is Internal Representation for DiagnosticNode. It contains useful helper functions to collect
* diagnostic information throughout the lifetime of Diagnostic session.
* The functions toDiagnosticNode() & toDiagnostic() are given to convert it to public facing counterpart.
*/
export class DiagnosticNodeInternal {
/**
* @internal
*/
constructor(diagnosticLevel, type, parent, data = {}, startTimeUTCInMs = getCurrentTimestampInMs(), ctx = new CosmosDiagnosticContext()) {
this.id = randomUUID();
this.nodeType = type;
this.startTimeUTCInMs = startTimeUTCInMs;
this.data = data;
this.children = [];
this.durationInMs = 0;
this.parent = parent;
this.diagnosticCtx = ctx;
this.diagnosticLevel = diagnosticLevel;
// Initialize EncryptionDiagnostics
this.encryptionDiagnostics = {
encryptContent: {},
decryptContent: {},
processingDurationInMs: 0,
};
}
/**
* @internal
*/
addLog(msg) {
if (!this.data.log) {
this.data.log = [];
}
this.data.log.push(msg);
}
/**
* @internal
*/
sanitizeHeaders(headers) {
return headers;
}
/**
* Updated durationInMs for node, based on endTimeUTCInMs provided.
* @internal
*/
updateTimestamp(endTimeUTCInMs = getCurrentTimestampInMs()) {
this.durationInMs = endTimeUTCInMs - this.startTimeUTCInMs;
}
/**
* @internal
*/
recordSuccessfulNetworkCall(startTimeUTCInMs, requestContext, pipelineResponse, substatus, url) {
const responseHeaders = pipelineResponse.headers.toJSON();
const gatewayRequest = {
activityId: responseHeaders[Constants.HttpHeaders.ActivityId],
correlateActivityId: requestContext.headers[Constants.HttpHeaders.CorrelatedActivityId],
startTimeUTCInMs,
durationInMs: getCurrentTimestampInMs() - startTimeUTCInMs,
statusCode: pipelineResponse.status,
subStatusCode: substatus,
requestPayloadLengthInBytes: calculateRequestPayloadLength(requestContext),
responsePayloadLengthInBytes: calculateResponsePayloadLength(pipelineResponse),
operationType: requestContext.operationType,
resourceType: requestContext.resourceType,
partitionKeyRangeId: requestContext.partitionKeyRangeId,
};
let requestData = {
OperationType: gatewayRequest.operationType,
resourceType: gatewayRequest.resourceType,
requestPayloadLengthInBytes: gatewayRequest.requestPayloadLengthInBytes,
};
if (allowTracing(CosmosDbDiagnosticLevel.debugUnsafe, this.diagnosticLevel)) {
requestData = Object.assign(Object.assign({}, requestData), { headers: this.sanitizeHeaders(requestContext.headers), requestBody: requestContext.body, responseBody: pipelineResponse.bodyAsText, url: url });
}
this.addData({
requestPayloadLengthInBytes: gatewayRequest.requestPayloadLengthInBytes,
responsePayloadLengthInBytes: gatewayRequest.responsePayloadLengthInBytes,
startTimeUTCInMs: gatewayRequest.startTimeUTCInMs,
durationInMs: gatewayRequest.durationInMs,
requestData,
});
this.diagnosticCtx.recordNetworkCall(gatewayRequest);
}
/**
* @internal
*/
recordFailedNetworkCall(startTimeUTCInMs, requestContext, retryAttemptNumber, statusCode, substatusCode, responseHeaders) {
this.addData({ failedAttempty: true });
const requestPayloadLengthInBytes = calculateRequestPayloadLength(requestContext);
this.diagnosticCtx.recordFailedAttempt({
activityId: responseHeaders[Constants.HttpHeaders.ActivityId],
correlatedActivityId: requestContext.headers[Constants.HttpHeaders.CorrelatedActivityId],
startTimeUTCInMs,
durationInMs: getCurrentTimestampInMs() - startTimeUTCInMs,
statusCode,
subStatusCode: substatusCode,
requestPayloadLengthInBytes,
responsePayloadLengthInBytes: 0,
operationType: requestContext.operationType,
resourceType: requestContext.resourceType,
}, retryAttemptNumber);
let requestData = {
OperationType: requestContext.operationType,
resourceType: requestContext.resourceType,
requestPayloadLengthInBytes,
};
if (allowTracing(CosmosDbDiagnosticLevel.debugUnsafe, this.diagnosticLevel)) {
requestData = Object.assign(Object.assign({}, requestData), { headers: this.sanitizeHeaders(requestContext.headers), requestBody: requestContext.body, url: prepareURL(requestContext.endpoint, requestContext.path) });
}
this.addData({
failedAttempty: true,
requestData,
});
}
/**
* @internal
*/
recordEndpointResolution(location) {
this.addData({ selectedLocation: location });
this.diagnosticCtx.recordEndpointResolution(location);
}
/**
* @internal
*/
addData(data, msg, level = this.diagnosticLevel) {
if (level !== CosmosDbDiagnosticLevel.info) {
this.data = Object.assign(Object.assign({}, this.data), data);
if (msg) {
this.addLog(msg);
}
}
}
/**
* Merge given DiagnosticNodeInternal's context to current node's DiagnosticContext, Treating GatewayRequests of
* given DiagnosticContext, as metadata requests. Given DiagnosticNodeInternal becomes a child of this node.
* @internal
*/
addChildNode(child, level, metadataType) {
this.diagnosticCtx.mergeDiagnostics(child.diagnosticCtx, metadataType);
if (allowTracing(level, this.diagnosticLevel)) {
child.parent = this;
this.children.push(child);
}
return child;
}
/**
* Merge given DiagnosticNodeInternal's context to current node's DiagnosticContext for bulk.
* Given DiagnosticNodeInternal becomes a child of this node.
* @internal
*/
addBulkChildNode(child, level) {
this.diagnosticCtx.mergeBulkDiagnostics(child.diagnosticCtx);
if (allowTracing(level, this.diagnosticLevel)) {
child.parent = this;
this.children.push(child);
}
return child;
}
/**
* @internal
*/
initializeChildNode(type, level, data = {}) {
if (allowTracing(level, this.diagnosticLevel)) {
const child = new DiagnosticNodeInternal(this.diagnosticLevel, type, this, data, getCurrentTimestampInMs(), this.diagnosticCtx);
this.children.push(child);
return child;
}
else {
return this;
}
}
/**
* @internal
*/
recordQueryResult(resources, level) {
var _a;
if (allowTracing(level, this.diagnosticLevel)) {
const previousCount = (_a = this.data.queryRecordsRead) !== null && _a !== void 0 ? _a : 0;
if (Array.isArray(resources)) {
this.data.queryRecordsRead = previousCount + resources.length;
}
}
}
/**
* @internal
* record startTime for encryption in an operation
*/
beginEncryptionDiagnostics(operation) {
const startTime = getCurrentTimestampInMs();
switch (operation) {
case Constants.Encryption.DiagnosticsEncryptOperation:
this.encryptionDiagnostics.encryptContent[Constants.Encryption.DiagnosticsStartTime] =
startTime;
break;
case Constants.Encryption.DiagnosticsDecryptOperation:
this.encryptionDiagnostics.decryptContent[Constants.Encryption.DiagnosticsStartTime] =
startTime;
break;
default:
throw new ErrorResponse("Invalid operation type for encryption diagnostics");
}
}
/**
* @internal
* record duration from startTime and properties count for encryption in an operation
*/
endEncryptionDiagnostics(operation, propertiesCount) {
const endTime = getCurrentTimestampInMs();
let processingDuration = 0;
switch (operation) {
case Constants.Encryption.DiagnosticsEncryptOperation:
processingDuration =
endTime -
this.encryptionDiagnostics.encryptContent[Constants.Encryption.DiagnosticsStartTime];
this.encryptionDiagnostics.encryptContent[Constants.Encryption.DiagnosticsDuration] =
processingDuration;
// will be undefined in case of bulk/batch
if (propertiesCount !== undefined) {
this.encryptionDiagnostics.encryptContent[Constants.Encryption.DiagnosticsPropertiesEncryptedCount] = propertiesCount;
}
break;
case Constants.Encryption.DiagnosticsDecryptOperation:
processingDuration =
endTime -
this.encryptionDiagnostics.decryptContent[Constants.Encryption.DiagnosticsStartTime];
this.encryptionDiagnostics.decryptContent[Constants.Encryption.DiagnosticsDuration] =
processingDuration;
if (propertiesCount !== undefined) {
this.encryptionDiagnostics.decryptContent[Constants.Encryption.DiagnosticsPropertiesDecryptedCount] = propertiesCount;
}
break;
default:
throw new ErrorResponse("Invalid operation type for encryption diagnostics");
}
this.diagnosticCtx.recordEncryptionDiagnostics(this.encryptionDiagnostics);
}
/**
* Convert DiagnosticNodeInternal (internal representation) to DiagnosticNode (public, sanitized representation)
* @internal
*/
toDiagnosticNode() {
return {
id: this.id,
nodeType: this.nodeType,
children: this.children.map((child) => child.toDiagnosticNode()),
data: this.data,
startTimeUTCInMs: this.startTimeUTCInMs,
durationInMs: this.durationInMs,
};
}
/**
* Convert to CosmosDiagnostics
* @internal
*/
toDiagnostic(clientConfigDiagnostic) {
const rootNode = getRootNode(this);
const diagnostiNode = allowTracing(CosmosDbDiagnosticLevel.debug, this.diagnosticLevel)
? rootNode.toDiagnosticNode()
: undefined;
const clientConfig = allowTracing(CosmosDbDiagnosticLevel.debug, this.diagnosticLevel)
? clientConfigDiagnostic
: undefined;
const cosmosDiagnostic = new CosmosDiagnostics(this.diagnosticCtx.getClientSideStats(), diagnostiNode, clientConfig);
return cosmosDiagnostic;
}
}
/**
* @hidden
*/
export var DiagnosticNodeType;
(function (DiagnosticNodeType) {
DiagnosticNodeType["CLIENT_REQUEST_NODE"] = "CLIENT_REQUEST_NODE";
DiagnosticNodeType["METADATA_REQUEST_NODE"] = "METADATA_REQUEST_NODE";
DiagnosticNodeType["HTTP_REQUEST"] = "HTTP_REQUEST";
DiagnosticNodeType["BATCH_REQUEST"] = "BATCH_REQUEST";
DiagnosticNodeType["PARALLEL_QUERY_NODE"] = "PARALLEL_QUERY_NODE";
DiagnosticNodeType["DEFAULT_QUERY_NODE"] = "DEFAULT_QUERY_NODE";
DiagnosticNodeType["QUERY_REPAIR_NODE"] = "QUERY_REPAIR_NODE";
DiagnosticNodeType["BACKGROUND_REFRESH_THREAD"] = "BACKGROUND_REFRESH_THREAD";
DiagnosticNodeType["REQUEST_ATTEMPTS"] = "REQUEST_ATTEMPTS";
})(DiagnosticNodeType || (DiagnosticNodeType = {}));
function calculateResponsePayloadLength(response) {
var _a;
return ((_a = response === null || response === void 0 ? void 0 : response.bodyAsText) === null || _a === void 0 ? void 0 : _a.length) || 0;
}
function calculateRequestPayloadLength(requestContext) {
return requestContext.body ? requestContext.body.length : 0;
}
//# sourceMappingURL=DiagnosticNodeInternal.js.map