# Show GN: Spanlens - LLM 호출과 에이전트 trace를 한 곳에서 보는 오픈소스 관측 플랫폼

> Clean Markdown view of GeekNews topic #30076. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=30076](https://news.hada.io/topic?id=30076)
- GeekNews Markdown: [https://news.hada.io/topic/30076.md](https://news.hada.io/topic/30076.md)
- Type: show
- Author: [jhs6312](https://news.hada.io/@jhs6312)
- Published: 2026-06-01T22:00:26+09:00
- Updated: 2026-06-01T22:00:26+09:00
- Original source: [spanlens.io](https://www.spanlens.io)
- Points: 1
- Comments: 0

## Topic Body

안녕하세요. LLM 호출 로깅, 비용 추적, 에이전트 trace를 한 곳에서 보는 오픈소스 관측 플랫폼 Spanlens를 만들고 있습니다.  
  
### 만든 이유  
  
개인 사이드 프로젝트로 LLM을 쓰다 보니 두 가지가 계속 거슬렸습니다.  
  
하나는 비용 추적입니다.  
GPT 기반 브라우저 확장을 만들다가, 월말에 OpenAI 청구서를 받고 처음 놀랐던 게 시작이었습니다. 대학생이라 큰 금액은 아니었지만, 총액은 보이는데 어느 기능에서 얼마가 나왔는지, 어느 모델이 평균 토큰을 얼마나 쓰는지는 따로 안 보였습니다. 그래서 매번 코드에 console.log를 박고, CSV로 내려서 엑셀에서 합산하고, 모델별 단가를 곱해서 추정치를 내는 작업을 반복했습니다.  
  
다른 하나는 에이전트 디버깅이었습니다.  
Spanlens에 LangGraph 통합을 붙이면서 직접 겪은 일인데, 멀티 LLM 호출이 섞인 trace 하나가 30초 걸렸을 때  
- 어느 노드에서 시간이 빠졌는지  
- 같은 도구를 왜 두 번 불렀는지  
- LangGraph state가 어느 시점에 바뀌었는지  
이런 걸 로그 펼쳐놓고 손으로 따라가야 했습니다.  
  
이 두 가지를 줄여보고 싶어서 Spanlens를 만들었습니다.  
  
### 주요 기능  
  
1. baseURL 한 줄 통합  
  
OpenAI/Anthropic/Gemini SDK의 baseURL을 https://api.spanlens.io/proxy/openai/v1 식으로 바꾸면 요청, 응답, 토큰, 비용이 자동 기록됩니다. 응답은 그대로 passthrough라 스트리밍, tool calling, JSON mode 모두 원본과 동일하게 동작합니다.  
  
에이전트처럼 wrap 방식이 필요한 경우는 SDK로 trace_id, span_id를 주입해서 부모 자식 관계를 같이 기록할 수 있게 했습니다.  
  
2. 에이전트 trace + LangGraph topology view  
  
trace를 시간순 타임라인뿐 아니라 실제 그래프 노드 위에 얹어서 봅니다. LangGraph로 짠 에이전트라면 어느 노드에서 시간이 빠졌고, 어느 엣지가 가장 자주 도는지 한 화면에 보입니다.  
  
3. Critical Path 자동 분석  
  
trace 안에서 latency를 가장 많이 잡아먹은 호출 체인을 자동으로 표시합니다. '이 trace는 왜 느렸지'의 답을 찾기까지 클릭 수를 줄여보려 했습니다.  
  
4. Prompts A/B 통계 비교  
  
같은 프롬프트의 두 버전을 latency, 비용, 토큰 사용량 기준으로 비교합니다. 단순 평균 차이가 아니라 Welch t-test를 적용해서, 표본 분산까지 본 차이를 보여줍니다. '그냥 평균이 좀 낮네'가 아니라 '유의미한 차이다'를 말할 수 있게 하려고 넣었습니다.  
  
5. 셀프호스팅  
  
Docker 이미지로 자체 서버에 올릴 수 있습니다. SaaS 버전과 동일한 코드가 public 레포에 그대로 있습니다. 전체 코드 MIT 라이선스입니다.  
  
### 어떻게 구현했나  
  
현재 파이프라인은 대략 이런 식입니다.  
  
- 프록시 요청은 Hono로 받아서 Authorization 헤더와 X-Spanlens-* 메타데이터를 분리하고, provider key를 AES-256-GCM으로 복호화해서 호출 직전 메모리에서만 사용합니다.  
- 스트리밍 응답은 body.tee()로 원본 스트림을 클라이언트에 즉시 반환하고, 복사본은 백그라운드에서 파서가 토큰과 비용을 계산합니다.  
- 로그는 ClickHouse에 비동기로 적재합니다. INSERT 실패 시 Supabase 폴백 큐로 보관하고 cron이 재시도하는 구조라, fire-and-forget이지만 데이터 유실은 피하려고 했습니다.  
- 모델 가격은 DB 테이블에 두고 5분 TTL stale-while-revalidate로 캐싱합니다. Fallback 가격이 콜드스타트 안전망입니다.  
  
처음에는 단순 프록시였는데, 실제로 써보니 더 중요한 건 raw 로그가 아니라 정규화된 trace였습니다. '이 trace는 왜 느렸지'의 답을 찾으려면 호출 순서뿐 아니라 부모 자식 관계, 평행 호출, Critical Path가 필요해서 LangGraph topology view나 자동 Critical Path 같은 기능이 거기서 나왔습니다.  
  
스택은 Next.js 14, Hono, Supabase Postgres, ClickHouse, 전부 TypeScript pnpm monorepo입니다.  
  
### 아직 고민 중인 점  
  
- 셀프호스팅을 한 줄 docker run으로 만들고 싶은데, 그러려면 Supabase Postgres까지 같이 내려야 합니다. 지금은 managed Supabase 전제로 docker-compose가 web, server, ClickHouse 3개 컨테이너인데, Supabase까지 자체 호스팅 옵션을 만드는 게 나을지 아니면 managed 전제를 유지하는 게 나을지 고민입니다.  
  
- Prompts A/B 비교에서 Welch t-test 계산은 들어있는데, p-value를 그대로 보여주는 게 도움이 될지 결론 배지(유의미함/아님)만 보여주는 게 좋을지 결정을 못 했습니다. 표본이 작을 때(n<30) 어떻게 경고를 띄울지도 같이 고민입니다.  
  
- LangGraph topology view에서 노드, 엣지, Critical Path는 시각화하는데 state channel 변화는 노이즈가 너무 많아서 일부러 빼뒀습니다. 실제 디버깅에 state 변화 추적이 더 필요한지, 지금 수준이 적절한지 의견을 듣고 싶습니다.  
  
직접 겪은 불편함에서 시작해서 꾸준히 다듬고 있는 프로젝트입니다.  
  
[ Spanlens ]  
웹: https://www.spanlens.io  
GitHub: https://github.com/spanlens/Spanlens  
셀프호스팅 가이드: https://www.spanlens.io/docs/self-host  
  
대시보드 UX, 트레이스 시각화, 프록시 통합 방식, 셀프호스팅 경험, 어떤 관점이든 피드백 주시면 정말 감사하겠습니다. 특히 OpenAI/Anthropic/Gemini 외에 지원했으면 하는 provider가 있다면 댓글로 알려주세요.  
  
웹사이트에 데모 버전이 있으니 많은 관심 부탁드립니다.  
현재 UI는 영어로 되어 있고, 한국어 지원도 곧 추가할 예정입니다.

## Comments



_No public comments on this page._
