@microsoft/dev-tunnels-ssh
Version:
SSH library for Dev Tunnels
215 lines • 8.49 kB
JavaScript
"use strict";
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.SessionMetrics = void 0;
const vscode_jsonrpc_1 = require("vscode-jsonrpc");
const trace_1 = require("../trace");
/**
* Collects current and cumulative measurements about a session.
*/
class SessionMetrics {
/* @internal */
constructor() {
this.startTime = 0;
this.messagesSentCount = 0;
this.messagesReceivedCount = 0;
this.bytesSentSum = 0;
this.bytesReceivedSum = 0;
this.reconnectionsCount = 0;
this.currentLatency = 0;
this.minLatency = 0;
this.maxLatency = 0;
this.latencySum = 0;
this.latencyCount = 0;
this.messageSentEmitter = new vscode_jsonrpc_1.Emitter();
this.onMessageSent = this.messageSentEmitter.event;
this.messageReceivedEmitter = new vscode_jsonrpc_1.Emitter();
this.onMessageReceived = this.messageReceivedEmitter.event;
this.latencyUpdatedEmitter = new vscode_jsonrpc_1.Emitter();
this.onLatencyUpdated = this.latencyUpdatedEmitter.event;
this.sessionClosedEmitter = new vscode_jsonrpc_1.Emitter();
this.onSessionClosed = this.sessionClosedEmitter.event;
if (typeof performance === 'object' && typeof performance.now === 'function') {
Object.defineProperty(this, 'time', { get: this.browserTime });
}
else if (typeof process === 'object' && typeof process.hrtime === 'function') {
Object.defineProperty(this, 'time', { get: this.nodejsTime });
}
this.startTime = this.time;
}
/**
* Gets the current stopwatch value in milliseconds (possibly including fractional milliseconds),
* used for measuring latency.
*/
/* @internal */
get time() {
// The SessionMetrics constructor may replace this with either of the below
// high-precision implementations, depending on availability of platform APIs.
return Date.now() - this.startTime;
}
browserTime() {
// Use the browser high-resolution time API.
// Note the precision may be reduced for pricacy depending on browser and page policy.
return performance.now() - this.startTime;
}
nodejsTime() {
// Use Node.js high-resolution time API.
const [s, ns] = process.hrtime();
return s * 1000 + ns / 1000000 - this.startTime;
}
/**
* Gets the total cumulative number of messages sent for the duration of the session,
* including all channels and non-channel protocol messages.
*/
get messagesSent() {
return this.messagesSentCount;
}
/**
* Gets the total cumulative number of messages received for the duration of the session,
* including all channels and non-channel protocol messages.
*/
get messagesReceived() {
return this.messagesReceivedCount;
}
/**
* Gets the total cumulative number of bytes sent for the duration of the session,
* including all channels and non-channel protocol messages, and including message
* framing, padding, and MAC bytes.
*/
get bytesSent() {
return this.bytesSentSum;
}
/**
* Gets the total cumulative number of bytes received for the duration of the session,
* including all channels and non-channel protocol messages, and including message
* framing, padding, and MAC bytes.
*/
get bytesReceived() {
return this.bytesReceivedSum;
}
/**
* Gets the number of times the session has reconnected.
* </summary>
* <remarks>
* Reconnection requires both sides to support the
* <see cref="SshProtocolExtensionNames.SessionReconnect" /> protocol extension.
*/
get reconnections() {
return this.reconnectionsCount;
}
/**
* Gets the average measured round-trip connection latency between client and server
* over the duration of the session, in milliseconds.
* </summary>
* <remarks>
* Latency measurement requires both sides to support the
* <see cref="SshProtocolExtensionNames.SessionLatency" /> protocol extension.
* If not supported, this Sum will be 0.
*/
get latencyAverageMs() {
return this.latencyCount === 0 ? 0 : this.latencySum / this.latencyCount;
}
/**
* Gets the minimum measured round-trip connection latency between client and server
* over the duration of the session, in milliseconds.
* </summary>
* <remarks>
* Latency measurement requires both sides to support the
* <see cref="SshProtocolExtensionNames.SessionLatency" /> protocol extension.
* If not supported, this Sum will be 0.
*/
get latencyMinMs() {
return this.minLatency;
}
/**
* Gets the maximum measured round-trip connection latency between client and server
* over the duration of the session, in milliseconds.
* </summary>
* <remarks>
* Latency measurement requires both sides to support the
* <see cref="SshProtocolExtensionNames.SessionLatency" /> protocol extension.
* If not supported, this Sum will be 0.
*/
get latencyMaxMs() {
return this.maxLatency;
}
/**
* Gets the most recent measurement of round-trip connection latency between client and
* server, in milliseconds.
* </summary>
* <remarks>
* Latency measurement requires both sides to support the
* <see cref="SshProtocolExtensionNames.SessionLatency" /> protocol extension.
* If not supported or the session is not currently connected, this Sum will be 0.
*/
get latencyCurrentMs() {
return this.currentLatency;
}
/* @internal */
addMessageSent(size) {
this.messagesSentCount++;
this.bytesSentSum += size;
this.messageSentEmitter.fire({ time: this.time, size });
}
/* @internal */
addMessageReceived(size) {
this.messagesReceivedCount++;
this.bytesReceivedSum += size;
this.messageReceivedEmitter.fire({ time: this.time, size });
}
/* @internal */
addReconnection() {
this.reconnectionsCount++;
}
/* @internal */
updateLatency(latencyMs, trace) {
if (latencyMs < 0) {
if (trace) {
trace(trace_1.TraceLevel.Warning, trace_1.SshTraceEventIds.metricsError, `Measured latency was negative: ${latencyMs} us`);
}
return;
}
this.currentLatency = latencyMs;
if (latencyMs === 0) {
// Disconnected.
return;
}
if (this.minLatency === 0 || latencyMs < this.minLatency) {
this.minLatency = latencyMs;
}
if (this.maxLatency === 0 || latencyMs > this.maxLatency) {
this.maxLatency = latencyMs;
}
// Enable computing the average.
this.latencySum += latencyMs;
this.latencyCount++;
this.latencyUpdatedEmitter.fire({ time: this.time, latency: latencyMs });
}
/* @internal */
close() {
this.currentLatency = 0;
this.sessionClosedEmitter.fire();
}
toString() {
let s = `Messages S/R: ${this.messagesSent} / ${this.messagesReceived}; ` +
`Bytes S/R: ${this.bytesSent} / ${this.bytesReceived}; ` +
`Reconnections: ${this.reconnections}; `;
// Show extra precision for a low-latency connection.
const precision = this.minLatency >= 10 ? 1 : this.minLatency >= 1 ? 10 : 100;
if (this.maxLatency > 0) {
const min = Math.round(this.minLatency * precision) / precision;
const avg = Math.round((this.latencySum / this.latencyCount) * precision) / precision;
const max = Math.round(this.maxLatency * precision) / precision;
s += `Latency Min-Avg-Max: ${min} - ${avg} - ${max} ms; `;
}
if (this.currentLatency > 0) {
const current = Math.round(this.currentLatency * precision) / precision;
s += `Current Latency: ${current} ms; `;
}
return s;
}
}
exports.SessionMetrics = SessionMetrics;
//# sourceMappingURL=sessionMetrics.js.map