분산 추적 구현하기

분산 추적 구현하기

때로는 여러 서비스에 걸쳐 요청을 추적해야 할 때가 있어요.

LangSmith는 분산 추적을 기본으로 지원하며, 컨텍스트 전파 헤더(langsmith-trace와 메타데이터/태그용 옵션 baggage)를 사용해 서비스 간에 트레이스 내의 런을 연결해요.

클라이언트-서버 설정 예시:

  • 트레이스가 클라이언트에서 시작
  • 서버에서 계속

신뢰할 수 있는 서비스의 분산 추적 헤더만 수락하세요. langsmith-tracebaggage 헤더는 신뢰되는 추적 컨텍스트로 사용돼요. 신뢰할 수 없는 제3자나 공개 인터넷에서 직접 요청을 받는 서비스에는 TracingMiddleware를 추가하지 마세요(또는 인바운드 요청 헤더를 추적 parent로 전달하지 마세요). 분산 추적은 내부 서비스 간 호출로 제한하고, 게이트웨이나 프록시에서 신뢰할 수 없는 인바운드 요청의 이러한 헤더를 제거하세요. 외부 호출자의 baggage를 신뢰하면 그들이 런이 어떻게 기록되는지에 영향을 줄 수 있어요.

출처: 문서

본문

Python의 분산 추적

# client.py
from langsmith.run_helpers import get_current_run_tree, traceable
import httpx

@traceable
async def my_client_function():
    headers = {}
    async with httpx.AsyncClient(base_url="...") as client:
        if run_tree := get_current_run_tree():
            # add langsmith-id to headers
            headers.update(run_tree.to_headers())
        return await client.post("/my-route", headers=headers)

그런 다음 서버(또는 다른 서비스)는 헤더를 적절히 처리해 트레이스를 계속할 수 있어요. asgi 앱 Starlette이나 FastAPI를 사용한다면 LangSmith의 TracingMiddleware로 분산 트레이스를 연결할 수 있어요.

TracingMiddleware 클래스는 langsmith==0.1.133에서 추가됐어요.

FastAPI 사용 예시:

from langsmith import traceable
from langsmith.middleware import TracingMiddleware
from fastapi import FastAPI, Request

app = FastAPI()  # Or Flask, Django, or any other framework
app.add_middleware(TracingMiddleware)

@traceable
async def some_function():
    ...

@app.post("/my-route")
async def fake_route(request: Request):
    return await some_function()

또는 Starlette:

from starlette.applications import Starlette
from starlette.middleware import Middleware
from langsmith.middleware import TracingMiddleware

routes = ...
middleware = [
    Middleware(TracingMiddleware),
]
app = Starlette(..., middleware=middleware)

다른 서버 프레임워크를 사용한다면 langsmith_extra를 통해 헤더를 전달해 분산 트레이스를 "수신"할 수 있어요:

# server.py
import langsmith as ls
from fastapi import FastAPI, Request

@ls.traceable
async def my_application():
    ...

app = FastAPI()  # Or Flask, Django, or any other framework

@app.post("/my-route")
async def fake_route(request: Request):
    # request.headers:  {"langsmith-trace": "..."}
    # as well as optional metadata/tags in `baggage`
    with ls.tracing_context(parent=request.headers):
        return await my_application()

위 예시는 tracing_context 컨텍스트 매니저를 사용해요. @traceable로 래핑된 메서드의 langsmith_extra 파라미터에서 상위 런 컨텍스트를 직접 지정할 수도 있어요.

# ... same as above

@app.post("/my-route")
async def fake_route(request: Request):
    # request.headers:  {"langsmith-trace": "..."}
    my_application(langsmith_extra={"parent": request.headers})

TypeScript의 분산 추적

TypeScript의 분산 추적에는 langsmith>=0.1.31이 필요해요.

먼저 클라이언트에서 현재 런 트리를 가져와 langsmith-tracebaggage 헤더 값으로 변환해 서버에 전달할 수 있어요:

// client.mts
import { getCurrentRunTree, traceable } from "langsmith/traceable";

const client = traceable(
    async () => {
        const runTree = getCurrentRunTree();
        return await fetch("...", {
            method: "POST",
            headers: runTree.toHeaders(),
        }).then((a) => a.text());
    },
    { name: "client" }
);

await client();

그런 다음 서버는 헤더를 런 트리로 다시 변환하고, 이를 사용해 추적을 계속해요.

새로 만든 런 트리를 traceable 함수에 전달하려면 withRunTree 헬퍼를 사용해 traceable 호출 내에서 런 트리가 전파되도록 할 수 있어요.

Express.JS:

// server.mts
import { RunTree } from "langsmith";
import { traceable, withRunTree } from "langsmith/traceable";
import express from "express";
import bodyParser from "body-parser";

    const server = traceable(
        (text: string) => `Hello from the server! Received "${text}"`,
        { name: "server" }
    );

    const app = express();
    app.use(bodyParser.text());

app.post("/", async (req, res) => {
    const runTree = RunTree.fromHeaders(req.headers);
    const result = await withRunTree(runTree, () => server(req.body));
    res.send(result);
});

Hono:

// server.mts
import { RunTree } from "langsmith";
import { traceable, withRunTree } from "langsmith/traceable";
import { Hono } from "hono";

    const server = traceable(
        (text: string) => `Hello from the server! Received "${text}"`,
        { name: "server" }
    );

    const app = new Hono();

app.post("/", async (c) => {
    const body = await c.req.text();
    const runTree = RunTree.fromHeaders(c.req.raw.headers);
    const result = await withRunTree(runTree, () => server(body));
    return c.body(result);
});

더 알아보기 (Learn more)