preface

Recently miscellaneous miscellaneous things more, rare time to make up for the previous series, owe everyone buried point series now started to go

Why do we need a burial system

In the movie

Very proud of the separation of business and UI development, a variety of design patterns, algorithm optimization in turn, the code to write Perfect (labor code world No.1), No BUG, Perfect program, compatibility No.1, code can play can resist high quality. Punch in after work easily, go home to see baby.

In the real world

In fact, the development environment is not the same as the production environment, and even after the testing process is perfected, there will still be some missed tests. Considering the use of the client environment, network environment and a series of uncertain factors exist.

So there are three principles to keep in mind during development.

  1. There is no perfect code, only undiscovered bugs
  2. Never trust a test environment. No test environment covers all online situations
  3. If there’s no feedback online, don’t be suspicious. The problem should be deep, deep

What is a buried point system

Burial sites are like cameras in cities. From the perspective of products, they can monitor users’ behavior tracks in our products, providing basis for product iteration and project stability. WHO, WHEN, WHERE, HOW and WHAT are the basic dimensions of data collected at burial sites.

For front-end development, it can monitor page resource loading performance and exceptions, provide page experience and health index, provide basis for subsequent performance optimization, and report exceptions and occurrence scenarios in time. So as to be able to timely fix problems, improve the quality of the project, etc.

Burial sites can be roughly divided into three categories:

  1. Non-trace buried point – collect all information on the page including page in and out, event click and so on, need to carry out data flushing to get useful information
  2. Visual buried point – According to the generated page structure to obtain a specific point, separate buried point analysis
  3. Business code manual burial point – According to the specific complex business, remove the above two can not cover the place of business code burial point
Code buried point Visual burial point Non-trace buried point
A typical scenario Non-trace buries cannot be covered, such as business data Simple canonical page scenario Simple canonical page scenarios,
advantage Clear business data The development cost is low, and the operation personnel can directly carry out the relevant burial site configuration No configuration is required and data is traceable
insufficient Data is not traceable and development costs are high Business data cannot be associated, and data cannot be traceable The amount of data is large and cannot be associated with service data

In most cases, we can collect all the information and data through the non-trace burial point, and then cooperate with the visual burial point to locate a specific point, so that most of the burial point information can be analyzed accordingly.

In special cases, you can add more business code to manually bury points to deal with special scenarios (most of the cases are strong business and normal click, refresh the event has nothing to do with the information to be reported)

Bury some SDK development

Data collection and analysis of burial sites

  • Event Basic data
    • Event time
    • Snapshot of page information when it occurs
  • page
    • Page PV, UV
    • User page duration
    • Page Jump Event
    • The page goes to background
    • The user leaves the page
  • The user information
    • User’s uid
    • User device fingerprint
    • Equipment information
    • ip
    • positioning
  • User action
    • The user clicks
      • Click on the target
  • Page AJAX request
    • The request is successful
    • The request failed
    • The request timeout
  • An error page
    • An error occurred during resource loading
    • JS runtime error
  • Resource loading new performance
  • The picture
  • The script
  • Page loading performance

The above data defines buried point events in three dimensions

  • ,LEVEL: Describes the log level of buried data
    • INFOSome user operations, request success, resource loading, etc
    • ERROR: JS error, interface error and so on error type data record
    • DEBUG: Reserved for developers to manually call back data records that exclude bugs
    • WARN: Reserved for developers to manually call back data records of abnormal user behavior
  • CATEGORY: Describes the classification of buried point data
    • TRACK: The lifecycle of the buried point SDK object manages the entire buried point data.
      • WILL_MOUNT: The SDK object is about to be initialized to load, generate a default ID, and track all related events
      • DID_MOUNTED: The SDK object initialization is completed, and asynchronous operations such as obtaining device fingerprint are completed
    • AJAX: AJAX data
    • ERROR: Abnormal data on the page
    • PERFORMANCE: About performance data
    • OPERATION: Users operate related data
  • EVENT_NAME: Specifies the event name

Based on the above dimensions, we can simply design the following architecture

Based on the architecture above, proceed with the following specific code development

The agent requests

There are now two main ways to request in the browser, XMLHttpRequest and Fetch.

Agent XMLHttpRequest

function NewXHR() {
  var realXHR: any = new OldXHR(); // The proxy mode is mentioned
  realXHR.id = guid()
  const oldSend = realXHR.send;

  realXHR.send = function (body) {
    oldSend.call(this, body)
    // Record the burial point
  }
  realXHR.addEventListener('load'.function () {
    // Record the burial point
  }, false);
  realXHR.addEventListener('abort'.function () {
    // Record the burial point
  }, false);

  realXHR.addEventListener('error'.function () {
    // Record the burial point
  }, false);
  realXHR.addEventListener('timeout'.function () {
    // Record the burial point
  }, false);

  return realXHR;
}
Copy the code

The agent the Fetch

 const oldFetch = window.fetch;
  function newFetch(url, init) {
    const fetchObj = {
      url: url,
      method: method,
      body: body,
    }
    ajaxEventTrigger.call(fetchObj, AJAX_START);
    return oldFetch.apply(this.arguments).then(function (response) {
      if (response.ok) {
       // Record the burial point
      } else {
       // Report an error
      }
      return response
    }).catch(function (error) {
      fetchObj.error = error
        // Record the burial point
        throw error
    })
  }
Copy the code

Page-listeningPV.UV

When entering the page, we generated a unique session ID through the algorithm, which was used as the global ID of the buried point behavior, and reported the user ID, device fingerprint and device information. When the user is not logged in, the device fingerprint is used to calculate UV and the session ID is used to calculate PV.

Exception handling

An exception is an unusual incident that interferes with the normal flow of a program

RUNTIME ERROR

Runtime exceptions can be caught in JS via window.onError and window.addeventListener (‘error’, callback). Window.onerror is more compatible.

window.onerror = function(message, url, lineno, columnNo, error) {
    const lowCashMessage = message.toLowerCase()
    if(lowCashMessage.indexOf('script error') > -1) {
      return
    }
    const detail = {
      url: url    
      filename: filename,
      columnNo: columnNo,
      lineno: lineno,
      stack: error.stack,
      message: message
    }
    // Record the burial point
}
Copy the code

Script Error

Here we filter Script Error, which is mainly caused by the Error of the third-party cross-domain Script loaded in the page, such as THE JS Script hosted in the third-party CDN. This kind of problem is difficult to identify. The solutions are:

  • Open theCORS(Cross Origin Resource Sharing), perform the following steps
    • <srcipt src="another domain/main.js" cossorigin="anonymous"></script>
    • Modify theAccess - Control - Allow - Origin: * | specify the domain name
  • usetry catch
      <script scr="crgt.js"></script> // Load the CRGT script, window.crgt = {getUser: () => string} try{window.crgt.getUser(); }catch(error) { throw error // output the correct error stack}Copy the code

Promise reject

An unhandledrejection error is thrown when a Promise object is rejected and not processed. This error is not caught by any of the above methods, so separate processing events need to be added.

window.addEventListener("unhandledrejection".event= > {
  throw event.reason
});
Copy the code

Abnormal resource loading

In the browser, window.addeventListener (‘error’, callback) can be used to listen for resource loading exceptions, such as missing JS or CSS script files.

window.addEventListener('error'.(event) = > {
  if (event.target instanceof HTMLElement) {
    const target = parseDom(event.target, ['src']);
    const detail = {
      target: target,
      path: parseXPath(target),
    }
    // Record the burial point}},true)
Copy the code

Listening to user behavior

Listen for the click event via addEventListener Click

window.addEventListener('click'.(event) = > {
    // Record the burial point
}, true)
Copy the code

The element is located here by the component’s displaName, which represents the component’s file directory, For example, the component FormItem exported from the SRC /components/ form.js file is automatically added to the property @components/ form.formItem by the Babel Plugin. Or the consumer actively adds the static displayName property to the component.

Page Route change

  • hashRouter

Listen for hash changes on the page and parse the hash

window.addEventListener('hashchange'.event= > {
  const { oldURL, newURL } = event;
  const oldURLObj = url.parseUrl(oldURL);
  const newURLObj = url.parseUrl(newURL);
  const from = oldURLObj.hash && url.parseHash(oldURLObj.hash);
  const to = newURLObj.hash && url.parseHash(newURLObj.hash);
  if(!from && !to ) return;
  // Record the burial point
})
Copy the code

Listen to the page leave

Listen for off-page events via addEventListener beforeUnload

window.addEventListener('beforeunload'.(event) = > {
    // Record the burial point
})
Copy the code

The SDK framework

class Observable {
    constructor(observer) {
        observer(this.emit)
    }
    emit = (data) = > {
        this.listeners.forEach(listener= > {
            listener(data)
        })
    }
    listeners = [];
    
    subscribe = (listener) = > {
        this.listeners.push(listeners);
        return () = > {
            const index = this.listeners.indexOf(listener);
            if(index === -1) {
                return false
            }
            
            this.listeners.splice(index, 1);
            return true; }}}Copy the code
const clickObservable = new Observable((emit) = > {
    window.addEventListener('click', emit)
})
Copy the code

However, when dealing with Ajax, which requires multiple data combinations and merg operations, it is less elegant and difficult to adapt to subsequent complex data flow operations.

const ajaxErrorObservable = new Observable((emit) = > {
    window.addEventListener(AJAX_ERROR, emit)
})

const ajaxSuccessObservable = new Observable((emit) = > {
    window.addEventListener(AJAX_SUCCESS, emit)
})
const ajaxTimeoutObservable = new Observable((emit) = > {
    window.addEventListener(AJAX_TIMEOUT, emit)
})

Copy the code

You can choose RxJS to optimize your code

export const ajaxError$ = fromEvent(window.'AJAX_ERROR'.true)
export const ajaxSuccess$ = fromEvent(window.'AJAX_SUCCESS'.true)
export const ajaxTimeout$ = fromEvent(window.'AJAX_TIMEOUT'.true)
Copy the code
ajaxError$.pipe(
    merge(ajaxSuccess$, ajaxTimeout$), 
    map(data= > (data) = > ({category: 'ajax', data; data}))
    subscribe(data= > console.log(data))
Copy the code

The map and merge operators are used to merge and process data.

The data flow

The project structure

  • core
    • event$Data stream merge
    • snapshotGet the current device snapshot, for exampleurl.userID.router
    • trackBuried point classes, combining data flows and logs.
  • logger
    • loggerThe log class
      • info
      • warn
      • debug
      • error
  • observable
    • ajax
    • beforeUpload
    • opeartion
    • routerChange
    • logger
    • track

reference

  • www.alibabacloud.com/help/zh/doc…

At the end

Self-built buried-point systems are a collaborative effort between the front and back ends. If manpower is scarce, it is recommended to use third-party analytics plug-ins, such as Sentry, which are sufficient for most daily use

However, it is recommended to understand more, in the third party plug-in can not meet the business needs of the time, you can top.

Project Actual Combat Series

| cache handling project practical experience

Project practice | basic request encapsulation

Implement project of actual combat | business processing layer