설치
공식 Go OpenTelemetry SDK로 트레이스와 로그를 OTLP로 전송하도록 연결하고, 환경 변수로 설정하여 Sophonz로 텔레메트리를 보내세요.
이 문서는 Sophonz 패키지가 아니라 업스트림 go.opentelemetry.io/otel SDK를 연결합니다. Go에는 자동 계측 에이전트가 없으므로 한 번만 작성하면 되는 짧은 설정 함수를 사용합니다. 여기서 쓰는 OTLP 익스포터는 이미 OTEL_EXPORTER_OTLP_* 환경 변수를 스스로 읽습니다.
빠른 시작
아래에서 수집기 URL, 프로젝트, 앱 이름, 버전, 앱 키를 입력하고 탭(net/http · Gin)을 선택하면 그 값으로 초기화 코드가 생성됩니다. 고급 옵션은 "고급 옵션" 토글 뒤에 있습니다.
// otel.go — 트래픽을 받기 전에 시작 시 setupOTel()을 한 번 호출하세요
package main
import (
"context"
"log"
"net/http"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/log/global"
sdklog "go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
func setupOTel(ctx context.Context) (shutdown func(context.Context) error, err error) {
res, err := resource.New(ctx,
resource.WithFromEnv(),
resource.WithAttributes(
semconv.ServiceName("next-sample"),
semconv.ServiceVersion("1.0.0"),
semconv.ServiceNamespace("sophonz"),
attribute.String("service.key", "sk_tYtJUa4WweXXedj6lxjSqoPW5O0DnbXz"),
),
)
if err != nil {
return nil, err
}
traceExporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpointURL("https://in.sophonz.ai"),
otlptracehttp.WithHeaders(map[string]string{"authorization": "sk_tYtJUa4WweXXedj6lxjSqoPW5O0DnbXz"}),
)
if err != nil {
return nil, err
}
tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(traceExporter),
sdktrace.WithResource(res),
)
otel.SetTracerProvider(tracerProvider)
logExporter, err := otlploghttp.New(ctx,
otlploghttp.WithEndpointURL("https://in.sophonz.ai"),
otlploghttp.WithHeaders(map[string]string{"authorization": "sk_tYtJUa4WweXXedj6lxjSqoPW5O0DnbXz"}),
)
if err != nil {
return nil, err
}
loggerProvider := sdklog.NewLoggerProvider(
sdklog.WithProcessor(sdklog.NewBatchProcessor(logExporter)),
sdklog.WithResource(res),
)
global.SetLoggerProvider(loggerProvider)
return func(ctx context.Context) error {
_ = tracerProvider.Shutdown(ctx)
return loggerProvider.Shutdown(ctx)
}, nil
}
func main() {
ctx := context.Background()
shutdown, err := setupOTel(ctx)
if err != nil {
log.Fatal(err)
}
defer shutdown(ctx)
handler := otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}), "root")
http.ListenAndServe(":8080", handler)
}
// 위처럼 직접 넘기는 대신 환경 변수로 지정할 수도 있습니다:
// OTEL_SERVICE_NAME service.name (appName)
// OTEL_EXPORTER_OTLP_ENDPOINT 콜렉터 base URL (collectorUrl)
// OTEL_EXPORTER_OTLP_HEADERS authorization=<appKey>
// OTEL_RESOURCE_ATTRIBUTES service.key=<appKey>,service.namespace=<project>사전 요구 사항
- Go 1.22 이상.
- Sophonz 콘솔에 등록한 서비스와 그 서비스에 발급된
sk_...API 키.
패키지 설치
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
go get go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp
go get go.opentelemetry.io/otel/log
go get go.opentelemetry.io/otel/sdk/log
go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttpNOTE — 로그 API의 안정성
go.opentelemetry.io/otel/log와 go.opentelemetry.io/otel/sdk/log는 트레이스 API가 이미 안정 버전인 v1.x인 것과 달리 아직 v0.2x의 프리 1.0 상태입니다. 마이너 버전 사이에 API가 바뀔 수 있으니 go.mod에 특정 버전을 고정하고, 업그레이드 전에 릴리스 노트를 확인하세요. 트레이스는 이런 걱정 없이 동일하게 동작합니다.
TIP — API 키 발급
Sophonz 콘솔에 로그인해 서비스를 등록하고 발급된 sk_... 키를 복사하세요. 이 키는 테넌트 신원을 나타냅니다 — 아래 service.key 참고.
SDK 설정
트래픽을 처리하기 전, 시작 시점에 한 번 호출합니다.
package main
import (
"context"
"log"
"net/http"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/log/global"
sdklog "go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
// setupOTel은 SDK가 OTLP/HTTP로 내보내도록 연결합니다. 익스포터들은
// OTEL_EXPORTER_OTLP_ENDPOINT / _PROTOCOL / _HEADERS를 스스로 읽고,
// 리소스는 OTEL_SERVICE_NAME과 OTEL_RESOURCE_ATTRIBUTES를 읽습니다.
func setupOTel(ctx context.Context) (shutdown func(context.Context) error, err error) {
res, err := resource.New(ctx, resource.WithFromEnv())
if err != nil {
return nil, err
}
traceExporter, err := otlptracehttp.New(ctx)
if err != nil {
return nil, err
}
tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(traceExporter),
sdktrace.WithResource(res),
)
otel.SetTracerProvider(tracerProvider)
logExporter, err := otlploghttp.New(ctx)
if err != nil {
return nil, err
}
loggerProvider := sdklog.NewLoggerProvider(
sdklog.WithProcessor(sdklog.NewBatchProcessor(logExporter)),
sdklog.WithResource(res),
)
global.SetLoggerProvider(loggerProvider)
return func(ctx context.Context) error {
_ = tracerProvider.Shutdown(ctx)
return loggerProvider.Shutdown(ctx)
}, nil
}
func main() {
ctx := context.Background()
shutdown, err := setupOTel(ctx)
if err != nil {
log.Fatal(err)
}
defer shutdown(ctx)
handler := otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}), "root")
http.ListenAndServe(":8080", handler)
}otelhttp.NewHandler는 자신에게 도달하는 모든 요청에 스팬을 시작하고, otelhttp.NewTransport로 감싼 클라이언트로 나가는 호출에 트레이스 컨텍스트를 전파합니다. 정상 종료 시 shutdown(ctx)를 호출해 배치 익스포터가 프로세스 종료 전에 플러시되도록 하세요.
환경 변수 구성
OTEL_SERVICE_NAME=my-go-service
OTEL_EXPORTER_OTLP_ENDPOINT=https://in.sophonz.ai
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS=authorization=sk_your_api_key
OTEL_RESOURCE_ATTRIBUTES=service.key=sk_your_api_key,service.namespace=my-project,deployment.environment.name=production| 변수 | 값 | 설명 |
|---|---|---|
OTEL_SERVICE_NAME | 서비스 이름 | resource.WithFromEnv()가 읽습니다. 콘솔에 등록한 서비스 이름과 일치해야 합니다 — 포털은 (project, service.name) 조합으로 텔레메트리를 등록된 서비스에 연결합니다. |
OTEL_EXPORTER_OTLP_ENDPOINT | https://in.sophonz.ai | 기본 OTLP 엔드포인트. otlptracehttp.New와 otlploghttp.New가 여기서 /v1/traces와 /v1/logs를 파생시킵니다. |
OTEL_EXPORTER_OTLP_PROTOCOL | http/protobuf | 두 익스포터 모두 자동으로 읽습니다. |
OTEL_EXPORTER_OTLP_HEADERS | authorization=sk_your_api_key | 모든 전송 요청에 HTTP 헤더로 포함됩니다. |
OTEL_RESOURCE_ATTRIBUTES | service.key=sk_your_api_key,service.namespace=...,deployment.environment.name=... | service.key는 컬렉터가 검사하는 테넌트 신원으로 필수입니다. service.namespace는 서비스들을 프로젝트로 묶고, deployment.environment.name은 환경을 표시합니다. |
CAUTION — service.key는 필수입니다
컬렉터는 강제 모드로 동작합니다. 해석 가능한 service.key 리소스 속성이 없는 텔레메트리는 조용히 폐기됩니다. 애플리케이션은 계속 실행되고 익스포터에서 오류가 반환되지도 않지만, Sophonz에는 아무것도 나타나지 않습니다. 항상 OTEL_RESOURCE_ATTRIBUTES에 service.key를 포함하고, OTEL_EXPORTER_OTLP_HEADERS를 통해 같은 키를 authorization 헤더로도 전송하세요.
프레임워크 참고 사항
otelhttp는 순수 net/http를 다룹니다. 라우터를 쓴다면 전용 미들웨어를 추가하세요 — 각각 요청 처리를 감싸 스팬을 시작하고 라우트, 메서드, 상태 코드를 기록합니다.
| 프레임워크 | 패키지 |
|---|---|
net/http | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp |
| Gin | go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin — router.Use(otelgin.Middleware("my-go-service")) |
| Echo | go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho — e.Use(otelecho.Middleware("my-go-service")) |
나가는 HTTP 호출의 경우 클라이언트의 Transport를 otelhttp.NewTransport(http.DefaultTransport)로 감싸면 트레이스 컨텍스트가 하위로 전파되어 자식 스팬으로 나타납니다.
확인
환경 변수를 설정한 뒤 앱을 실행하세요.
OTEL_SERVICE_NAME=my-go-service \
OTEL_EXPORTER_OTLP_ENDPOINT=https://in.sophonz.ai \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_EXPORTER_OTLP_HEADERS=authorization=sk_your_api_key \
OTEL_RESOURCE_ATTRIBUTES=service.key=sk_your_api_key,service.namespace=my-project,deployment.environment.name=production \
go run .전송 실패는 배치 프로세서 내부에서 처리되며(요청으로 반환되지 않고 SDK의 에러 핸들러로 로깅됨), 오류 없이 실행된다고 해서 텔레메트리가 도착했다는 보장은 없습니다 — service.key가 잘못되었거나 없어도 똑같이 조용히 실패합니다. 핸들러에 트래픽을 조금 발생시킨 뒤 Sophonz 콘솔에서 등록한 서비스를 확인하세요.
TIP — 이제 시작입니다
이제 앱을 배포하세요. Sophonz가 실사용자가 겪는 문제를 포착하고, 당신의 앱은 스스로 진화합니다.