ردیابی توزیع شده با OpenTelemetry و Jaeger برای Nest Application

Summarize this content to 400 words in Persian Lang
مقدمه
آیا تا به حال برای شما پیش آمده است که اشکالی در تولید رخ داده باشد و هیچ ایده ای نداشته باشید که چه مشکلی رخ داده است، زیرا گزارش های شما دقیقاً به شما نمی گوید که چه چیزی اشتباه رخ داده است یا درخواستی که معمولاً پردازش آن طول می کشد.
گاهی اوقات اشکال زدایی این مسائل بدون سیستم ردیابی غیرممکن است. سیستم ردیابی مانند یک دوربین مداربسته است که همه چیز را ضبط می کند چه اتفاقی افتاد، چه زمانی اتفاق افتاد، ترتیب وقایع چگونه بود، هر رویداد چقدر طول کشید. این اطلاعات برای اشکال زدایی و شناسایی گلوگاه های عملکرد در برنامه های پیچیده توزیع شده حیاتی است.
پیش نیاز
NodeJS
تایپ اسکریپت
NestJS
داکر
اصطلاحات
ردیابی: یک ردیابی مانند یک نقشه سفر کامل از یک درخواست است که در کل سیستم توزیع شده شما حرکت می کند. آن را به عنوان یک گزارش سفر با جزئیات تصور کنید که درخواستی را از نقطه شروع تا مقصد نهایی خود دنبال می کند و هر توقف و تعامل در طول مسیر را به تصویر می کشد.
ابزار دقیق: فرآیند افزودن کد به برنامه شما برای جمع آوری داده های تله متری. مانند نصب ردیاب های GPS در قسمت های مختلف سیستم شماست.
صادر کننده: جزء مسئول ارسال داده های ردیابی جمع آوری شده به یک سیستم Back-end برای ذخیره سازی و تجزیه و تحلیل. به آن به عنوان یک سرویس پستی فکر کنید که گزارش سفر شما را به یک بایگانی مرکزی ارسال می کند.
دهانه:
Root span: اولین دهانه در یک ردیابی که شروع کل سفر درخواست را نشان می دهد. مثل نقطه شروع سفرنامه شماست.
Child span: دهانه ای که در یک دهانه دیگر تودرتو شده است، که نشان دهنده یک عملیات خاص تر در یک فرآیند گسترده تر است.
انتشار متن: مکانیسم انتقال اطلاعات ردیابی بین خدمات و اجزای مختلف. مانند پاسپورت یک مسافر است که حاوی جزئیات کامل سفر است.
معیارها: متریک ها داده های عددی هستند که عملکرد، سلامت و رفتار برنامه را نشان می دهند.
سیاههها: گزارشها ورودیهای متنی هستند که الگوهای استفاده، فعالیتها و عملیات را در برنامه شما توصیف میکنند.
سه سوار قابل مشاهده
مشاهدهپذیری به شما امکان میدهد یک سیستم را از بیرون درک کنید و به شما اجازه میدهد در مورد آن سیستم بدون اطلاع از عملکرد درونی آن سؤال بپرسید. این به شما امکان می دهد تا به راحتی مشکلات جدید را عیب یابی و مدیریت کنید، یعنی “ناشناخته های ناشناخته”. همچنین به سوال پاسخ می دهد “چرا این اتفاق می افتد؟”
راه اندازی پروژه
pnpm i -g @nestjs/cli
nest new tracing-app
cd tracing-app
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
نصب وابستگی ها
کتابخانه های مرتبط Jaeger و OpenTelemetry را نصب کنید:
pnpm install @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/sdk-trace-base
pnpm install @opentelemetry/instrumentation @prisma/instrumentation @opentelemetry/instrumentation-net @opentelemetry/instrumentation-http @opentelemetry/instrumentation-express
pnpm install @opentelemetry/exporter-trace-otlp-http
pnpm install @opentelemetry/api @opentelemetry/semantic-conventions
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
Prisma ORM و SQLite را نصب کنید:
pnpm install @prisma/client sqlite3 class-validator
pnpm install prisma –save-dev
pnpm install –save @nestjs/swagger
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
Prisma را راه اندازی کنید:
npx prisma init
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
این یک ایجاد خواهد کرد prisma دایرکتوری با a schema.prisma فایل
datasource db {
provider = “sqlite”
url = “file:./dev.db”
}
generator client {
provider = “prisma-client-js”
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
مهاجرت های Prisma را اجرا کنید:
npx prisma migrate dev –name init
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
کلاینت Prisma را ایجاد کنید:
npx prisma generate
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
یک نقطه پایانی CRUD را تنظیم کنید
یک ماژول CRUD برای کاربران ایجاد کنید
pnpm nest generate resource users
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
این یک ایجاد خواهد کرد users ماژول با کنترلر، سرویس و DTO.
ایجاد یک prisma.service.ts فایل در پوشه پریسما
import { Injectable, OnModuleInit, OnModuleDestroy } from ‘@nestjs/common’;
import { PrismaClient } from ‘@prisma/client’;
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
را به روز کنید users.module.ts فایل برای گنجاندن PrismaService:
import { Module } from ‘@nestjs/common’;
import { UsersService } from ‘./users.service’;
import { UsersController } from ‘./users.controller’;
import { PrismaService } from ‘../../prisma/prisma.service’;
@Module({
controllers: [UsersController],
providers: [UsersService, PrismaService],
})
export class UsersModule { }
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
یک فایل به نام ایجاد کنید create-user.dto.ts در users/dto دایرکتوری:
import { IsEmail, IsNotEmpty, IsString } from ‘class-validator’;
import { ApiProperty } from ‘@nestjs/swagger’;
export class CreateUserDto {
@ApiProperty({
description: ‘The name of the user’,
example: ‘John Doe’,
})
@IsNotEmpty()
@IsString()
name: string;
@ApiProperty({
description: ‘The email of the user’,
example: ’email@domain.com’,
})
@IsNotEmpty()
@IsEmail()
email: string;
}
export class UpdateUserDto extends PartialType(CreateUserDto) {}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
را به روز کنید users.service.ts فایل برای استفاده از Prisma:
import { Injectable } from ‘@nestjs/common’;
import { PrismaService } from ‘../prisma/prisma.service’;
import { CreateUserDto } from ‘./dto/create-user.dto’;
import { UpdateUserDto } from ‘./dto/update-user.dto’;
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
create(createUserDto: CreateUserDto) {
return this.prisma.user.create({
data: createUserDto,
});
}
findAll() {
return this.prisma.user.findMany();
}
findOne(id: number) {
return this.prisma.user.findUnique({
where: { id },
});
}
update(id: number, updateUserDto: UpdateUserDto) {
return this.prisma.user.update({
where: { id },
data: updateUserDto,
});
}
remove(id: number) {
return this.prisma.user.delete({
where: { id },
});
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
را به روز کنید users.controller.ts فایل:
import { Controller, Get, Post, Body, Patch, Param, Delete } from ‘@nestjs/common’;
import { UsersService } from ‘./users.service’;
import { CreateUserDto } from ‘./dto/create-user.dto’;
import { UpdateUserDto } from ‘./dto/update-user.dto’;
import { ApiGoneResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from ‘@nestjs/swagger’;
@ApiTags(‘users’)
@Controller(‘users’)
export class UsersController {
constructor(private readonly usersService: UsersService) { }
@ApiOperation({ summary: ‘Create user’ })
@ApiOkResponse({ description: ‘User created’ })
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@ApiOperation({ summary: ‘Get all users’ })
@ApiOkResponse({ description: ‘Users found’ })
@Get()
findAll() {
return this.usersService.findAll();
}
@ApiOperation({ summary: ‘Get user by id’ })
@ApiOkResponse({ description: ‘User found’ })
@ApiNotFoundResponse({ description: ‘User not found’ })
@ApiParam({ name: ‘id’, description: ‘User id’ })
@Get(‘:id’)
findOne(@Param(‘id’) id: string) {
return this.usersService.findOne(+id);
}
@ApiOperation({ summary: ‘Update user’ })
@ApiOkResponse({ description: ‘User updated’ })
@ApiNotFoundResponse({ description: ‘User not found’ })
@ApiParam({ name: ‘id’, description: ‘User id’ })
@Patch(‘:id’)
update(@Param(‘id’) id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(+id, updateUserDto);
}
@ApiOperation({ summary: ‘Delete user’ })
@ApiGoneResponse({ description: ‘User deleted’ })
@ApiParam({ name: ‘id’, description: ‘User id’ })
@Delete(‘:id’)
remove(@Param(‘id’) id: string) {
return this.usersService.remove(+id);
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
پیکربندی صادرکنندگان
یک فایل ایجاد کنید ردیابی.ts در شما src دایرکتوری:
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from ‘@opentelemetry/semantic-conventions’;
import { BatchSpanProcessor } from ‘@opentelemetry/sdk-trace-base’;
import { ExpressInstrumentation } from ‘@opentelemetry/instrumentation-express’;
import { HttpInstrumentation } from ‘@opentelemetry/instrumentation-http’;
import { NetInstrumentation } from ‘@opentelemetry/instrumentation-net’;
import { NodeTracerProvider } from ‘@opentelemetry/sdk-trace-node’;
import { OTLPTraceExporter } from ‘@opentelemetry/exporter-trace-otlp-http’;
import { PrismaInstrumentation } from ‘@prisma/instrumentation’;
import { Resource } from ‘@opentelemetry/resources’;
import { diag, DiagConsoleLogger, DiagLogLevel } from ‘@opentelemetry/api’;
import { registerInstrumentations } from ‘@opentelemetry/instrumentation’;
export function setupTracing() {
// Enable OpenTelemetry diagnostic logging
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
// Create a resource with service information
const resource = new Resource({
[ATTR_SERVICE_NAME]: process.env.SERVICE_NAME || ‘tracer-app’,
[ATTR_SERVICE_VERSION]: process.env.npm_package_version || ‘1.0.0’,
});
const otlpExporter = new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || ‘http://localhost:4318/v1/traces’,
});
// Create tracer provider with resource and span processors
const provider = new NodeTracerProvider({
resource,
spanProcessors: [
new BatchSpanProcessor(otlpExporter, {
maxQueueSize: 100,
scheduledDelayMillis: 5000,
exportTimeoutMillis: 30000,
maxExportBatchSize: 50,
})
]
});
// Register instrumentations with more comprehensive coverage
registerInstrumentations({
tracerProvider: provider,
instrumentations: [
new HttpInstrumentation({
requestHook: (span, request) => {
span.setAttribute(‘http.request.method’, request.method);
},
}),
new NetInstrumentation(),
new ExpressInstrumentation(),
new PrismaInstrumentation({ middleware: true }),
],
});
// Register the provider
provider.register();
// Return the provider for potential manual instrumentation
return provider;
}
// Call this at application startup
setupTracing();
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
توضیح
ثبت تشخیصی: ورود به سیستم تشخیصی را با استفاده از ثبتکننده کنسول در قسمت فعال میکند INFO سطح برای رفع اشکال تنظیم ردیابی.
اولیه سازی منابع:
متادیتا را درباره سرویس تعریف می کند، مانند SERVICE_NAME و SERVICE_VERSION.
این ابرداده به هر ردیابی متصل می شود و به شناسایی خدماتی که ردیابی متعلق است کمک می کند.
صادرکننده ردیابی OTLP: پروتکل OpenTelemetry را پیکربندی می کند (OTLP) صادر کننده برای ارسال داده های ردیابی به باطن که در حال حاضر Jaeger از آن استفاده می کند HTTP پروتکل توجه داشته باشید که Jaeger را می توان با باطن دیگری مانند Honeycomb، Zipkin و غیره تعویض کرد و شما پروتکل را به کارآمدتر تغییر دهید. GRPC از جاری HTTP.
ارائه دهنده Tracer با پردازنده Span: a را ایجاد می کند NodeTracerProvider، که ردیاب ها و گستره های زیر را مدیریت می کند:
ثبت ابزار دقیق: به طور خودکار ردیابی کتابخانه ها و چارچوب ها را می گیرد:
HttpInstrumentation: زمان صرف شده توسط درخواست ها/پاسخ های HTTP را ثبت می کند.
NetInstrumentation: زمان صرف شده توسط رویدادهای شبکه سطح پایین را ثبت می کند.
ExpressInstrumentation: زمان مصرف شده توسط میان افزار Express و مسیرها را ردیابی می کند.
PrismaInstrumentation: زمان صرف شده توسط پرسش های SQL تولید شده توسط Prisma را ردیابی می کند
کد ابزار دقیق را در برنامه خود وارد کنید
پیکربندی ردیابی را در فایل برنامه اصلی خود وارد کرده و مقداردهی اولیه کنید main.ts:
import { NestFactory } from ‘@nestjs/core’;
import { SwaggerModule, DocumentBuilder } from ‘@nestjs/swagger’;
import { AppModule } from ‘./app.module’;
import ‘./tracing’;
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle(‘Tracing example’)
.setDescription(‘The tracing API description’)
.setVersion(‘1.0’)
.addTag(‘tracing’)
.build();
const documentFactory = () => SwaggerModule.createDocument(app, config);
SwaggerModule.setup(‘api-docs’, app, documentFactory);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
برنامه خود را اجرا کنید
pnpm run start:dev
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
تنظیم Jaeger برای محیط توسعه
ساده ترین راه برای راه اندازی Jaeger این است که وینچ docker-compose در محیط توسعه به خوبی کار می کند.
ایجاد کنید docker-compose.yaml فایل:
services:
jaeger:
image: jaegertracing/all-in-one:1.63.0
container_name: jaeger
environment:
COLLECTOR_OTLP_ENABLED: “true”
ports:
– “4317:4317” # For Jaeger-GRPC
– “4318:4318” # For Jaeger-HTTP
– “16686:16686” # # Web UI
networks:
default:
driver: bridge
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
برنامه Containerize (اختیاری)
می توانید استفاده کنید docker init اگر نسخه جدیدتر Docker را نصب کرده باشید، دستور تولید خودکار یک Dockerfile بهینه شده را صادر کنید.
# Arguments for versions
ARG NODE_VERSION=20.18.0
ARG PNPM_VERSION=9.12.2
ARG ALPINE_VERSION=3.20
################################################################################
# Base stage: Build the application
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS builder
# Set working directory
WORKDIR /usr/src/app
# Install pnpm globally with cache
RUN –mount=type=cache,target=/root/.npm \
npm install -g pnpm@${PNPM_VERSION}
# Copy package.json and pnpm-lock.yaml to install dependencies
COPY ../package.json pnpm-lock.yaml ./
# Install dependencies with cache
RUN –mount=type=cache,target=/root/.pnpm-store \
pnpm install –frozen-lockfile
# Copy the all application code
COPY .. .
# Setup prisma
RUN pnpm prisma generate
# Build the application
RUN pnpm run build
# Runner Stage
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS runner
# Set working directory
WORKDIR /usr/src/app
# Copy the built application from the builder stage
COPY –from=builder /usr/src/app/dist ./dist
COPY ../package.json pnpm-lock.yaml ./
COPY ../prisma/schema.prisma ./prisma/schema.prisma
# Install pnpm globally
RUN –mount=type=cache,target=/root/.npm \
npm install -g pnpm@${PNPM_VERSION}
# Install dependencies with cache
RUN –mount=type=cache,target=/root/.pnpm-store \
pnpm install –frozen-lockfile –prod
# Set NODE_ENV to production
ENV NODE_ENV=production
# Run the application
CMD [“pnpm”, “run”, “start:prod”]
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
Swagger UI
از http://localhost:3000/api-docs دیدن کنید و چند تماس API برقرار کنید
تجسم آثار
مرورگر خود را باز کنید و به http://localhost:16686 برای دیدن رابط کاربری Jaeger. چند درخواست را اجرا کنید و کلیک کنید ردیابی را پیدا کنید و روی یک Trace کلیک کنید
مقدمه
آیا تا به حال برای شما پیش آمده است که اشکالی در تولید رخ داده باشد و هیچ ایده ای نداشته باشید که چه مشکلی رخ داده است، زیرا گزارش های شما دقیقاً به شما نمی گوید که چه چیزی اشتباه رخ داده است یا درخواستی که معمولاً پردازش آن طول می کشد.
گاهی اوقات اشکال زدایی این مسائل بدون سیستم ردیابی غیرممکن است. سیستم ردیابی مانند یک دوربین مداربسته است که همه چیز را ضبط می کند چه اتفاقی افتاد، چه زمانی اتفاق افتاد، ترتیب وقایع چگونه بود، هر رویداد چقدر طول کشید. این اطلاعات برای اشکال زدایی و شناسایی گلوگاه های عملکرد در برنامه های پیچیده توزیع شده حیاتی است.
پیش نیاز
-
NodeJS
-
تایپ اسکریپت
-
NestJS
-
داکر
اصطلاحات
- ردیابی: یک ردیابی مانند یک نقشه سفر کامل از یک درخواست است که در کل سیستم توزیع شده شما حرکت می کند. آن را به عنوان یک گزارش سفر با جزئیات تصور کنید که درخواستی را از نقطه شروع تا مقصد نهایی خود دنبال می کند و هر توقف و تعامل در طول مسیر را به تصویر می کشد.
-
ابزار دقیق: فرآیند افزودن کد به برنامه شما برای جمع آوری داده های تله متری. مانند نصب ردیاب های GPS در قسمت های مختلف سیستم شماست.
-
صادر کننده: جزء مسئول ارسال داده های ردیابی جمع آوری شده به یک سیستم Back-end برای ذخیره سازی و تجزیه و تحلیل. به آن به عنوان یک سرویس پستی فکر کنید که گزارش سفر شما را به یک بایگانی مرکزی ارسال می کند.
-
دهانه:
- Root span: اولین دهانه در یک ردیابی که شروع کل سفر درخواست را نشان می دهد. مثل نقطه شروع سفرنامه شماست.
- Child span: دهانه ای که در یک دهانه دیگر تودرتو شده است، که نشان دهنده یک عملیات خاص تر در یک فرآیند گسترده تر است.
-
انتشار متن: مکانیسم انتقال اطلاعات ردیابی بین خدمات و اجزای مختلف. مانند پاسپورت یک مسافر است که حاوی جزئیات کامل سفر است.
-
معیارها: متریک ها داده های عددی هستند که عملکرد، سلامت و رفتار برنامه را نشان می دهند.
-
سیاههها: گزارشها ورودیهای متنی هستند که الگوهای استفاده، فعالیتها و عملیات را در برنامه شما توصیف میکنند.
سه سوار قابل مشاهده
مشاهدهپذیری به شما امکان میدهد یک سیستم را از بیرون درک کنید و به شما اجازه میدهد در مورد آن سیستم بدون اطلاع از عملکرد درونی آن سؤال بپرسید. این به شما امکان می دهد تا به راحتی مشکلات جدید را عیب یابی و مدیریت کنید، یعنی “ناشناخته های ناشناخته”. همچنین به سوال پاسخ می دهد “چرا این اتفاق می افتد؟”
راه اندازی پروژه
pnpm i -g @nestjs/cli
nest new tracing-app
cd tracing-app
نصب وابستگی ها
کتابخانه های مرتبط Jaeger و OpenTelemetry را نصب کنید:
pnpm install @opentelemetry/sdk-trace-node @opentelemetry/resources @opentelemetry/sdk-trace-base
pnpm install @opentelemetry/instrumentation @prisma/instrumentation @opentelemetry/instrumentation-net @opentelemetry/instrumentation-http @opentelemetry/instrumentation-express
pnpm install @opentelemetry/exporter-trace-otlp-http
pnpm install @opentelemetry/api @opentelemetry/semantic-conventions
Prisma ORM و SQLite را نصب کنید:
pnpm install @prisma/client sqlite3 class-validator
pnpm install prisma --save-dev
pnpm install --save @nestjs/swagger
Prisma را راه اندازی کنید:
npx prisma init
این یک ایجاد خواهد کرد prisma
دایرکتوری با a schema.prisma
فایل
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
}
مهاجرت های Prisma را اجرا کنید:
npx prisma migrate dev --name init
کلاینت Prisma را ایجاد کنید:
npx prisma generate
یک نقطه پایانی CRUD را تنظیم کنید
یک ماژول CRUD برای کاربران ایجاد کنید
pnpm nest generate resource users
این یک ایجاد خواهد کرد users
ماژول با کنترلر، سرویس و DTO.
ایجاد یک prisma.service.ts
فایل در پوشه پریسما
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
را به روز کنید users.module.ts
فایل برای گنجاندن PrismaService
:
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { PrismaService } from '../../prisma/prisma.service';
@Module({
controllers: [UsersController],
providers: [UsersService, PrismaService],
})
export class UsersModule { }
یک فایل به نام ایجاد کنید create-user.dto.ts
در users/dto
دایرکتوری:
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class CreateUserDto {
@ApiProperty({
description: 'The name of the user',
example: 'John Doe',
})
@IsNotEmpty()
@IsString()
name: string;
@ApiProperty({
description: 'The email of the user',
example: 'email@domain.com',
})
@IsNotEmpty()
@IsEmail()
email: string;
}
export class UpdateUserDto extends PartialType(CreateUserDto) {}
را به روز کنید users.service.ts
فایل برای استفاده از Prisma:
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
create(createUserDto: CreateUserDto) {
return this.prisma.user.create({
data: createUserDto,
});
}
findAll() {
return this.prisma.user.findMany();
}
findOne(id: number) {
return this.prisma.user.findUnique({
where: { id },
});
}
update(id: number, updateUserDto: UpdateUserDto) {
return this.prisma.user.update({
where: { id },
data: updateUserDto,
});
}
remove(id: number) {
return this.prisma.user.delete({
where: { id },
});
}
}
را به روز کنید users.controller.ts
فایل:
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { ApiGoneResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
@ApiTags('users')
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) { }
@ApiOperation({ summary: 'Create user' })
@ApiOkResponse({ description: 'User created' })
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@ApiOperation({ summary: 'Get all users' })
@ApiOkResponse({ description: 'Users found' })
@Get()
findAll() {
return this.usersService.findAll();
}
@ApiOperation({ summary: 'Get user by id' })
@ApiOkResponse({ description: 'User found' })
@ApiNotFoundResponse({ description: 'User not found' })
@ApiParam({ name: 'id', description: 'User id' })
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
@ApiOperation({ summary: 'Update user' })
@ApiOkResponse({ description: 'User updated' })
@ApiNotFoundResponse({ description: 'User not found' })
@ApiParam({ name: 'id', description: 'User id' })
@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(+id, updateUserDto);
}
@ApiOperation({ summary: 'Delete user' })
@ApiGoneResponse({ description: 'User deleted' })
@ApiParam({ name: 'id', description: 'User id' })
@Delete(':id')
remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
پیکربندی صادرکنندگان
یک فایل ایجاد کنید ردیابی.ts در شما src دایرکتوری:
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { NetInstrumentation } from '@opentelemetry/instrumentation-net';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { PrismaInstrumentation } from '@prisma/instrumentation';
import { Resource } from '@opentelemetry/resources';
import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
export function setupTracing() {
// Enable OpenTelemetry diagnostic logging
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
// Create a resource with service information
const resource = new Resource({
[ATTR_SERVICE_NAME]: process.env.SERVICE_NAME || 'tracer-app',
[ATTR_SERVICE_VERSION]: process.env.npm_package_version || '1.0.0',
});
const otlpExporter = new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces',
});
// Create tracer provider with resource and span processors
const provider = new NodeTracerProvider({
resource,
spanProcessors: [
new BatchSpanProcessor(otlpExporter, {
maxQueueSize: 100,
scheduledDelayMillis: 5000,
exportTimeoutMillis: 30000,
maxExportBatchSize: 50,
})
]
});
// Register instrumentations with more comprehensive coverage
registerInstrumentations({
tracerProvider: provider,
instrumentations: [
new HttpInstrumentation({
requestHook: (span, request) => {
span.setAttribute('http.request.method', request.method);
},
}),
new NetInstrumentation(),
new ExpressInstrumentation(),
new PrismaInstrumentation({ middleware: true }),
],
});
// Register the provider
provider.register();
// Return the provider for potential manual instrumentation
return provider;
}
// Call this at application startup
setupTracing();
توضیح
-
ثبت تشخیصی: ورود به سیستم تشخیصی را با استفاده از ثبتکننده کنسول در قسمت فعال میکند
INFO
سطح برای رفع اشکال تنظیم ردیابی. -
اولیه سازی منابع:
- متادیتا را درباره سرویس تعریف می کند، مانند
SERVICE_NAME
وSERVICE_VERSION
. - این ابرداده به هر ردیابی متصل می شود و به شناسایی خدماتی که ردیابی متعلق است کمک می کند.
- متادیتا را درباره سرویس تعریف می کند، مانند
-
صادرکننده ردیابی OTLP: پروتکل OpenTelemetry را پیکربندی می کند (OTLP) صادر کننده برای ارسال داده های ردیابی به باطن که در حال حاضر Jaeger از آن استفاده می کند HTTP پروتکل توجه داشته باشید که Jaeger را می توان با باطن دیگری مانند Honeycomb، Zipkin و غیره تعویض کرد و شما پروتکل را به کارآمدتر تغییر دهید. GRPC از جاری HTTP.
-
ارائه دهنده Tracer با پردازنده Span: a را ایجاد می کند
NodeTracerProvider
، که ردیاب ها و گستره های زیر را مدیریت می کند: -
ثبت ابزار دقیق: به طور خودکار ردیابی کتابخانه ها و چارچوب ها را می گیرد:
-
HttpInstrumentation
: زمان صرف شده توسط درخواست ها/پاسخ های HTTP را ثبت می کند. -
NetInstrumentation
: زمان صرف شده توسط رویدادهای شبکه سطح پایین را ثبت می کند. -
ExpressInstrumentation
: زمان مصرف شده توسط میان افزار Express و مسیرها را ردیابی می کند. -
PrismaInstrumentation
: زمان صرف شده توسط پرسش های SQL تولید شده توسط Prisma را ردیابی می کند
-
کد ابزار دقیق را در برنامه خود وارد کنید
پیکربندی ردیابی را در فایل برنامه اصلی خود وارد کرده و مقداردهی اولیه کنید main.ts:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
import './tracing';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('Tracing example')
.setDescription('The tracing API description')
.setVersion('1.0')
.addTag('tracing')
.build();
const documentFactory = () => SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api-docs', app, documentFactory);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
برنامه خود را اجرا کنید
pnpm run start:dev
تنظیم Jaeger برای محیط توسعه
ساده ترین راه برای راه اندازی Jaeger این است که وینچ docker-compose در محیط توسعه به خوبی کار می کند.
ایجاد کنید docker-compose.yaml
فایل:
services:
jaeger:
image: jaegertracing/all-in-one:1.63.0
container_name: jaeger
environment:
COLLECTOR_OTLP_ENABLED: "true"
ports:
- "4317:4317" # For Jaeger-GRPC
- "4318:4318" # For Jaeger-HTTP
- "16686:16686" # # Web UI
networks:
default:
driver: bridge
برنامه Containerize (اختیاری)
می توانید استفاده کنید docker init اگر نسخه جدیدتر Docker را نصب کرده باشید، دستور تولید خودکار یک Dockerfile بهینه شده را صادر کنید.
# Arguments for versions
ARG NODE_VERSION=20.18.0
ARG PNPM_VERSION=9.12.2
ARG ALPINE_VERSION=3.20
################################################################################
# Base stage: Build the application
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS builder
# Set working directory
WORKDIR /usr/src/app
# Install pnpm globally with cache
RUN --mount=type=cache,target=/root/.npm \
npm install -g pnpm@${PNPM_VERSION}
# Copy package.json and pnpm-lock.yaml to install dependencies
COPY ../package.json pnpm-lock.yaml ./
# Install dependencies with cache
RUN --mount=type=cache,target=/root/.pnpm-store \
pnpm install --frozen-lockfile
# Copy the all application code
COPY .. .
# Setup prisma
RUN pnpm prisma generate
# Build the application
RUN pnpm run build
# Runner Stage
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS runner
# Set working directory
WORKDIR /usr/src/app
# Copy the built application from the builder stage
COPY --from=builder /usr/src/app/dist ./dist
COPY ../package.json pnpm-lock.yaml ./
COPY ../prisma/schema.prisma ./prisma/schema.prisma
# Install pnpm globally
RUN --mount=type=cache,target=/root/.npm \
npm install -g pnpm@${PNPM_VERSION}
# Install dependencies with cache
RUN --mount=type=cache,target=/root/.pnpm-store \
pnpm install --frozen-lockfile --prod
# Set NODE_ENV to production
ENV NODE_ENV=production
# Run the application
CMD ["pnpm", "run", "start:prod"]
Swagger UI
از http://localhost:3000/api-docs دیدن کنید و چند تماس API برقرار کنید
تجسم آثار
مرورگر خود را باز کنید و به http://localhost:16686
برای دیدن رابط کاربری Jaeger. چند درخواست را اجرا کنید و کلیک کنید ردیابی را پیدا کنید و روی یک Trace کلیک کنید