OpenTelemetry
알아두면 좋은 점: 이 기능은 실험적입니다.
next.config.js
에experimental.instrumentationHook = true;
를 제공하여 명시적으로 옵트인해야 합니다.
관찰 가능성은 Next.js 앱의 동작과 성능을 이해하고 최적화하는 데 매우 중요합니다.
애플리케이션이 점점 더 복잡해짐에 따라 발생할 수 있는 문제를 식별하고 진단하는 것이 점점 더 어려워집니다. 로깅 및 메트릭과 같은 관찰 가능성 도구를 활용하면 개발자는 애플리케이션의 동작에 대한 인사이트를 얻고 최적화할 영역을 식별할 수 있습니다. 관찰 가능성을 통해 개발자는 주요 문제가 되기 전에 문제를 사전에 해결하고 더 나은 사용자 경험을 제공할 수 있습니다. 따라서 Next.js 애플리케이션에서 성능을 개선하고 리소스를 최적화하며 사용자 경험을 향상시키기 위해 관찰 가능성을 사용하는 것이 매우 권장됩니다.
앱을 계측하기 위해 OpenTelemetry를 사용하는 것이 좋습니다. 이는 관찰 가능성 공급자를 변경하더라도 코드를 변경할 필요가 없는 플랫폼에 구애받지 않는 계측 방법입니다. OpenTelemetry에 대한 자세한 내용과 작동 방식을 확인하려면 공식 OpenTelemetry 문서 (opens in a new tab)를 참조하세요.
이 문서에서는 Span, Trace 또는 Exporter와 같은 용어를 사용하며, 이는 모두 OpenTelemetry Observability Primer (opens in a new tab)에서 찾을 수 있습니다.
Next.js는 기본적으로 OpenTelemetry 계측을 지원하므로 Next.js 자체도 이미 계측되었습니다.
OpenTelemetry를 활성화하면 getStaticProps
와 같은 모든 코드를 유용한 속성이 포함된 spans로 자동 래핑합니다.
Getting Started
OpenTelemetry는 확장 가능하지만 적절히 설정하는 데 상당히 번거로울 수 있습니다.
그래서 우리는 빠르게 시작할 수 있도록 도와주는 @vercel/otel
패키지를 준비했습니다.
Using @vercel/otel
시작하려면 @vercel/otel
을 설치해야 합니다:
npm install @vercel/otel
다음으로, 프로젝트의 루트 디렉토리에 사용자 정의 instrumentation.ts
(또는 .js
) 파일을 만듭니다(src
폴더를 사용하는 경우 해당 폴더 내에 만듭니다):
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel({ serviceName: 'next-app' })
}
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel({ serviceName: 'next-app' })
}
추가 구성 옵션은 @vercel/otel
문서 (opens in a new tab)를 참조하세요.
알아두면 좋은 점
instrumentation
파일은 프로젝트의 루트에 있어야 하며,app
또는pages
디렉토리 내부에 있어서는 안 됩니다.src
폴더를 사용하는 경우pages
및app
과 함께src
내부에 파일을 배치하세요.pageExtensions
구성 옵션을 사용하여 접미사를 추가한 경우,instrumentation
파일 이름도 일치하도록 업데이트해야 합니다.- 사용할 수 있는 기본 with-opentelemetry (opens in a new tab) 예제가 있습니다.
Manual OpenTelemetry configuration
@vercel/otel
패키지는 많은 구성 옵션을 제공하며 대부분의 일반적인 사용 사례에 적합합니다. 그러나 필요에 맞지 않는 경우 OpenTelemetry를 수동으로 구성할 수 있습니다.
먼저 OpenTelemetry 패키지를 설치해야 합니다:
npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http
이제 instrumentation.ts
에서 NodeSDK
를 초기화할 수 있습니다.
@vercel/otel
과 달리 NodeSDK
는 엣지 런타임과 호환되지 않으므로 process.env.NEXT_RUNTIME === 'nodejs'
일 때만 가져오는지 확인해야 합니다. 우리는 노드에서 사용할 때만 조건부로 가져오는 새 파일 instrumentation.node.ts
를 만드는 것을 권장합니다:
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('./instrumentation.node.ts')
}
}
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('./instrumentation.node.js')
}
}
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } from '@opentelemetry/resources'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'next-app',
}),
spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } from '@opentelemetry/resources'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'next-app',
}),
spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()
이렇게 하면 @vercel/otel
을 사용하는 것과 동일하지만, @vercel/otel
에서 노출하지 않는 일부 기능을 수정하고 확장할 수 있습니다. 엣지 런타임 지원이 필요한 경우 @vercel/otel
을 사용해야 합니다.
Testing your instrumentation
OpenTelemetry 추적을 로컬에서 테스트하려면 호환 가능한 백엔드를 갖춘 OpenTelemetry 수집기가 필요합니다. OpenTelemetry dev environment (opens in a new tab)를 사용하는 것이 좋습니다.
모든 것이 잘 작동하면 GET /requested/pathname
으로 표시된 루트 서버 스팬을 볼 수 있어야 합니다.
해당 특정 추적의 모든 다른 스팬은 그 아래에 중첩됩니다.
Next.js는 기본적으로 내보내지는 스팬보다 더 많은 스팬을 추적합니다.
더 많은 스팬을 보려면 NEXT_OTEL_VERBOSE=1
을 설정해야 합니다.
Deployment
Using OpenTelemetry Collector
OpenTelemetry Collector를 사용하여 배포할 때 @vercel/otel
을 사용할 수 있습니다.
이는 Vercel과 자체 호스팅 모두에서 작동합니다.
Deploying on Verc
el
Vercel에서는 OpenTelemetry가 기본적으로 작동하도록 했습니다.
프로젝트를 관찰 가능성 공급자에 연결하는 방법은 Vercel 문서 (opens in a new tab)를 참조하세요.
Self-hosting
다른 플랫폼에 배포하는 것도 간단합니다. Next.js 앱에서 전송된 텔레메트리 데이터를 수신하고 처리하기 위해 OpenTelemetry Collector를 직접 설정해야 합니다.
이를 위해 OpenTelemetry Collector Getting Started guide (opens in a new tab)를 따라 수집기를 설정하고 Next.js 앱에서 데이터를 수신하도록 구성하세요.
수집기를 설정한 후에는 각 플랫폼의 배포 가이드를 따라 Next.js 앱을 선택한 플랫폼에 배포할 수 있습니다.
Custom Exporters
OpenTelemetry Collector는 필수는 아닙니다. @vercel/otel
또는 수동 OpenTelemetry 구성으로 사용자 지정 OpenTelemetry 익스포터를 사용할 수 있습니다.
Custom Spans
OpenTelemetry APIs (opens in a new tab)를 사용하여 사용자 정의 스팬을 추가할 수 있습니다.
npm install @opentelemetry/api
다음 예제는 GitHub 스타를 가져오고 가져오기 요청의 결과를 추적하는 사용자 정의 fetchGithubStars
스팬을 추가하는 함수를 보여줍니다:
import { trace } from '@opentelemetry/api'
export async function fetchGithubStars() {
return await trace
.getTracer('nextjs-example')
.startActiveSpan('fetchGithubStars', async (span) => {
try {
return await getValue()
} finally {
span.end()
}
})
}
register
함수는 새로운 환경에서 코드가 실행되기 전에 실행됩니다.
새 스팬을 만들기 시작할 수 있으며, 이는 올바르게 내보낸 추적에 추가되어야 합니다.
Default Spans in Next.js
Next.js는 애플리케이션 성능에 대한 유용한 인사이트를 제공하기 위해 여러 스팬을 자동으로 계측합니다.
스팬의 속성은 OpenTelemetry semantic conventions (opens in a new tab)을 따릅니다. 또한 next
네임스페이스 아래에 일부 사용자 정의 속성을 추가합니다:
next.span_name
- 스팬 이름을 중복next.span_type
- 각 스팬 유형에는 고유한 식별자가 있음next.route
- 요청의 라우트 패턴 (예:/[param]/user
)next.rsc
(true/false) - 요청이 사전 로드와 같은 RSC 요청인지 여부next.page
- 이는 앱 라우터에서 사용되는 내부 값
- 특별한 파일(예:
page.ts
,layout.ts
,loading.ts
등)로의 라우트로 생각할 수 있음 next.route
와 함께 사용될 때만 고유 식별자로 사용할 수 있음. 예를 들어/layout
은/(groupA)/layout.ts
와/(groupB)/layout.ts
를 모두 식별하는 데 사용될 수 있음
[http.method] [next.route]
next.span_type
:BaseServer.handleRequest
이 스팬은 Next.js 애플리케이션에 대한 각 들어오는 요청에 대한 루트 스팬을 나타냅니다. HTTP 메소드, 라우트, 타겟 및 상태 코드를 추적합니다.
속성:
- 공통 HTTP 속성 (opens in a new tab)
http.method
http.status_code
- 서버 HTTP 속성 (opens in a new tab)
http.route
http.target
next.span_name
next.span_type
next.route
render route (app) [next.route]
next.span_type
:AppRender.getBodyResult
.
이 스팬은 앱 라우터에서 라우트를 렌더링하는 프로세스를 나타냅니다.
속성:
next.span_name
next.span_type
next.route
fetch [http.method] [http.url]
next.span_type
:AppRender.fetch
이 스팬은 코드에서 실행된 가져오기 요청을 나타냅니다.
속성:
- 공통 HTTP 속성 (opens in a new tab)
http.method
- 클라이언트 HTTP 속성 (opens in a new tab)
http.url
net.peer.name
net.peer.port
(명시된 경우에만)
next.span_name
next.span_type
이 스팬은 환경에서 NEXT_OTEL_FETCH_DISABLED=1
을 설정하여 비활성화할 수 있습니다. 이는 사용자 정의 가져오기 계측 라이브러리를 사용하려는 경우에 유용합니다.
executing api route (app) [next.route]
next.span_type
:AppRouteRouteHandlers.runHandler
.
이 스팬은 앱 라우터에서 API 라우트 핸들러의 실행을 나타냅니다.
속성:
next.span_name
next.span_type
next.route
getServerSideProps [next.route]
next.span_type
:Render.getServerSideProps
.
이 스팬은 특정 라우트에 대한 getServerSideProps
의 실행을 나타냅니다.
속성:
next.span_name
next.span_type
next.route
getStaticProps [next.route]
next.span_type
:Render.getStaticProps
.
이 스팬은 특정 라우트에 대한 getStaticProps
의 실행을 나타냅니다.
속성:
next.span_name
next.span_type
next.route
render route (pages) [next.route]
next.span_type
:Render.renderDocument
.
이 스팬은 특정 라우트에 대한 문서 렌더링 프로세스를 나타냅니다.
속성:
next.span_name
next.span_type
next.route
generateMetadata [next.page]
next.span_type
:ResolveMetadata.generateMetadata
.
이 스팬은 특정 페이지에 대한 메타데이터 생성 프로세스를 나타냅니다(단일 라우트에 여러 개의 이 스팬이 있을 수 있음).
속성:
next.span_name
next.span_type
next.page
resolve page components
next.span_type
:NextNodeServer.findPageComponents
.
이 스팬은 특정 페이지에 대한 페이지 컴포넌트를 해결하는 프로세스를 나타냅니다.
속성:
next.span_name
next.span_type
next.route
resolve segment modules
next.span_type
:NextNodeServer.getLayoutOrPageModule
.
이 스팬은 레이아웃 또는 페이지에 대한 코드 모듈을 로드하는 것을 나타냅니다.
속성:
next.span_name
next.span_type
next.segment
start response
next.span_type
:NextNodeServer.startResponse
.
이 제로 길이 스팬은 응답에서 첫 번째 바이트가 전송된 시간을 나타냅니다.