メインコンテンツまでスキップ

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ロガーを使用する完全で動作する実装例を提供しています。ソースコードはこちらを参照してください。

MomentoLoggerFactoryのインスタンスを作成するときに、特定のロガーレベルを指定することができます。その後、指定したロガーレベルを使用する MomentoLogger` インスタンスをそのファクトリーから取得できます。

// Setting the level to ERROR means you will see error messages but
// no trace, info, debug, or warning messages.
const errorLoggerFactory = new DefaultMomentoLoggerFactory(DefaultMomentoLoggerLevel.ERROR);
const errorLogger = errorLoggerFactory.getLogger('momento-error-logger');
errorLogger.error('error in the code!');

// Setting the level to DEBUG means you will see error, info, debug,
// and warning messages but no trace messages.
const debugLoggerFactory = new DefaultMomentoLoggerFactory(DefaultMomentoLoggerLevel.DEBUG);
const debugLogger = debugLoggerFactory.getLogger('momento-debug-logger');
debugLogger.debug('helpful debugging message');

MomentoLoggerFactoryMomentoLogger` を定義したら、最後に 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の場合、リクエストの数、時間、リクエストやレスポンスのサイズ、失敗率など、クライアント側のメトリクスを取得したい場合があります。

これらのメトリクスを出力する最も簡単な方法は、ExperimentalMetricsMiddlewareクラスのいずれかを使用することです。これらのクラスはJSON形式でメトリクスを出力します:

(Momento: _ExperimentalMetricsLoggingMiddleware):
{
"momento": {
"numActiveRequestsAtStart": 1,
"numActiveRequestsAtFinish": 1,
"requestType": "_GetRequest",
"status": 0,
"startTime": 1697663118489,
"requestBodyTime": 1697663118489,
"endTime": 1697663118492,
"duration": 3,
"requestSize": 32,
"responseSize": 2,
"connectionID": "0"
}
}

将来のリリースでは、このフォーマットが安定したとみなされた時点で、このクラスは Experimental という接頭辞を取り除いた名前に変更される予定です。利用可能なミドルウェア・クラスは2つあります:

  • ExperimentalMetricsLoggingMiddleware:選択したロガーにメトリクスを出力します。警告: リクエスト量に応じて、このミドルウェアは大量のログを出力します。ローカルディスクに直接ログを書き込む場合は、ディスクの使用量に注意し、logrotateのようなツールでログのローテーション/圧縮を有効にしてください。
  • ExperimentalMetricsCsvMiddleware:メトリクスをCSVファイルに出力します。警告: このミドルウェアを有効にすると、ちょっとしたパフォーマンス上の影響があるかもしれません。リクエスト量によっては、CSVファイルのサイズがすぐに大きくなる可能性があります。

ログファイルやCSVを分析したり、Momentoと共有することで、パフォーマンスの問題を診断することができます。また、ログを AWS CloudWatch Log Group に転送し、CloudWatch ダッシュボードを作成して Momento リクエストを監視することもできます。Momento メトリクスダッシュボードの起動例 とオプションのサンプルアプリケーションは、Node.js SDK で利用できる。LambdaとFargateのサンプルアプリケーションは、ExperimentalMetricsLoggingMiddlewareクラスとCloudWatchメトリックフィルタを利用して、下図のようなダッシュボードを生成します。

An image of a CloudWatch dashboard with nine graphs populated by Momento metrics

また、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の例です: 画像](./images/grafana_screenshot.png)

トレース

トレースは、アプリケーション内のプロセスの詳細なタイムラインを提供し、特定のリクエストや操作に関与する異なるコンポーネントやサービス間の関係を示します。これにより、開発者はこれらの操作のシーケンスと期間を分析することができ、システム内をデータがどのように流れるかをより理解しやすくなります。

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%のみが作成されるようになります。