Repository navigation
fix(server): T3 Connect keeps a log file of its tunnel connector #24485
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
ef5d4be
ad1f716
408505e
019d109
59a2665
3cdbfda
8b690a3
70bb02d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import type { RelayManagedEndpointRuntimeConfig } from "@t3tools/contracts/relay"; | ||
| import { RotatingFileSink } from "@t3tools/shared/logging"; | ||
| import * as RelayClient from "@t3tools/shared/relayClient"; | ||
| import * as Clock from "effect/Clock"; | ||
| import * as Context from "effect/Context"; | ||
|
|
@@ -7,7 +8,9 @@ import * as Duration from "effect/Duration"; | |
| import * as Effect from "effect/Effect"; | ||
| import * as Exit from "effect/Exit"; | ||
| import * as Layer from "effect/Layer"; | ||
| import * as Logger from "effect/Logger"; | ||
| import * as Option from "effect/Option"; | ||
| import * as Path from "effect/Path"; | ||
| import * as Queue from "effect/Queue"; | ||
| import * as Ref from "effect/Ref"; | ||
| import * as Result from "effect/Result"; | ||
|
|
@@ -19,6 +22,8 @@ import { HttpClient, HttpClientResponse } from "effect/http"; | |
| import * as ChildProcess from "effect/process/ChildProcess"; | ||
| import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; | ||
|
|
||
| import * as ServerConfig from "../config.ts"; | ||
|
|
||
| export type CloudManagedEndpointRuntimeStatus = | ||
| | { | ||
| readonly status: "disabled"; | ||
|
|
@@ -119,6 +124,12 @@ const QUIC_STALL_WINDOW_MS = 5 * 60_000; | |
| // than this, so a crash-looping connector or an offline host does not redownload | ||
| // on every reconcile. | ||
| const RELAY_CLIENT_INSTALL_RETRY_INTERVAL_MS = 10 * 60_000; | ||
| // The connector's fibers outlive the span that started them, so the trace file | ||
| // misses their messages, and a desktop backend's stdout reaches disk only after | ||
| // a crash. This file keeps what the runtime logs, connector output included. | ||
| export const RELAY_CLIENT_LOG_FILE = "relay-client.ndjson"; | ||
| const RELAY_CLIENT_LOG_MAX_BYTES = 1024 * 1024; | ||
| const RELAY_CLIENT_LOG_MAX_FILES = 2; | ||
|
|
||
| /** | ||
| * A PATH binary or an explicit override is the user's choice, and the CLI asks | ||
|
|
@@ -214,6 +225,40 @@ export const make = Effect.gen(function* () { | |
| const quicStallsRef = yield* Ref.make<ReadonlyArray<number>>([]); | ||
| let reconcileConfig: CloudManagedEndpointRuntime["Service"]["applyConfig"]; | ||
|
|
||
| const { logsDir } = yield* ServerConfig.ServerConfig; | ||
| const path = yield* Path.Path; | ||
| const relayClientLogPath = path.join(logsDir, RELAY_CLIENT_LOG_FILE); | ||
| // A synchronous append per line keeps a timer off an idle server; the | ||
| // connector logs a few lines a minute. | ||
| const relayClientLog = yield* Effect.try( | ||
| () => | ||
| new RotatingFileSink({ | ||
| filePath: relayClientLogPath, | ||
| maxBytes: RELAY_CLIENT_LOG_MAX_BYTES, | ||
| maxFiles: RELAY_CLIENT_LOG_MAX_FILES, | ||
| }), | ||
| ).pipe( | ||
| Effect.map((sink) => | ||
|
Comment on lines
+244
to
+250
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| Logger.make((options) => { | ||
| try { | ||
| sink.write(`${Logger.formatJson.log(options)}\n`); | ||
| } catch { | ||
| // A log file that stops taking writes must not fail the connector fibers. | ||
| } | ||
| }), | ||
| ), | ||
| Effect.catch((error) => | ||
| Effect.logWarning("Relay client log unavailable", { | ||
| filePath: relayClientLogPath, | ||
| cause: error, | ||
| }).pipe(Effect.as(undefined)), | ||
| ), | ||
| ); | ||
| // Fibers inherit their loggers, so the connector fibers forked under | ||
| // applyConfig write here too. | ||
| const withRelayClientLog = <A, E, R>(effect: Effect.Effect<A, E, R>) => | ||
| relayClientLog ? Effect.withLogger(effect, relayClientLog) : effect; | ||
|
|
||
| const stopActive = Effect.gen(function* () { | ||
| const active = yield* Ref.getAndSet(activeRef, null); | ||
| yield* stopConnector(active); | ||
|
|
@@ -747,6 +792,7 @@ export const make = Effect.gen(function* () { | |
| return yield* reconcileConfig(config); | ||
| }), | ||
| ), | ||
| withRelayClientLog, | ||
| ); | ||
|
|
||
| const readMetrics = <S extends Schema.Decoder<unknown>>(url: string, schema: S) => | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.