Universal log streaming SDK for Node.js, React Native & Browser
npm install @tracivo/sdkVersion 0.2.0
Node.js 애플리케이션을 위한 실시간 로그 스트리밍 SDK입니다. 콘솔 로그 자동 수집, 함수 로깅, HTTP 요청/응답 추적 기능을 제공합니다.
- Zero-Config Console Logging: console.log/error/warn 자동 캡처
- Function Logging: 자동 함수 계측 (입력/출력/에러/실행시간)
- Network Tracking: Outbound/Inbound HTTP 요청 모니터링
- Performance Monitoring: CPU, Memory, Network 메트릭 수집
- Winston & Pino Integration: 인기 로깅 라이브러리 지원
- Batch Processing: 설정 가능한 배치 크기로 효율적 로그 집계
- Failure Resilient: Circuit Breaker 패턴으로 자동 복구
- TypeScript: 완전한 타입 지원
``bash`
npm install @tracivo/sdk
`typescript
import Tracivo from '@tracivo/sdk';
// 초기화
Tracivo.init({
apiKey: 'YOUR_API_KEY',
});
// 모든 console 로그가 자동으로 캡처됩니다
console.log('Hello from Node.js');
console.error('An error occurred');
`
- 함수 로깅: 모든 함수 호출을 자동 계측 (입력/출력/에러/실행시간)
- Inbound HTTP 로깅: HTTP 요청/응답을 자동 캡처 (미들웨어 필요)
`typescript
// register.ts - 앱의 가장 첫 번째 import로 실행
import Tracivo from '@tracivo/sdk';
Tracivo.init({
apiKey: 'YOUR_API_KEY',
functionLogging: {
enabled: true, // 함수 자동 계측
},
networkLogging: {
enableInboundHttpLogging: true, // Inbound HTTP 요청/응답 캡처
},
});
`
실행 (CJS로 컴파일 후):
`bash`
node -r ./dist/register.js ./dist/app.js
> Note: Inbound HTTP 로깅은 Tracivo.middleware()가 적용된 경우에만 동작합니다.
함수 로깅을 사용하려면 SDK 초기화를 별도 파일로 분리하고, 다른 모듈보다 먼저 import해야 합니다.
`typescript
// register.ts - 반드시 첫 번째로 import
import Tracivo from '@tracivo/sdk';
Tracivo.init({
apiKey: 'YOUR_API_KEY',
functionLogging: { enabled: true },
networkLogging: { enableInboundHttpLogging: true },
});
`
`typescript
// app.ts
import './register'; // 반드시 첫 번째 import
import express from 'express';
import Tracivo from '@tracivo/sdk';
const app = express();
// 미들웨어 추가 - 요청별 TraceID 생성 및 Inbound HTTP 로깅
app.use(Tracivo.middleware());
app.get('/users', (req, res) => {
// 이 요청 내 모든 함수 로그가 동일한 traceId를 공유합니다
const traceId = Tracivo.getTraceId();
res.json({ users: [], traceId });
});
app.listen(3000);
`
실행 (CJS로 컴파일 후):
`bash`
node dist/app.js
> Note: 함수 로깅 없이 콘솔 로깅, 네트워크 추적, 미들웨어만 사용하려면 별도 파일 분리 없이 같은 파일에서 init()을 호출해도 됩니다.
Express 외 프레임워크는 runWithContext()를 사용하여 TraceID 컨텍스트를 설정합니다.
> Note: 아래 예제들은 함수 로깅 없이 컨텍스트 관리만 보여줍니다. 함수 로깅을 사용하려면 위 Express 예제처럼 register.js를 별도로 만들어 첫 번째로 import하세요.
#### Fastify
`typescript
import Fastify from 'fastify';
import Tracivo from '@tracivo/sdk';
import { randomUUID } from 'crypto';
Tracivo.init({
apiKey: 'YOUR_API_KEY',
});
const fastify = Fastify();
// preHandler 훅으로 컨텍스트 설정
fastify.addHook('preHandler', (request, reply, done) => {
const traceId = (request.headers['x-trace-id'] as string) || randomUUID();
reply.header('x-trace-id', traceId);
Tracivo.runWithContext({ traceId, requestId: traceId }, () => {
done();
});
});
fastify.get('/users', async () => {
const traceId = Tracivo.getTraceId();
return { users: [], traceId };
});
fastify.listen({ port: 3000 });
`
#### Koa
`typescript
import Koa from 'koa';
import Tracivo from '@tracivo/sdk';
import { randomUUID } from 'crypto';
Tracivo.init({
apiKey: 'YOUR_API_KEY',
});
const app = new Koa();
// 미들웨어로 컨텍스트 설정
app.use(async (ctx, next) => {
const traceId = (ctx.headers['x-trace-id'] as string) || randomUUID();
ctx.set('x-trace-id', traceId);
await new Promise
Tracivo.runWithContext({ traceId, requestId: traceId }, async () => {
await next();
resolve();
});
});
});
app.use(async (ctx) => {
const traceId = Tracivo.getTraceId();
ctx.body = { users: [], traceId };
});
app.listen(3000);
`
#### NestJS
NestJS가 Express 기반이면 Tracivo 미들웨어를 그대로 사용할 수 있습니다.
`typescript
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import Tracivo from '@tracivo/sdk';
async function bootstrap() {
Tracivo.init({
apiKey: 'YOUR_API_KEY',
functionLogging: { enabled: true },
});
const app = await NestFactory.create(AppModule);
// Express 미들웨어 적용
app.use(Tracivo.middleware());
await app.listen(3000);
}
bootstrap();
`
Fastify 기반 NestJS는 Interceptor를 사용합니다:
`typescript
// trace.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import Tracivo from '@tracivo/sdk';
import { randomUUID } from 'crypto';
@Injectable()
export class TraceInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable
const request = context.switchToHttp().getRequest();
const traceId = request.headers['x-trace-id'] || randomUUID();
return new Observable((subscriber) => {
Tracivo.runWithContext({ traceId, requestId: traceId }, () => {
next.handle().subscribe({
next: (value) => subscriber.next(value),
error: (err) => subscriber.error(err),
complete: () => subscriber.complete(),
});
});
});
}
}
`
#### Hapi
`typescript
import Hapi from '@hapi/hapi';
import Tracivo from '@tracivo/sdk';
import { randomUUID } from 'crypto';
Tracivo.init({
apiKey: 'YOUR_API_KEY',
});
const init = async () => {
const server = Hapi.server({ port: 3000 });
// onPreHandler 확장으로 컨텍스트 설정
server.ext('onPreHandler', (request, h) => {
const traceId = (request.headers['x-trace-id'] as string) || randomUUID();
return new Promise((resolve) => {
Tracivo.runWithContext({ traceId, requestId: traceId }, () => {
resolve(h.continue);
});
});
});
server.route({
method: 'GET',
path: '/users',
handler: () => {
const traceId = Tracivo.getTraceId();
return { users: [], traceId };
},
});
await server.start();
};
init();
`
#### 순수 Node.js HTTP
`typescript
import http from 'http';
import Tracivo from '@tracivo/sdk';
import { randomUUID } from 'crypto';
Tracivo.init({
apiKey: 'YOUR_API_KEY',
});
const server = http.createServer((req, res) => {
const traceId = (req.headers['x-trace-id'] as string) || randomUUID();
res.setHeader('x-trace-id', traceId);
Tracivo.runWithContext({ traceId, requestId: traceId }, () => {
// 요청 처리
if (req.url === '/users') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ users: [], traceId: Tracivo.getTraceId() }));
} else {
res.writeHead(404);
res.end('Not Found');
}
});
});
server.listen(3000);
`
`javascript
const winston = require('winston');
const Tracivo = require('@tracivo/sdk').default;
Tracivo.init({ apiKey: 'YOUR_API_KEY' });
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
Tracivo.winstonTransport(),
],
});
logger.info('Winston log');
logger.error('Winston error');
`
`javascript
const pino = require('pino');
const Tracivo = require('@tracivo/sdk').default;
Tracivo.init({ apiKey: 'YOUR_API_KEY' });
const logger = pino({
transport: {
targets: [
{ target: 'pino-pretty', level: 'info' },
Tracivo.pinoStream(),
],
},
});
logger.info('Pino log');
`
`typescript
Tracivo.init({
// === 기본 설정 ===
apiKey: 'YOUR_API_KEY', // 필수
endpoint: 'http://localhost:3000/v1', // 기본값
enabled: true, // SDK 활성화 여부
// === 배치 설정 ===
batchSize: 50, // 배치당 로그 수
flushInterval: 1000, // 배치 전송 주기 (ms)
maxBufferSize: 10000, // 최대 버퍼 크기
// === 샘플링 ===
sampleRate: 1.0, // 샘플링 비율 (0.0 ~ 1.0)
// === 재시도 및 타임아웃 ===
maxRetries: 5, // 최대 재시도 횟수
recoveryTimeout: 30000, // 실패 후 복구 대기 시간 (ms)
transportTimeout: 5000, // HTTP 요청 타임아웃 (ms)
// === 메트릭 ===
collectMetrics: true, // 성능 메트릭 수집
metricsInterval: 30000, // 메트릭 수집 주기 (ms)
// === 네트워크 ===
collectNetwork: true, // Outbound HTTP 추적
// === 함수 로깅 ===
functionLogging: {
enabled: true, // 함수 로깅 활성화
include: [/src\//], // 포함할 경로 패턴
exclude: [/node_modules/], // 제외할 경로 패턴
excludeFunctions: [/^_/], // 제외할 함수명 패턴
wrapNestedObjects: true, // 중첩 객체 함수도 래핑
wrapGettersSetters: true, // getter/setter 래핑
enableAutoMasking: true, // 자동 민감정보 마스킹
sensitiveKeywords: ['secret'], // 추가 민감 키워드
onFunctionLog: (entry) => {}, // 함수 로그 콜백
onRequest: (info) => {}, // 요청 시작 콜백
onResponse: (info) => {}, // 응답 완료 콜백
},
// === 네트워크 로깅 ===
networkLogging: {
enableInboundHttpLogging: true, // Inbound HTTP 로깅 (미들웨어 필요)
},
// === 에러 리포팅 ===
errorReporting: {
enabled: true, // SDK 내부 에러 리포팅
debug: false, // 디버그 모드 (콘솔 출력)
},
// === 메타데이터 ===
metadata: {
service: 'my-app',
environment: 'production',
},
// === 에러 핸들러 ===
onError: (error) => {
console.error('Tracivo Error:', error);
},
});
`
`typescript`
interface FunctionLogEntry {
event: 'start' | 'end' | 'error'; // 이벤트 타입
functionName: string; // 함수 이름
modulePath?: string; // 모듈 경로
traceId?: string; // 추적 ID
args?: unknown[]; // 함수 인자 (start)
result?: unknown; // 반환값 (end)
error?: string; // 에러 메시지 (error)
duration?: number; // 실행 시간 (ms)
timestamp: number; // 타임스탬프
}
`javascript
// password, token, secret 등이 포함된 파라미터는 자동으로 마스킹됩니다
function login(email, password) {
// ...
}
// 로그 출력: args: ["user@example.com", "[REDACTED]"]
`
`javascript
// 미들웨어가 요청별 TraceID를 생성합니다
app.use(Tracivo.middleware());
app.get('/api/users', async (req, res) => {
// 현재 요청의 TraceID 조회
const traceId = Tracivo.getTraceId();
// 커스텀 컨텍스트에서 실행
Tracivo.runWithContext({ traceId: 'custom-trace-id' }, () => {
// 이 블록 내 모든 함수 로그는 'custom-trace-id'를 사용합니다
doSomething();
});
});
`
Tracivo은 자동으로 성능 메트릭을 수집합니다:
`javascript
const stats = Tracivo.getStats();
console.log(stats);
// {
// bufferSize: 15,
// isHealthy: true,
// failureCount: 0,
// metrics: {
// timestamp: 1234567890,
// platform: 'node',
// cpu: { usage: 45.2, cores: 8 },
// memory: {
// used: 125829120,
// total: 16777216000,
// percentage: 0.75
// },
// network: {
// requests: { total: 150, success: 148, failed: 2, pending: 0 },
// traffic: { sent: 52480, received: 1048576 },
// avgResponseTime: 245.6
// }
// }
// }
`
| Metric | Description |
|--------|-------------|
| CPU Usage | 프로세스 CPU 사용률 |
| Memory Usage | 메모리 사용량/비율 |
| Network In/Out | 송수신 트래픽 |
| Request Success/Failure | HTTP 요청 성공/실패 |
| Avg Response Time | 평균 응답 시간 |
#### init(config: ExtendedTracivoConfig): Tracivo
SDK를 초기화합니다.
#### isInitialized(): boolean
SDK 초기화 여부를 확인합니다.
#### destroy(): void
SDK를 정리하고 원래 console 메서드를 복원합니다.
#### flush(): void
버퍼의 로그를 즉시 전송합니다.
#### getStats(): Stats | null
현재 버퍼 상태와 메트릭을 반환합니다.
#### winstonTransport(): WinstonTransport
Winston transport 인스턴스를 반환합니다.
#### pinoStream(): PinoStream
Pino stream 인스턴스를 반환합니다.
#### middleware(): MiddlewareFunction
Express/Connect 호환 미들웨어를 반환합니다. 요청별 TraceID 생성 및 Inbound HTTP 로깅을 담당합니다.
#### getTraceId(): string | undefined
현재 컨텍스트의 TraceID를 반환합니다.
#### getContext(): FunctionLogContext | undefined
현재 컨텍스트를 반환합니다.
#### runWithContext
지정된 컨텍스트 내에서 콜백을 실행합니다.
#### getFunctionLogs(limit?: number): FunctionLogEntry[]
최근 함수 로그를 반환합니다 (기본값: 100개).
#### clearFunctionLogs(): void
함수 로그 버퍼를 비웁니다.
#### isFunctionLoggingActive(): boolean
함수 로깅 활성화 여부를 확인합니다.
PM2와 자동으로 호환됩니다. 로그에 PM2 메타데이터가 포함됩니다:
- pm2: booleanpm2_instance
- : 인스턴스 IDpid
- : 프로세스 ID
SDK는 애플리케이션 크래시를 유발하지 않도록 설계되었습니다:
- Circuit Breaker: 5회 연속 실패 시 자동 비활성화
- Auto Recovery: 30초 후 자동 재활성화
- Buffer Limits: 설정 가능한 최대 버퍼 크기로 메모리 오버플로우 방지
- Timeout Protection: 모든 네트워크 요청에 타임아웃 적용 (기본 5초)
- Graceful Degradation: SDK 실패 시에도 정상 로깅 유지
완전한 TypeScript 지원:
`typescript
import Tracivo, {
ExtendedTracivoConfig,
FunctionLogEntry,
FunctionLoggingConfig,
} from '@tracivo/sdk';
const config: ExtendedTracivoConfig = {
apiKey: 'YOUR_API_KEY',
functionLogging: {
enabled: true,
onFunctionLog: (entry: FunctionLogEntry) => {
console.log(entry.functionName, entry.duration);
},
},
};
Tracivo.init(config);
`
- Node.js: >= 22.0.0
- Module System: CommonJS (CJS)
`bash의존성 설치
pnpm install
UNLICENSED - All rights reserved
이슈 및 질문: https://github.com/olivelabs/logstream-sdk