Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions apps/desktop/src/app/DesktopObservability.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { assert, describe, it } from "@effect/vitest";
import * as ConfigProvider from "effect/ConfigProvider";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
Expand Down Expand Up @@ -427,6 +428,36 @@ describe("DesktopObservability", () => {
);
});

it.effect("exports kill switch warnings through the configured logger", () => {
const requests: Array<ExportedRequest> = [];
return Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const baseDir = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-desktop-observability-test-",
});
const environmentLayer = makeEnvironmentLayer(baseDir, true, {
T3CODE_OTLP_LOGS_URL: "https://collector.example.com/v1/logs",
});

yield* Effect.scoped(
Effect.void.pipe(
Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))),
),
);

assert.include(requests[0]?.body ?? "", "OTEL_SDK_DISABLED=1 was read as false");
}).pipe(
Effect.scoped,
Effect.provide(
Layer.mergeAll(
NodeServices.layer,
collectorLayer(requests),
ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "1" } })),
),
),
);
});

it.effect("reads every signal endpoint from Settings when the environment names none", () => {
const requests: Array<ExportedRequest> = [];
return Effect.gen(function* () {
Expand Down Expand Up @@ -491,4 +522,37 @@ describe("DesktopObservability", () => {
Effect.provide(Layer.mergeAll(NodeServices.layer, collectorLayer(requests))),
);
});

it.effect("stops every export when the OpenTelemetry SDK is disabled", () => {
const requests: Array<ExportedRequest> = [];
return Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const baseDir = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-desktop-observability-test-",
});
const environmentLayer = makeEnvironmentLayer(baseDir);
yield* writeObservabilitySettings(environmentLayer, {
otlpTracesUrl: "https://settings.example.com/v1/traces",
otlpLogsUrl: "https://settings.example.com/v1/logs",
});

yield* Effect.scoped(
Effect.logInfo("desktop log stays local when disabled").pipe(
Effect.withSpan("desktop-disabled-test"),
Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))),
),
);

assert.lengthOf(requests, 0);
}).pipe(
Effect.scoped,
Effect.provide(
Layer.mergeAll(
NodeServices.layer,
collectorLayer(requests),
ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "true" } })),
),
),
);
});
});
14 changes: 13 additions & 1 deletion apps/desktop/src/app/DesktopObservability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
makeTraceSink,
otlpSerializationLayer,
} from "@t3tools/shared/observability";
import * as OtelEnvironment from "@t3tools/shared/otelEnvironment";
import {
parsePersistedServerObservabilitySettings,
type PersistedServerObservabilitySettings,
Expand Down Expand Up @@ -353,12 +354,18 @@ const readPersistedObservabilitySettings: Effect.Effect<
* resolve traces against one revision of the file and logs against another.
*/
const resolveOtlpEndpoints = Effect.gen(function* () {
const otel = yield* OtelEnvironment.load;
if (otel.disabled) {
return { traces: undefined, metrics: undefined, logs: undefined, warnings: otel.warnings };
}

const environment = yield* DesktopEnvironment.DesktopEnvironment;
const persisted = yield* readPersistedObservabilitySettings;
return {
traces: Option.getOrUndefined(environment.otlpTracesUrl) ?? persisted.otlpTracesUrl,
metrics: Option.getOrUndefined(environment.otlpMetricsUrl) ?? persisted.otlpMetricsUrl,
logs: Option.getOrUndefined(environment.otlpLogsUrl) ?? persisted.otlpLogsUrl,
warnings: otel.warnings,
};
});

Expand Down Expand Up @@ -671,7 +678,12 @@ const telemetryLayer = Layer.unwrap(
// resource,
// }).pipe(Layer.provide(serializationLayer));

return Layer.mergeAll(loggerLayer, tracerLayer);
// Logged once the loggers above are installed, so the warnings use them.
const otelWarningsLayer = Layer.effectDiscard(
Effect.forEach(endpoints.warnings, (warning) => Effect.logWarning(warning)),
);

return otelWarningsLayer.pipe(Layer.provideMerge(Layer.mergeAll(loggerLayer, tracerLayer)));
}),
);

Expand Down
43 changes: 43 additions & 0 deletions apps/desktop/src/backend/DesktopBackendConfiguration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,49 @@ describe("DesktopBackendConfiguration", () => {
}).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),
);

it.effect("resolveWsl carries the kill switch into the distro", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
const baseDir = yield* fileSystem.makeTempDirectoryScoped({
prefix: "t3-desktop-backend-config-test-",
});

const previousWslEnv = process.env.WSLENV;
const previousDisabled = process.env.OTEL_SDK_DISABLED;
try {
delete process.env.WSLENV;
process.env.OTEL_SDK_DISABLED = "true";

yield* Effect.gen(function* () {
const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;
const config = yield* configuration.resolveWsl({ port: 5050, distro: null });

assert.equal(config.env.OTEL_SDK_DISABLED, "true");
assert.include((config.env.WSLENV ?? "").split(":"), "OTEL_SDK_DISABLED");
}).pipe(
Effect.provide(
DesktopBackendConfiguration.layer.pipe(
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(DesktopAppSettings.layerTest()),
Layer.provideMerge(DesktopWslServerTree.layerTest()),
Layer.provideMerge(
DesktopWslEnvironment.layerTest({
isAvailable: true,
windowsToWslPath: () => Option.some("/mnt/c/repo/apps/server/src/index.ts"),
getDistroIp: () => Option.some("172.27.0.99"),
}),
),
Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })),
),
),
);
} finally {
restoreEnv("WSLENV", previousWslEnv);
restoreEnv("OTEL_SDK_DISABLED", previousDisabled);
}
}).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),
);

it.effect("resolveWsl preserves existing WSLENV entries when forwarding backend secrets", () =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/backend/DesktopBackendConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ const DESKTOP_BACKEND_ENV_NAMES = [
const WSL_FORWARDED_ENV_NAMES = [
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
// Otherwise the WSL server keeps exporting to endpoints from the bootstrap.
"T3CODE_OTEL_SDK_DISABLED",
"OTEL_SDK_DISABLED",
Comment thread
juliusmarminge marked this conversation as resolved.
"T3CODE_OTLP_HEADERS",
"T3CODE_OTLP_PROTOCOL",
] as const;
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/bin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import * as NetService from "@t3tools/shared/Net";
import { HostProcessEnvironment } from "@t3tools/shared/hostProcess";
import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability";
import * as OtelEnvironment from "@t3tools/shared/otelEnvironment";
import { assert, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as DateTime from "effect/DateTime";
Expand Down Expand Up @@ -106,6 +107,7 @@ const makeCliTestServerConfig = (baseDir: string) =>
otlpMetricsExport: DEFAULT_SIGNAL_EXPORT,
otlpLogsExport: DEFAULT_SIGNAL_EXPORT,
otlpServiceName: "t3-server",
otelEnvironment: OtelEnvironment.none,
mode: "web",
port: 0,
host: "127.0.0.1",
Expand Down
105 changes: 105 additions & 0 deletions apps/server/src/cli/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from "@t3tools/contracts";
import * as NetService from "@t3tools/shared/Net";
import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability";
import * as OtelEnvironment from "@t3tools/shared/otelEnvironment";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { deriveServerPaths } from "../config.ts";
import { resolveServerConfig } from "./config.ts";
Expand Down Expand Up @@ -56,6 +57,7 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => {
otlpMetricsExport: DEFAULT_SIGNAL_EXPORT,
otlpLogsExport: DEFAULT_SIGNAL_EXPORT,
otlpServiceName: "t3-server",
otelEnvironment: OtelEnvironment.none,
devAllowedOrigins: [],
} as const;

Expand Down Expand Up @@ -666,6 +668,109 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => {
}),
);

it.effect("zeroes an endpoint stored in Settings when the SDK is disabled", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-cli-config-otel-off-" });
const derivedPaths = yield* deriveExplicitServerPaths(baseDir, undefined);
yield* fs.makeDirectory(path.dirname(derivedPaths.settingsPath), { recursive: true });
yield* fs.writeFileString(
derivedPaths.settingsPath,
// @effect-diagnostics-next-line preferSchemaOverJson:off
`${JSON.stringify({
observability: {
otlpTracesUrl: "http://localhost:4318/v1/traces",
otlpMetricsUrl: "http://localhost:4318/v1/metrics",
otlpLogsUrl: "http://localhost:4318/v1/logs",
},
})}\n`,
);

const resolved = yield* resolveServerConfig(
{
mode: Option.some("desktop"),
port: Option.some(4888),
host: Option.none(),
baseDir: Option.some(baseDir),
cwd: Option.none(),
devUrl: Option.none(),
noBrowser: Option.none(),
bootstrapFd: Option.none(),
autoBootstrapProjectFromCwd: Option.none(),
logWebSocketEvents: Option.none(),
tailscaleServeEnabled: Option.none(),
tailscaleServePort: Option.none(),
},
Option.none(),
).pipe(
Effect.provide(
Layer.mergeAll(
ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "true" } })),
NetService.layer,
),
),
);

// The switch beats every source, including an endpoint stored in Settings.
expect(resolved.otlpTracesUrl).toBeUndefined();
expect(resolved.otlpMetricsUrl).toBeUndefined();
expect(resolved.otlpLogsUrl).toBeUndefined();
expect(resolved.otelEnvironment.disabled).toBe(true);
}),
);

it.effect("lets T3CODE_OTEL_SDK_DISABLED=false override an ambient OTEL_SDK_DISABLED=true", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-cli-config-otel-on-" });
const derivedPaths = yield* deriveExplicitServerPaths(baseDir, undefined);
yield* fs.makeDirectory(path.dirname(derivedPaths.settingsPath), { recursive: true });
yield* fs.writeFileString(
derivedPaths.settingsPath,
// @effect-diagnostics-next-line preferSchemaOverJson:off
`${JSON.stringify({
observability: {
otlpTracesUrl: "http://localhost:4318/v1/traces",
},
})}\n`,
);

const resolved = yield* resolveServerConfig(
{
mode: Option.some("desktop"),
port: Option.some(4888),
host: Option.none(),
baseDir: Option.some(baseDir),
cwd: Option.none(),
devUrl: Option.none(),
noBrowser: Option.none(),
bootstrapFd: Option.none(),
autoBootstrapProjectFromCwd: Option.none(),
logWebSocketEvents: Option.none(),
tailscaleServeEnabled: Option.none(),
tailscaleServePort: Option.none(),
},
Option.none(),
).pipe(
Effect.provide(
Layer.mergeAll(
ConfigProvider.layer(
ConfigProvider.fromEnv({
env: { T3CODE_OTEL_SDK_DISABLED: "false", OTEL_SDK_DISABLED: "true" },
}),
),
NetService.layer,
),
),
);

expect(resolved.otelEnvironment.disabled).toBe(false);
expect(resolved.otlpTracesUrl).toBe("http://localhost:4318/v1/traces");
}),
);

it.effect("forces noBrowser and disables auto-bootstrap for headless startup presentation", () =>
Effect.gen(function* () {
const { join } = yield* Path.Path;
Expand Down
27 changes: 17 additions & 10 deletions apps/server/src/cli/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
OtlpProtocol,
type SignalExport,
} from "@t3tools/shared/observability";
import * as OtelEnvironment from "@t3tools/shared/otelEnvironment";
import { parsePersistedServerObservabilitySettings } from "@t3tools/shared/serverSettings";
import { DesktopBackendBootstrap, PortSchema } from "@t3tools/contracts";
import * as Config from "effect/Config";
Expand Down Expand Up @@ -386,6 +387,8 @@ export const resolveServerConfig = (
);
const logLevel = Option.getOrElse(cliLogLevel, () => env.logLevel);

const otel = yield* OtelEnvironment.load;

// T3 Code's own OTLP variables name no signal, so the one answer they give
// is the answer for all three.
const signalExport: SignalExport = {
Expand All @@ -401,20 +404,24 @@ export const resolveServerConfig = (
traceBatchWindowMs: env.traceBatchWindowMs,
traceMaxBytes: env.traceMaxBytes,
traceMaxFiles: env.traceMaxFiles,
otlpTracesUrl:
env.otlpTracesUrl ??
bootstrap?.otlpTracesUrl ??
persistedObservabilitySettings.otlpTracesUrl,
otlpMetricsUrl:
env.otlpMetricsUrl ??
bootstrap?.otlpMetricsUrl ??
persistedObservabilitySettings.otlpMetricsUrl,
otlpLogsUrl:
env.otlpLogsUrl ?? bootstrap?.otlpLogsUrl ?? persistedObservabilitySettings.otlpLogsUrl,
otlpTracesUrl: otel.disabled
? undefined
: (env.otlpTracesUrl ??
bootstrap?.otlpTracesUrl ??
persistedObservabilitySettings.otlpTracesUrl),
otlpMetricsUrl: otel.disabled
? undefined
: (env.otlpMetricsUrl ??
bootstrap?.otlpMetricsUrl ??
persistedObservabilitySettings.otlpMetricsUrl),
otlpLogsUrl: otel.disabled
? undefined
: (env.otlpLogsUrl ?? bootstrap?.otlpLogsUrl ?? persistedObservabilitySettings.otlpLogsUrl),
otlpTracesExport: signalExport,
otlpMetricsExport: signalExport,
otlpLogsExport: signalExport,
otlpServiceName: env.otlpServiceName,
otelEnvironment: otel,
mode,
port,
cwd,
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/cli/pair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from "@t3tools/contracts";
import { resolveWorktreeT3Home } from "@t3tools/shared/devHome";
import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability";
import * as OtelEnvironment from "@t3tools/shared/otelEnvironment";
import {
buildTailscaleHttpsBaseUrl,
DEFAULT_TAILSCALE_SERVE_PORT,
Expand Down Expand Up @@ -326,6 +327,7 @@ const makePairServerConfig = Effect.fn(function* (input: {
otlpMetricsExport: DEFAULT_SIGNAL_EXPORT,
otlpLogsExport: DEFAULT_SIGNAL_EXPORT,
otlpServiceName: "t3-server",
otelEnvironment: OtelEnvironment.none,
mode: "web",
port: state.port,
host: state.host,
Expand Down
Loading
Loading