preface

Recently, the company’s data collection by Google analysis to god’s buried point, completed a day ahead of schedule, in the spirit of “know what is and know why”, began to study the implementation of God’s buried point, just as it happens, there is no collection, the research found that is because the browser installed the intercepting plug-in (uBlock Origin), Sensorsdata.min. js is blocked, naturally cannot collect, problem solved; Is it that simple? See below. (Key words: Subscription publishing)

Read this and look forward to receiving the goods

  • If your company is going to do a smart burial site, it will take you to understand how smart data collection actually works.
  • If not, understand the subscribe publish design pattern through strategic data flow

The problem

The point is, js can’t even be introduced, so why don’t collection methods fire without an error? Key points:

  • Js cannot be introduced, why does the collection method not report an error when triggered
  • Globally, it still has sensors attribute and can still trigger track method, but there is no data in the background

Connect to the operation

In fact, the access process is very simple, divided into full buried point and code buried point two parts, full buried point does not need to care about developers, code buried point as long as the specific location of the trigger encapsulation method, incoming data can; The key, of course, is the logic of initializing the magic policy when the page loads. (Attached at the end of the article is all the initialization code, in fact, most of the divine policy documentation provided); Step by step analysis :(it is recommended to copy all the code for comparison)

The first step is the key self-executing function, passing in the configuration parameters of the user and entering the function content analysis

  1. Initialize the sensors object

    The focus here is on _q, which is essentially the queue stored in the subscription publication to store the data for the event

// n = 'sensors' w = window
w[n] = w[n] || function(a) { return function() {
            (w[n]._q = w[n]._q || []).push([a, arguments]); }};// Now there is an attribute sensor on the window which is a higher-order function
function(a) { return function() {(window['sensors']._q = window['sensors']._q || []).push([a, arguments]);
} 
// This function will hang the _q property array on the sensors and pass in the call parameters and default array
Copy the code

  1. Initialize properties

    Equivalent to the mount method, the trigger method is to collect events and data into _Q (queue), that is, subscription

var ifs = ['track'.'quick'.'register'.'registerPage'.'registerOnce'.'clearAllRegister'.'trackSignup'.'trackAbtest'.'setProfile'.'setOnceProfile'.'appendProfile'.'incrementProfile'.'deleteProfile'.'unsetProfile'.'identify'.'login'.'logout'.'trackLink'.'clearAllRegister'];
// Iterate over the mount
    for (var i = 0; i < ifs.length; i++) {
        w[n][ifs[i]] = w[n].call(null, ifs[i]);
    }
Copy the code

In order to track, for example

w[n][ifs[i]] = w[n].call(null, ifs[i]);
/ / equivalent to
sensors['track'] = sensors.call(null.'sensors') = function() {(window['sensors']._q = window['sensors']._q || []).push(['sensors'.arguments]);
} 
// If the method is triggered, the sensors will have an array with the attribute _q to store the collection event in the format of [method,[event,data]].
Copy the code

When triggered, _q will be saved

  1. Import sensorsdata.min.js and insert the file (along with the user-defined parameter on the para property of the Sensors object)
 x = d.createElement(s), y = d.getElementsByTagName(s)[0];
        x.async = 1;
        x.src = p;
        w[n].para = para;
        y.parentNode.insertBefore(x, y);
Copy the code

So far, we have seen (implemented) the collection of events and required data into _q when code is buried; My personal understanding is that this is actually the data collection logic of the buried point of The Divine policy. After collecting, the data will be passed into the analysis background of the divine policy to generate visual tables.

And we were able to explain our original question

answer

  • Js cannot be introduced, why does the collection method not report an error when triggered

    Because it is to call our encapsulated object, the function also has, naturally will not report an errorCopy the code
  • Globally, it still has sensors attribute and can still trigger track method, but there is no data in the background

    Sensorsdata.min.js is blocked, it can't "publish", it can't collect data, all data is kept in the sensorsCopy the code
validation
  1. If uBlock is enabled, the feedback_click event is triggered. The data will be stored in _Q and will not be consumed

  1. The feedback_click event is triggered when uBlock is closed. The data will be stored in _Q and then consumed. The data will not be found in _Q and will be collected in the background

So that’s it? Don’t

Extension: How are local sensors connected to Sensorsdata.min.js? In other words, how did Shenze get sensor. _Q? (It is not a problem to get operations like natural execution import)

Download the blocked JS file of Shence and put it in the local area to start the research. (It involves the name of the company, and it is directly disclosed and worried about the privacy of Shence, so it is impossible to provide the address. I am sorry that part of the code of my research will be provided.)

  1. First of all, on a personal project, there will be a mount ‘sensorsDataAnalytic201505’ properties on the window of the operation, the attribute value is to define the object name (I this is sensors)

    // n = 'sensors' w = window
    w['sensorsDataAnalytic201505'] = n;
    Copy the code
  2. In Shence JS, it will perform the value operation, obtain the user-defined string, and then initialize it (in fact, it takes out the data of the local sensor object, publishes it, and then replaces it with the object defined by itself).

    1. If yes, call sd.setPreConfig
    "string"! =typeof window.sensorsDataAnalytic201505) return "undefined"= =typeof window.sensorsDataAnalytic201505 ? (window.sensorsDataAnalytic201505 = sd, sd) : window.sensorsDataAnalytic201505;
            sd.setPreConfig(window[sensorsDataAnalytic201505]),
    Copy the code
    1. The setPreConfig method is simple, fetching user-passed configuration parameters and event queues
    sd.setPreConfig = function(e) {
                sd.para = e.para,
                sd._q = e._q
            },
    Copy the code
    1. Replace the object
      window[sensorsDataAnalytic201505] = sd,
      sd.init(),
      window.sensorsDataAnalytic201505 = sd
    Copy the code

At this point, local custom objects (actually functions) sensors are associated with magic policies, in short

The user only cares about the “publishing” of the events that are put into the queue by the user, but not how they are put into the queue. A perfect ending

Initialize all the code

var currentHost = window.location.host;
var sensor_server_url = 'Test environment Data acquisition background address';
// Environmental judgment
if (currentHost == Production Environment domain name) {
    sensor_server_url = 'Production environment data collection background address';
};
(function(para) {
    var p = para.sdk_url,
        n = para.name,
        w = window,
        d = document,
        s = 'script',
        x = null,
        y = null;
    w['sensorsDataAnalytic201505'] = n;
    w[n] = w[n] || function(a) { return function() {
            (w[n]._q = w[n]._q || []).push([a, arguments]); }};var ifs = ['track'.'quick'.'register'.'registerPage'.'registerOnce'.'clearAllRegister'.'trackSignup'.'trackAbtest'.'setProfile'.'setOnceProfile'.'appendProfile'.'incrementProfile'.'deleteProfile'.'unsetProfile'.'identify'.'login'.'logout'.'trackLink'.'clearAllRegister'];
    for (var i = 0; i < ifs.length; i++) {
        w[n][ifs[i]] = w[n].call(null, ifs[i]);
    }
    if(! w[n]._t) { x = d.createElement(s), y = d.getElementsByTagName(s)[0];
        x.async = 1; x.src = p; w[n].para = para; y.parentNode.insertBefore(x, y); } ({})sdk_url: 'https://assets-cdn.lanqb.com/js/sensors/sensorsdata.min.js'.name: 'sensors'.server_url: sensor_server_url,
    heatmap: {
        clickmap: 'default'.scroll_notice_map: 'default'.collect_element: function(element_target) {
            // If this element has an attribute sensors-disable=true, no collection
            return element_target.getAttribute('sensors-disable') = = ='true' ? false : true}},send_type: 'ajax'.show_log: true});var anonymous_id = null;
sensors.quick('isReady'.function() {
    var user_type = null,
        roleArr = [];
    anonymous_id = sensors.quick('getAnonymousID');
    if (window.auth_user && window.auth_user.id) {
        sensors.login(window.auth_user.id);
        // Get user identity
        var roles = window.auth_user.roles;
        if (typeofroles ! ="object") return;
        for (var k in roles) {
            user_type = 'Registered User'
        }
    }
    sensors.registerPage({ // Set the public properties
        platform_type: window.form_type,
        user_type: function() {
            return user_type ? user_type : 'visitors';
        },
    })

    sensors.quick('autoTrack'); $pageView ($pageView)
});


function ssCustomTrack(ele_t, ele_type, class_name, content) {
    sensors.quick('trackHeatMap', ele_t, { // Click collection of non-a,input,button elements
        '$element_type': ele_type,
        '$element_class_name': class_name,
        '$element_content': content
    });
}

function sensorTrack(event_name, prop_obj) {
    sensors.track(event_name, prop_obj)
}

Copy the code