mirror of
https://github.com/cline/cline.git
synced 2026-09-11 05:47:07 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c3021d9aa | ||
|
|
69ab2d6b08 | ||
|
|
a1fe2a87d0 | ||
|
|
0f1c834769 | ||
|
|
6043f7e864 | ||
|
|
7ac0f32c6b | ||
|
|
736c1c6462 |
+3
-1
@@ -139,6 +139,7 @@
|
||||
"!**/*.test.ts",
|
||||
"!src/dev/**",
|
||||
"!src/extension.ts",
|
||||
"!src/test-otel-settings.ts",
|
||||
"!src/integrations/git/commit-message-generator.ts",
|
||||
"!src/integrations/terminal/**",
|
||||
"!src/core/controller/ui/openWalkthrough.ts"
|
||||
@@ -156,7 +157,8 @@
|
||||
"!src/common.ts",
|
||||
"!src/services/logging/distinctId.ts",
|
||||
"!src/core/storage/utils/state-helpers.ts",
|
||||
"!src/extension.ts"
|
||||
"!src/extension.ts",
|
||||
"!src/test-otel-settings.ts"
|
||||
],
|
||||
"plugins": [
|
||||
"src/dev/grit/use-cache-service.grit"
|
||||
|
||||
@@ -198,6 +198,11 @@
|
||||
"command": "cline.reconstructTaskHistory",
|
||||
"title": "Reconstruct Task History",
|
||||
"category": "Cline"
|
||||
},
|
||||
{
|
||||
"command": "cline.testOtelSettings",
|
||||
"title": "Test Otel Settings",
|
||||
"category": "Cline"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { RemoteConfig } from "@shared/remote-config/schema"
|
||||
import { clearOtelConfigCache } from "@shared/services/config/otel-config"
|
||||
import { GlobalStateAndSettings } from "@shared/storage/state-keys"
|
||||
import { telemetryService } from "@/services/telemetry"
|
||||
import { StateManager } from "../StateManager"
|
||||
|
||||
/**
|
||||
@@ -24,6 +26,50 @@ export function transformRemoteConfigToStateShape(remoteConfig: RemoteConfig): P
|
||||
}
|
||||
}
|
||||
|
||||
// Map OpenTelemetry settings
|
||||
if (remoteConfig.openTelemetryEnabled !== undefined) {
|
||||
transformed.openTelemetryEnabled = remoteConfig.openTelemetryEnabled
|
||||
}
|
||||
if (remoteConfig.openTelemetryMetricsExporter !== undefined) {
|
||||
transformed.openTelemetryMetricsExporter = remoteConfig.openTelemetryMetricsExporter
|
||||
}
|
||||
if (remoteConfig.openTelemetryLogsExporter !== undefined) {
|
||||
transformed.openTelemetryLogsExporter = remoteConfig.openTelemetryLogsExporter
|
||||
}
|
||||
if (remoteConfig.openTelemetryOtlpProtocol !== undefined) {
|
||||
transformed.openTelemetryOtlpProtocol = remoteConfig.openTelemetryOtlpProtocol
|
||||
}
|
||||
if (remoteConfig.openTelemetryOtlpEndpoint !== undefined) {
|
||||
transformed.openTelemetryOtlpEndpoint = remoteConfig.openTelemetryOtlpEndpoint
|
||||
}
|
||||
if (remoteConfig.openTelemetryOtlpMetricsProtocol !== undefined) {
|
||||
transformed.openTelemetryOtlpMetricsProtocol = remoteConfig.openTelemetryOtlpMetricsProtocol
|
||||
}
|
||||
if (remoteConfig.openTelemetryOtlpMetricsEndpoint !== undefined) {
|
||||
transformed.openTelemetryOtlpMetricsEndpoint = remoteConfig.openTelemetryOtlpMetricsEndpoint
|
||||
}
|
||||
if (remoteConfig.openTelemetryOtlpLogsProtocol !== undefined) {
|
||||
transformed.openTelemetryOtlpLogsProtocol = remoteConfig.openTelemetryOtlpLogsProtocol
|
||||
}
|
||||
if (remoteConfig.openTelemetryOtlpLogsEndpoint !== undefined) {
|
||||
transformed.openTelemetryOtlpLogsEndpoint = remoteConfig.openTelemetryOtlpLogsEndpoint
|
||||
}
|
||||
if (remoteConfig.openTelemetryMetricExportInterval !== undefined) {
|
||||
transformed.openTelemetryMetricExportInterval = remoteConfig.openTelemetryMetricExportInterval
|
||||
}
|
||||
if (remoteConfig.openTelemetryOtlpInsecure !== undefined) {
|
||||
transformed.openTelemetryOtlpInsecure = remoteConfig.openTelemetryOtlpInsecure
|
||||
}
|
||||
if (remoteConfig.openTelemetryLogBatchSize !== undefined) {
|
||||
transformed.openTelemetryLogBatchSize = remoteConfig.openTelemetryLogBatchSize
|
||||
}
|
||||
if (remoteConfig.openTelemetryLogBatchTimeout !== undefined) {
|
||||
transformed.openTelemetryLogBatchTimeout = remoteConfig.openTelemetryLogBatchTimeout
|
||||
}
|
||||
if (remoteConfig.openTelemetryLogMaxQueueSize !== undefined) {
|
||||
transformed.openTelemetryLogMaxQueueSize = remoteConfig.openTelemetryLogMaxQueueSize
|
||||
}
|
||||
|
||||
// Map OpenAiCompatible provider settings
|
||||
const openAiSettings = remoteConfig.providerSettings?.OpenAiCompatible
|
||||
if (openAiSettings) {
|
||||
@@ -90,4 +136,12 @@ export function applyRemoteConfig(remoteConfig?: RemoteConfig): void {
|
||||
for (const [key, value] of Object.entries(transformed)) {
|
||||
stateManager.setRemoteConfigField(key as keyof GlobalStateAndSettings, value)
|
||||
}
|
||||
|
||||
// Reinitialize telemetry providers with new config
|
||||
// Fire-and-forget to avoid blocking remote config application
|
||||
clearOtelConfigCache()
|
||||
telemetryService.reinitializeAllProviders().catch((error) => {
|
||||
console.error("[RemoteConfig] Failed to reinitialize telemetry providers:", error)
|
||||
// Continue with old config on error
|
||||
})
|
||||
}
|
||||
|
||||
@@ -249,6 +249,40 @@ export async function readGlobalStateFromDisk(context: ExtensionContext): Promis
|
||||
const autoCondenseThreshold =
|
||||
context.globalState.get<GlobalStateAndSettings["autoCondenseThreshold"]>("autoCondenseThreshold") // number from 0 to 1
|
||||
const hooksEnabled = context.globalState.get<GlobalStateAndSettings["hooksEnabled"]>("hooksEnabled")
|
||||
|
||||
// OpenTelemetry configuration
|
||||
const openTelemetryEnabled =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryEnabled"]>("openTelemetryEnabled")
|
||||
const openTelemetryMetricsExporter =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryMetricsExporter"]>("openTelemetryMetricsExporter")
|
||||
const openTelemetryLogsExporter =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryLogsExporter"]>("openTelemetryLogsExporter")
|
||||
const openTelemetryOtlpProtocol =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryOtlpProtocol"]>("openTelemetryOtlpProtocol")
|
||||
const openTelemetryOtlpEndpoint =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryOtlpEndpoint"]>("openTelemetryOtlpEndpoint")
|
||||
const openTelemetryOtlpMetricsProtocol = context.globalState.get<
|
||||
GlobalStateAndSettings["openTelemetryOtlpMetricsProtocol"]
|
||||
>("openTelemetryOtlpMetricsProtocol")
|
||||
const openTelemetryOtlpMetricsEndpoint = context.globalState.get<
|
||||
GlobalStateAndSettings["openTelemetryOtlpMetricsEndpoint"]
|
||||
>("openTelemetryOtlpMetricsEndpoint")
|
||||
const openTelemetryOtlpLogsProtocol =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryOtlpLogsProtocol"]>("openTelemetryOtlpLogsProtocol")
|
||||
const openTelemetryOtlpLogsEndpoint =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryOtlpLogsEndpoint"]>("openTelemetryOtlpLogsEndpoint")
|
||||
const openTelemetryMetricExportInterval = context.globalState.get<
|
||||
GlobalStateAndSettings["openTelemetryMetricExportInterval"]
|
||||
>("openTelemetryMetricExportInterval")
|
||||
const openTelemetryOtlpInsecure =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryOtlpInsecure"]>("openTelemetryOtlpInsecure")
|
||||
const openTelemetryLogBatchSize =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryLogBatchSize"]>("openTelemetryLogBatchSize")
|
||||
const openTelemetryLogBatchTimeout =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryLogBatchTimeout"]>("openTelemetryLogBatchTimeout")
|
||||
const openTelemetryLogMaxQueueSize =
|
||||
context.globalState.get<GlobalStateAndSettings["openTelemetryLogMaxQueueSize"]>("openTelemetryLogMaxQueueSize")
|
||||
|
||||
// Get mode-related configurations
|
||||
const mode = context.globalState.get<GlobalStateAndSettings["mode"]>("mode")
|
||||
|
||||
@@ -574,6 +608,22 @@ export async function readGlobalStateFromDisk(context: ExtensionContext): Promis
|
||||
// Feature flag - defaults to false
|
||||
// For now, always return false to disable multi-root support by default
|
||||
multiRootEnabled: !!multiRootEnabled,
|
||||
|
||||
// OpenTelemetry configuration
|
||||
openTelemetryEnabled,
|
||||
openTelemetryMetricsExporter,
|
||||
openTelemetryLogsExporter,
|
||||
openTelemetryOtlpProtocol,
|
||||
openTelemetryOtlpEndpoint,
|
||||
openTelemetryOtlpMetricsProtocol,
|
||||
openTelemetryOtlpMetricsEndpoint,
|
||||
openTelemetryOtlpLogsProtocol,
|
||||
openTelemetryOtlpLogsEndpoint,
|
||||
openTelemetryMetricExportInterval,
|
||||
openTelemetryOtlpInsecure,
|
||||
openTelemetryLogBatchSize,
|
||||
openTelemetryLogBatchTimeout,
|
||||
openTelemetryLogMaxQueueSize,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[StateHelpers] Failed to read global state:", error)
|
||||
|
||||
+3
-6
@@ -7,7 +7,6 @@ import * as vscode from "vscode"
|
||||
import { sendAccountButtonClickedEvent } from "./core/controller/ui/subscribeToAccountButtonClicked"
|
||||
import { sendChatButtonClickedEvent } from "./core/controller/ui/subscribeToChatButtonClicked"
|
||||
import { sendHistoryButtonClickedEvent } from "./core/controller/ui/subscribeToHistoryButtonClicked"
|
||||
import { sendMcpButtonClickedEvent } from "./core/controller/ui/subscribeToMcpButtonClicked"
|
||||
import { sendSettingsButtonClickedEvent } from "./core/controller/ui/subscribeToSettingsButtonClicked"
|
||||
import { WebviewProvider } from "./core/webview"
|
||||
import { createClineAPI } from "./exports"
|
||||
@@ -38,6 +37,7 @@ import { LogoutReason } from "./services/auth/types"
|
||||
import { telemetryService } from "./services/telemetry"
|
||||
import { SharedUriHandler } from "./services/uri/SharedUriHandler"
|
||||
import { ShowMessageType } from "./shared/proto/host/window"
|
||||
import { registerTestOtelSettingsCommand } from "./test-otel-settings"
|
||||
import { fileExistsAtPath } from "./utils/fs"
|
||||
/*
|
||||
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
|
||||
@@ -82,11 +82,8 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
}),
|
||||
)
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand(commands.McpButton, () => {
|
||||
sendMcpButtonClickedEvent()
|
||||
}),
|
||||
)
|
||||
// Register test command for OpenTelemetry settings (for testing precedence)
|
||||
registerTestOtelSettingsCommand(context)
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand(commands.SettingsButton, () => {
|
||||
|
||||
@@ -127,6 +127,11 @@ export class NoOpTelemetryProvider implements ITelemetryProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public async reinitializeIfNeeded(): Promise<boolean> {
|
||||
Logger.info("[NoOpTelemetryProvider] reinitializeIfNeeded called (no-op)")
|
||||
return false
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
Logger.info("[NoOpTelemetryProvider] Disposing")
|
||||
}
|
||||
|
||||
@@ -1532,6 +1532,30 @@ export class TelemetryService {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize all telemetry providers if their configuration has changed.
|
||||
* Each provider decides internally whether reinitialization is needed.
|
||||
*
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
public async reinitializeAllProviders(): Promise<void> {
|
||||
console.log("[TelemetryService] Checking all providers for reinitialization...")
|
||||
|
||||
const results = await Promise.all(
|
||||
this.providers.map(async (provider) => {
|
||||
try {
|
||||
return await provider.reinitializeIfNeeded()
|
||||
} catch (error) {
|
||||
console.error("[TelemetryService] Error reinitializing provider:", error)
|
||||
return false
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
const reinitializedCount = results.filter((r) => r).length
|
||||
console.log(`[TelemetryService] Reinitialized ${reinitializedCount} of ${this.providers.length} provider(s)`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up resources when the service is disposed
|
||||
*/
|
||||
|
||||
@@ -104,6 +104,15 @@ export interface ITelemetryProvider {
|
||||
*/
|
||||
recordHistogram?(name: string, value: number, attributes?: TelemetryProperties): void
|
||||
|
||||
/**
|
||||
* Reinitialize the provider if configuration has changed.
|
||||
* Provider internally compares current config with new config
|
||||
* and only reinitializes if necessary.
|
||||
*
|
||||
* @returns Promise<boolean> - true if reinitialized, false if no change needed
|
||||
*/
|
||||
reinitializeIfNeeded(): Promise<boolean>
|
||||
|
||||
/**
|
||||
* Clean up resources when the provider is disposed
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
*/
|
||||
export class OpenTelemetryClientProvider {
|
||||
private static _instance: OpenTelemetryClientProvider | null = null
|
||||
private static currentConfig: OpenTelemetryClientValidConfig | null = null
|
||||
|
||||
public static getInstance(): OpenTelemetryClientProvider {
|
||||
if (!OpenTelemetryClientProvider._instance) {
|
||||
@@ -47,9 +48,50 @@ export class OpenTelemetryClientProvider {
|
||||
return process.env.TEL_DEBUG_DIAGNOSTICS === "true" || process.env.IS_DEV === "true"
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if OpenTelemetry configuration has changed.
|
||||
* Compares current config with new config from StateManager.
|
||||
*/
|
||||
public static hasConfigChanged(): boolean {
|
||||
const newConfig = getValidOpenTelemetryConfig()
|
||||
const changed = JSON.stringify(OpenTelemetryClientProvider.currentConfig) !== JSON.stringify(newConfig)
|
||||
|
||||
if (changed) {
|
||||
console.log("[OTEL] Configuration has changed")
|
||||
}
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize the OpenTelemetry client providers.
|
||||
* Disposes existing providers and creates new ones with updated config.
|
||||
*/
|
||||
public static async reinitialize(): Promise<void> {
|
||||
console.log("[OTEL] Reinitializing OpenTelemetry providers...")
|
||||
|
||||
const instance = OpenTelemetryClientProvider.getInstance()
|
||||
|
||||
// Dispose existing providers
|
||||
await instance.dispose()
|
||||
|
||||
// Clear singleton instance
|
||||
OpenTelemetryClientProvider._instance = null
|
||||
OpenTelemetryClientProvider.currentConfig = null
|
||||
|
||||
// Clear config cache to force re-evaluation
|
||||
// Note: clearOtelConfigCache will be exported from otel-config.ts
|
||||
// For now, we'll just clear the instance which will trigger re-read on next getInstance()
|
||||
|
||||
console.log("[OTEL] OpenTelemetry providers reinitialized")
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
this.config = getValidOpenTelemetryConfig()
|
||||
|
||||
// Store current config for comparison
|
||||
OpenTelemetryClientProvider.currentConfig = this.config
|
||||
|
||||
if (!this.config) {
|
||||
console.log("[OTEL DEBUG] OpenTelemetry is disabled or not configured")
|
||||
return
|
||||
|
||||
@@ -193,6 +193,38 @@ export class OpenTelemetryTelemetryProvider implements ITelemetryProvider {
|
||||
histogram.record(value, this.flattenProperties(attributes))
|
||||
}
|
||||
|
||||
public async reinitializeIfNeeded(): Promise<boolean> {
|
||||
// Check if config has changed
|
||||
if (!OpenTelemetryClientProvider.hasConfigChanged()) {
|
||||
return false
|
||||
}
|
||||
|
||||
console.log("[OTEL] Config changed, reinitializing...")
|
||||
|
||||
try {
|
||||
// Reinitialize the client provider
|
||||
await OpenTelemetryClientProvider.reinitialize()
|
||||
|
||||
// Get new meter and logger from reinitialized provider
|
||||
const meterProvider = OpenTelemetryClientProvider.getMeterProvider()
|
||||
const loggerProvider = OpenTelemetryClientProvider.getLoggerProvider()
|
||||
|
||||
// Update our references
|
||||
this.meter = meterProvider ? meterProvider.getMeter("cline") : null
|
||||
this.logger = loggerProvider ? loggerProvider.getLogger("cline") : null
|
||||
|
||||
// Clear cached instruments since we have new meter
|
||||
this.counters.clear()
|
||||
this.histograms.clear()
|
||||
|
||||
console.log("[OTEL] Reinitialization complete")
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error("[OTEL] Reinitialization failed:", error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
// OpenTelemetry client provider handles shutdown
|
||||
// Individual providers don't need to do anything
|
||||
|
||||
@@ -14,6 +14,7 @@ export class PostHogTelemetryProvider implements ITelemetryProvider {
|
||||
private client: PostHog
|
||||
private telemetrySettings: TelemetrySettings
|
||||
private isSharedClient: boolean
|
||||
private currentConfig: any = null
|
||||
|
||||
constructor(sharedClient?: PostHog) {
|
||||
this.isSharedClient = !!sharedClient
|
||||
@@ -139,6 +140,23 @@ export class PostHogTelemetryProvider implements ITelemetryProvider {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public async reinitializeIfNeeded(): Promise<boolean> {
|
||||
// Get new config from posthog-config
|
||||
const newConfig = posthogConfig
|
||||
|
||||
// Compare with current config
|
||||
if (JSON.stringify(this.currentConfig) === JSON.stringify(newConfig)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Update current config
|
||||
this.currentConfig = { ...newConfig }
|
||||
|
||||
// For now, just log - actual reinitialization can be added later
|
||||
console.log("[PostHogTelemetryProvider] Config changed, would reinitialize")
|
||||
return false
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
// Only shut down the client if it's not shared (we own it)
|
||||
if (!this.isSharedClient) {
|
||||
|
||||
@@ -80,6 +80,23 @@ export const RemoteConfigSchema = z.object({
|
||||
// If the user is allowed to enable YOLO mode. Note this is different from the extension setting
|
||||
// yoloModeEnabled, because we do not want to force YOLO enabled for the user.
|
||||
yoloModeAllowed: z.boolean().optional(),
|
||||
|
||||
// OpenTelemetry configuration
|
||||
openTelemetryEnabled: z.boolean().optional(),
|
||||
openTelemetryMetricsExporter: z.string().optional(),
|
||||
openTelemetryLogsExporter: z.string().optional(),
|
||||
openTelemetryOtlpProtocol: z.string().optional(),
|
||||
openTelemetryOtlpEndpoint: z.string().optional(),
|
||||
openTelemetryOtlpMetricsProtocol: z.string().optional(),
|
||||
openTelemetryOtlpMetricsEndpoint: z.string().optional(),
|
||||
openTelemetryOtlpLogsProtocol: z.string().optional(),
|
||||
openTelemetryOtlpLogsEndpoint: z.string().optional(),
|
||||
openTelemetryMetricExportInterval: z.number().optional(),
|
||||
openTelemetryOtlpInsecure: z.boolean().optional(),
|
||||
openTelemetryLogBatchSize: z.number().optional(),
|
||||
openTelemetryLogBatchTimeout: z.number().optional(),
|
||||
openTelemetryLogMaxQueueSize: z.number().optional(),
|
||||
|
||||
// Other top-level settings can be added here later.
|
||||
|
||||
// Provider specific settings
|
||||
|
||||
@@ -91,13 +91,13 @@ const isTestEnv = process.env.E2E_TEST === "true" || process.env.IS_TEST === "tr
|
||||
let otelConfig: OpenTelemetryClientConfig | null = null
|
||||
|
||||
/**
|
||||
* Gets or creates the OpenTelemetry configuration from environment variables.
|
||||
* Gets or creates the OpenTelemetry configuration with proper precedence.
|
||||
* Configuration is cached after first access for performance.
|
||||
*
|
||||
* Configuration Sources:
|
||||
* - **Production Build**: Environment variables injected by esbuild at build time
|
||||
* via .github/workflows/publish.yml
|
||||
* - **Development**: Environment variables from .env file loaded by VSCode
|
||||
* Configuration Precedence (lowest to highest):
|
||||
* 1. **Build-time**: Environment variables injected by esbuild at build time via .github/workflows/publish.yml
|
||||
* 2. **Runtime Environment Variables**: Environment variables from .env file or startup
|
||||
* 3. **Settings**: Values from StateManager (can be set via UI or remote config)
|
||||
*
|
||||
* Supported Environment Variables:
|
||||
* - OTEL_TELEMETRY_ENABLED: "1" to enable OpenTelemetry (default: off)
|
||||
@@ -121,7 +121,8 @@ let otelConfig: OpenTelemetryClientConfig | null = null
|
||||
*/
|
||||
function getOtelConfig(): OpenTelemetryClientConfig {
|
||||
if (!otelConfig) {
|
||||
otelConfig = {
|
||||
// Start with build-time/environment variable config (lowest precedence)
|
||||
const baseConfig: OpenTelemetryClientConfig = {
|
||||
enabled: process.env.OTEL_TELEMETRY_ENABLED === "1",
|
||||
metricsExporter: process.env.OTEL_METRICS_EXPORTER,
|
||||
logsExporter: process.env.OTEL_LOGS_EXPORTER,
|
||||
@@ -145,10 +146,104 @@ function getOtelConfig(): OpenTelemetryClientConfig {
|
||||
? Math.max(1, parseInt(process.env.OTEL_LOG_MAX_QUEUE_SIZE, 10))
|
||||
: undefined,
|
||||
}
|
||||
|
||||
// Try to get settings from StateManager (highest precedence)
|
||||
// Note: StateManager may not be initialized yet during early extension startup
|
||||
const settingsOverrides: Partial<OpenTelemetryClientConfig> = {}
|
||||
try {
|
||||
// Dynamic import to avoid circular dependencies
|
||||
const { StateManager } = require("@/core/storage/StateManager")
|
||||
const stateManager = StateManager.get()
|
||||
|
||||
// Apply settings overrides if they exist (using getter methods)
|
||||
const enabled = stateManager.getGlobalSettingsKey("openTelemetryEnabled")
|
||||
if (enabled !== undefined) {
|
||||
settingsOverrides.enabled = enabled
|
||||
}
|
||||
|
||||
const metricsExporter = stateManager.getGlobalSettingsKey("openTelemetryMetricsExporter")
|
||||
if (metricsExporter !== undefined) {
|
||||
settingsOverrides.metricsExporter = metricsExporter
|
||||
}
|
||||
|
||||
const logsExporter = stateManager.getGlobalSettingsKey("openTelemetryLogsExporter")
|
||||
if (logsExporter !== undefined) {
|
||||
settingsOverrides.logsExporter = logsExporter
|
||||
}
|
||||
|
||||
const otlpProtocol = stateManager.getGlobalSettingsKey("openTelemetryOtlpProtocol")
|
||||
if (otlpProtocol !== undefined) {
|
||||
settingsOverrides.otlpProtocol = otlpProtocol
|
||||
}
|
||||
|
||||
const otlpEndpoint = stateManager.getGlobalSettingsKey("openTelemetryOtlpEndpoint")
|
||||
if (otlpEndpoint !== undefined) {
|
||||
settingsOverrides.otlpEndpoint = otlpEndpoint
|
||||
}
|
||||
|
||||
const otlpMetricsProtocol = stateManager.getGlobalSettingsKey("openTelemetryOtlpMetricsProtocol")
|
||||
if (otlpMetricsProtocol !== undefined) {
|
||||
settingsOverrides.otlpMetricsProtocol = otlpMetricsProtocol
|
||||
}
|
||||
|
||||
const otlpMetricsEndpoint = stateManager.getGlobalSettingsKey("openTelemetryOtlpMetricsEndpoint")
|
||||
if (otlpMetricsEndpoint !== undefined) {
|
||||
settingsOverrides.otlpMetricsEndpoint = otlpMetricsEndpoint
|
||||
}
|
||||
|
||||
const otlpLogsProtocol = stateManager.getGlobalSettingsKey("openTelemetryOtlpLogsProtocol")
|
||||
if (otlpLogsProtocol !== undefined) {
|
||||
settingsOverrides.otlpLogsProtocol = otlpLogsProtocol
|
||||
}
|
||||
|
||||
const otlpLogsEndpoint = stateManager.getGlobalSettingsKey("openTelemetryOtlpLogsEndpoint")
|
||||
if (otlpLogsEndpoint !== undefined) {
|
||||
settingsOverrides.otlpLogsEndpoint = otlpLogsEndpoint
|
||||
}
|
||||
|
||||
const metricExportInterval = stateManager.getGlobalSettingsKey("openTelemetryMetricExportInterval")
|
||||
if (metricExportInterval !== undefined) {
|
||||
settingsOverrides.metricExportInterval = metricExportInterval
|
||||
}
|
||||
|
||||
const otlpInsecure = stateManager.getGlobalSettingsKey("openTelemetryOtlpInsecure")
|
||||
if (otlpInsecure !== undefined) {
|
||||
settingsOverrides.otlpInsecure = otlpInsecure
|
||||
}
|
||||
|
||||
const logBatchSize = stateManager.getGlobalSettingsKey("openTelemetryLogBatchSize")
|
||||
if (logBatchSize !== undefined) {
|
||||
settingsOverrides.logBatchSize = logBatchSize
|
||||
}
|
||||
|
||||
const logBatchTimeout = stateManager.getGlobalSettingsKey("openTelemetryLogBatchTimeout")
|
||||
if (logBatchTimeout !== undefined) {
|
||||
settingsOverrides.logBatchTimeout = logBatchTimeout
|
||||
}
|
||||
|
||||
const logMaxQueueSize = stateManager.getGlobalSettingsKey("openTelemetryLogMaxQueueSize")
|
||||
if (logMaxQueueSize !== undefined) {
|
||||
settingsOverrides.logMaxQueueSize = logMaxQueueSize
|
||||
}
|
||||
} catch (error) {
|
||||
// StateManager not available yet (early startup) - use base config only
|
||||
console.log("[OTEL] StateManager not available, using environment config only")
|
||||
}
|
||||
|
||||
// Merge base config with settings overrides
|
||||
otelConfig = { ...baseConfig, ...settingsOverrides }
|
||||
}
|
||||
return otelConfig
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cached OpenTelemetry configuration.
|
||||
* Should be called when settings change to force re-evaluation.
|
||||
*/
|
||||
export function clearOtelConfigCache(): void {
|
||||
otelConfig = null
|
||||
}
|
||||
|
||||
export function isOpenTelemetryConfigValid(config: OpenTelemetryClientConfig): config is OpenTelemetryClientValidConfig {
|
||||
// Disable in test environment to enable mocking and stubbing
|
||||
if (isTestEnv) {
|
||||
|
||||
@@ -173,6 +173,22 @@ export interface Settings {
|
||||
actModeVercelAiGatewayModelInfo: ModelInfo | undefined
|
||||
actModeOcaModelId: string | undefined
|
||||
actModeOcaModelInfo: OcaModelInfo | undefined
|
||||
|
||||
// OpenTelemetry configuration
|
||||
openTelemetryEnabled: boolean | undefined
|
||||
openTelemetryMetricsExporter: string | undefined
|
||||
openTelemetryLogsExporter: string | undefined
|
||||
openTelemetryOtlpProtocol: string | undefined
|
||||
openTelemetryOtlpEndpoint: string | undefined
|
||||
openTelemetryOtlpMetricsProtocol: string | undefined
|
||||
openTelemetryOtlpMetricsEndpoint: string | undefined
|
||||
openTelemetryOtlpLogsProtocol: string | undefined
|
||||
openTelemetryOtlpLogsEndpoint: string | undefined
|
||||
openTelemetryMetricExportInterval: number | undefined
|
||||
openTelemetryOtlpInsecure: boolean | undefined
|
||||
openTelemetryLogBatchSize: number | undefined
|
||||
openTelemetryLogBatchTimeout: number | undefined
|
||||
openTelemetryLogMaxQueueSize: number | undefined
|
||||
}
|
||||
|
||||
export interface Secrets {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import * as vscode from "vscode"
|
||||
import { StateManager } from "@/core/storage/StateManager"
|
||||
import { HostProvider } from "@/hosts/host-provider"
|
||||
import { telemetryService } from "@/services/telemetry"
|
||||
import { ShowMessageType } from "@/shared/proto/host/window"
|
||||
import { clearOtelConfigCache } from "@/shared/services/config/otel-config"
|
||||
|
||||
export function registerTestOtelSettingsCommand(context: vscode.ExtensionContext) {
|
||||
// biome-ignore lint/correctness/noUnusedVariables: Test utility for OpenTelemetry settings
|
||||
// biome-ignore plugin: Test utility - direct vscode API usage is acceptable here
|
||||
const disposable = vscode.commands.registerCommand("cline.testOtelSettings", async () => {
|
||||
try {
|
||||
const stateManager = StateManager.get()
|
||||
|
||||
// Set OpenTelemetry settings via StateManager
|
||||
await stateManager.setGlobalStateBatch({
|
||||
openTelemetryOtlpProtocol: "http/protobuf",
|
||||
openTelemetryOtlpEndpoint: "http://localhost:4318",
|
||||
})
|
||||
|
||||
// Clear the config cache so it will be re-read on next access
|
||||
clearOtelConfigCache()
|
||||
|
||||
// Reinitialize telemetry providers with new config
|
||||
await telemetryService.reinitializeAllProviders()
|
||||
|
||||
console.log("[TEST] OpenTelemetry settings updated:")
|
||||
console.log("[TEST] - Protocol: http/protobuf")
|
||||
console.log("[TEST] - Endpoint: http://localhost:4318")
|
||||
console.log("[TEST] Config cache cleared")
|
||||
console.log("[TEST] Telemetry providers reinitialized")
|
||||
console.log("[TEST] Settings saved to disk")
|
||||
|
||||
// Show success message
|
||||
await HostProvider.window.showMessage({
|
||||
type: ShowMessageType.INFORMATION,
|
||||
message:
|
||||
"OpenTelemetry settings updated and providers reinitialized! Protocol: http/protobuf, Endpoint: http://localhost:4318",
|
||||
})
|
||||
} catch (error) {
|
||||
await HostProvider.window.showMessage({
|
||||
type: ShowMessageType.ERROR,
|
||||
message: `Failed to update settings: ${error}`,
|
||||
})
|
||||
console.error("[TEST] Error updating settings:", error)
|
||||
}
|
||||
})
|
||||
|
||||
context.subscriptions.push(disposable)
|
||||
}
|
||||
Reference in New Issue
Block a user