歩哨のグラフィカルな能力


フィールドガイドでHasura 露出するGraphQL PostgresのAPIは、ノードで実装されたカスタム型で拡張されます.JSアプリケーションApollo Server . 当社のフロントエンド反応アプリケーションを介してHathuraとの対話Apollo Client , そして、我々のアプリケーションはHeroku . GraphSQLの固有の自己ドキュメンテーションは、開発者のツールの生態系を燃料化し、typescriptとの使用は非常に効率的な内部APIの開発に結果をもたらす.
反復速度は確かに重要な製品開発メトリックですが、機能の動作を理解することは等しく重要です.この相補的な情報は、将来の反復を通知するフィードバックループを提供する開発仮定と表面の不可避のバグを確認します.メトリック、ログ、トレースなどの適切なテレメトリデータを生成することにより、アプリケーションの動作を観察することができる.
採用したSentry , エラー追跡とパフォーマンス監視プラットフォームは、当社の製品の開始の最初の数週間です.我々は、パフォーマンス(トレース)とトリアージエラー(ログのサブセット)を診断する私たちの能力を向上させる、過去1年間での統合を繰り返している.この歩哨統合の概要は、特定のノードから派生しています.GraphSQLサーバーと反応GraphSQLクライアントが、任意のシステムにグラフを相互に適用することができます.

グラフサーバ


歩哨は情報を提供するguides 多くのプラットフォームで.サーバの場合、アポロサーバV 2をExpress ミドルウェアしたがって、歩哨のExpress Guide リクエスト、トレース、エラーハンドラでは大きな出発点です.
初期化の一部として、設定 tracesSampleRate このようなトレースのサンプリングは、我々のクォータに向かってカウントされます.さらに、我々はgit - commitハッシュをバインドしますDyno Metadata 特徴release version , 歩哨を監視するrelease health .
SentryのExpress互換の追跡ハンドラは、名前を持つすべての着信リクエストのトランザクションを開始するderived from HTTPメソッドとパス.これは休息APIのためによく働きます、しかし、GraphSQL実体はURLによって特定されません、そして、デフォルトで、すべてのGraphSQL要求はPOST /graphql . 適切な特異性を達成するために、我々はcustom plugin その資格transaction names アポロが要求を受け取るとき、文脈的なGraphSQL操作で.
アポロサーバプラグインrequestDidStart イベント
import * as Sentry from "@sentry/node";
import { ApolloServerPlugin } from "apollo-server-plugin-base";

export const sentryPlugin: ApolloServerPlugin = {
    requestDidStart({ request }) {
        if (request.operationName) {
            const scope = Sentry.getCurrentHub().getScope();
            const transaction = scope?.getTransaction(); // retrieve ongoing transaction

            if (transaction) {
                // qualify transaction name
                // i.e. "POST /graphql" -> "POST /graphql: MyOperation"
                scope?.setTransactionName(
                    `${transaction.name}: ${request.operationName}`
                );
            }
        }
    },
};
同様に、GraphSQLエラーは従来のREST APIと異なります.GraphSQL操作を実行している間にスローされる例外はerrors 応答体フィールドは、sentryのExpress互換のエラーハンドラによって本質的にキャプチャされません.我々は、特定のユーザとコンテキストで、これらのエラーを報告しますthis Sentry blog .
拡張アポロサーバプラグインdidEncounterErrors イベント
import * as Sentry from "@sentry/node";
import { ApolloError } from "apollo-server-express";
import { ApolloServerPlugin } from "apollo-server-plugin-base";

export const sentryPlugin: ApolloServerPlugin = {
    requestDidStart({ request }) {
        if (request.operationName) {
            // qualify transaction name
            // ...
        }

        return {
            didEncounterErrors(ctx) {
                if (!ctx.operation) {
                    return; // ignore unparsed operations
                }

                Sentry.withScope((scope) => {
                    if (ctx.context.currentUser) {
                        scope.setUser({
                            id: String(ctx.context.currentUser.id),
                            // ...
                        });
                    }

                    for (const error of ctx.errors) {
                        if (error.originalError instanceof ApolloError) {
                            continue; // ignore user-facing errors
                        }

                        Sentry.captureException(error, {
                            tags: {
                                graphqlOperation: ctx.operation?.operation,
                                graphqlOperationName: ctx.operationName,
                            },
                            contexts: {
                                graphql: {
                                    query: ctx.request.query,
                                    variables: JSON.stringify(
                                        ctx.request.variables,
                                        null,
                                        2
                                    ),
                                    errorPath: error.path,
                                },
                            },
                        });
                    }
                });
            },
        };
    },
};
最後に、Herokuが我々のアプリケーションを再開するとき(すなわち、新しいバージョンを展開するとき)、シナリオを優雅に扱いますdrain 前までの哨戒イベントclosing Expressサーバー.
優雅なシャットダウンのためのイベントの排水
import * as Sentry from "@sentry/node";

const server = app.listen(PORT);

process.on("SIGTERM", async function shutdown(signal: string) {
    console.log(`Shutting down via ${signal}`);

    try {
        await Sentry.close(2000);
    } catch (e) {
        console.error(e);
    }

    server.close(() => {
        console.log("HTTP server closed");
    });
});

グラフィカルクライアント


我々の反応アプリケーションの構成は、歩哨の次のReact Guide との統合をトレースしたサンプルブラウザでReact Router instrumentation . さらに、git commitハッシュをrelease version , 我々の急行アプリケーションに類似している.
アポロクライアントV 3テレメトリは、部分的に計装されますApollo Link Sentry , 安Apollo Link GraphSQL操作を役に立つものとして記録するミドルウェアbreadcrumbs 他の機能の中で.私たちは意図的にトランザクションと指紋設定を無効にします.
アポロリンク
import { SentryLink } from "apollo-link-sentry";

const sentryLink = new SentryLink({
    setTransaction: false,
    setFingerprint: false,
    attachBreadcrumbs: {
        includeError: true,
    },
});
このライブラリを補完する onError link 実際には、明示的なトランザクション名とコンテキストを使用してSentryにGraphSQLとネットワークエラーを報告します.エラーハンドラの引数は実際にはJavaScriptError オブジェクトしたがって、 Sentry.captureMessage が必要です.GraphSQLエラーは、より粒状でキャプチャされますfingerprint , SegtryイベントをGraphSQLの操作名でグループに分割します.onError リンク実装
import { onError } from "@apollo/client/link/error";
import * as Sentry from "@sentry/react";

const errorLink = onError(({ operation, graphQLErrors, networkError }) => {
    Sentry.withScope((scope) => {
        scope.setTransactionName(operation.operationName);
        scope.setContext("apolloGraphQLOperation", {
            operationName: operation.operationName,
            variables: operation.variables,
            extensions: operation.extensions,
        });

        graphQLErrors?.forEach((error) => {
            Sentry.captureMessage(error.message, {
                level: Sentry.Severity.Error,
                fingerprint: ["{{ default }}", "{{ transaction }}"],
                contexts: {
                    apolloGraphQLError: {
                        error,
                        message: error.message,
                        extensions: error.extensions,
                    },
                },
            });
        });

        if (networkError) {
            Sentry.captureMessage(networkError.message, {
                level: Sentry.Severity.Error,
                contexts: {
                    apolloNetworkError: {
                        error: networkError,
                        extensions: (networkError as any).extensions,
                    },
                },
            });
        }
    });
});
GraphSQL操作に関連したトランザクションとエラーの捕捉は、アプリケーションの動作をよりよく理解することを可能にしました.しかし、この値は、チームやプロセスに最も効果的な方法でテレメトリデータの操作可能なサブセットを浮上させることによって解除されます.機能の変化とソフトウェアの抽象化が進化するにつれて、計装はそれに同調しなければならない.継続的な注意力によってチームは積極的に問題を特定し、将来の開発を知らせるロバストなフィードバックループを作成することができる.
あなたは観察可能な製品開発に情熱的ですか?We're hiring エンジニアリング、製品、デザインを横断!