c8686b5f1c
Closes #<issue> ## ✅ Checklist - [x] I have followed every step in the [contributing guide](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md) - [x] The PR title follows the convention. - [x] I ran and tested the code works --- ## Testing - Verified log detail view displays correctly with message, metadata, and attributes - Tested search highlighting functionality in log messages (escapes special regex characters) - Confirmed tabs (Details/Run) switch properly with keyboard shortcuts (d/r) - Verified run information loads via async fetcher in Run tab - Tested close button and Escape key for dismissing the panel - Verified log details display correct information: level badges, kind badges, timestamps, trace IDs, span IDs - Confirmed links to parent spans and run pages work correctly - Tested with various log levels (ERROR, WARN, INFO, DEBUG, TRACE) and kinds (SPAN, SPAN_EVENT, LOG_*) - Verified admin-only fields display correctly when user has admin access - Tested data loading states and error states (log not found, run not found) --- ## Changelog Created new Logs page. The information shown is gathered from the spans from each run. The feature supports all run filters with two new filters for level and logs text search. --- ## Screenshots <img width="2059" height="1196" alt="Logs page preview" src="https://github.com/user-attachments/assets/70b667b4-98cc-4728-855a-2766dd5c1aa5" /> 💯 --------- Co-authored-by: James Ritchie <james@trigger.dev>
552 lines
15 KiB
TypeScript
552 lines
15 KiB
TypeScript
import type { RuntimeEnvironment, TaskRun, WorkerDeployment } from "@trigger.dev/database";
|
|
import { z } from "zod";
|
|
import { type TaskRunListSearchFilters } from "~/components/runs/v3/RunFilters";
|
|
import type { Organization } from "~/models/organization.server";
|
|
import type { Project } from "~/models/project.server";
|
|
import { objectToSearchParams } from "./searchParams";
|
|
import { type WaitpointSearchParams } from "~/components/runs/v3/WaitpointTokenFilters";
|
|
export type OrgForPath = Pick<Organization, "slug">;
|
|
export type ProjectForPath = Pick<Project, "slug">;
|
|
export type EnvironmentForPath = Pick<RuntimeEnvironment, "slug">;
|
|
export type v3RunForPath = Pick<TaskRun, "friendlyId">;
|
|
export type v3SpanForPath = Pick<TaskRun, "spanId">;
|
|
export type DeploymentForPath = Pick<WorkerDeployment, "shortCode">;
|
|
export type TaskForPath = {
|
|
taskIdentifier: string;
|
|
};
|
|
|
|
export const OrganizationParamsSchema = z.object({
|
|
organizationSlug: z.string(),
|
|
});
|
|
|
|
export const ProjectParamSchema = OrganizationParamsSchema.extend({
|
|
projectParam: z.string(),
|
|
});
|
|
|
|
export const EnvironmentParamSchema = ProjectParamSchema.extend({
|
|
envParam: z.string(),
|
|
});
|
|
|
|
//v3
|
|
export const v3TaskParamsSchema = EnvironmentParamSchema.extend({
|
|
taskParam: z.string(),
|
|
});
|
|
|
|
export const v3RunParamsSchema = EnvironmentParamSchema.extend({
|
|
runParam: z.string(),
|
|
});
|
|
|
|
export const v3SpanParamsSchema = v3RunParamsSchema.extend({
|
|
spanParam: z.string(),
|
|
});
|
|
|
|
export const v3RunStreamParamsSchema = v3RunParamsSchema.extend({
|
|
streamKey: z.string(),
|
|
});
|
|
|
|
export const v3DeploymentParams = EnvironmentParamSchema.extend({
|
|
deploymentParam: z.string(),
|
|
});
|
|
|
|
export const v3ScheduleParams = EnvironmentParamSchema.extend({
|
|
scheduleParam: z.string(),
|
|
});
|
|
|
|
export function rootPath() {
|
|
return `/`;
|
|
}
|
|
|
|
export function accountPath() {
|
|
return `/account`;
|
|
}
|
|
|
|
export function personalAccessTokensPath() {
|
|
return `/account/tokens`;
|
|
}
|
|
|
|
export function accountSecurityPath() {
|
|
return `/account/security`;
|
|
}
|
|
|
|
export function invitesPath() {
|
|
return `/invites`;
|
|
}
|
|
|
|
export function confirmBasicDetailsPath() {
|
|
return `/confirm-basic-details`;
|
|
}
|
|
|
|
export function acceptInvitePath(token: string) {
|
|
return `/invite-accept?token=${token}`;
|
|
}
|
|
|
|
export function resendInvitePath() {
|
|
return `/invite-resend`;
|
|
}
|
|
|
|
export function logoutPath() {
|
|
return `/logout`;
|
|
}
|
|
|
|
export function revokeInvitePath() {
|
|
return `/invite-revoke`;
|
|
}
|
|
|
|
// Org
|
|
export function organizationPath(organization: OrgForPath) {
|
|
return `/orgs/${organizationParam(organization)}`;
|
|
}
|
|
|
|
export function newOrganizationPath() {
|
|
return `/orgs/new`;
|
|
}
|
|
|
|
export function selectPlanPath(organization: OrgForPath) {
|
|
return `${organizationPath(organization)}/select-plan`;
|
|
}
|
|
|
|
export function organizationTeamPath(organization: OrgForPath) {
|
|
return `${organizationPath(organization)}/settings/team`;
|
|
}
|
|
|
|
export function inviteTeamMemberPath(organization: OrgForPath) {
|
|
return `${organizationPath(organization)}/invite`;
|
|
}
|
|
|
|
export function organizationBillingPath(organization: OrgForPath) {
|
|
return `${organizationPath(organization)}/billing`;
|
|
}
|
|
|
|
export function organizationSettingsPath(organization: OrgForPath) {
|
|
return `${organizationPath(organization)}/settings`;
|
|
}
|
|
|
|
function organizationParam(organization: OrgForPath) {
|
|
return organization.slug;
|
|
}
|
|
|
|
// Project
|
|
export function newProjectPath(organization: OrgForPath, message?: string) {
|
|
return `${organizationPath(organization)}/projects/new${
|
|
message ? `?message=${encodeURIComponent(message)}` : ""
|
|
}`;
|
|
}
|
|
|
|
function projectParam(project: ProjectForPath) {
|
|
return project.slug;
|
|
}
|
|
|
|
function environmentParam(environment: EnvironmentForPath) {
|
|
return environment.slug;
|
|
}
|
|
|
|
//v3 project
|
|
export function v3ProjectPath(organization: OrgForPath, project: ProjectForPath) {
|
|
return `/orgs/${organizationParam(organization)}/projects/${projectParam(project)}`;
|
|
}
|
|
|
|
export function githubAppInstallPath(organizationSlug: string, redirectTo: string) {
|
|
return `/github/install?org_slug=${organizationSlug}&redirect_to=${encodeURIComponent(
|
|
redirectTo
|
|
)}`;
|
|
}
|
|
|
|
export function v3EnvironmentPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `/orgs/${organizationParam(organization)}/projects/${projectParam(
|
|
project
|
|
)}/env/${environmentParam(environment)}`;
|
|
}
|
|
|
|
export function v3TasksStreamingPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/tasks/stream`;
|
|
}
|
|
|
|
export function v3ApiKeysPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/apikeys`;
|
|
}
|
|
|
|
export function v3BulkActionsPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/bulk-actions`;
|
|
}
|
|
|
|
export function v3BulkActionPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
bulkAction: { friendlyId: string }
|
|
) {
|
|
return `${v3BulkActionsPath(organization, project, environment)}/${bulkAction.friendlyId}`;
|
|
}
|
|
|
|
export function v3EnvironmentVariablesPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/environment-variables`;
|
|
}
|
|
|
|
export function v3NewEnvironmentVariablesPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentVariablesPath(organization, project, environment)}/new`;
|
|
}
|
|
|
|
export function v3ProjectAlertsPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/alerts`;
|
|
}
|
|
|
|
export function v3NewProjectAlertPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3ProjectAlertsPath(organization, project, environment)}/new`;
|
|
}
|
|
|
|
export function v3NewProjectAlertPathConnectToSlackPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3ProjectAlertsPath(organization, project, environment)}/new/connect-to-slack`;
|
|
}
|
|
|
|
export function v3TestPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/test`;
|
|
}
|
|
|
|
export function queryPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/query`;
|
|
}
|
|
|
|
export function v3TestTaskPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
task: TaskForPath
|
|
) {
|
|
return `${v3TestPath(organization, project, environment)}/tasks/${encodeURIComponent(
|
|
task.taskIdentifier
|
|
)}`;
|
|
}
|
|
|
|
export function v3RunsPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
filters?: TaskRunListSearchFilters
|
|
) {
|
|
const searchParams = objectToSearchParams(filters);
|
|
const query = searchParams ? `?${searchParams.toString()}` : "";
|
|
return `${v3EnvironmentPath(organization, project, environment)}/runs${query}`;
|
|
}
|
|
|
|
export function v3CreateBulkActionPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
filters?: TaskRunListSearchFilters,
|
|
mode?: "selected" | "filters",
|
|
action?: "replay" | "cancel"
|
|
) {
|
|
const searchParams = objectToSearchParams(filters) ?? new URLSearchParams();
|
|
searchParams.set("bulkInspector", "show");
|
|
if (mode) {
|
|
searchParams.set("mode", mode);
|
|
}
|
|
if (action) {
|
|
searchParams.set("action", action);
|
|
}
|
|
const query = `?${searchParams.toString()}`;
|
|
return `${v3RunsPath(organization, project, environment)}${query}`;
|
|
}
|
|
|
|
export function v3RunPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
run: v3RunForPath,
|
|
searchParams?: URLSearchParams
|
|
) {
|
|
const query = searchParams ? `?${searchParams.toString()}` : "";
|
|
return `${v3RunsPath(organization, project, environment)}/${run.friendlyId}${query}`;
|
|
}
|
|
|
|
export function v3RunRedirectPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
run: v3RunForPath
|
|
) {
|
|
return `${v3ProjectPath(organization, project)}/runs/${run.friendlyId}`;
|
|
}
|
|
|
|
export function v3RunPathFromFriendlyId(runId: string) {
|
|
return `/runs/${runId}`;
|
|
}
|
|
|
|
export function v3RunDownloadLogsPath(run: v3RunForPath) {
|
|
return `/resources/runs/${run.friendlyId}/logs/download`;
|
|
}
|
|
|
|
export function v3RunSpanPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
run: v3RunForPath,
|
|
span: v3SpanForPath,
|
|
searchParams?: URLSearchParams
|
|
) {
|
|
searchParams = searchParams ?? new URLSearchParams();
|
|
searchParams.set("span", span.spanId);
|
|
return `${v3RunPath(organization, project, environment, run, searchParams)}`;
|
|
}
|
|
|
|
export function v3RunStreamingPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
run: v3RunForPath
|
|
) {
|
|
return `${v3RunPath(organization, project, environment, run)}/stream`;
|
|
}
|
|
|
|
export function v3RunIdempotencyKeyResetPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
run: v3RunForPath
|
|
) {
|
|
return `/resources/orgs/${organizationParam(organization)}/projects/${projectParam(
|
|
project
|
|
)}/env/${environmentParam(environment)}/runs/${run.friendlyId}/idempotencyKey/reset`;
|
|
}
|
|
|
|
export function v3SchedulesPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/schedules`;
|
|
}
|
|
|
|
export function v3SchedulePath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
schedule: { friendlyId: string }
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/schedules/${
|
|
schedule.friendlyId
|
|
}`;
|
|
}
|
|
|
|
export function v3EditSchedulePath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
schedule: { friendlyId: string }
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/schedules/edit/${
|
|
schedule.friendlyId
|
|
}`;
|
|
}
|
|
|
|
export function v3NewSchedulePath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/schedules/new`;
|
|
}
|
|
|
|
export function v3QueuesPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/queues`;
|
|
}
|
|
|
|
export function v3WaitpointTokensPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
filters?: WaitpointSearchParams
|
|
) {
|
|
const searchParams = objectToSearchParams(filters);
|
|
const query = searchParams ? `?${searchParams.toString()}` : "";
|
|
return `${v3EnvironmentPath(organization, project, environment)}/waitpoints/tokens${query}`;
|
|
}
|
|
|
|
export function v3WaitpointTokenPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
token: { id: string },
|
|
filters?: WaitpointSearchParams
|
|
) {
|
|
const searchParams = objectToSearchParams(filters);
|
|
const query = searchParams ? `?${searchParams.toString()}` : "";
|
|
return `${v3WaitpointTokensPath(organization, project, environment)}/${token.id}${query}`;
|
|
}
|
|
|
|
export function v3BatchesPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/batches`;
|
|
}
|
|
|
|
export function v3BatchPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
batch: { friendlyId: string }
|
|
) {
|
|
return `${v3BatchesPath(organization, project, environment)}/${batch.friendlyId}`;
|
|
}
|
|
|
|
export function v3BatchRunsPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
batch: { friendlyId: string }
|
|
) {
|
|
return `${v3RunsPath(organization, project, environment, { batchId: batch.friendlyId })}`;
|
|
}
|
|
|
|
export function v3ProjectSettingsPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/settings`;
|
|
}
|
|
|
|
export function v3LogsPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/logs`;
|
|
}
|
|
|
|
export function v3DeploymentsPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/deployments`;
|
|
}
|
|
|
|
export function v3DeploymentPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
deployment: DeploymentForPath,
|
|
currentPage: number
|
|
) {
|
|
const query = currentPage ? `?page=${currentPage}` : "";
|
|
return `${v3DeploymentsPath(organization, project, environment)}/${deployment.shortCode}${query}`;
|
|
}
|
|
|
|
export function v3DeploymentVersionPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath,
|
|
version: string
|
|
) {
|
|
return `${v3DeploymentsPath(organization, project, environment)}?version=${version}`;
|
|
}
|
|
|
|
export function branchesPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/branches`;
|
|
}
|
|
|
|
export function concurrencyPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/concurrency`;
|
|
}
|
|
|
|
export function regionsPath(
|
|
organization: OrgForPath,
|
|
project: ProjectForPath,
|
|
environment: EnvironmentForPath
|
|
) {
|
|
return `${v3EnvironmentPath(organization, project, environment)}/regions`;
|
|
}
|
|
|
|
export function v3BillingPath(organization: OrgForPath, message?: string) {
|
|
return `${organizationPath(organization)}/settings/billing${
|
|
message ? `?message=${encodeURIComponent(message)}` : ""
|
|
}`;
|
|
}
|
|
|
|
export function v3BillingAlertsPath(organization: OrgForPath) {
|
|
return `${organizationPath(organization)}/settings/billing-alerts`;
|
|
}
|
|
|
|
export function v3StripePortalPath(organization: OrgForPath) {
|
|
return `/resources/${organization.slug}/subscription/portal`;
|
|
}
|
|
|
|
export function v3UsagePath(organization: OrgForPath) {
|
|
return `${organizationPath(organization)}/settings/usage`;
|
|
}
|
|
|
|
// Docs
|
|
export function docsRoot() {
|
|
return "https://trigger.dev/docs";
|
|
}
|
|
|
|
export function docsPath(path: string) {
|
|
return `${docsRoot()}/${path}`;
|
|
}
|
|
|
|
export function docsTroubleshootingPath(path: string) {
|
|
return `${docsRoot()}/v3/troubleshooting`;
|
|
}
|
|
|
|
export function adminPath() {
|
|
return `/@`;
|
|
}
|