Skip to content

Commit

Permalink
chore: more elaborate response log when API request fails (#1000)
Browse files Browse the repository at this point in the history
  • Loading branch information
juliusmarminge authored Oct 8, 2024
1 parent ab572ac commit 8ae602d
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 78 deletions.
109 changes: 40 additions & 69 deletions packages/uploadthing/src/internal/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ import * as pkgJson from "../../package.json";
import { configProvider, IngestUrl, IsDevelopment, UTToken } from "./config";
import { formatError } from "./error-formatter";
import { handleJsonLineStream } from "./jsonl";
import { withLogFormat, withMinimalLogLevel } from "./logger";
import {
logHttpClientError,
logHttpClientResponse,
withLogFormat,
withMinimalLogLevel,
} from "./logger";
import { getParseFn } from "./parser";
import { assertFilesMeetConfig, extractRouterConfig } from "./route-config";
import {
Expand Down Expand Up @@ -379,12 +384,8 @@ const handleCallbackRequest = (opts: {
}),
HttpClientRequest.jsonBody(payload),
Effect.flatMap(HttpClient.filterStatusOk(httpClient)),
Effect.tapErrorTag("ResponseError", ({ response: res }) =>
Effect.flatMap(res.json, (json) =>
Effect.logError(
`Failed to register callback result (${res.status})`,
).pipe(Effect.annotateLogs("error", json)),
),
Effect.tapError(
logHttpClientError("Failed to register callback result"),
),
HttpClientResponse.schemaBodyJsonScoped(CallbackResultResponse),
Effect.tap(Effect.log("Sent callback result to UploadThing")),
Expand Down Expand Up @@ -612,25 +613,6 @@ const handleUploadAction = (opts: {
awaitServerData: routeOptions.awaitServerData ?? true,
}),
Effect.flatMap(HttpClient.filterStatusOk(httpClient)),
Effect.tapBoth({
onSuccess: (res) =>
Effect.logDebug("Registerred metadata").pipe(
Effect.annotateLogs("response", res),
),
onFailure: (err) =>
err._tag === "ResponseError"
? Effect.flatMap(err.response.json, (json) =>
Effect.logError(
`Failed to register metadata (${err.response.status})`,
).pipe(
Effect.annotateLogs("response", err.response),
Effect.annotateLogs("json", json),
),
)
: Effect.logError("Failed to register metadata").pipe(
Effect.annotateLogs("error", err),
),
}),
);

// Send metadata to UT server (non blocking as a daemon)
Expand All @@ -639,55 +621,44 @@ const handleUploadAction = (opts: {
const fiber = yield* Effect.if(isDev, {
onTrue: () =>
metadataRequest.pipe(
Effect.tapBoth({
onSuccess: logHttpClientResponse("Registered metadata", {
mixin: "None", // We're reading the stream so can't call a body mixin
}),
onFailure: logHttpClientError("Failed to register metadata"),
}),
HttpClientResponse.stream,
handleJsonLineStream(
MetadataFetchStreamPart,
({ payload, signature, hook }) =>
devHookRequest.pipe(
HttpClientRequest.setHeaders({
"uploadthing-hook": hook,
"x-uploadthing-signature": signature,
}),
HttpClientRequest.setBody(
HttpBody.text(payload, "application/json"),
),
httpClient,

HttpClientResponse.arrayBuffer,
Effect.asVoid,
Effect.tapBoth({
onSuccess: (res) =>
Effect.logDebug(
"Successfully forwarded callback request from dev stream",
).pipe(
Effect.annotateLogs("response", res),
Effect.annotateLogs("hook", hook),
Effect.annotateLogs("signature", signature),
Effect.annotateLogs("payload", payload),
),
onFailure: (err) =>
err._tag === "ResponseError"
? Effect.flatMap(err.response.json, (json) =>
Effect.logError(
`Failed to forward callback request from dev stream (${err.response.status})`,
).pipe(
Effect.annotateLogs("response", err.response),
Effect.annotateLogs("json", json),
Effect.annotateLogs("hook", hook),
Effect.annotateLogs("signature", signature),
Effect.annotateLogs("payload", payload),
),
)
: Effect.logError(
"Failed to forward callback request from dev stream",
).pipe(Effect.annotateLogs("error", err)),
}),
Effect.ignoreLogged,
handleJsonLineStream(MetadataFetchStreamPart, (chunk) =>
devHookRequest.pipe(
HttpClientRequest.setHeaders({
"uploadthing-hook": chunk.hook,
"x-uploadthing-signature": chunk.signature,
}),
HttpClientRequest.setBody(
HttpBody.text(chunk.payload, "application/json"),
),
HttpClient.filterStatusOk(httpClient),
Effect.tapBoth({
onSuccess: logHttpClientResponse(
"Successfully forwarded callback request from dev stream",
),
onFailure: logHttpClientError(
"Failed to forward callback request from dev stream",
),
}),
Effect.annotateLogs(chunk),
HttpClientResponse.json,
Effect.asVoid,
Effect.ignoreLogged,
),
),
),
onFalse: () =>
metadataRequest.pipe(
Effect.tapBoth({
onSuccess: logHttpClientResponse("Registered metadata"),
onFailure: logHttpClientError("Failed to register metadata"),
}),
HttpClientResponse.schemaBodyJsonScoped(MetadataFetchResponse),
),
}).pipe(Effect.forkDaemon);
Expand Down
34 changes: 34 additions & 0 deletions packages/uploadthing/src/internal/logger.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import type {
HttpBody,
HttpClientError,
HttpClientResponse,
} from "@effect/platform";
import * as Config from "effect/Config";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
Expand Down Expand Up @@ -51,3 +56,32 @@ export const withLogFormat = Effect.gen(function* () {
),
Layer.unwrapEffect,
);

type HttpClientResponseMixinMethod = "json" | "text" | "arrayBuffer" | "None";

export const logHttpClientResponse = (
message: string,
opts?: {
/** Level to log on, default "Debug" */
level?: LogLevel.Literal;
/** What body mixin to use to get the response body, default "json" */
mixin?: HttpClientResponseMixinMethod;
},
) => {
const mixin = opts?.mixin ?? "json";
const level = LogLevel.fromLiteral(opts?.level ?? "Debug");

return (response: HttpClientResponse.HttpClientResponse) =>
Effect.flatMap(mixin !== "None" ? response[mixin] : Effect.void, () =>
Effect.logWithLevel(level, `${message} (${response.status})`).pipe(
Effect.annotateLogs("response", response),
),
);
};

export const logHttpClientError =
(message: string) =>
(err: HttpClientError.HttpClientError | HttpBody.HttpBodyError) =>
err._tag === "ResponseError"
? logHttpClientResponse(message, { level: "Error" })(err.response)
: Effect.logError(message).pipe(Effect.annotateLogs("error", err));
17 changes: 8 additions & 9 deletions packages/uploadthing/src/sdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ import {
UPLOADTHING_VERSION,
UTToken,
} from "../internal/config";
import { withLogFormat, withMinimalLogLevel } from "../internal/logger";
import {
logHttpClientError,
logHttpClientResponse,
withLogFormat,
withMinimalLogLevel,
} from "../internal/logger";
import type {
ACLUpdateOptions,
DeleteFilesOptions,
Expand Down Expand Up @@ -78,14 +83,8 @@ export class UTApi {
}),
HttpClient.filterStatusOk(httpClient),
Effect.tapBoth({
onSuccess: (res) =>
Effect.logDebug(`UT Response`).pipe(
Effect.annotateLogs("res", res),
),
onFailure: (err) =>
Effect.logError("UploadThing error").pipe(
Effect.annotateLogs("error", err),
),
onSuccess: logHttpClientResponse("UploadThing API Response"),
onFailure: logHttpClientError("Failed to request UploadThing API"),
}),
HttpClientResponse.schemaBodyJsonScoped(responseSchema),
);
Expand Down

0 comments on commit 8ae602d

Please sign in to comment.