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
63 changes: 59 additions & 4 deletions packages/restate-sdk/src/endpoint/handlers/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
TerminalError,
} from "../../types/errors.js";
import type {
ProtocolMode,
Endpoint as EndpointManifest,
ProtocolMode,
} from "../discovery.js";
import type { Component, ComponentHandler } from "../components.js";
import { parseUrlComponents } from "../components.js";
Expand All @@ -37,6 +37,7 @@ import {
} from "../../logging/console_logger_transport.js";
import {
LoggerContext,
type LoggerTransport,
LogSource,
RestateLogLevel,
} from "../../logging/logger_transport.js";
Expand Down Expand Up @@ -83,6 +84,47 @@ const ENDPOINT_MANIFEST_V2 = "application/vnd.restate.endpointmanifest.v2+json";
const ENDPOINT_MANIFEST_V3 = "application/vnd.restate.endpointmanifest.v3+json";
const ENDPOINT_MANIFEST_V4 = "application/vnd.restate.endpointmanifest.v4+json";

export function tryCreateContextualLogger(
loggerTransport: LoggerTransport,
url: string,
headers: Headers,
additionalContext?: { [name: string]: string }
): Logger | undefined {
try {
const path = new URL(url, "https://example.com").pathname;
const parsed = parseUrlComponents(path);
if (parsed.type !== "invoke") {
return undefined;
}
const invocationId = invocationIdFromHeaders(headers);
return createLogger(
loggerTransport,
LogSource.SYSTEM,
new LoggerContext(
invocationId,
parsed.componentName,
parsed.handlerName,
undefined,
undefined,
additionalContext
)
);
} catch (e) {
return undefined;
}
}

function invocationIdFromHeaders(headers: Headers) {
const invocationIdHeader = headers["x-restate-invocation-id"];
const invocationId =
typeof invocationIdHeader === "string"
? invocationIdHeader
: Array.isArray(invocationIdHeader)
? invocationIdHeader[0] ?? "unknown id"
: "unknown id";
return invocationId;
}

/**
* This is an internal API to support 'fetch' like handlers.
* It supports both request-reply mode and bidirectional streaming mode.
Expand Down Expand Up @@ -130,8 +172,14 @@ export class GenericHandler implements RestateHandler {
return await this._handle(request, context);
} catch (e) {
const error = ensureError(e);
this.endpoint.rlog.error(
"Error while handling invocation: " + (error.stack ?? error.message)
(
tryCreateContextualLogger(
this.endpoint.loggerTransport,
request.url,
request.headers
) ?? this.endpoint.rlog
).error(
"Error while handling request: " + (error.stack ?? error.message)
);
return this.toErrorResponse(
error instanceof RestateError ? error.code : 500,
Expand Down Expand Up @@ -285,7 +333,14 @@ export class GenericHandler implements RestateHandler {
createLogger(
this.endpoint.loggerTransport,
LogSource.JOURNAL,
undefined
new LoggerContext(
invocationIdFromHeaders(headers),
service.name(),
handler.name(),
undefined,
undefined,
additionalContext
)
)
);

Expand Down
9 changes: 8 additions & 1 deletion packages/restate-sdk/src/endpoint/handlers/lambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
RestateRequest,
RestateResponse,
} from "./generic.js";
import { tryCreateContextualLogger } from "./generic.js";
import { WritableStream, type ReadableStream } from "node:stream/web";
import { OnceStream } from "../../utils/streams.js";
import { X_RESTATE_SERVER } from "../../user_agent.js";
Expand Down Expand Up @@ -131,7 +132,13 @@ export class LambdaHandler {
} catch (e) {
// unlike in the streaming case, we can actually catch errors in the response body and form a nicer error
const error = ensureError(e);
this.handler.endpoint.rlog.error(
(
tryCreateContextualLogger(
this.handler.endpoint.loggerTransport,
request.url,
request.headers
) ?? this.handler.endpoint.rlog
).error(
"Error while collecting invocation response: " +
(error.stack ?? error.message)
);
Expand Down
32 changes: 28 additions & 4 deletions packages/restate-sdk/src/endpoint/node_endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import type { Http2ServerRequest, Http2ServerResponse } from "http2";
import * as http2 from "http2";
import type { Endpoint } from "./endpoint.js";
import { EndpointBuilder } from "./endpoint.js";
import { GenericHandler } from "./handlers/generic.js";
import {
GenericHandler,
tryCreateContextualLogger,
} from "./handlers/generic.js";
import { Readable, Writable } from "node:stream";
import type { WritableStream } from "node:stream/web";
import { ensureError } from "../types/errors.js";
Expand Down Expand Up @@ -155,9 +158,30 @@ function nodeHttp2Handler(
await resp.body.pipeTo(responseWeb);
} catch (e) {
const error = ensureError(e);
endpoint.rlog.error(
"Error while handling connection: " + (error.stack ?? error.message)
);

const logger =
tryCreateContextualLogger(
endpoint.loggerTransport,
request.url,
request.headers
) ?? endpoint.rlog;
if (error.name === "AbortError") {
logger.error(
"Got abort error from connection: " +
error.message +
"\n" +
"This might indicate that:\n" +
"* The restate-server aborted the connection after hitting the 'abort-timeout'\n" +
"* The connection with the restate-server was lost\n" +
"\n" +
"Please check the invocation in the Restate UI for more details."
);
} else {
logger.error(
"Error while handling request: " + (error.stack ?? error.message)
);
}

response.destroy(error);
abortController.abort();
}
Expand Down
Loading