Observability
Proxyline exposes its decisions through two channels: an onEvent callback and an explain() method on the handle. Both share the same ProxylineDecision shape and the same credential-redaction rules.
#Events
Subscribe at install time:
const proxy = installGlobalProxy({
mode: "managed",
proxyUrl: "https://user:secret@proxy.corp.example:8443",
onEvent: (event) => log(event),
});
Event types:
runtime.installed— fired once at install. Includesmode,active, and the redactedproxyUrl.runtime.stopped— fired fromhandle.stop(). Includesmode.decision— fired from everyexplain()call. Includes the full decision.warning— reserved for future runtime diagnostics.
runtime.installed is the cleanest place to confirm "this is the proxy we're actually using" in logs.
#explain()
Ask Proxyline what it would do for a given URL:
const decision = proxy.explain("https://api.example.com/v1/users", {
surface: "undici",
});
// decision.kind: "proxied" | "direct" | "blocked"
// decision.reason: "managed-proxy-active" | "ambient-proxy-active" | ...
// decision.surface: "undici"
// decision.url: "https://api.example.com/v1/users"
// decision.proxyUrl: "https://proxy.corp.example:8443/" (redacted)
explain() does not perform a request; it inspects the resolver and returns its verdict. It also emits a decision event, so a single callback can capture both implicit (real requests) and explicit (probe) routing decisions in your logs.
After stop(), explain() returns kind: "direct", reason: "runtime-stopped".
#Credential redaction
Anywhere Proxyline reports a proxy URL — in handle.proxyUrl, decision.proxyUrl, and the runtime.installed event — it passes the URL through redactProxyUrl. That strips userinfo, search, and fragment:
redactProxyUrl("https://user:secret@proxy.example:8443/path?token=abc#fragment");
// → "https://proxy.example:8443/path"
Use it on URLs you log yourself to keep credentials out of operational data.
#Logging recipes
#Per-request decision log
const proxy = installGlobalProxy({
mode: "managed",
proxyUrl: process.env.PROXY_URL!,
onEvent: (event) => {
if (event.type === "decision") {
logger.info({
url: event.decision.url,
proxyUrl: event.decision.proxyUrl,
kind: event.decision.kind,
reason: event.decision.reason,
surface: event.decision.surface,
}, "proxy decision");
}
},
});
explain() is decoupled from real requests, so wire it into the codepaths you actually care about — e.g. inside a fetch wrapper:
async function loggedFetch(url: string, init?: RequestInit) {
proxy.explain(url, { surface: "undici" });
return fetch(url, init);
}
#Install / stop audit
onEvent: (event) => {
if (event.type === "runtime.installed") {
logger.info({ mode: event.mode, active: event.active, proxyUrl: event.proxyUrl }, "proxyline up");
} else if (event.type === "runtime.stopped") {
logger.info({ mode: event.mode }, "proxyline down");
}
}