90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
|
|
import { typedjson } from "remix-typedjson";
|
|
import { z } from "zod";
|
|
import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstance.server";
|
|
import { requireUserId } from "~/services/session.server";
|
|
import { LogDetailPresenter } from "~/presenters/v3/LogDetailPresenter.server";
|
|
import { findProjectBySlug } from "~/models/project.server";
|
|
import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
|
|
import { $replica } from "~/db.server";
|
|
import { runStore } from "~/v3/runStore.server";
|
|
import { ServiceValidationError } from "~/v3/services/baseService.server";
|
|
import type { TaskRunStatus } from "@trigger.dev/database";
|
|
|
|
const LogIdParamsSchema = z.object({
|
|
organizationSlug: z.string(),
|
|
projectParam: z.string(),
|
|
envParam: z.string(),
|
|
logId: z.string(),
|
|
});
|
|
|
|
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
|
const userId = await requireUserId(request);
|
|
const { organizationSlug, projectParam, envParam, logId } = LogIdParamsSchema.parse(params);
|
|
|
|
// Validate access to project and environment
|
|
const project = await findProjectBySlug(organizationSlug, projectParam, userId);
|
|
if (!project) {
|
|
throw new Response("Project not found", { status: 404 });
|
|
}
|
|
|
|
const environment = await findEnvironmentBySlug(project.id, envParam, userId);
|
|
if (!environment) {
|
|
throw new Response("Environment not found", { status: 404 });
|
|
}
|
|
|
|
// Parse logId to extract traceId, spanId, runId, and startTime
|
|
// Format: {traceId}::{spanId}::{runId}::{startTime}
|
|
// All 4 parts are needed to uniquely identify a log entry (multiple events can share the same spanId)
|
|
const decodedLogId = decodeURIComponent(logId);
|
|
const parts = decodedLogId.split("::");
|
|
if (parts.length !== 4) {
|
|
throw new Response("Invalid log ID format", { status: 400 });
|
|
}
|
|
|
|
const [traceId, spanId, , startTime] = parts;
|
|
|
|
const logsClickhouse = await clickhouseFactory.getClickhouseForOrganization(
|
|
project.organizationId,
|
|
"logs"
|
|
);
|
|
const presenter = new LogDetailPresenter($replica, logsClickhouse);
|
|
|
|
let result;
|
|
try {
|
|
result = await presenter.call({
|
|
environmentId: environment.id,
|
|
organizationId: project.organizationId,
|
|
projectId: project.id,
|
|
spanId,
|
|
traceId,
|
|
startTime,
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof ServiceValidationError) {
|
|
throw new Response(error.message, { status: 400 });
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
if (!result) {
|
|
throw new Response("Log not found", { status: 404 });
|
|
}
|
|
|
|
// Look up the run status from Postgres
|
|
let runStatus: TaskRunStatus | undefined;
|
|
if (result.runId) {
|
|
const run = await runStore.findRun(
|
|
{
|
|
friendlyId: result.runId,
|
|
runtimeEnvironmentId: environment.id,
|
|
},
|
|
{ select: { status: true } },
|
|
$replica
|
|
);
|
|
runStatus = run?.status;
|
|
}
|
|
|
|
return typedjson({ ...result, runStatus });
|
|
};
|