UNPKG

@channel.io/be-user-chat-mcp

Version:

Backend UserChat Model Context Protocol server for Channel.io

156 lines 7.19 kB
const SENTRY_ORGANIZATION_SLUG = "channel-io"; // 요청에 따라 하드코딩 // Sentry API 기본 URL const SENTRY_API_BASE_URL = "https://us.sentry.io/api/0"; /** * 특정 프로젝트의 Sentry 로그 (Issues)를 검색합니다. * Sentry API는 'Issues'라는 용어를 사용하며, 이는 그룹화된 오류들을 의미합니다. * @param projectSlug Sentry 프로젝트 슬러그 (예: 'your-project-name') * @param query 검색 쿼리 (예: "user.id:123 error.message:\"Database timeout\"") * @param limit 반환할 결과 수 (기본값: 50) * @param cursor 페이징을 위한 커서 (옵션) * @param statsPeriod 검색 기간 (기본값: "24h") * @returns 검색된 Sentry 이슈 목록 */ export async function searchSentryLogs(projectSlug, query, statsPeriod = "24h", limit = 50, cursor, context) { if (!projectSlug) { throw new Error("Sentry project slug must be provided."); } const url = new URL(`${SENTRY_API_BASE_URL}/projects/${SENTRY_ORGANIZATION_SLUG}/${projectSlug}/issues/`); if (query) { url.searchParams.append("query", query); } url.searchParams.append("statsPeriod", statsPeriod); // 전달받은 statsPeriod 사용 url.searchParams.append("limit", String(limit)); if (cursor) { url.searchParams.append("cursor", cursor); } try { const response = await fetch(url.toString(), { headers: { Authorization: `Bearer ${context.sentryAuthToken}`, "Content-Type": "application/json", }, }); if (!response.ok) { const errorText = await response.text(); try { const errorData = JSON.parse(errorText); throw new Error(`Sentry API Error (${response.status}) for project ${projectSlug}: ${errorData.detail || errorText}`); } catch (e) { const errorMessage = e instanceof Error ? e.message : String(e); throw new Error(`Sentry API Error (${response.status}) for project ${projectSlug}: ${errorText}. Parsing error: ${errorMessage}`); } } const issues = (await response.json()); // 페이징을 위한 Link 헤더 처리 로직이 필요하다면 여기에 추가 // const linkHeader = response.headers.get('Link'); return issues; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error searching Sentry logs for project ${projectSlug}: ${errorMessage}`, error); // Sentry SDK가 설정되어 있다면, 이 에러를 Sentry로 전송할 수 있습니다. // if (Sentry.getCurrentHub().getClient()) { Sentry.captureException(error); } throw error; // 에러를 다시 throw하여 호출 측에서 처리할 수 있도록 함 } } /** * 특정 프로젝트의 Sentry 이벤트들을 검색합니다. * @param projectSlug Sentry 프로젝트 슬러그 * @param query 검색 쿼리 * @param limit 반환할 결과 수 * @param cursor 페이징 커서 * @returns 검색된 Sentry 이벤트 목록 */ export async function searchSentryEvents(projectSlug, query, limit = 50, cursor, context) { // SentryEvent[] 반환 if (!projectSlug) { throw new Error("Sentry project slug must be provided."); } const url = new URL(`${SENTRY_API_BASE_URL}/projects/${SENTRY_ORGANIZATION_SLUG}/${projectSlug}/events/`); if (query) { url.searchParams.append("query", query); } url.searchParams.append("limit", String(limit)); if (cursor) { url.searchParams.append("cursor", cursor); } try { const response = await fetch(url.toString(), { headers: { Authorization: `Bearer ${context.sentryAuthToken}`, "Content-Type": "application/json", }, }); if (!response.ok) { const errorText = await response.text(); try { const errorData = JSON.parse(errorText); throw new Error(`Sentry API Error (${response.status}) searching events in project ${projectSlug} with query "${query}": ${errorData.detail || errorText}`); } catch (e) { const errorMessage = e instanceof Error ? e.message : String(e); throw new Error(`Sentry API Error (${response.status}) searching events in project ${projectSlug} with query "${query}": ${errorText}. Parsing error: ${errorMessage}`); } } const events = (await response.json()); return events; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error searching Sentry events in project ${projectSlug} with query "${query}": ${errorMessage}`, error); throw error; } } /** * 특정 Sentry 이슈(로그 그룹)의 이벤트 세부 정보를 가져옵니다. * eventId가 제공되면 해당 특정 이벤트를, 그렇지 않으면 이슈의 최신 이벤트를 가져옵니다. * @param issueId Sentry 이슈 ID * @param eventId (선택적) Sentry 이벤트 ID * @returns 해당 이벤트의 세부 정보 */ export async function getSentryLogDetails(issueId, eventId, context) { if (!issueId) { throw new Error("Sentry issue ID must be provided."); } let urlString; // 변수명을 url에서 urlString으로 변경 (URL 객체와 혼동 방지) let contextDescription; if (eventId) { // 특정 이벤트를 가져오는 경우 urlString = `${SENTRY_API_BASE_URL}/projects/${SENTRY_ORGANIZATION_SLUG}/*/events/${eventId}/`; contextDescription = `event ${eventId} in issue ${issueId}`; } else { // 이슈의 최신 이벤트를 가져오는 경우 urlString = `${SENTRY_API_BASE_URL}/issues/${issueId}/events/latest/`; contextDescription = `latest event in issue ${issueId}`; } try { const response = await fetch(urlString, { headers: { Authorization: `Bearer ${context.sentryAuthToken}`, "Content-Type": "application/json", }, }); if (!response.ok) { const errorText = await response.text(); try { const errorData = JSON.parse(errorText); throw new Error(`Sentry API Error (${response.status}) for ${contextDescription}: ${errorData.detail || errorText}`); } catch (e) { const errorMessage = e instanceof Error ? e.message : String(e); throw new Error(`Sentry API Error (${response.status}) for ${contextDescription}: ${errorText}. Parsing error: ${errorMessage}`); } } const eventDetails = (await response.json()); return eventDetails; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error fetching Sentry log details for ${contextDescription}: ${errorMessage}`, error); throw error; } } //# sourceMappingURL=sentry.service.js.map