Google Analytics の サイトの速度指標を javascript timing api で 再現する


Navigation Timing API - Web API インターフェイス | MDN をここ直近で調べていて、Google Analytics の サイトの速度 との関係が気になりましたので、まとめます。
各計測方法は、ドキュメントが見つけられず推測になります。


前提

以下の環境で作業は実施しています。

  • OS

    % sw_vers 
    ProductName:    Mac OS X
    ProductVersion: 10.12.6
    BuildVersion:   16G29
    

  • 動作確認を行ったブラウザ

    Google Chrome
    バージョン: 61.0.3163.79(Official Build) (64 ビット)
    


動機

  1. ブラウザ操作ツールで、フロントエンドのパフォーンマンスを測定したい。

  2. フロンドエンドのパフォーマンス測定用途で、javascript timing api 使えそうに思ったが、なるべく一般化された指標をもちいたい。

  3. Google Analytics 上で閲覧できる速度指標が、javascript timing api で計算されているので、その計算式を再現すれば、画面と一致して見やすい。(かもしれない。)

というのが計算式を作成する動機になります。


参考


Google Analytics の サイトの速度に関する理解

確認方法

現状(2017/09/07)だと、左メニュー の [行動] > [サイトの速度] > [ページ速度] から確認できます。

エクスプローラ タブ

時系列でのデータを閲覧することができます。選択した指標の平均値に対して、プライマリディメンションに紐づく指標がどれくらい乖離しているかがわかるので、パフォーマンスの悪い箇所を効率的に見つけるのに役に立つ。という理解でいます。

分布

以下の時間指標があり、どのような時間のばらつきで、ページが表示されているのかを確認できます。
[ページ読み込み時間バケット(秒)] が全ての合算です。
計測している速度指標としては以下があります。

  • ページ読み込み時間バケット(秒)

  • リダイレクト時間バケット(秒)

  • ドメイン ルックアップ時間バケット(秒)

  • サーバー接続時間バケット(秒)

  • サーバー応答時間バケット(秒)

  • ページ ダウンロード時間バケット(秒)

  • ドキュメント インタラクティブ時間バケット(秒)

  • ドキュメント コンテンツ読み込み時間バケット(秒)

この記事では、この 速度指標の javascirptでの計算方法とまとめます。

地図表示

全世界での、ページ平均読み込み時間の確認ができます。
直帰率との組み合わせを閲覧できたりしますので、3G回線がメインの国だと、ページ平均読み込み時間が長く、直帰率が高い等の相関も見れるのではないかと思います。


Google Analytics の サイトの速度指標に関するドキュメント

Google Analytics が取得している速度指標は、公式ドキュメントが参考になりました。

javascript navigation time API の計算で、各 Metrics が計算されています。
Metrics の平均値で、Calculated Metrics が算出されています。

Metrics

Metrics には以下があります。
末尾にTimeのついた指標は、計測した値の合計値になり、実際にかかった時間を得るには、末尾Sample で取得できるサンプリング数で割る必要があります。

  • ga:pageLoadTime

  • ga:pageLoadSample

  • ga:domainLookupTime

  • ga:pageDownloadTime

  • ga:redirectionTime

  • ga:serverConnectionTime

  • ga:serverResponseTime

  • ga:speedMetricsSample

  • ga:domInteractiveTime

  • ga:domContentLoadedTime

  • ga:domLatencyMetricsSample

Calculated Metrics

Calculated Metrics には以下があります。
こちらは、ドキュメントで計算式が説明されていて、Metrics を元に計算した値であることがわかります。
個別指標をサンプル数で割り、1000で割った(ミリ秒からを秒換算にする)式になります。
Google Analytics での日本語名の指標と対応していますので、日本語名と紐づけて表形式で記載します。

指標名(日本語名)指標名(英語)計算式
ページ読み込み時間バケット(秒)ga:avgPageLoadTime(ga:pageLoadTime / ga:pageLoadSample / 1000)
リダイレクト時間バケット(秒)ga:avgRedirectionTime(ga:redirectionTime / ga:speedMetricsSample / 1000)
ドメイン ルックアップ時間バケット(秒)ga:avgDomainLookupTime(ga:domainLookupTime / ga:speedMetricsSample / 1000)
サーバー接続時間バケット(秒)ga:avgServerConnectionTime(ga:serverConnectionTime / ga:speedMetricsSample / 1000)
サーバー応答時間バケット(秒)ga:avgServerResponseTime(ga:serverResponseTime / ga:speedMetricsSample / 1000)
ページ ダウンロード時間バケット(秒)ga:avgPageDownloadTime(ga:pageDownloadTime / ga:speedMetricsSample / 1000)
ドキュメント インタラクティブ時間バケット(秒)ga:avgDomInteractiveTime(ga:domInteractiveTime / ga:domLatencyMetricsSample / 1000)
ドキュメント コンテンツ読み込み時間バケット(秒)ga:avgDomContentLoadedTime(ga:domContentLoadedTime / ga:domLatencyMetricsSample / 1000)

Metrics を javascript に置き換える。

本題です。
Metrics を javascript に置き換えます。
Calculated MetricsMetrics 同士 の 計算式なので Metrics の置き換えができれば芋づるでCalculated Metrics の置き換えもできます。

置き換えの前提

  • ga:pageLoadTime 等のGA上の指標は、ページ単位で計測した時間 * 計測ページ 数の合計で表されますが、以下のjavascriptは ページ単位で計測した時間 を計算するjavascriptになります。

  • あくまでドキュメントを見ながら推測でこれじゃないかという計算式を記述しています。
    Gooogle Analytics 側の実際の計算式は違ったはあり得ると思います。が、近い値は出ているかと思ってます。

ga:pageLoadTime ページ読み込み時間

Navigation Timing API - Web API インターフェイス | MDN を見る限り、

    var perfData = window.performance.timing; 
    var pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
    console.log(pageLoadTime);
です。
statckovergflow には以下が記載されていました。サイトの速度レポートの分析 - アナリティクス ヘルプで、ページの読み込みが完了するまでなので perfData.loadEventEnd - perfData.navigationStart; での計算が正しいのかなと思いました。
あまり自信はございません。
    // stackoverflowの記事には以下が記載されていました。   
    var pageLoadTime = perfData.loadEventStart - perfData.navigationStart;
    console.log(pageLoadTime);
時刻はUNIX時間 - Wikipedia で表現されているので、時刻差を人間が見てわかりやすいようにミリ秒 > 秒 に変換します。
    var perfData = window.performance.timing; 
    var pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
    console.log(pageLoadTime / 1000 + ":seconds");
実際にブラウザで計測してみたところ、perfData.loadEventEnd はページの読み込みが完全に完了するまでは、負の値をとります。
実行はページの読み込み完了を検知して実行する必要があります。

ga:redirectionTime リダイレクト時間

リダイレクトにかかった時間です。
プロパティ redirectEndredirectStart を使います。

    var perfData = window.performance.timing;
    var redirectionTime = perfData.redirectEnd - perfData.redirectStart;
    console.log(redirectionTime / 1000 + ":seconds");

    // stackoverflowの記載だとこちら
    // ユーザーからみてのリダイレクト時間はこちらな気がします。
    // が、ブラウザ側を採用
    var redirectionTime = perfData.fetchStart - perfData.navigationStart;
    console.log(redirectionTime / 1000 + ":seconds");

ga:domainLookupTime ドメイン ルックアップ時間

DSN LookUp にかかった時間です。
navigation timing api に domainLookupStartdomainLookupEnd というプロパティがあります。
それを計算に使用します。

    var perfData = window.performance.timing; 
    var domainLookupTime = perfData.domainLookupEnd - perfData.domainLookupStart;    
    console.log(domainLookupTime / 1000 + ":seconds");

ga:serverConnectionTime サーバー接続時間

サーバへの接続が確立にかかった時間です。
プロパティ connectEndconnectStart を使います。

    var perfData = window.performance.timing;
    var serverConnectionTime = perfData.connectEnd - perfData.connectStart;
    console.log(serverConnectionTime / 1000 + ":seconds");

ga:serverResponseTime サーバー応答時間

ユーザーの所在地からサーバーまでのネットワーク時間など、ユーザーの要求にサーバーが応答するまでの時間。
なので、プロパティ responseStartrequestStart の差と考えました。

    var perfData = window.performance.timing;
    var serverResponseTime = perfData.responseStart - perfData.requestStart;
    console.log(serverResponseTime / 1000 + ":seconds");

ga:pageDownloadTime 平均ダウンロード時間

ページのダウンロードにかかった時間です。
プロパティ responseEndresponseStart を使います。

    var perfData = window.performance.timing;
    var pageDownloadTime = perfData.responseEnd - perfData.responseStart;
    console.log(pageDownloadTime / 1000 + ":seconds");

ga:domInteractiveTime ドキュメント インタラクティブ時間

ブラウザがドキュメントを解析(DOMInteractive)するのにかかった平均時間(秒数)です。
計算式はあまり自信がございません。
domInteractive と、domComplete の差と考えました。

    var perfData = window.performance.timing;
    var domInteractiveTime = perfData.domComplete - perfData.domInteractive;
    console.log(domInteractiveTime / 1000 + ":seconds");

ga:domContentLoadedTime ドキュメント コンテンツ読み込み時間

ブラウザがドキュメントを解析し、繰延スクリプトやパーサー挿入スクリプト(DOMContentLoaded)を実行するのにかかった平均時間(秒数)です。
domContentLoadedEventStartdomContentLoadedEventEnd の差と考えました。

    var perfData = window.performance.timing;
    var domContentLoadedTime = perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart;
    console.log(domContentLoadedTime / 1000 + ":seconds");  


バックエンド側の指標、フロントエンド側の指標でグループ化して、JSONで書き出す。

上記個別に記載した javascript をまとめ、サーバー側指標、クライアント側指標でグループ化してJSONに書き出します。
backEnd を バックエンド側 指標、
frontEnd を フロントエンド側 指標として設定します。

  • dumpTimingMetrix.js

    function dumpTimingMetrix() {
    
        var perfData = window.performance.timing;
    
        var jsonObject = {
            "backEnd" : { 
                "pageLoadTime" : perfData.fetchStart - perfData.navigationStart,
                "redirectionTime" : perfData.redirectEnd - perfData.redirectStart,
                "domainLookupTime" : perfData.domainLookupEnd - perfData.domainLookupStart,  
                "serverConnectionTime" : perfData.connectEnd - perfData.connectStart,  
                "serverResponseTime" : perfData.responseStart - perfData.requestStart,
                "pageDownloadTime" : perfData.responseEnd - perfData.responseStart
            },
            "frontEnd": { 
                "pageLoadTime" : perfData.fetchStart - perfData.navigationStart,
                "domInteractiveTime" : perfData.domComplete - perfData.domInteractive,
                "domContentLoadedTime" : perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart
            }
        };
        return JSON.stringify(jsonObject);
    }
    

  • OUTPUT
    OUTPUTとして、以下が得られます。

    {
        "backEnd":{  
           "pageLoadTime":21,
           "redirectionTime":0,
           "domainLookupTime":3072,
           "serverConnectionTime":83,
           "serverResponseTime":257,
           "pageDownloadTime":245
        },
        "frontEnd":{  
           "pageLoadTime":21,
           "domInteractiveTime":4830,
           "domContentLoadedTime":51
        }
    }
    

これをセレニウム からの javascirpt 実行により、結果をストレージに記録しようと思います。
以上です。

コメント