Himanshu Garg
Himanshu Garg

Reputation: 151

Track user time in completing a particular action in a website

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

Answers (5)

webblover
webblover

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

  • "Edit/Create post" is captured
  • "timeTaken" is captured in seconds/milliseconds depending upon configuration
  • "type:activity" is captured
  • "activityStart" is captured
  • "activityEnd" is captured
  • "TOSUserId" // who does the action along with TOSSessionKey to uniquely identify the session.

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

harvey
harvey

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

Navjot Ahuja
Navjot Ahuja

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

Heehaaw
Heehaaw

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

Gonzalo Matheu
Gonzalo Matheu

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

Related Questions