MCP Terminal Server
by dillip285
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Counter, Histogram, Meter, metrics } from '@opentelemetry/api';
import { ReadableSpan } from '@opentelemetry/sdk-trace-base';
import { PathMetadata } from 'genkit/tracing';
export const METER_NAME = 'genkit';
export const METRIC_NAME_PREFIX = 'genkit';
const METRIC_DIMENSION_MAX_CHARS = 32;
export function internalMetricNamespaceWrap(...namespaces) {
return [METRIC_NAME_PREFIX, ...namespaces].join('/');
}
type MetricCreateFn<T> = (meter: Meter) => T;
/**
* Wrapper for OpenTelemetry metrics.
*
* The OpenTelemetry {MeterProvider} can only be accessed through the metrics
* API after the NodeSDK library has been initialized. To prevent race
* conditions we defer the instantiation of the metric to when it is first
* ticked.
*/
class Metric<T> {
readonly createFn: MetricCreateFn<T>;
readonly meterName: string;
metric?: T;
constructor(createFn: MetricCreateFn<T>, meterName: string = METER_NAME) {
this.meterName = meterName;
this.createFn = createFn;
}
get(): T {
if (!this.metric) {
this.metric = this.createFn(
metrics.getMeterProvider().getMeter(this.meterName)
);
}
return this.metric;
}
}
/**
* Wrapper for an OpenTelemetry Counter.
*
* By using this wrapper, we defer initialization of the counter until it is
* need, which ensures that the OpenTelemetry SDK has been initialized before
* the metric has been defined.
*/
export class MetricCounter extends Metric<Counter> {
constructor(name: string, options: any) {
super((meter) => meter.createCounter(name, options));
}
add(val?: number, opts?: any) {
if (val) {
truncateDimensions(opts);
this.get().add(val, opts);
}
}
}
/**
* Wrapper for an OpenTelemetry Histogram.
*
* By using this wrapper, we defer initialization of the counter until it is
* need, which ensures that the OpenTelemetry SDK has been initialized before
* the metric has been defined.
*/
export class MetricHistogram extends Metric<Histogram> {
constructor(name: string, options: any) {
super((meter) => meter.createHistogram(name, options));
}
record(val?: number, opts?: any) {
if (val) {
truncateDimensions(opts);
this.get().record(val, opts);
}
}
}
/**
* Truncates all of the metric dimensions to avoid long, high-cardinality
* dimensions being added to metrics.
*/
function truncateDimensions(opts?: any) {
if (opts) {
Object.keys(opts).forEach((k) => {
// We don't want to truncate paths. They are known to be long but with
// relatively low cardinality, and are useful for downstream monitoring.
if (!k.startsWith('path') && typeof opts[k] == 'string') {
opts[k] = opts[k].substring(0, METRIC_DIMENSION_MAX_CHARS);
}
});
}
}
export interface Telemetry {
tick(
span: ReadableSpan,
paths: Set<PathMetadata>,
logIO: boolean,
projectId?: string
): void;
}