分享一个Chrome控制台数据获取的例子

简介: 讲了一下获取Chrome控制台数据的前因后果

问题背景

我们公司有开发一个用户访问监测数据采集的sdk,前端应用使用这个SDK呢,就会把用户操作的行为,浏览的页面,访问的请求,加载的时长等操作的数据会采集并上报给服务端。

同时支持应用性能监测(apm 链路追踪)的关联,例如都是非常知名的datadog ddtrace,zipkin,skywalkingotel,jaeger

如下图:

左侧大红框住的是APM链路火焰图(应用性能监测后端调用情况),右侧上小框住的是相关view(与之关联的前端操作)


问题现象

领导碰到一个问题,部分APM火焰图没有与之关联的相关view

这个难道是用户访问监测数据采集的sdk有bug吗?,按道理这个数据与应用性能监测数据是一样上报的,难道是没有采集吗?

我暂时认为上面的是一种现象,至于是不是问题,还不能过早下定论。

得先理解技术的实现,看看是否会存在这样的现象。

前端项目中的配置

<scriptsrc="https://static.guance.com/browser-sdk/v2/dataflux-rum.js"type="text/javascript"></script><script>window.DATAFLUX_RUM&&window.DATAFLUX_RUM.init({
applicationId: 'appid_e0da4faf71844ce68ff434db143eccc6',
datakitOrigin: 'https://aliyun-df-rum-dk.guance.com', // 协议(包括://),域名(或IP地址)[和端口号]env: 'production',
version: '1.0.0',
trackInteractions: true,
traceType: 'ddtrace', // 非必填,默认为ddtrace,目前支持 ddtrace、zipkin、skywalking_v3、jaeger、zipkin_single_header、w3c_traceparent 6种类型allowedTracingOrigins: ['https://console-api.guance.com', 'https://.*.my-api-domain.com/'],  // 非必填,允许注入trace采集器所需header头部的所有请求列表。可以是请求的origin,也可以是是正则    })
</script>


我们这里需要关注的是traceType,allowedTracingOrigins配置内容,这里的意思是,凡是请求https://console-api.guance.com  (请求类型XHR),就会携带一些ddtrace相关的链路追踪请求头数据

操作流程

像如下的,我 在前端做一些操作:

添加仪表盘

添加仪表板的请求信息:

可以看到我在前端添加数据,调用API/api/v1/board/add时,请求头有以下几项数据x-datadog开头的请求头,这些数据通过trace_id与前端关联起来

前端用户访问监测SDK采集上报的数据

去应用性能监测通过trace_id把数据查询出来


我观测后,发现前端做完操作,不是立即马上发送,而是一部分数据合在一起发送,如果任何数据都立即发送:会有大量的发小数据包的请求,这个是比较影响性能的。

如果我做完各种操作,立即关闭浏览器,是很有可能存在,就是应用性能监测有数据,而没有与之关联的用户访问监测数据。当然,这不能说用户访问监测采集就是不存在问题的,说不定真的就是这个采集数据的SDK么有发送呢,这个也说不定,所以我们需要有测试手段来测试了。

测试思路

我的思路比较简单:

第一步:

把所有的请求,就是前面配置的https://console-api.guance.comxhr调用的请求头信息都获取到,从请求头信息中把x-datadog-trace-id的值提取出来,写入到文件中(xhr.log)

第二步:

把所有的用户访问监测上报的数据获取到,然后通过正则把trace_id=7866483901034644545这个值7866483901034644545提取出来写入到一个文件中(rum.log)。

最后diff比较下,求xhr.log  与 rum.log 的差集

至于用什么方式获取到请求头及请求内容呢?

可以通过Chrome devtools protocol 简称cdp


代码分享

目录结构

$ tree.
├── cdpdemo
├── cdpdemo.go
├── go.mod
├── go.sum
├── page.pdf
├── procer
│   ├── diff.go
│   └── req.go
├── rum_trace_id.log
└── xhr_trace_id.log

diff.go 

packageprocerimport (
"bufio""fmt""io""os""strings")
funcDiff_trace_id() {
rum, err :=os.OpenFile("rum_trace_id.log", os.O_RDONLY, 0644)
iferr!=nil {
fmt.Println("rum_trace_id.log file read error")
os.Exit(1)
    }
rum_list :=read(rum)
deferrum.Close()
xhr, err :=os.OpenFile("xhr_trace_id.log", os.O_RDONLY, 0644)
iferr!=nil {
fmt.Println("xhr_trace_id.log file read error")
os.Exit(1)
    }
xhr_list :=read(xhr)
deferxhr.Close()
trace_ids :=DiffArray(xhr_list, rum_list)
for_, item :=rangetrace_ids {
fmt.Println(strings.Trim(item, "\n"))
    }
}
funcread(f*os.File) []string {
list := []string{}
bf :=bufio.NewReader(f)
for {
line, err :=bf.ReadSlice('\n')
iferr!=nil&&err==io.EOF {
returnlist        }
list=append(list, string(line))
    }
returnlist}
funcDiffArray(a []string, b []string) []string {
vardiffArray []stringtemp :=map[string]struct{}{}
for_, val :=rangeb {
if_, ok :=temp[val]; !ok {
temp[val] =struct{}{}
        }
    }
for_, val :=rangea {
if_, ok :=temp[val]; !ok {
diffArray=append(diffArray, val)
        }
    }
returndiffArray}

req.go 

packageprocerimport (
"encoding/json""os""regexp""strings""sync""github.com/mafredri/cdp/protocol/network")
varrum*os.Filevarxhr*os.Filefuncinit() {
rum_trace_id, err :=os.OpenFile("rum_trace_id.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
rum=rum_trace_idiferr!=nil {
panic(err)
    }
xhr, err=os.OpenFile("xhr_trace_id.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
iferr!=nil {
panic(err)
    }
}
funcHandle(reqnetwork.RequestWillBeSentReply, wgsync.WaitGroup) {
deferwg.Done()
head :=make(map[string]string)
ifstrings.Contains(req.Request.URL, "/v1/write/rum?precision") {
reg :=regexp.MustCompile(`,?trace_id=(\d+),`)
list :=reg.FindAllStringSubmatch(*req.Request.PostData, -1)
for_, item :=rangelist {
iflen(item[len(item)-1]) >0 {
rum.WriteString(item[len(item)-1] +"\n")
            }
        }
return    }
ifreq.Request.Method!="OPTIONS"&&req.Type!="Font"&&req.Type!="Stylesheet"&&req.Type!="Script"&&req.Type!="Image" {
json.Unmarshal(req.Request.Headers, &head)
iflen(head["x-datadog-trace-id"]) >0 {
xhr.WriteString(head["x-datadog-trace-id"] +"\n")
        }
return    }
}

cdpdemo.go 

packagemainimport (
"cdpdemo/procer""context""flag""fmt""os""strings""sync""time""github.com/mafredri/cdp""github.com/mafredri/cdp/devtool""github.com/mafredri/cdp/rpcc")
varaddressmap[string]*rpcc.Connvarwgsync.WaitGroupfuncmain() {
targurl :=flag.String("url", "https://console.guance.com/", "tagurl")
devtool_address :=flag.String("devtool", "http://127.0.0.1:9222", "devtools address")
diff :=flag.Bool("diff", false, "diff trace_id")
flag.Parse()
if*diff {
procer.Diff_trace_id()
os.Exit(0)
    }
run(*targurl, *devtool_address, &wg)
wg.Wait()
}
funcrun(url, devtool_addressstring, wg*sync.WaitGroup) error {
address=make(map[string]*rpcc.Conn)
ctx :=context.Background()
// Use the DevTools HTTP/JSON API to manage targets (e.g. pages, webworkers).devt :=devtool.New(devtool_address)
wg.Add(1)
gofunc() {
deferwg.Done()
for {
time.Sleep(time.Millisecond*650)
tar, err :=devt.List(ctx)
iferr!=nil {
return            }
for_, t :=rangetar {
ifstrings.HasPrefix(t.URL, url) {
rpconn, err :=rpcc.Dial(t.WebSocketDebuggerURL)
iferr!=nil {
fmt.Println(err)
continue                    }
if_, ok :=address[t.WebSocketDebuggerURL]; !ok {
address[t.WebSocketDebuggerURL] =rpconnwg.Add(1)
goaction(rpconn, ctx, wg)
                    }
                }
            }
        }
    }()
returnnil}
funcaction(rpconn*rpcc.Conn, ctxcontext.Context, wg*sync.WaitGroup) {
deferwg.Done()
cdpclient :=cdp.NewClient(rpconn)
cdpclient.Network.Enable(ctx, nil)
net, err :=cdpclient.Network.RequestWillBeSent(ctx)
iferr!=nil {
return    }
defernet.Close()
for {
reqdata, err :=net.Recv()
iferr!=nil {
return        }
wg.Add(1)
goprocer.Handle(*reqdata, *wg)
    }
}

我在页面各种操作下来,有产生500条左右的请求(各页面乱点,操作5分钟左右),然后关闭了浏览器,最后发现1个 trace_id 存在差异

$ go run cdpdemo.go -diff5669634225797671685

image.png

image.png


去查找下这个差异的trace_id,是在中途出现的,那与关闭浏览器就没啥关系了

image.png



根据数据得出结论:是存在有应用性能监测数据,而没有用户访问监测的问题。用户访问监测 SDK 是存在漏报的现象,需要找开发核实下。

参考连接

https://github.com/ChromeDevTools/awesome-chrome-devtools#chrome-devtools-protocol  包含各语言实现的第三方库

https://github.com/mafredri/cdp  go的第三方库,这个是我使用的

https://chromedevtools.github.io/devtools-protocol/tot/Network/

目录
相关文章
|
Web App开发 开发者
利用chrome控制台调试post请求
利用chrome控制台调试post请求
263 0
|
Web App开发 数据安全/隐私保护
Chrome谷歌浏览器密码数据导出与导入管理(实现数据无缝同步)
Chrome谷歌浏览器密码数据导出与导入管理(实现数据无缝同步)
642 0
|
6月前
|
Web App开发 前端开发 JavaScript
控制台出现报错DevTools failed to load source map: Could not load content for chrome-extension://的原因及解决方案
控制台出现报错DevTools failed to load source map: Could not load content for chrome-extension://的原因及解决方案
341 0
控制台出现报错DevTools failed to load source map: Could not load content for chrome-extension://的原因及解决方案
|
1月前
|
分布式计算 Java Hadoop
Hadoop-18 Flume HelloWorld 第一个Flume尝试!编写conf实现Source+Channel+Sink 控制台查看收集到的数据 流式收集
Hadoop-18 Flume HelloWorld 第一个Flume尝试!编写conf实现Source+Channel+Sink 控制台查看收集到的数据 流式收集
30 1
|
6月前
|
XML 机器学习/深度学习 JSON
在火狐浏览器调ajax获取json数据时,控制台提示“XML 解析错误:格式不佳”。
在火狐浏览器调ajax获取json数据时,控制台提示“XML 解析错误:格式不佳”。
77 0
在火狐浏览器调ajax获取json数据时,控制台提示“XML 解析错误:格式不佳”。
|
4月前
|
JavaScript
JS字符串数据类型转换,字符串如何转成变量,+号只要有一个是字符串,就会把另外一个转成字符串,- * / 都会把数据转成数字类型,数字型控制台是蓝色,字符型控制台是黑色,
JS字符串数据类型转换,字符串如何转成变量,+号只要有一个是字符串,就会把另外一个转成字符串,- * / 都会把数据转成数字类型,数字型控制台是蓝色,字符型控制台是黑色,
|
5月前
|
JavaScript 前端开发 算法
[练习]用Js获取html页面中表单提交的数据并且返回到控制台
[练习]用Js获取html页面中表单提交的数据并且返回到控制台
36 0
|
缓存 Java Windows
IDEA查询控制台打印的历史数据
IDEA查询控制台打印的历史数据
999 0
|
11月前
|
Web App开发
chrome控制台小技巧
chrome控制台小技巧
87 1
|
Web App开发
chrome 浏览器在 112 正式版本以及 114 canary 版本从 devtools 控制台复制文本不会复制高亮显示的文本?
chrome 浏览器在 112 正式版本以及 114 canary 版本从 devtools 控制台复制文本不会复制高亮显示的文本?
117 0