Node.js の Momento によるオブザーバビリティ
ロギング
すべての Momento SDK のゴールは、開発者が Momento のログ出力を、アプリケーションのログで使用しているのと同じ宛先に向けることができるようにすることです。
node.js で利用できるロギングライブラリはたくさんあります。人気のあるものをいくつか紹介します:
Momento がこれらのライブラリ (そしてそれ以上に) と互換性があることを保証するために、私たちは軽量なロギングファサードを提供しています。これを使うには、 MomentoLoggerFactory
インターフェイスと MomentoLogger
インターフェイスを実装するだけです:
export interface MomentoLogger {
error(msg: string, ...args: unknown[]): void;
warn(msg: string, ...args: unknown[]): void;
info(msg: string, ...args: unknown[]): void;
debug(msg: string, ...args: unknown[]): void;
trace(msg: string, ...args: unknown[]): void;
}
export interface MomentoLoggerFactory {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getLogger(loggerName: string | any): MomentoLogger;
}
ユーザの実装は、選択したロギングライブラリの薄いラッパーになるでしょう。私たちは、pino
ロガーを使用し動作する実装例を提供しています: https://github.com/momentohq/client-sdk-javascript/blob/main/examples/nodejs/doc-example-files/pino-logger.ts。
MomentoLoggerFactory
と MomentoLogger
を定義したら、最後に Momento クライアントを設定します:
return new CacheClient({
configuration: Configurations.Laptop.v1(
new PinoMomentoLoggerFactory({
transport: {
target: 'pino-pretty',
options: {
colorize: true,
},
},
})
),
credentialProvider: CredentialProvider.fromEnvironmentVariable({environmentVariableName: 'MOMENTO_API_KEY'}),
defaultTtlSeconds: 60,
});
そうすると、Momento からのログメッセージが pino のロギング環境を通して表示されるはずです。この場合、以下のようなログメッセージが表示されるはずです:
[1685649962168] INFO (CacheClient/4386 on mycomputer.local): Creating Momento CacheClient
[1685649962168] INFO (ControlClient/4386 on mycomputer.local): Creating cache: test-cache
メトリクス
メトリクスとは、システムのパフォーマンスや動作に関する定量的な情報を提供する測定値のことです。一定の間隔で取得・記録される数値であり、システムの傾向やパターンを理解するのに役立つ統計データを提供します。
特に Momento については、リクエストの数、時間、リクエストやレスポンスのサイズ、失敗率などのメトリクスを取得したいと思うかもしれません。Node.js SDK では、Momento の gRPC 呼び出しと応答をインターセプトするミドルウェアを使用して、これらをキャプチャします。以下は、OpenTelemetry と Prometheus を使用して、リクエストの種類によってファセットされたリクエスト数をキャプチャする例です:
まず初めにアプリケーション側でメトリクスの設定します:
const resource = Resource.default();
const metricsExporter = new PrometheusExporter({}, () => {
console.log('prometheus scrape endpoint: http://localhost:9464/metrics');
});
const meterProvider = new MeterProvider({
resource: resource,
});
meterProvider.addMetricReader(metricsExporter);
metrics.setGlobalMeterProvider(meterProvider);
次に、メトリックを取得するミドルウェアを実装します:
import {Middleware, MiddlewareRequestHandler} from '@gomomento/sdk';
import {metrics} from '@opentelemetry/api';
import {Counter} from '@opentelemetry/api/build/src/metrics/Metric';
import {
MiddlewareMessage,
MiddlewareMetadata,
MiddlewareStatus,
} from '@gomomento/sdk/dist/src/config/middleware/middleware';
class ExampleMetricMiddlewareRequestHandler implements MiddlewareRequestHandler {
private requestCounter: Counter;
constructor(requestCounter: Counter) {
this.requestCounter = requestCounter;
}
onRequestMetadata(metadata: MiddlewareMetadata): Promise<MiddlewareMetadata> {
return Promise.resolve(metadata);
}
onRequestBody(request: MiddlewareMessage): Promise<MiddlewareMessage> {
const requestType = request.constructor.name;
this.requestCounter.add(1, {'request.type': requestType});
return Promise.resolve(request);
}
onResponseMetadata(metadata: MiddlewareMetadata): Promise<MiddlewareMetadata> {
return Promise.resolve(metadata);
}
onResponseBody(response: MiddlewareMessage | null): Promise<MiddlewareMessage | null> {
return Promise.resolve(response);
}
onResponseStatus(status: MiddlewareStatus): Promise<MiddlewareStatus> {
return Promise.resolve(status);
}
}
/**
* Basic middleware implementation that captures a request count metric. See experimental-metrics-csv-middleware.ts for
* more comprehensive metrics, although be aware that that class is meant for troubleshooting and will eat disk space quickly.
*/
export class ExampleMetricMiddleware implements Middleware {
private readonly requestCounter: Counter;
constructor() {
const meter = metrics.getMeter('metric-middleware-meter');
this.requestCounter = meter.createCounter('momento_requests_counter', {
description: 'Momento GRPC requests',
});
}
onNewRequest(): MiddlewareRequestHandler {
return new ExampleMetricMiddlewareRequestHandler(this.requestCounter);
}
}
Momento CacheClient
を作成し、ミドルウェアを追加すると、リクエストごとにメトリックがインクリメントされます:
new CacheClient({
configuration: Configurations.Laptop.v1().addMiddleware(new ExampleMetricMiddleware()),
credentialProvider: CredentialProvider.fromEnvironmentVariable({
environmentVariableName: 'MOMENTO_API_KEY',
}),
defaultTtlSeconds: 60,
});
以下は、Momento に対して行われた get リクエストと set リクエストのグラフを表示する Grafana UI の例です:
トレース
トレースは、アプリケーション内のプロセスの詳細なタイムラインを提供し、特定のリクエストや操作に関係する異なるコンポーネントやサービス間の関係を示します。これにより、開発者はこれらの操作のシーケンスと期間を分析し、システム内のデータの流れをより深く理解することができます。
Momento Node.js SDK は、Momento サーバーとの通信に内部的に gRPC を使用しています。OpenTelemetry は、すべての gRPC 呼び出しをトレースで自動計測する機能を提供します。メトリクスのように、トレースを生成するためにミドルウェアのコードを追加する必要はありません。以下は、これらのコールのトレースを自動的に生成し、Zipkin にエクスポートする例です:
const resource = Resource.default();
const provider = new NodeTracerProvider({
resource: resource,
});
const exporter = new ZipkinExporter();
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
provider.register();
registerInstrumentations({
instrumentations: [new GrpcInstrumentation()],
});
これは Momento を組み込んだコードの前に実行する必要があります。
以下は Zipkin UI でキャッシュの作成、取得、セットのトレースを表示した例です:
アプリケーションのパフォーマンスがトレース生成によって影響を受けるのであれば、生成されるトレース数を削減するために、サンプリングを検討すべきです。OpenTelemetry でこれを行うには、2つの環境変数を設定します:
export OTEL_TRACES_SAMPLER="traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.1"
これらを設定することで、トレースの10%のみが作成されるようになります。