mirror of https://github.com/kortix-ai/suna.git
feat(sentry): enhance Sentry integration with user tracking and error handling improvements
This commit is contained in:
parent
eac741e867
commit
03c95fa346
|
@ -1,6 +1,7 @@
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
import sentry
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from agentpress.thread_manager import ThreadManager
|
from agentpress.thread_manager import ThreadManager
|
||||||
from services.supabase import DBConnection
|
from services.supabase import DBConnection
|
||||||
|
@ -12,18 +13,6 @@ from utils.logger import logger
|
||||||
import uuid
|
import uuid
|
||||||
import time
|
import time
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import sentry_sdk
|
|
||||||
import os
|
|
||||||
|
|
||||||
sentry_dsn = os.getenv("SENTRY_DSN", None)
|
|
||||||
if sentry_dsn:
|
|
||||||
sentry_sdk.init(
|
|
||||||
dsn=sentry_dsn,
|
|
||||||
send_default_pii=True,
|
|
||||||
_experiments={
|
|
||||||
"enable_logs": True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Import the agent API module
|
# Import the agent API module
|
||||||
from agent import api as agent_api
|
from agent import api as agent_api
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import sentry
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -56,6 +57,8 @@ async def run_agent_background(
|
||||||
"""Run the agent in the background using Redis for state."""
|
"""Run the agent in the background using Redis for state."""
|
||||||
await initialize()
|
await initialize()
|
||||||
|
|
||||||
|
sentry.sentry.set_tag("thread_id", thread_id)
|
||||||
|
|
||||||
logger.info(f"Starting background agent run: {agent_run_id} for thread: {thread_id} (Instance: {instance_id})")
|
logger.info(f"Starting background agent run: {agent_run_id} for thread: {thread_id} (Instance: {instance_id})")
|
||||||
logger.info(f"🚀 Using model: {model_name} (thinking: {enable_thinking}, reasoning_effort: {reasoning_effort})")
|
logger.info(f"🚀 Using model: {model_name} (thinking: {enable_thinking}, reasoning_effort: {reasoning_effort})")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import sentry_sdk
|
||||||
|
from sentry_sdk.integrations.dramatiq import DramatiqIntegration
|
||||||
|
import os
|
||||||
|
|
||||||
|
sentry_dsn = os.getenv("SENTRY_DSN", None)
|
||||||
|
if sentry_dsn:
|
||||||
|
sentry_sdk.init(
|
||||||
|
dsn=sentry_dsn,
|
||||||
|
integrations=[DramatiqIntegration()],
|
||||||
|
traces_sample_rate=0.1,
|
||||||
|
send_default_pii=True,
|
||||||
|
_experiments={
|
||||||
|
"enable_logs": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
sentry = sentry_sdk
|
|
@ -1,3 +1,4 @@
|
||||||
|
import sentry
|
||||||
from fastapi import HTTPException, Request
|
from fastapi import HTTPException, Request
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import jwt
|
import jwt
|
||||||
|
@ -46,6 +47,7 @@ async def get_current_user_id_from_jwt(request: Request) -> str:
|
||||||
headers={"WWW-Authenticate": "Bearer"}
|
headers={"WWW-Authenticate": "Bearer"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sentry.sentry.set_user({ "id": user_id })
|
||||||
return user_id
|
return user_id
|
||||||
|
|
||||||
except PyJWTError:
|
except PyJWTError:
|
||||||
|
@ -119,6 +121,7 @@ async def get_user_id_from_stream_auth(
|
||||||
# For Supabase JWT, we just need to decode and extract the user ID
|
# For Supabase JWT, we just need to decode and extract the user ID
|
||||||
payload = jwt.decode(token, options={"verify_signature": False})
|
payload = jwt.decode(token, options={"verify_signature": False})
|
||||||
user_id = payload.get('sub')
|
user_id = payload.get('sub')
|
||||||
|
sentry.sentry.set_user({ "id": user_id })
|
||||||
if user_id:
|
if user_id:
|
||||||
return user_id
|
return user_id
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
@ -24,6 +24,8 @@ RUN npm install
|
||||||
# Copy the frontend code
|
# Copy the frontend code
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
ENV NEXT_PUBLIC_VERCEL_ENV production
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { checkApiHealth } from '@/lib/api';
|
||||||
import { MaintenancePage } from '@/components/maintenance/maintenance-page';
|
import { MaintenancePage } from '@/components/maintenance/maintenance-page';
|
||||||
import { DeleteOperationProvider } from '@/contexts/DeleteOperationContext';
|
import { DeleteOperationProvider } from '@/contexts/DeleteOperationContext';
|
||||||
import { StatusOverlay } from '@/components/ui/status-overlay';
|
import { StatusOverlay } from '@/components/ui/status-overlay';
|
||||||
|
import { VSentry } from '@/components/sentry';
|
||||||
|
|
||||||
interface DashboardLayoutProps {
|
interface DashboardLayoutProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
@ -99,6 +100,7 @@ export default function DashboardLayout({ children }: DashboardLayoutProps) {
|
||||||
onOpenChange={setShowMaintenanceAlert}
|
onOpenChange={setShowMaintenanceAlert}
|
||||||
closeable={true}
|
closeable={true}
|
||||||
/>
|
/>
|
||||||
|
<VSentry />
|
||||||
|
|
||||||
{/* Status overlay for deletion operations */}
|
{/* Status overlay for deletion operations */}
|
||||||
<StatusOverlay />
|
<StatusOverlay />
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
const SENTRY_URL = new URL(
|
||||||
|
process.env.NEXT_PUBLIC_SENTRY_DSN ?? 'https://example.com/abc',
|
||||||
|
);
|
||||||
|
const SENTRY_HOST = SENTRY_URL.hostname;
|
||||||
|
const SENTRY_PROJECT_ID = SENTRY_URL.pathname.split('/').pop();
|
||||||
|
|
||||||
|
export const POST = async (req: Request) => {
|
||||||
|
try {
|
||||||
|
if (!process.env.NEXT_PUBLIC_SENTRY_DSN) {
|
||||||
|
return Response.json(
|
||||||
|
{ error: 'Sentry is not configured' },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const envelopeBytes = await req.arrayBuffer();
|
||||||
|
const envelope = new TextDecoder().decode(envelopeBytes);
|
||||||
|
const piece = envelope.split('\n')[0];
|
||||||
|
const header = JSON.parse(piece) as { dsn: string };
|
||||||
|
const dsn = new URL(header.dsn);
|
||||||
|
const project_id = dsn.pathname.replace('/', '');
|
||||||
|
|
||||||
|
if (dsn.hostname !== SENTRY_HOST) {
|
||||||
|
throw new Error(`Invalid sentry hostname: ${dsn.hostname}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (project_id !== SENTRY_PROJECT_ID) {
|
||||||
|
throw new Error(`Invalid sentry project id: ${project_id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const upstream_sentry_url = `https://${SENTRY_HOST}/api/${project_id}/envelope/`;
|
||||||
|
const response = await fetch(upstream_sentry_url, {
|
||||||
|
body: envelopeBytes,
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error tunneling to sentry', e);
|
||||||
|
return Response.json(
|
||||||
|
{ error: 'error tunneling to sentry' },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,17 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import * as Sentry from '@sentry/nextjs';
|
||||||
|
import { useAuth } from '../AuthProvider';
|
||||||
|
|
||||||
|
export const VSentry: React.FC = () => {
|
||||||
|
const { user } = useAuth();
|
||||||
|
useEffect(() => {
|
||||||
|
if (!document) return;
|
||||||
|
const scope = Sentry.getCurrentScope();
|
||||||
|
if (!user) scope.setUser(null);
|
||||||
|
else scope.setUser({ email: user.email, id: user.id });
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
|
@ -1,5 +1,11 @@
|
||||||
export const SentryConfig = {
|
import { consoleLoggingIntegration, type init } from '@sentry/nextjs';
|
||||||
|
|
||||||
|
type SentryConfig = Parameters<typeof init>[0];
|
||||||
|
|
||||||
|
export const SentryConfig: SentryConfig = {
|
||||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
tracesSampleRate: 1,
|
tracesSampleRate: 0.1,
|
||||||
debug: false,
|
debug: false,
|
||||||
|
_experiments: { enableLogs: true },
|
||||||
|
integrations: [consoleLoggingIntegration()],
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue