enable logs; add sentry logging to block runs (debugger) (#3205)
This commit is contained in:
@@ -5,30 +5,37 @@ import { QueryClientProvider } from "@tanstack/react-query";
|
|||||||
import { queryClient } from "./api/QueryClient";
|
import { queryClient } from "./api/QueryClient";
|
||||||
|
|
||||||
import { PostHogProvider } from "posthog-js/react";
|
import { PostHogProvider } from "posthog-js/react";
|
||||||
|
import { LoggingContext, loggingStub } from "@/store/LoggingContext";
|
||||||
import { UserContext } from "@/store/UserContext";
|
import { UserContext } from "@/store/UserContext";
|
||||||
|
|
||||||
const postHogOptions = {
|
const postHogOptions = {
|
||||||
api_host: "https://app.posthog.com",
|
api_host: "https://app.posthog.com",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getLogging = () => {
|
||||||
|
return loggingStub;
|
||||||
|
};
|
||||||
|
|
||||||
const getUser = () => {
|
const getUser = () => {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<UserContext.Provider value={getUser}>
|
<LoggingContext.Provider value={getLogging}>
|
||||||
<PostHogProvider
|
<UserContext.Provider value={getUser}>
|
||||||
apiKey="phc_bVT2ugnZhMHRWqMvSRHPdeTjaPxQqT3QSsI3r5FlQR5"
|
<PostHogProvider
|
||||||
options={postHogOptions}
|
apiKey="phc_bVT2ugnZhMHRWqMvSRHPdeTjaPxQqT3QSsI3r5FlQR5"
|
||||||
>
|
options={postHogOptions}
|
||||||
<QueryClientProvider client={queryClient}>
|
>
|
||||||
<ThemeProvider defaultTheme="dark">
|
<QueryClientProvider client={queryClient}>
|
||||||
<RouterProvider router={router} />
|
<ThemeProvider defaultTheme="dark">
|
||||||
</ThemeProvider>
|
<RouterProvider router={router} />
|
||||||
</QueryClientProvider>
|
</ThemeProvider>
|
||||||
</PostHogProvider>
|
</QueryClientProvider>
|
||||||
</UserContext.Provider>
|
</PostHogProvider>
|
||||||
|
</UserContext.Provider>
|
||||||
|
</LoggingContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
skyvern-frontend/src/hooks/useLogging.ts
Normal file
9
skyvern-frontend/src/hooks/useLogging.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { LoggingContext } from "@/store/LoggingContext";
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
|
function useLogging() {
|
||||||
|
const getLogging = useContext(LoggingContext);
|
||||||
|
return getLogging();
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useLogging };
|
||||||
@@ -6,13 +6,13 @@ function DebuggerRun() {
|
|||||||
|
|
||||||
const workflowFailureReason = workflowRun?.failure_reason ? (
|
const workflowFailureReason = workflowRun?.failure_reason ? (
|
||||||
<div
|
<div
|
||||||
className="align-self-start max-h-[8rem] w-full overflow-y-auto rounded-md border border-red-600 p-4"
|
className="align-self-start h-[8rem] w-full overflow-y-auto rounded-md border border-red-600 p-4"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "rgba(220, 38, 38, 0.10)",
|
backgroundColor: "rgba(220, 38, 38, 0.10)",
|
||||||
width: "calc(100% - 2rem)",
|
width: "calc(100% - 2rem)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="font-bold">Workflow Failure Reason</div>
|
<div className="font-bold">Run Failure Reason</div>
|
||||||
<div className="text-sm">{workflowRun.failure_reason}</div>
|
<div className="text-sm">{workflowRun.failure_reason}</div>
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { getClient } from "@/api/AxiosClient";
|
|||||||
import { ProxyLocation } from "@/api/types";
|
import { ProxyLocation } from "@/api/types";
|
||||||
import { Timer } from "@/components/Timer";
|
import { Timer } from "@/components/Timer";
|
||||||
import { toast } from "@/components/ui/use-toast";
|
import { toast } from "@/components/ui/use-toast";
|
||||||
|
import { useLogging } from "@/hooks/useLogging";
|
||||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||||
|
|
||||||
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
||||||
@@ -131,6 +132,7 @@ function NodeHeader({
|
|||||||
totpUrl,
|
totpUrl,
|
||||||
type,
|
type,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const log = useLogging();
|
||||||
const {
|
const {
|
||||||
blockLabel: urlBlockLabel,
|
blockLabel: urlBlockLabel,
|
||||||
workflowPermanentId,
|
workflowPermanentId,
|
||||||
@@ -214,7 +216,7 @@ function NodeHeader({
|
|||||||
await saveWorkflow.mutateAsync();
|
await saveWorkflow.mutateAsync();
|
||||||
|
|
||||||
if (!workflowPermanentId) {
|
if (!workflowPermanentId) {
|
||||||
console.error("There is no workflowPermanentId");
|
log.error("Run block: there is no workflowPermanentId");
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Failed to start workflow block run",
|
title: "Failed to start workflow block run",
|
||||||
@@ -224,7 +226,7 @@ function NodeHeader({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!debugSession) {
|
if (!debugSession) {
|
||||||
console.error("There is no debug session, yet");
|
log.error("Run block: there is no debug session, yet");
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Failed to start workflow block run",
|
title: "Failed to start workflow block run",
|
||||||
@@ -263,6 +265,12 @@ function NodeHeader({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!body) {
|
if (!body) {
|
||||||
|
log.error("Run block: could not construct run payload", {
|
||||||
|
workflowPermanentId,
|
||||||
|
blockLabel,
|
||||||
|
debugSessionId: debugSession.debug_session_id,
|
||||||
|
browserSessionId: debugSession.browser_session_id,
|
||||||
|
});
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Failed to start workflow block run",
|
title: "Failed to start workflow block run",
|
||||||
@@ -271,6 +279,13 @@ function NodeHeader({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("Run block: sending run payload", {
|
||||||
|
workflowPermanentId,
|
||||||
|
blockLabel,
|
||||||
|
debugSessionId: debugSession.debug_session_id,
|
||||||
|
browserSessionId: debugSession.browser_session_id,
|
||||||
|
});
|
||||||
|
|
||||||
return await client.post<Payload, { data: { run_id: string } }>(
|
return await client.post<Payload, { data: { run_id: string } }>(
|
||||||
"/run/workflows/blocks",
|
"/run/workflows/blocks",
|
||||||
body,
|
body,
|
||||||
@@ -278,7 +293,12 @@ function NodeHeader({
|
|||||||
},
|
},
|
||||||
onSuccess: (response) => {
|
onSuccess: (response) => {
|
||||||
if (!response) {
|
if (!response) {
|
||||||
console.error("No response");
|
log.error("Run block: no response", {
|
||||||
|
workflowPermanentId,
|
||||||
|
blockLabel,
|
||||||
|
debugSessionId: debugSession?.debug_session_id,
|
||||||
|
browserSessionId: debugSession?.browser_session_id,
|
||||||
|
});
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Failed to start workflow block run",
|
title: "Failed to start workflow block run",
|
||||||
@@ -287,6 +307,14 @@ function NodeHeader({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("Run block: run started", {
|
||||||
|
workflowPermanentId,
|
||||||
|
blockLabel,
|
||||||
|
debugSessionId: debugSession?.debug_session_id,
|
||||||
|
browserSessionId: debugSession?.browser_session_id,
|
||||||
|
runId: response.data.run_id,
|
||||||
|
});
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: "Workflow block run started",
|
title: "Workflow block run started",
|
||||||
@@ -299,6 +327,13 @@ function NodeHeader({
|
|||||||
},
|
},
|
||||||
onError: (error: AxiosError) => {
|
onError: (error: AxiosError) => {
|
||||||
const detail = (error.response?.data as { detail?: string })?.detail;
|
const detail = (error.response?.data as { detail?: string })?.detail;
|
||||||
|
log.error("Run block: error", {
|
||||||
|
workflowPermanentId,
|
||||||
|
blockLabel,
|
||||||
|
debugSessionId: debugSession?.debug_session_id,
|
||||||
|
browserSessionId: debugSession?.browser_session_id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Failed to start workflow block run",
|
title: "Failed to start workflow block run",
|
||||||
@@ -310,7 +345,10 @@ function NodeHeader({
|
|||||||
const cancelBlock = useMutation({
|
const cancelBlock = useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
if (!debugSession) {
|
if (!debugSession) {
|
||||||
console.error("Missing debug session");
|
log.error("Cancel block: missing debug session", {
|
||||||
|
workflowPermanentId,
|
||||||
|
blockLabel,
|
||||||
|
});
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Failed to cancel workflow block run",
|
title: "Failed to cancel workflow block run",
|
||||||
@@ -326,6 +364,12 @@ function NodeHeader({
|
|||||||
.then((response) => response.data);
|
.then((response) => response.data);
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
log.info("Cancel block: canceled", {
|
||||||
|
workflowPermanentId,
|
||||||
|
blockLabel,
|
||||||
|
debugSessionId: debugSession?.debug_session_id,
|
||||||
|
browserSessionId: debugSession?.browser_session_id,
|
||||||
|
});
|
||||||
toast({
|
toast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: "Workflow Canceled",
|
title: "Workflow Canceled",
|
||||||
@@ -333,6 +377,13 @@ function NodeHeader({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
log.error("Cancel block: error", {
|
||||||
|
workflowPermanentId,
|
||||||
|
blockLabel,
|
||||||
|
debugSessionId: debugSession?.debug_session_id,
|
||||||
|
browserSessionId: debugSession?.browser_session_id,
|
||||||
|
error,
|
||||||
|
});
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Error",
|
title: "Error",
|
||||||
|
|||||||
26
skyvern-frontend/src/store/LoggingContext.ts
Normal file
26
skyvern-frontend/src/store/LoggingContext.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
type LogFn = (message: string, data?: Record<string, unknown>) => void;
|
||||||
|
|
||||||
|
interface Logging {
|
||||||
|
info: LogFn;
|
||||||
|
warn: LogFn;
|
||||||
|
error: LogFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make this a stub of LogFn that does nothing
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const noop: LogFn = (..._: Parameters<LogFn>) => {};
|
||||||
|
|
||||||
|
const stub: Logging = {
|
||||||
|
info: noop,
|
||||||
|
warn: noop,
|
||||||
|
error: noop,
|
||||||
|
};
|
||||||
|
|
||||||
|
type GetLogging = () => Logging;
|
||||||
|
|
||||||
|
const LoggingContext = createContext<GetLogging>(() => stub);
|
||||||
|
|
||||||
|
export { LoggingContext, stub as loggingStub };
|
||||||
Reference in New Issue
Block a user