1

我正在嘗試爲AngularJS(1.5.x)實現一個攔截器,它可以測量每個api調用並將它們的持續時間發送到Google Analytics。如何衡量AngularJS中的請求(XHR)持續時間?

我已經開始與一個虛擬實現這一點,只用new Date().getTime()

(function (angular) { 
    'use strict'; 

    angular.module('MODULE') 
    .factory('HttpLoadingInterceptor', HttpLoadingInterceptor) 
    .config(HttpLoadingInterceptorConfig); 

    HttpLoadingInterceptor.$inject = ['$injector', '$q', 'AnalyticsService', '_']; 

    HttpLoadingInterceptorConfig.$inject = ['$httpProvider']; 

    function HttpLoadingInterceptor ($injector, $q, AnalyticsService, _) { 
    var REQUEST_GA_EVENT_NAME = 'REQUEST'; 

    return { 
     request: request, 
     requestError: requestError, 
     response: response, 
     responseError: responseError 
    }; 

    function request (config) { 
     config.requestTimestamp = now(); 
     return config || $q.when(config); 
    } 

    function requestError (rejection) { 
     rejection.config.responseTimestamp = now(); 
     trackRequest(rejection); 
     return $q.reject(rejection); 
    } 

    function response (response) { 
     response.config.responseTimestamp = now(); 
     trackRequest(response); 
     return response || $q.when(response); 
    } 

    function responseError (rejection) { 
     rejection.config.responseTimestamp = now(); 
     trackRequest(rejection); 
     return $q.reject(rejection); 
    } 

    function trackRequest (response) { 
     if (!_.startsWith(response.config.url, 'api')) { 
     return; 
     } 
     AnalyticsService.trackEvent(
     REQUEST_GA_EVENT_NAME, 
     response.config.url, 
     response.status, 
     response.config.responseTimestamp - response.config.requestTimestamp 
    ); 
    } 

    function now() { 
     return new Date().getTime(); 
    } 
    } 

    function HttpLoadingInterceptorConfig ($httpProvider) { 
    $httpProvider.interceptors.push('HttpLoadingInterceptor'); 
    } 
})(window.angular); 

它看起來不錯乍一看,但相比那些在Chrome中網絡選項卡收集了請求的持續時間,我注意到那些收集在我的代碼中的人總是比Chrome收集的更高(有時太多!)。

說,我便想起另一個想法,就是利用用戶導航API的,所以我改變了我的代碼:

(function (angular, performance) { 
    'use strict'; 

    angular.module('MODULE') 
    .factory('HttpLoadingInterceptor', HttpLoadingInterceptor) 
    .config(HttpLoadingInterceptorConfig); 

    HttpLoadingInterceptor.$inject = ['$injector', '$q', 'AnalyticsService', '_']; 

    HttpLoadingInterceptorConfig.$inject = ['$httpProvider']; 

    function HttpLoadingInterceptor ($injector, $q, AnalyticsService, _) { 
    var REQUEST_GA_EVENT_NAME = 'REQUEST'; 
    var measureReqCnt = 1; 

    return { 
     request: request, 
     requestError: requestError, 
     response: response, 
     responseError: responseError 
    }; 

    function request (config) { 
     if (shouldMeasure(config.url)) { 
     measureRequest(config); 
     } 
     return config || $q.when(config); 
    } 

    function requestError (rejection) { 
     if (shouldMeasure(rejection.config.url)) { 
     trackRequest(rejection); 
     } 
     return $q.reject(rejection); 
    } 

    function response (response) { 
     if (shouldMeasure(response.config.url)) { 
     trackRequest(response); 
     } 
     return response || $q.when(response); 
    } 

    function responseError (rejection) { 
     if (shouldMeasure(rejection.config.url)) { 
     trackRequest(rejection); 
     } 
     return $q.reject(rejection); 
    } 

    function shouldMeasure (url) { 
     return performance && _.startsWith(url, 'api'); 
    } 

    function measureRequest (config) { 
     config.measureName = [config.url, measureReqCnt++].join('_'); 
     config.markStartName = [config.measureName, 'start'].join('_'); 
     config.markEndName = [config.measureName, 'end'].join('_'); 
     performance.mark(config.markStartName); 
    } 

    function trackRequest (response) { 
     performance.mark(response.config.markEndName); 
     performance.measure(response.config.measureName, response.config.markStartName, response.config.markEndName); 
     var entries = performance.getEntriesByName(response.config.measureName, 'measure'); 
     if (entries && entries.length) { 
     AnalyticsService.trackEvent(
      REQUEST_GA_EVENT_NAME, 
      response.config.url, 
      response.status, 
      entries[0].duration 
     ); 
     } 
    } 
    } 

    function HttpLoadingInterceptorConfig ($httpProvider) { 
    $httpProvider.interceptors.push('HttpLoadingInterceptor'); 
    } 
})(window.angular, window.performance); 

..但我再次收到那些收集不同的結果鉻甚至不是那些測量使用new Date().getTime()

我做錯了什麼?我該怎麼做對嗎?也許資源時間API? AngularJS肯定會強加一點。

我無法使用用戶導航API方法 - window.performacne.getEntries()來查找我的請求持續時間,因爲我無法識別特定請求。每個請求的參數在那裏都不可用,而且我只有很多具有不同參數的API調用。

我應該修飾AngularJS使用的原生XHR請求嗎?

回答

0

如何利用performance.now()? (https://developer.mozilla.org/en-US/docs/Web/API/Performance/now),這是專爲高精度性能跟蹤相比,只是日期()?

請參見: performance.now() vs Date.now()

另外,你關心絕對數字?對於性能跟蹤,有時相對是最好的,因爲比較蘋果和橙子很少給出真正的數據點。

我的猜測是在使用計時器和計時時間點之間只有差異。 Chrome開發工具可能更接近「接線」,而不是包含應用程序代碼。如果有幫助,我們也有一個API分析和調試工具(www.moesif.com/features),但完全披露我是首席執行官。

+0

當然,我試過性能API,我得到了相同的結果==沒有有效的結果。正如我在Angular團隊確定的那樣,Angular給了我們自己的開銷,所以用這些方法衡量時間是無效的。 – mrzepinski

+0

如果你不想在你的數字中包含任何開銷,我想你必須找到一種方法來使用'performance.getEntries()'(或[PerformanceObserver](https://developer.mozilla.org/) EN-US /文檔/網絡/ API/PerformanceObserver))。順便說一下,我不知道你的確切目標與這個測量呼叫,但你確定你不想要那種開銷?因爲,這不僅僅是框架開銷,它可能是因爲你的響應太大並且解析json需要時間。我相信這是你應該衡量和關心的事情。 – inancsevinc