Add support for HTTP OpenTelemetry exporters (#6357)
This commit is contained in:
parent
4896c7739f
commit
d57cc0b930
|
@ -435,6 +435,8 @@ Arguments passed directly when running the CLI can override other configurations
|
||||||
- Sets the telemetry target. See [telemetry](../telemetry.md) for more information.
|
- Sets the telemetry target. See [telemetry](../telemetry.md) for more information.
|
||||||
- **`--telemetry-otlp-endpoint`**:
|
- **`--telemetry-otlp-endpoint`**:
|
||||||
- Sets the OTLP endpoint for telemetry. See [telemetry](../telemetry.md) for more information.
|
- Sets the OTLP endpoint for telemetry. See [telemetry](../telemetry.md) for more information.
|
||||||
|
- **`--telemetry-otlp-protocol`**:
|
||||||
|
- Sets the OTLP protocol for telemetry (`grpc` or `http`). Defaults to `grpc`. See [telemetry](../telemetry.md) for more information.
|
||||||
- **`--telemetry-log-prompts`**:
|
- **`--telemetry-log-prompts`**:
|
||||||
- Enables logging of prompts for telemetry. See [telemetry](../telemetry.md) for more information.
|
- Enables logging of prompts for telemetry. See [telemetry](../telemetry.md) for more information.
|
||||||
- **`--checkpointing`**:
|
- **`--checkpointing`**:
|
||||||
|
|
|
@ -74,7 +74,11 @@ gemini --telemetry \
|
||||||
## Running an OTEL Collector
|
## Running an OTEL Collector
|
||||||
|
|
||||||
An OTEL Collector is a service that receives, processes, and exports telemetry data.
|
An OTEL Collector is a service that receives, processes, and exports telemetry data.
|
||||||
The CLI sends data using the OTLP/gRPC protocol.
|
The CLI can send data using either the OTLP/gRPC or OTLP/HTTP protocol.
|
||||||
|
You can specify which protocol to use via the `--telemetry-otlp-protocol` flag
|
||||||
|
or the `telemetry.otlpProtocol` setting in your `settings.json` file. See the
|
||||||
|
[configuration docs](./cli/configuration.md#--telemetry-otlp-protocol) for more
|
||||||
|
details.
|
||||||
|
|
||||||
Learn more about OTEL exporter standard configuration in [documentation][otel-config-docs].
|
Learn more about OTEL exporter standard configuration in [documentation][otel-config-docs].
|
||||||
|
|
||||||
|
|
|
@ -1626,6 +1626,25 @@
|
||||||
"@opentelemetry/api": "^1.0.0"
|
"@opentelemetry/api": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@opentelemetry/exporter-logs-otlp-http": {
|
||||||
|
"version": "0.52.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.52.1.tgz",
|
||||||
|
"integrity": "sha512-qKgywId2DbdowPZpOBXQKp0B8DfhfIArmSic15z13Nk/JAOccBUQdPwDjDnjsM5f0ckZFMVR2t/tijTUAqDZoA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@opentelemetry/api-logs": "0.52.1",
|
||||||
|
"@opentelemetry/core": "1.25.1",
|
||||||
|
"@opentelemetry/otlp-exporter-base": "0.52.1",
|
||||||
|
"@opentelemetry/otlp-transformer": "0.52.1",
|
||||||
|
"@opentelemetry/sdk-logs": "0.52.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@opentelemetry/api": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@opentelemetry/exporter-metrics-otlp-grpc": {
|
"node_modules/@opentelemetry/exporter-metrics-otlp-grpc": {
|
||||||
"version": "0.52.1",
|
"version": "0.52.1",
|
||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.52.1.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.52.1.tgz",
|
||||||
|
@ -12496,8 +12515,11 @@
|
||||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.52.0",
|
"@opentelemetry/exporter-logs-otlp-grpc": "^0.52.0",
|
||||||
|
"@opentelemetry/exporter-logs-otlp-http": "^0.52.0",
|
||||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.52.0",
|
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.52.0",
|
||||||
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.52.0",
|
||||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.52.0",
|
"@opentelemetry/exporter-trace-otlp-grpc": "^0.52.0",
|
||||||
|
"@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
|
||||||
"@opentelemetry/instrumentation-http": "^0.52.0",
|
"@opentelemetry/instrumentation-http": "^0.52.0",
|
||||||
"@opentelemetry/sdk-node": "^0.52.0",
|
"@opentelemetry/sdk-node": "^0.52.0",
|
||||||
"@types/glob": "^8.1.0",
|
"@types/glob": "^8.1.0",
|
||||||
|
|
|
@ -536,6 +536,60 @@ describe('loadCliConfig telemetry', () => {
|
||||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
expect(config.getTelemetryLogPromptsEnabled()).toBe(true);
|
expect(config.getTelemetryLogPromptsEnabled()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use telemetry OTLP protocol from settings if CLI flag is not present', async () => {
|
||||||
|
process.argv = ['node', 'script.js'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
const settings: Settings = {
|
||||||
|
telemetry: { otlpProtocol: 'http' },
|
||||||
|
};
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getTelemetryOtlpProtocol()).toBe('http');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prioritize --telemetry-otlp-protocol CLI flag over settings', async () => {
|
||||||
|
process.argv = ['node', 'script.js', '--telemetry-otlp-protocol', 'http'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
const settings: Settings = {
|
||||||
|
telemetry: { otlpProtocol: 'grpc' },
|
||||||
|
};
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getTelemetryOtlpProtocol()).toBe('http');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use default protocol if no OTLP protocol is provided via CLI or settings', async () => {
|
||||||
|
process.argv = ['node', 'script.js'];
|
||||||
|
const argv = await parseArguments();
|
||||||
|
const settings: Settings = { telemetry: { enabled: true } };
|
||||||
|
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||||
|
expect(config.getTelemetryOtlpProtocol()).toBe('grpc');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject invalid --telemetry-otlp-protocol values', async () => {
|
||||||
|
process.argv = [
|
||||||
|
'node',
|
||||||
|
'script.js',
|
||||||
|
'--telemetry-otlp-protocol',
|
||||||
|
'invalid',
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
|
||||||
|
throw new Error('process.exit called');
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockConsoleError = vi
|
||||||
|
.spyOn(console, 'error')
|
||||||
|
.mockImplementation(() => {});
|
||||||
|
|
||||||
|
await expect(parseArguments()).rejects.toThrow('process.exit called');
|
||||||
|
|
||||||
|
expect(mockConsoleError).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('Invalid values:'),
|
||||||
|
);
|
||||||
|
|
||||||
|
mockExit.mockRestore();
|
||||||
|
mockConsoleError.mockRestore();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => {
|
describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => {
|
||||||
|
|
|
@ -64,6 +64,7 @@ export interface CliArgs {
|
||||||
checkpointing: boolean | undefined;
|
checkpointing: boolean | undefined;
|
||||||
telemetryTarget: string | undefined;
|
telemetryTarget: string | undefined;
|
||||||
telemetryOtlpEndpoint: string | undefined;
|
telemetryOtlpEndpoint: string | undefined;
|
||||||
|
telemetryOtlpProtocol: string | undefined;
|
||||||
telemetryLogPrompts: boolean | undefined;
|
telemetryLogPrompts: boolean | undefined;
|
||||||
telemetryOutfile: string | undefined;
|
telemetryOutfile: string | undefined;
|
||||||
allowedMcpServerNames: string[] | undefined;
|
allowedMcpServerNames: string[] | undefined;
|
||||||
|
@ -172,6 +173,12 @@ export async function parseArguments(): Promise<CliArgs> {
|
||||||
description:
|
description:
|
||||||
'Set the OTLP endpoint for telemetry. Overrides environment variables and settings files.',
|
'Set the OTLP endpoint for telemetry. Overrides environment variables and settings files.',
|
||||||
})
|
})
|
||||||
|
.option('telemetry-otlp-protocol', {
|
||||||
|
type: 'string',
|
||||||
|
choices: ['grpc', 'http'],
|
||||||
|
description:
|
||||||
|
'Set the OTLP protocol for telemetry (grpc or http). Overrides settings files.',
|
||||||
|
})
|
||||||
.option('telemetry-log-prompts', {
|
.option('telemetry-log-prompts', {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
description:
|
description:
|
||||||
|
@ -491,6 +498,11 @@ export async function loadCliConfig(
|
||||||
argv.telemetryOtlpEndpoint ??
|
argv.telemetryOtlpEndpoint ??
|
||||||
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ??
|
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ??
|
||||||
settings.telemetry?.otlpEndpoint,
|
settings.telemetry?.otlpEndpoint,
|
||||||
|
otlpProtocol: (['grpc', 'http'] as const).find(
|
||||||
|
(p) =>
|
||||||
|
p ===
|
||||||
|
(argv.telemetryOtlpProtocol ?? settings.telemetry?.otlpProtocol),
|
||||||
|
),
|
||||||
logPrompts: argv.telemetryLogPrompts ?? settings.telemetry?.logPrompts,
|
logPrompts: argv.telemetryLogPrompts ?? settings.telemetry?.logPrompts,
|
||||||
outfile: argv.telemetryOutfile ?? settings.telemetry?.outfile,
|
outfile: argv.telemetryOutfile ?? settings.telemetry?.outfile,
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,8 +24,11 @@
|
||||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.52.0",
|
"@opentelemetry/exporter-logs-otlp-grpc": "^0.52.0",
|
||||||
|
"@opentelemetry/exporter-logs-otlp-http": "^0.52.0",
|
||||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.52.0",
|
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.52.0",
|
||||||
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.52.0",
|
||||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.52.0",
|
"@opentelemetry/exporter-trace-otlp-grpc": "^0.52.0",
|
||||||
|
"@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
|
||||||
"@opentelemetry/instrumentation-http": "^0.52.0",
|
"@opentelemetry/instrumentation-http": "^0.52.0",
|
||||||
"@opentelemetry/sdk-node": "^0.52.0",
|
"@opentelemetry/sdk-node": "^0.52.0",
|
||||||
"@types/glob": "^8.1.0",
|
"@types/glob": "^8.1.0",
|
||||||
|
|
|
@ -567,5 +567,30 @@ describe('Server Config (config.ts)', () => {
|
||||||
const config = new Config(paramsWithoutTelemetry);
|
const config = new Config(paramsWithoutTelemetry);
|
||||||
expect(config.getTelemetryOtlpEndpoint()).toBe(DEFAULT_OTLP_ENDPOINT);
|
expect(config.getTelemetryOtlpEndpoint()).toBe(DEFAULT_OTLP_ENDPOINT);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return provided OTLP protocol', () => {
|
||||||
|
const params: ConfigParameters = {
|
||||||
|
...baseParams,
|
||||||
|
telemetry: { enabled: true, otlpProtocol: 'http' },
|
||||||
|
};
|
||||||
|
const config = new Config(params);
|
||||||
|
expect(config.getTelemetryOtlpProtocol()).toBe('http');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return default OTLP protocol if not provided', () => {
|
||||||
|
const params: ConfigParameters = {
|
||||||
|
...baseParams,
|
||||||
|
telemetry: { enabled: true },
|
||||||
|
};
|
||||||
|
const config = new Config(params);
|
||||||
|
expect(config.getTelemetryOtlpProtocol()).toBe('grpc');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return default OTLP protocol if telemetry object is not provided', () => {
|
||||||
|
const paramsWithoutTelemetry: ConfigParameters = { ...baseParams };
|
||||||
|
delete paramsWithoutTelemetry.telemetry;
|
||||||
|
const config = new Config(paramsWithoutTelemetry);
|
||||||
|
expect(config.getTelemetryOtlpProtocol()).toBe('grpc');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -81,6 +81,7 @@ export interface TelemetrySettings {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
target?: TelemetryTarget;
|
target?: TelemetryTarget;
|
||||||
otlpEndpoint?: string;
|
otlpEndpoint?: string;
|
||||||
|
otlpProtocol?: 'grpc' | 'http';
|
||||||
logPrompts?: boolean;
|
logPrompts?: boolean;
|
||||||
outfile?: string;
|
outfile?: string;
|
||||||
}
|
}
|
||||||
|
@ -292,6 +293,7 @@ export class Config {
|
||||||
enabled: params.telemetry?.enabled ?? false,
|
enabled: params.telemetry?.enabled ?? false,
|
||||||
target: params.telemetry?.target ?? DEFAULT_TELEMETRY_TARGET,
|
target: params.telemetry?.target ?? DEFAULT_TELEMETRY_TARGET,
|
||||||
otlpEndpoint: params.telemetry?.otlpEndpoint ?? DEFAULT_OTLP_ENDPOINT,
|
otlpEndpoint: params.telemetry?.otlpEndpoint ?? DEFAULT_OTLP_ENDPOINT,
|
||||||
|
otlpProtocol: params.telemetry?.otlpProtocol,
|
||||||
logPrompts: params.telemetry?.logPrompts ?? true,
|
logPrompts: params.telemetry?.logPrompts ?? true,
|
||||||
outfile: params.telemetry?.outfile,
|
outfile: params.telemetry?.outfile,
|
||||||
};
|
};
|
||||||
|
@ -564,6 +566,10 @@ export class Config {
|
||||||
return this.telemetrySettings.otlpEndpoint ?? DEFAULT_OTLP_ENDPOINT;
|
return this.telemetrySettings.otlpEndpoint ?? DEFAULT_OTLP_ENDPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTelemetryOtlpProtocol(): 'grpc' | 'http' {
|
||||||
|
return this.telemetrySettings.otlpProtocol ?? 'grpc';
|
||||||
|
}
|
||||||
|
|
||||||
getTelemetryTarget(): TelemetryTarget {
|
getTelemetryTarget(): TelemetryTarget {
|
||||||
return this.telemetrySettings.target ?? DEFAULT_TELEMETRY_TARGET;
|
return this.telemetrySettings.target ?? DEFAULT_TELEMETRY_TARGET;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { Config } from '../config/config.js';
|
||||||
|
import { initializeTelemetry, shutdownTelemetry } from './sdk.js';
|
||||||
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
|
||||||
|
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc';
|
||||||
|
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
|
||||||
|
import { OTLPTraceExporter as OTLPTraceExporterHttp } from '@opentelemetry/exporter-trace-otlp-http';
|
||||||
|
import { OTLPLogExporter as OTLPLogExporterHttp } from '@opentelemetry/exporter-logs-otlp-http';
|
||||||
|
import { OTLPMetricExporter as OTLPMetricExporterHttp } from '@opentelemetry/exporter-metrics-otlp-http';
|
||||||
|
import { NodeSDK } from '@opentelemetry/sdk-node';
|
||||||
|
|
||||||
|
vi.mock('@opentelemetry/exporter-trace-otlp-grpc');
|
||||||
|
vi.mock('@opentelemetry/exporter-logs-otlp-grpc');
|
||||||
|
vi.mock('@opentelemetry/exporter-metrics-otlp-grpc');
|
||||||
|
vi.mock('@opentelemetry/exporter-trace-otlp-http');
|
||||||
|
vi.mock('@opentelemetry/exporter-logs-otlp-http');
|
||||||
|
vi.mock('@opentelemetry/exporter-metrics-otlp-http');
|
||||||
|
vi.mock('@opentelemetry/sdk-node');
|
||||||
|
|
||||||
|
describe('Telemetry SDK', () => {
|
||||||
|
let mockConfig: Config;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
mockConfig = {
|
||||||
|
getTelemetryEnabled: () => true,
|
||||||
|
getTelemetryOtlpEndpoint: () => 'http://localhost:4317',
|
||||||
|
getTelemetryOtlpProtocol: () => 'grpc',
|
||||||
|
getTelemetryOutfile: () => undefined,
|
||||||
|
getDebugMode: () => false,
|
||||||
|
getSessionId: () => 'test-session',
|
||||||
|
} as unknown as Config;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await shutdownTelemetry(mockConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use gRPC exporters when protocol is grpc', () => {
|
||||||
|
initializeTelemetry(mockConfig);
|
||||||
|
|
||||||
|
expect(OTLPTraceExporter).toHaveBeenCalledWith({
|
||||||
|
url: 'http://localhost:4317',
|
||||||
|
compression: 'gzip',
|
||||||
|
});
|
||||||
|
expect(OTLPLogExporter).toHaveBeenCalledWith({
|
||||||
|
url: 'http://localhost:4317',
|
||||||
|
compression: 'gzip',
|
||||||
|
});
|
||||||
|
expect(OTLPMetricExporter).toHaveBeenCalledWith({
|
||||||
|
url: 'http://localhost:4317',
|
||||||
|
compression: 'gzip',
|
||||||
|
});
|
||||||
|
expect(NodeSDK.prototype.start).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use HTTP exporters when protocol is http', () => {
|
||||||
|
vi.spyOn(mockConfig, 'getTelemetryEnabled').mockReturnValue(true);
|
||||||
|
vi.spyOn(mockConfig, 'getTelemetryOtlpProtocol').mockReturnValue('http');
|
||||||
|
vi.spyOn(mockConfig, 'getTelemetryOtlpEndpoint').mockReturnValue(
|
||||||
|
'http://localhost:4318',
|
||||||
|
);
|
||||||
|
|
||||||
|
initializeTelemetry(mockConfig);
|
||||||
|
|
||||||
|
expect(OTLPTraceExporterHttp).toHaveBeenCalledWith({
|
||||||
|
url: 'http://localhost:4318/',
|
||||||
|
});
|
||||||
|
expect(OTLPLogExporterHttp).toHaveBeenCalledWith({
|
||||||
|
url: 'http://localhost:4318/',
|
||||||
|
});
|
||||||
|
expect(OTLPMetricExporterHttp).toHaveBeenCalledWith({
|
||||||
|
url: 'http://localhost:4318/',
|
||||||
|
});
|
||||||
|
expect(NodeSDK.prototype.start).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse gRPC endpoint correctly', () => {
|
||||||
|
vi.spyOn(mockConfig, 'getTelemetryOtlpEndpoint').mockReturnValue(
|
||||||
|
'https://my-collector.com',
|
||||||
|
);
|
||||||
|
initializeTelemetry(mockConfig);
|
||||||
|
expect(OTLPTraceExporter).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ url: 'https://my-collector.com' }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse HTTP endpoint correctly', () => {
|
||||||
|
vi.spyOn(mockConfig, 'getTelemetryOtlpProtocol').mockReturnValue('http');
|
||||||
|
vi.spyOn(mockConfig, 'getTelemetryOtlpEndpoint').mockReturnValue(
|
||||||
|
'https://my-collector.com',
|
||||||
|
);
|
||||||
|
initializeTelemetry(mockConfig);
|
||||||
|
expect(OTLPTraceExporterHttp).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ url: 'https://my-collector.com/' }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -8,6 +8,9 @@ import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';
|
||||||
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
|
||||||
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc';
|
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc';
|
||||||
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
|
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
|
||||||
|
import { OTLPTraceExporter as OTLPTraceExporterHttp } from '@opentelemetry/exporter-trace-otlp-http';
|
||||||
|
import { OTLPLogExporter as OTLPLogExporterHttp } from '@opentelemetry/exporter-logs-otlp-http';
|
||||||
|
import { OTLPMetricExporter as OTLPMetricExporterHttp } from '@opentelemetry/exporter-metrics-otlp-http';
|
||||||
import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';
|
import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';
|
||||||
import { NodeSDK } from '@opentelemetry/sdk-node';
|
import { NodeSDK } from '@opentelemetry/sdk-node';
|
||||||
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
|
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
|
||||||
|
@ -45,8 +48,9 @@ export function isTelemetrySdkInitialized(): boolean {
|
||||||
return telemetryInitialized;
|
return telemetryInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseGrpcEndpoint(
|
function parseOtlpEndpoint(
|
||||||
otlpEndpointSetting: string | undefined,
|
otlpEndpointSetting: string | undefined,
|
||||||
|
protocol: 'grpc' | 'http',
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
if (!otlpEndpointSetting) {
|
if (!otlpEndpointSetting) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -56,9 +60,13 @@ function parseGrpcEndpoint(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = new URL(trimmedEndpoint);
|
const url = new URL(trimmedEndpoint);
|
||||||
// OTLP gRPC exporters expect an endpoint in the format scheme://host:port
|
if (protocol === 'grpc') {
|
||||||
// The `origin` property provides this, stripping any path, query, or hash.
|
// OTLP gRPC exporters expect an endpoint in the format scheme://host:port
|
||||||
return url.origin;
|
// The `origin` property provides this, stripping any path, query, or hash.
|
||||||
|
return url.origin;
|
||||||
|
}
|
||||||
|
// For http, use the full href.
|
||||||
|
return url.href;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
diag.error('Invalid OTLP endpoint URL provided:', trimmedEndpoint, error);
|
diag.error('Invalid OTLP endpoint URL provided:', trimmedEndpoint, error);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -77,43 +85,70 @@ export function initializeTelemetry(config: Config): void {
|
||||||
});
|
});
|
||||||
|
|
||||||
const otlpEndpoint = config.getTelemetryOtlpEndpoint();
|
const otlpEndpoint = config.getTelemetryOtlpEndpoint();
|
||||||
const grpcParsedEndpoint = parseGrpcEndpoint(otlpEndpoint);
|
const otlpProtocol = config.getTelemetryOtlpProtocol();
|
||||||
const useOtlp = !!grpcParsedEndpoint;
|
const parsedEndpoint = parseOtlpEndpoint(otlpEndpoint, otlpProtocol);
|
||||||
|
const useOtlp = !!parsedEndpoint;
|
||||||
const telemetryOutfile = config.getTelemetryOutfile();
|
const telemetryOutfile = config.getTelemetryOutfile();
|
||||||
|
|
||||||
const spanExporter = useOtlp
|
let spanExporter:
|
||||||
? new OTLPTraceExporter({
|
| OTLPTraceExporter
|
||||||
url: grpcParsedEndpoint,
|
| OTLPTraceExporterHttp
|
||||||
|
| FileSpanExporter
|
||||||
|
| ConsoleSpanExporter;
|
||||||
|
let logExporter:
|
||||||
|
| OTLPLogExporter
|
||||||
|
| OTLPLogExporterHttp
|
||||||
|
| FileLogExporter
|
||||||
|
| ConsoleLogRecordExporter;
|
||||||
|
let metricReader: PeriodicExportingMetricReader;
|
||||||
|
|
||||||
|
if (useOtlp) {
|
||||||
|
if (otlpProtocol === 'http') {
|
||||||
|
spanExporter = new OTLPTraceExporterHttp({
|
||||||
|
url: parsedEndpoint,
|
||||||
|
});
|
||||||
|
logExporter = new OTLPLogExporterHttp({
|
||||||
|
url: parsedEndpoint,
|
||||||
|
});
|
||||||
|
metricReader = new PeriodicExportingMetricReader({
|
||||||
|
exporter: new OTLPMetricExporterHttp({
|
||||||
|
url: parsedEndpoint,
|
||||||
|
}),
|
||||||
|
exportIntervalMillis: 10000,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// grpc
|
||||||
|
spanExporter = new OTLPTraceExporter({
|
||||||
|
url: parsedEndpoint,
|
||||||
compression: CompressionAlgorithm.GZIP,
|
compression: CompressionAlgorithm.GZIP,
|
||||||
})
|
});
|
||||||
: telemetryOutfile
|
logExporter = new OTLPLogExporter({
|
||||||
? new FileSpanExporter(telemetryOutfile)
|
url: parsedEndpoint,
|
||||||
: new ConsoleSpanExporter();
|
|
||||||
const logExporter = useOtlp
|
|
||||||
? new OTLPLogExporter({
|
|
||||||
url: grpcParsedEndpoint,
|
|
||||||
compression: CompressionAlgorithm.GZIP,
|
compression: CompressionAlgorithm.GZIP,
|
||||||
})
|
});
|
||||||
: telemetryOutfile
|
metricReader = new PeriodicExportingMetricReader({
|
||||||
? new FileLogExporter(telemetryOutfile)
|
|
||||||
: new ConsoleLogRecordExporter();
|
|
||||||
const metricReader = useOtlp
|
|
||||||
? new PeriodicExportingMetricReader({
|
|
||||||
exporter: new OTLPMetricExporter({
|
exporter: new OTLPMetricExporter({
|
||||||
url: grpcParsedEndpoint,
|
url: parsedEndpoint,
|
||||||
compression: CompressionAlgorithm.GZIP,
|
compression: CompressionAlgorithm.GZIP,
|
||||||
}),
|
}),
|
||||||
exportIntervalMillis: 10000,
|
exportIntervalMillis: 10000,
|
||||||
})
|
});
|
||||||
: telemetryOutfile
|
}
|
||||||
? new PeriodicExportingMetricReader({
|
} else if (telemetryOutfile) {
|
||||||
exporter: new FileMetricExporter(telemetryOutfile),
|
spanExporter = new FileSpanExporter(telemetryOutfile);
|
||||||
exportIntervalMillis: 10000,
|
logExporter = new FileLogExporter(telemetryOutfile);
|
||||||
})
|
metricReader = new PeriodicExportingMetricReader({
|
||||||
: new PeriodicExportingMetricReader({
|
exporter: new FileMetricExporter(telemetryOutfile),
|
||||||
exporter: new ConsoleMetricExporter(),
|
exportIntervalMillis: 10000,
|
||||||
exportIntervalMillis: 10000,
|
});
|
||||||
});
|
} else {
|
||||||
|
spanExporter = new ConsoleSpanExporter();
|
||||||
|
logExporter = new ConsoleLogRecordExporter();
|
||||||
|
metricReader = new PeriodicExportingMetricReader({
|
||||||
|
exporter: new ConsoleMetricExporter(),
|
||||||
|
exportIntervalMillis: 10000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
sdk = new NodeSDK({
|
sdk = new NodeSDK({
|
||||||
resource,
|
resource,
|
||||||
|
|
Loading…
Reference in New Issue