Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize a front-end monitoring and playback system

2025-03-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "how to implement a front-end monitoring and playback system". Interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Next let the editor to take you to learn "how to achieve a front-end monitoring and playback system"!

1 / idea of implementing the solution

In order to give users access to a complete application status monitoring recording and playback, in addition to recording video, there are roughly two kinds of mainstream solutions through Web API.

The first solution is to record each change to the DOM, serialize the content, and then restore and play back the UI changes in a sandbox. One of the advantages of this scheme is that it can create a snapshot for DOM and collect it after each state change is applied. After stringing this sequence together, we can flexibly control the playback speed and customize playback for key nodes. Among the community open source solutions, nothing is more mature than rrweb https://github.com/rrweb-io/rrweb. In addition, some Internet companies also provide some commercial solutions, but the technical design is more or less the same, which can be divided into four aspects: DOM serialization, building snapshot sequence, deserialization playback and running sandbox environment.

Another feasible solution, from our research results, is to collect all the data on the user side, and then distribute the user event operations strictly according to the calibration time in a controllable operating environment, so as to control the playback. This is mainly divided into two parts, on the one hand, because we need high-precision timers and perfect user event collection strategies to replay users' behavior by dispatching events. On the other hand, in order to ensure the consistency of the changes in the state of the application at both ends, it is not enough to strictly trigger the user's actions, and we also need to accurately align any application with the previous interaction (request and response content) of the network. so we need to intercept all requests, immediate front-end build products, user action sequences, and so on.

The second scheme can almost simulate the state change of the user side, which is equivalent to moving the whole user to us at that time, so it is convenient for the development students to further debug. We have not found a similar implementation in the community open source solution, but https://logrocket.com/ is the closest to this idea in the commercial solution, and many of the features it provides are even more powerful than those mentioned here.

In this article, however, we only discuss the technical items involved when such a system needs to be implemented. So next, let's take a look at some useful Web API that we recorded in the survey. I hope it will be helpful to your implementation.

2 / Foundation of key technology

This chapter mainly introduces the four links to achieve such a front-end monitoring and playback system, about these contents, rrweb description will be more detailed, you can further consult their document description.

2.1 Serialization of applications and states

How to play back the state change of an application? First of all, we need to collect the content of the application and the state changes. If we use jQuery, we can collect and replace body content like this:

/ / record const snapshot = $('body') .clone (); / / replay $(' body') .replaceWith (snapshot)

If we switch to MediaRecorder API and use ondataavailable and onstop events to handle API, we can also turn DOM into playable media files, such as this:

StartRecording () {const stream = this.canvas as any. CaptureStream (); this.recorder = new MediaRecorder (stream, {mimeType: 'video/webm'}); const data = []; this.recorder.ondataavailable = (event) = > {if (event.data & & event.data.size) {data.push (event.data);}} This.recorder.onstop = () = > {const url = URL.createObjectURL (new Blob (data, {type: 'video/webm'})); this.videoUrl$.next (this.sanitizer.bypassSecurityTrustUrl (url));}; this.recorder.start (); this.recordVideo$.next (true);}

But the content cannot be serialized, and the media is too large for further analysis. For example, an input tag, if we only convert it to text for storage (like innerHTML) without doing any extra processing, then the value and other states contained in it will be lost, which is why we need to serialize DOM and its view state in the first place. There are also many open source solutions on how to serialize DOM, such as https://github.com/inikulin/parse5. Rrweb mentioned in the document why it was not adopted, and here I briefly list a few details to consider in the serialization process:

How to transform a DOM tree into a tree structure with view state, including view state not reflected in the HTML

How to define a unique identity to facilitate the traceability of state changes (snapshots)

How to handle special tags such as script and style varies from scheme to scheme

In short, the purpose of this section is to complete a data structure mapping from a DOM tree to a storable state.

2.2 Snapshots of DOM changes and interactions

If we only record DOM changes, we can easily use MutationObserver API to monitor and record changes, but we also need to consider how to convert the batch sequence of Mutation into incremental updates on snapshots.

For example, in order to uniquely determine the location of Node in the tree structure when adding or deleting DOM, we'd better design an appropriate unique marking strategy for DOM. In addition, how to optimize the view changes caused by mousemove and a large number of frequent input inputs. The design of the former can continue to be used in the implementation of incremental snapshots, while the performance of the latter directly affects the user experience, which can easily lead to the wrong order of Node records when DOM changes.

This part mainly depends on the serialization scheme of DOM to continue to deal with. After adding some other necessary information, such as time series number, you can store and so on.

2.3 playback and replay

To put it simply, the replay is to "play" the collected data sequentially. The playback of the video file requires an audio and video decoder, and the work we need to do in the replay link can be simply understood as a Web application decoder. The data structure collected from the client can not be directly used by the playback side in addition to cleaning and storage, and there are many details to consider.

To take a simple example, it is impossible for us to dispatch hover events using Web API, but there must be a large number of hover styles in our project, so how to deal with these interactions and complete the corresponding Node state changes has become a link to consider whether the trigger time for mousedown and mouseup events is enough? How are the mount nodes for event collection delineated? These are all areas that need to be considered. For example, if you want to skip the operation fragments that are considered meaningless in the playback, how can you design to keep the view state not missing because of the skipped operation on the front and back time nodes?

The purpose of this part is to play back the data structure stored by the snapshot. Because to ensure the strict consistency between the playback side and the collection side, details such as high-precision timing, DOM completion and interaction simulation need to be designed in detail.

2.4 Sandbox

Sandbox is to provide a safe and controllable operating environment for playback. How to adopt the DOM snapshot scheme, then you need to consider how to prohibit some "unsafe" DOM operations. For example, if the link within the application jumps, it is unlikely that we will directly open a new tab to the user. In order to ensure that the snapshot status is played back in turn, we also need to consider how to safely and accurately deserialize the DOM. If the idea of dispatching events is considered in the implementation, then how to accurately locate the dispatched Node node, how to match the data request and respond and so on need to be considered.

Both ideas have some common considerations, such as how to ensure that the running environment conforms to the security restrictions of the browser, the need for a rendering layer that is consistent with user operations, and so on. In this section, in the split of technical items section, we will mention two solutions, iframe and puppeteer, which I will not repeat here.

3 / Technical item split and related Web API

Web has a powerful API List, and this chapter explains all the API and related technologies that may be used.

3.1 MutationObserver

MutationObserver API can be used to listen for changes in DOM objects and return them in the form of a record array, which can be used in the recording part of application initialization and incremental snapshots. The methods and input parameters of MutationObserver are not described in detail here. In a nutshell, to listen for changes to a Node with MutationObserver API is roughly divided into the following steps:

Use API such as document.getElementById to locate the Node you need

Define a MutationObserverInit object whose configuration item describes what changes in DOM should be provided to the callback function of the current observer

Define the execution logic when the above callback function is called

Create an observer instance and pass in a callback function

Call the observe () method of MutationObserver to start observing

The following is an excerpt from a sample code from MDN for interpretation:

Const targetNode = document.getElementById ('some-id'); const config = {attributes: true, childList: true, subtree: true}; const callback = function (mutationsList, observer) {for (let mutation of mutationsList) {for (let mutation of mutationsList) {if (mutation.type =' childList') {console.log ('A child node has been added or removed.');}; const observer = new MutationObserver (callback); observer.observe (targetNode, config); 3.2 iframe tag

Iframe must have been used by everyone, it can embed another HTML page into the current page. With iframe, we can quickly build a secure sandbox mechanism, such as using it to restrict JavaScript execution. In addition to directly assigning values to the src attribute of iframe, iframe has many other attributes that are worth understanding, which will be helpful in improving the running sandbox environment. For example, the sandbox attribute can enable some additional restrictions on the content rendered in iframe.

In addition, communication between different containers in the sandbox can be achieved through postMessage API. Here is a very detailed article on all aspects of iframe, and you can take a closer look at https://blog.logrocket.com/the-ultimate-guide-to-iframes/.

3.3 HTTP Archive

HTTP request and response aggregation, which is what we often call HAR format data. Each data stream under the open chrome devtools,network tab represents a request, from http to weoscket,request to response, including request input parameters, headers, connection time, initiation time, response time, TTFB, content size, and so on. As mentioned earlier, if user operations are to be dispatched on the playback side, then all requests need to be intercepted and labeled during collection, so collecting HAR directly becomes the best choice.

However, if you want to collect HAR, you will encounter some restrictions, for example, this kind of data is only available in chrome devtools API, so to achieve this data collection, it must be developed in the form of a chrome plug-in, but in this way, how to collect and report data without user awareness has become a difficult problem.

3.4 network and webRequest API

Using chrome.webRequest API allows us to observe and analyze traffic and intercept, block, or modify it during operation. From the above description, the collection of HAR can only be realized through this API. Here is an example of calling network to intercept requests. For more information, please see the document https://developer.chrome.com/extensions/webRequest.

Chrome.devtools.network.onRequestFinished.addListener (function (req) {/ / Only collect Resource when XHR option is enabled if (document.getElementById ('check-xhr') .checked) {console.log (' Resource Collector pushed:', req.request.url) Req.getContent (function (body, encoding) {if (! body) {console.log ('No Content Detective, Resource Collector will ignore:', req.request.url);} else {reqs [req.request.url] = {body, encoding} } setResourceCount ();}); setResourceCount ();}}); 3.5 Service Worker and proxy

We all know that Servcie Worker's cache API is widely used in PWA applications, but in fact, in addition to being used to enhance the offline application experience, because Servcie Worker has finer and more complete control features, it can be used as a proxy middle layer between the page and the server to capture the page requests it is responsible for and return the corresponding resources.

Generally speaking, based on the framework HTTPClient (Angular) or native XMLHttpRequest snooping, there are more or less blind spots in the request interception of the page that can not be captured, but Service Worker will not, what you need to pay attention to is that you need to put it in your application root directory, or specify scope when registering through input parameters to make your Service Worker take effect within the specified scope.

In addition to understanding its life cycle, there are some details to pay attention to, such as scope scope. A single Service Worker can control multiple pages, and each page will not have its own unique worker, so please pay attention to the effective scope of scope; when the pages within your scope scope are loaded, Service Worker can start to control it, so please be careful to define the global variables in the Service Worker script.

Of course, to facilitate development, you can use TypeScript, Babel, webpack and other languages and tools to speed up your development experience. MDN has a tutorial on how to get started with Service Worker. You can take a look at https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers.

The picture above shows the life cycle of Service Worker.

3.6 Web Worker

The role of Web Worker is to create a multithreaded environment for JavaScript, allowing the main thread to create Worker threads and assign some tasks to the latter to run. While the main thread is running, the Worker thread runs in the background, and the two do not interfere with each other. Wait until the Worker thread finishes the calculation task, and then return the result to the main thread. The advantage is that some compute-intensive or high-latency tasks are burdened by Worker threads, and the main thread (usually responsible for UI interaction) will be smooth and will not be blocked or slowed down. -- Ruan Yifeng's web log

For the calculation work used to collect and report data, due to the re-calculation logic and frequent data processing, it is best not to operate in the main line, otherwise it will affect the interactive experience. Web Worker is a good solution, and after adopting it, the rest of the related operations related to BOM/DOM can also pass the required data to direct event notification or SharedArrayBuffer sharing.

So, how do you build a Web Worker? A worker file consists of simple JavaScript code. After writing, you need to pass URI into the main thread to build such a worker thread, as shown in the following figure:

Const myWorker = new Worker ('worker.js')

In worker, in addition to perfecting the logic code for computation, we can also introduce scripts, communicate with the main thread through postMessage, and so on:

/ / introduce script importScripts ('foo.js',' bar.js'); / / listen to messages and communicate outward in Web Worker onmessage = function (e) {console.log ('Message received from main script'); var workerResult =' Result:'+ (e.data [0] * e.data [1]); console.log ('Posting message back to main script'); postMessage (workerResult);}

The Service Worker mentioned above can also be regarded as a similarity to Web Worker, but unlike the general Web Worker, Service Worker has some additional features to achieve the purpose of the agent. As long as they are installed and activated, Service Worker can intercept any network request initiated in the main thread.

In addition, there is a special Worker called Worklet, these API are very interesting, worthy of another introduction, but not relevant to this article, so I will not elaborate.

3.7 Idle scheduling and requestIdleCallback

The window.requestIdleCallback () method queues the functions called during the browser's idle time. This enables developers to perform background and low-priority work on the main event loop without affecting the delay of critical events such as animation and input responses. Functions are generally executed in the first-in-first-call order, however, if the callback function specifies the execution timeout timeout, it is possible to disrupt the execution order in order to execute the function before the timeout. -- MDN

Because the system needs to collect all the user data, in addition to the burden sharing of computing logic, uploading structural data including serialization nodes and snapshots is bound to become a potential bottleneck and optimization point to be considered in the project. With requestIdleCallback API, we can ensure that the data escalation (network request) after processing will not affect the user interaction, such as user page stutter and so on.

A better-known Web API is requestAnimationFrame, which tells the browser to execute the incoming callback function before the next redraw, and because it is executed once per frame, it executes the same number of times per second as the browser screen refresh, usually 60 times per second. RequestIdleCallback, by contrast, executes at the end of each frame, but not every frame guarantees that requestIdleCallback will be executed. The reason is simple: we can't guarantee that we still have time at the end of each frame, so we can't guarantee the execution time of requestIdleCallback.

The design of requestIdleCallback API is simple, an idle scheduling function and an optional configuration parameter.

Const handle = window.requestIdleCallback (callback [, options])

For example, suppose we now need to track users' click events and report the data to the server. With this API, we can complete data collection and report scheduling as follows:

Const btns = btns.forEach (btn = > btn.addEventListener ('click', e = > {/ / other interactive putIntoQueue ({type:' click' / / collect data})); schedule ();}); function schedule () {requestIdleCallback (deadline = > {while (deadline > 0) {const event = queues.pop (); send (event)) }}, {timeout: 1000});} 3.8 Local persistent storage-localStorage and IndexedDB

Since you want to report, you should consider the unexpected exit of the user or the disconnection of the network when the data is not completed. In these cases, the browser's local storage scheme comes in handy. LocalStorage API and IndexedDB API are recommended for persistent storage because of the need to ensure the integrity of data reporting.

With localStorage API, we can quickly access key-value pairs in the form of strings, but limited by browsers, the storage size is generally limited, only a few megabytes.

With IndexedDB API, we can store a large amount of structured data (including files / binary large objects, etc.) on the client side, and IndexedDB is stored on the local disk by the browser, so you can approximately regard its storage limit as the remaining storage capacity of the computer.

IndexedDB is a transactional database system, similar to RDBMS based on SQL. However, unlike RDBMS, which uses fixed lists, IndexedDB is an object-oriented database based on JavaScript. IndexedDB allows you to store and retrieve objects indexed with keys; you can store any object supported by the structured cloning algorithm. You just need to specify the database schema, open the connection to the database, and then retrieve and update a series of transactions.

Both localStorage and IndexedDB are relatively easy to use, and there are more complete getting started guides on MDN, so no code will be posted here.

3.9 puppeteer-Headless Chrome Node.js API

Assuming that we do not use iframe as a convenient sandbox environment, a more powerful solution is puppeteer.

Puppeteer is an official Node library produced by Google to control headless Chrome through DevTools protocol. We can directly control Chrome through API provided by puppeteer, and then simulate the operations of most users on the browser to UI Test or visit pages as crawlers to collect data. For information on the use of puppeteer, please refer to the document https://pptr.dev/.

Going back to our scenario, when we need to replay the user's operation, we can easily use page API to do it, such as the following example:

Const puppeteer = require ('puppeteer') (async () = > {const browser = await puppeteer.launch () const page = await browser.newPage () await page.goto ('https://hijiangtao.github.io/') await page.setViewport ({width: 2510) Height: 1306}) await page.waitForSelector ('.ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav > div') await page.click (' .ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav > div > .an t-tabs-tab:nth-child (2)') await page.waitForSelector ('.ant-tabs-nav-wrap > .container') await page.click (' Ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav > div > .ant-tabs-tab:nth-child (1)') await browser.close ()}) ()

Of course, puppeteer has a wide range of use scenarios, such as generating page screenshots or PDF, automating UI testing, building crawler systems, capturing page timelines for performance diagnosis, and so on.

At this point, I believe you have a deeper understanding of "how to achieve a front-end monitoring and playback system". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report