90 lines
3.1 KiB
JavaScript
90 lines
3.1 KiB
JavaScript
/**
|
|
* Xget - High-performance acceleration engine for developer resources
|
|
* Copyright (C) Xi Xu
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* Performance monitoring utilities for Xget
|
|
*/
|
|
|
|
import { addSecurityHeaders } from './security.js';
|
|
|
|
/**
|
|
* Monitors performance metrics during request processing.
|
|
*
|
|
* This class tracks timing information throughout request handling lifecycle,
|
|
* allowing measurement of cache hits, upstream fetch attempts, and total processing time.
|
|
*/
|
|
export class PerformanceMonitor {
|
|
/**
|
|
* Initializes a new performance monitor.
|
|
*
|
|
* Sets the start time to the current timestamp and creates an empty marks collection.
|
|
* All subsequent timing marks will be relative to this start time.
|
|
*/
|
|
constructor() {
|
|
this.startTime = Date.now();
|
|
this.marks = new Map();
|
|
}
|
|
|
|
/**
|
|
* Marks a timing point with the given name.
|
|
*
|
|
* Records the elapsed time (in milliseconds) since the monitor was created.
|
|
* If a mark with the same name already exists, logs a warning and overwrites it.
|
|
* @param {string} name - The name of the timing mark (e.g., 'cache_hit', 'attempt_0', 'success')
|
|
*/
|
|
mark(name) {
|
|
if (this.marks.has(name)) {
|
|
console.warn(`Mark with name ${name} already exists.`);
|
|
}
|
|
this.marks.set(name, Date.now() - this.startTime);
|
|
}
|
|
|
|
/**
|
|
* Returns all collected metrics as a plain object.
|
|
*
|
|
* Converts the internal Map of timing marks to a JavaScript object suitable for
|
|
* JSON serialization and inclusion in response headers.
|
|
* @returns {{ [key: string]: number }} Object containing name-timestamp pairs in milliseconds
|
|
*/
|
|
getMetrics() {
|
|
return Object.fromEntries(this.marks.entries());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds performance metrics to response headers.
|
|
*
|
|
* Creates a new response with an X-Performance-Metrics header containing
|
|
* timing data from the PerformanceMonitor instance. Also ensures security
|
|
* headers are included.
|
|
*
|
|
* **Note:** This header is only added to non-protocol responses (not Git/Docker/AI).
|
|
* @param {Response} response - The original response object
|
|
* @param {PerformanceMonitor} monitor - Performance monitor instance with collected metrics
|
|
* @returns {Response} New response with added performance and security headers
|
|
*/
|
|
export function addPerformanceHeaders(response, monitor) {
|
|
const headers = new Headers(response.headers);
|
|
headers.set('X-Performance-Metrics', JSON.stringify(monitor.getMetrics()));
|
|
addSecurityHeaders(headers);
|
|
return new Response(response.body, {
|
|
status: response.status,
|
|
headers
|
|
});
|
|
}
|