Reputation: 151
I want to track how much time user is taking in completing a particular action (including server response time and render time(DOM related changes )) in website.
I have tried it in Angular framework. To do it, I am thinking of recording the time when user started the action and I want to note the time when the action is completed. As a developer, I will know when user started the activity and when user finish the action like search, filter, edit, add, delete etc. So, we can take the difference b/w them. But to note every action, we have to write code in every part of the app. Can we create a plugin so that we can use it everywhere instead of writing same code everywhere to track the time of user. Any approach to create it? Or is there any tool available to achieve this feature?
Upvotes: 15
Views: 2877
Reputation: 1196
Can we create a plugin so that we can use it everywhere instead of writing same code everywhere to track the time of user. Any approach to create it? Or is there any tool available to achieve this feature?
It's a very important Feature Request by many. So, I write a detailed, working and simple solution on the subject here.
@himanshu-garg You are requesting a feature already created for this workflow. It's a plugin you can include in any website. It's none other than activity tracking in timeonsite.js
Look at the following code,
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/timeonsite/1.2.0/timeonsitetracker.js"></script>
<script>
var config = {
// track page by seconds. Default tracking is by milliseconds
trackBy: 'seconds',
callback: function(data) { /* callback denotes your data tracking is real-time */
console.log(data);
var endPointUrl = 'http://example.com' //Replace with your actual backend API URL http://localhost/tos
if (data && data.trackingType) {
if (data.trackingType == 'tos') {
if (Tos.verifyData(data) != 'valid') {
console.log('Data abolished!');
return;
}
}
// make use of sendBeacon if this API is supported by your browser.
if (navigator && typeof navigator.sendBeacon === 'function') {
data.trasferredWith = 'sendBeacon';
var blob = new Blob([JSON.stringify(data)], {type : 'application/json'});
navigator.sendBeacon(endPointUrl, blob);
}
}
}
};
var Tos;
if (TimeOnSiteTracker) {
Tos = new TimeOnSiteTracker(config);
}
</script>
</head>
Then, when the user clicks on a specific action in the site, for example "edit the post" or "click on the create post",
You just initiate the Tos.startActivity() API like,
Tos.startActivity({actionPerfomed: 'Edit a post'});
Then when the user completes the edit or create post actions and when he finally clicks the "save/submit" button, you trigger the Tos.endActivity() API like,
Tos.endActivity({customData: 'custom data if any here...'});
You'll see following object directly saved into your table,
{
TOSId: 585872449448,
TOSSessionKey: "14802525481391382263",
TOSUserId: "anonymous",
title: "Test application - TimeOnSiteTracker",
URL: "http://example.com/post/nature-is-beautiful/edit.php",
activityStart: "2021-11-27 13:20:46.707",
activityEnd: "2021-11-27 13:20:50.213",
timeTaken:4,
timeTakenByDuration: "0d 00h 00m 04s"
timeTakenTrackedBy: "second",
trackingType: "activity",
actionPerfomed: "Edit a post", //optional fields
customData: "custom data if any here..." //optional fields
}
As you can see, the actions
What else you need? Since it's stored in SQL DB table, you can do analysis/reporting queries yourself and take it to top-level management for decisions. The same is the case for NoSQL as well. Timeonsite.js is supporting both RDBMS and NoSql DB types.
On top of it, 1.Minimize tab, 2.Inactive tab and 3.Switch tab's idle time are all computed and ignored automatically by the tracker itself.
This tracker can be plugged-in in any library Angular, React, Jquery etc. since it's plain vanilla JS library.
Let me know if you need more input on the subject. I can assist you on this.
Upvotes: 1
Reputation: 2953
I am going to recommend you use custom Google Analytics events. In particular User Timings. This allows you to log specific timings on your webpage, you can log with your own labels and categories.
To quote the documentation:
User timings allow developers to measure periods of time using the analytics.js library. This is particularly useful for developers to measure the latency, or time spent, making AJAX requests and loading web resources.
I have some sample code below, this just hooks into clicks, and will get a descriptor from attribute data-name
- if not available will just log as 'Anonymous Click' - you can customise this to not track unmarked items. You can also hook into ajax calls and other notable events, without knowing your specific requirements it's hard to give further examples.
Example markup helper to lock click events.
<button data-name="Foo"/>
The below code does the logging, note that it logs using window.performance.now()
- which will return the time from when the page was loaded in milliseconds. This will allow you to generate a timeline of user interactions as opposed to getting raw time spent on a single task, which by the way Google Analytics reports can calculate for you.
(function($, Analytics) {
init_hooks();
function init_hooks() {
$('body').on('click', track);
}
function track(e) {
// Get a name to record this against
var name = e.target.data(name) || "Anonymous Click";
// Time since page loaded
var time = window.performance.now()
Analytics('send', {
hitType: 'timing',
timingCategory: 'Front End Intereactions',
timingVar: name,
timingValue: time
});
}
})(jQuery, ga)
Find out more look at the docs.
Upvotes: 0
Reputation: 1171
You have to write a simple Event Tracker in your client code. Since I don't know which events you want to track, I'll provide the solution for a general case.
Also, you'll have to manually trigger the start and stop tracking.
EventTracker = {
trackedEvents: {},
start: function(key) {
var startTime = new Date();
this.trackedEvents[key] = {
start: startTime
}
},
stop: function(key) {
var endTime = new Date();
this.trackedEvents[key]['duration'] = (endTime - this.trackedEvents[key]['start']) / 1000 + 's';
this.trackedEvents[key]['end'] = endTime;
},
}
// Use EventTracker everywhere to track performance
// Example:
EventTracker.start('search_track'); // User searches, start tracking.
setTimeout(function() {
EventTracker.stop('search_track'); // Records fetched after 5 seconds. Stop tracking.
console.log(EventTracker.trackedEvents);
}, 5000);
You can track all events according to your need. For server response, use: EventTracker.start('search_ajax_track')
when you make the request and stop the tracking when you get the response.
You can modify above code to measure other parameters according to your requirements.
Upvotes: 0
Reputation: 2837
Would something like this help?
@Injectable({provideIn: 'root'})
export class TrackingService {
private cache: {[id: number]: {description: string, time: number}} = {};
private id: number = 0;
public startTracking(actionDescription: string): number{
const id = ++this.id;
this.cache[id] = { description: actionDescription, time: new Date().getTime() };
return id;
}
public stopTracking(actionId: number){
const data = this.cache[actionId];
if(data){
const elapsed = new Date().getTime() - data.time;
// ...
// Do something with your 'elapsed' and 'data.description'
// ...
delete this.cache[id];
return {...data, elapsed: elapsed};
}
throw `No action with id [${actionId}] running! `;
}
}
Ad then anywhere you need to track an action:
private actionId: number;
constructor(private trackingService: TrackingService){}
startAction(){
this.actionId = this.trackingService.startTracking('Description');
}
stopAction(){
const trackingResult = this.trackingService.stopTracking(this.actionId);
}
You can automate the tracking in some places, for example for routing:
// app.module.ts
private routeChangeSubscription: Subscription;
private configLoadActionId: number;
private navigationActionId: number;
constructor(private router: Router, private trackingService: TrackingService){
this.routeChangeSubscription = router.events.subscribe((event: Event) => {
if (event instanceof RouteConfigLoadStart) {
this.configLoadActionId = this.trackingService.startTracking('configLoad');
}
else if (event instanceof RouteConfigLoadEnd) {
const result = this.trackingService.stopTracking(this.configLoadActionId);
// ... process the result if you wish
}
else if (event instanceof NavigationStart) {
this.navigationActionId = this.trackingService.startTracking('navigation');
}
else if (event instanceof NavigationEnd) {
const result = this.trackingService.stopTracking(this.navigationActionId);
// ... process the result if you wish
}
});
}
Or for HTTP requests:
// http-tracking.interceptor
export class HttpTrackingInterceptor implements HttpInterceptor {
constructor(private trackingService: TrackingService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const actionId = this.trackingService.startTracking('HTTP request');
return next.handle(req.clone()).pipe(
tap(r => this.trackingService.stopTracking(actionId))
);
}
}
// app.module.ts
@NgModule({
// ... other module stuff
providers: [
// ... other providers
{
provide: HTTP_INTERCEPTORS,
useClass: HttpTrackingInterceptor,
multi: true,
deps: [TrackingService]
}
]
})
export class AppModule { ... }
You can easily extend the TrackingService
to return Promises or Observables or whatever else, in case you prefer that...
Hope this helps a little :-)
Upvotes: 4
Reputation: 10074
You could instrument your code with OpenTracing for Js.
You will need to add a request in your transaction start and end.
Also a OpenTracing server to receive request from the browser.
Upvotes: -1