本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。
在使用网络质量分析器排查问题时可能需要把业务数据串联起来。本文主要介绍在app中同时集成网络质量分析器SDK和Trace SDK来实现数据的交互串联。
前提条件
已开通Trace应用。具体操作,请参见开通Trace应用。
Trace应用开通后,记录project、project关联的endpoint、实例ID,用于后续配置SDK参数时使用。
SDK集成
Android
在app或module级别的build.gradle文件中加入以下配置内容:
// sls android SDK,如果已经集成,可以不更新版本号 implementation 'io.github.aliyun-sls:aliyun-log-android-sdk:2.7.0@aar' // 网络探测SDK implementation 'io.github.aliyun-sls:sls-android-network-diagnosis:2.2.1@aar' implementation 'io.github.aliyun-sls:sls-android-core:1.0.8@aar' implementation 'io.github.aliyun-sls:sls-android-ot:1.0.8.1@aar' // opentelemetry extension implementation 'io.github.aliyun-sls:android-otel-common:1.1.0@aar' implementation 'io.github.aliyun-sls:android-exporter-otlp:1.1.1@aar' // opentelemetry sdk implementation(platform("io.opentelemetry:opentelemetry-bom:1.30.0")) implementation("io.opentelemetry:opentelemetry-api") implementation("io.opentelemetry:opentelemetry-context") implementation('io.opentelemetry:opentelemetry-sdk') implementation("io.opentelemetry:opentelemetry-semconv:1.30.0-alpha")
iOS
iOS新版本的依赖包与之前版本有所不同,建议按照以下方式导入依赖。
在项目Podfile加入以下内容:
use_frameworks! source 'https://gitee.com/aliyun-sls/Specs.git' target '${your_target}' do |t| # SLS iOS SDK pod 'AliyunLogProducer', '4.1.0' # OpenTelemetry Extension pod 'AliyunLogOTelCommon', '4.1.0' pod 'AliyunLogOtlpExporter', '4.1.0' # 网络探测SDK pod 'AliyunLogNetworkDiagnosis', '4.1.0' end
SDK初始化
网络质量探测SDK初始化
具体操作,请参见SDK插件使用说明。
以下是初始化示例代码,供参考:
Android
Credentials credentials = new Credentials(); NetworkDiagnosisCredentials networkDiagnosisCredentials = credentials.getNetworkDiagnosisCredentials(); networkDiagnosisCredentials.endpoint = "${ipa_endpoint}"; networkDiagnosisCredentials.project = "${ipa_project}"; networkDiagnosisCredentials.secretKey = "${ipa_secretKey}" final OptionConfiguration optionConfiguration = configuration -> { configuration.enableNetworkDiagnosis = true; }; // 预先初始化。在用户同意数据隐私合规之前调用 SLSAndroid.preInit(context, credentials, optionConfiguration); // 用户同意数据隐私合规协议后,完整初始化 SLSAndroid.initialize(context, credentials, optionConfiguration); // 注册SDK回调 SLSAndroid.registerCredentialsCallback(new Callback() { @Override public void onCall(String feature, LogProducerResult result) { // 参数错误,或AK没有设置 if (LogProducerResult.LOG_PRODUCER_SEND_UNAUTHORIZED == result || LogProducerResult.LOG_PRODUCER_PARAMETERS_INVALID == result) { // 处理token过期,AK失效等鉴权类问题 Credentials credentials = new Credentials(); credentials.accessKeyId = "${ipa_accesskey_id}"; credentials.accessKeySecret = "${ipa_accesskey_secret}"; // credentials.securityToken = "${ipa_accesskey_token}";// 仅当通过STS方式获取的AK时才需要设置 // 如果是仅更新 AK 的话,可以不对NetworkDiagnosisCredentials进行更新 //NetworkDiagnosisCredentials networkDiagnosisCredentials = credentials // .getNetworkDiagnosisCredentials(); // networkDiagnosisCredentials.endpoint = "${ipa_endpoint}"; // networkDiagnosisCredentials.project = "${ipa_project}"; // networkDiagnosisCredentials.secretKey = ""; // secretKey不支持动态更新 SLSAndroid.setCredentials(credentials); } } });
iOS
// 导入依赖模块 import AliyunLogNetworkDiagnosis let credentials = SLSCredentials() let networkDiagnosisCredentials = credentials.createNetworkDiagnosisCredentials() networkDiagnosisCredentials.endpoint = "${ipa_endpoint}" networkDiagnosisCredentials.project = "${ipa_project}" networkDiagnosisCredentials.secretKey = "${ipa_secretKey}" let configutaionHandler: ((SLSConfiguration) -> Void) = { configuration in configuration.enableNetworkDiagnosis = true } // 预先初始化。在用户同意数据隐私合规之前调用 SLSCocoa.sharedInstance().preInit(credentials, configuration: configutaionHandler) // 用户同意数据隐私合规协议后,完整初始化 SLSCocoa.sharedInstance().initialize(credentials, configuration: configutaionHandler) // 注册SDK回调 SLSCocoa.sharedInstance().registerCredentialsCallback { feature, code in // 参数错误,或AK没有设置 if ("LogProducerParametersInvalid" == code) { let credentials = SLSCredentials() let networkDiagnosisCredentials = credentials.createNetworkDiagnosisCredentials() networkDiagnosisCredentials.endpoint = "${ipa_endpoint}" networkDiagnosisCredentials.project = "${ipa_project}" // networkDiagnosisCredentials.secretKey 不支持动态更新 credentials.accessKeyId = "${ipa_accesskey_id}" credentials.accessKeySecret = "${ipa_accesskey_secret}" // networkDiagnosisCredentials.securityToken = "${ipa_accesskey_token}" // 仅当通过STS方式获取的AK时才需要设置 SLSCocoa.sharedInstance().setCredentials(credentials) } // AK过期或无效 if ("LogProducerSendUnauthorized" == code) { let credentials = SLSCredentials() credentials.accessKeyId = "${ipa_accesskey_id}" credentials.accessKeySecret = "${ipa_accesskey_secret}" // credentials.securityToken = "${ipa_accesskey_token}" // 仅当通过STS方式获取的AK时才需要设置 SLSCocoa.sharedInstance().setCredentials(credentials) } }
Trace SDK初始化
必须确保网络探测SDK已经初始化,才能进行这一步。
Trace SDK基于OpenTelemetry SDK实现,使用方式可参考如下文档:
Android请参见通过OpenTelemetry接入Android Trace数据。
iOS请参见通过OpenTelemetry接入iOS Trace数据。
示例代码如下:
Android
// 全局设置 // 参数配置错误,或者AK访问失效时会回调以下方法 ConfigurationManager.getInstance().setProvider( scope -> { if ("ipa".equalsIgnoreCase(scope)) { return AccessKey.accessKey( "${ipa_accesskey_id}", "${ipa_accesskey_secret}", "${ipa_accesskey_token}" ); } if ("trace".equalsIgnoreCase(scope)) { AccessKey.accessKey( "${trace_accesskey_id}", "${trace_accesskey_secret}", "${trace_accesskey_token}" ); } return null; }, scope -> { if ("ipa".equalsIgnoreCase(scope)) { return Workspace.workspace( "${ipa_endpoint}", "${ipa_project}", "ipa-${ipa_instanceId}-raw" ); } if ("trace".equalsIgnoreCase(scope)) { return Workspace.workspace( "${trace_endpoint}", "${trace_project}", "${trace_instanceId}-traces" ); } return null; }, scope -> { // 该配置用不到,直接返回null即可 return null; } ); // 初始化Trace Exporter,用于导出数据到Trace OtlpSLSSpanExporter exporter = OtlpSLSSpanExporter.builder() .setScope("trace") .setEndpoint("${trace_endpoint}") .setProject("${trace_project}") .setLogstore("${trace_instanceId}-traces") .setAccessKey(PreferenceUtils.getAccessKeyId(this), PreferenceUtils.getAccessKeySecret(this), null) // 初始化时可以先不设置AK .build(); SdkTracerProviderBuilder builder = SdkTracerProvider.builder() .addSpanProcessor(BatchSpanProcessor.builder(exporter).build()) .setResource(io.opentelemetry.sdk.resources.Resource.create(Attributes.builder() .put(ResourceAttributes.SERVICE_NAME, "network-Trace") // 建议根据实际业务情况填写 .put(ResourceAttributes.SERVICE_NAMESPACE, "AndroidExamples") // 建议填写应用名称 .put(ResourceAttributes.SERVICE_VERSION, BuildConfig.VERSION_NAME) .put(ResourceAttributes.HOST_NAME, Build.HOST) .put(ResourceAttributes.OS_NAME, "Android") .put(ResourceAttributes.OS_TYPE, "Android") .put(ResourceAttributes.DEVICE_ID, Utdid.getInstance().getUtdid(this)) // 设置设备Id .build())); // 初始化网络质量探测 Exporter,用于导出数据到网络质量探测 NetworkDiagnosis.getInstance().setupTracer(builder); SdkTracerProvider tracerProvider = builder.build(); OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal(); NetworkDiagnosis.getInstance().setOpenTelemetrySdk(GlobalOpenTelemetry.get());
iOS
// 导入依赖模块 import AliyunLogOTelCommon import OpenTelemetryApi import OpenTelemetrySdk // 全局配置 // 参数配置错误,或者AK访问失效时会回调以下方法 ConfigurationManager.shared.setProvider ( accessKeyProvider: { scope in // 根据scope的不同,返回对应的AccessKey,按照实际情况填写 // 网络质量探测 if ("ipa" == scope) { return AccessKey.`init`( accessKeyId: "${ipa_accesskey_id}", accessKeySecret: "${ipa_accesskey_secret}", accessKeySecuritToken: "${ipa_accesskey_token}" ) } // Trace if ("trace" == scope) { return AccessKey.`init`( accessKeyId: "${trace_accesskey_id}", accessKeySecret: "${trace_accesskey_secret}", accessKeySecuritToken: "${trace_accesskey_token}" ) } return nil }, workspaceProvider: { scope in // 根据scope的不同,返回Workspace(包含endpoint、project、logstore) // 网络质量探测默认不用设置,如果需要动态更新endpoint、project等相关参数,则需要按照下述方式处理 // if ("ipa" == scope) { // return Workspace.`init`( // endpoint: "${ipa_endpoint}", // project: "${ipa_project}", // instanceId: "ipa-\("${ipa_instanceId}")-raw" // ) // } // Trace 如果涉及到动态更新,按照以下方式设置 if ("trace" == scope) { return Workspace.`init`( endpoint: "${trace_endpoint}", project: "${trace_project}", instanceId: "\("${trace_instanceId}")-traces" ) } return nil }) // 初始化Trace Exporter,用于导出数据到Trace let exporter = OtlpSLSSpanExporter.builder("trace") .setEndpoint("${trace_endpoint}") .setProject("${trace_project}") .setLogstore("${trace_instanceId}-traces") .build() // 初始化网络质量探测 Exporter,用于导出数据到网络质量探测 let ipaExporter = NetworkDiagnosisHelper.exporter() let spanExporters = MultiSpanExporter(spanExporters: [exporter, ipaExporter]) let spanProcessor = BatchSpanProcessor(spanExporter: spanExporters) let tracerProviderBuilder = TracerProviderBuilder() .add(spanProcessor: spanProcessor) .with(resource: Resource(attributes: [ ResourceAttributes.serviceName.rawValue: AttributeValue.string("iOS-Trace"), // 可根据业务情况填写 ResourceAttributes.serviceNamespace.rawValue: AttributeValue.string("iOSExamples"), // 建议填写App名称 ResourceAttributes.serviceVersion.rawValue: AttributeValue.string("1.0.0"), // 建议填写App版本号 ResourceAttributes.osName.rawValue: AttributeValue.string("iOS"), // 手机系统 ResourceAttributes.deviceId.rawValue: AttributeValue.string(SLSUtdid.getUtdid()) // 设备Id ])) OpenTelemetry.registerTracerProvider(tracerProvider:tracerProviderBuilder.build())
变量说明
上文SDK初始化过程使用到的变量说明如下:
变量名
描述
示例
${ipa_endpoint}
网络质量分析器Endpoint。
https://cn-hagnzhou.log.aliyuncs.com
${ipa_project}
选择目标Project。
日志服务将自动在该Project下生成Logstore,用于存储网络探测数据。
test-project
${ipa_secretKey}
网络质量分析器SecretKey,接入端应用的密钥。更多信息,请参见使用网络质量分析器。
ey********************************************************************************************************************************************=
${ipa_accesskey_id}
网络质量分析器AccessKey ID。
建议您使用只具备日志服务Project写入权限的RAM用户的AccessKey(包括AccessKey ID和AccessKey Secret)。授予RAM用户向指定Project写入数据权限的具体操作,请参见RAM自定义授权示例。如何获取AccessKey的具体操作,请参见访问密钥。
LA*****************
${ipa_accesskey_secret}
网络质量分析器AccessKey Secret。
建议您使用只具备日志服务Project写入权限的RAM用户的AccessKey。
fR*****************
${ipa_accesskey_token}
网络质量分析器AccessKey SecurityToken。仅当使用STS进行临时访问时候才需要配置的。
T****************v
${trace_endpoint}
Trace实例对应的Endpoint。
https://cn-hangzhou.log.aliyuncs.com
${trace_project}
Trace实例对应的Project名称。
test-project
${trace_instanceId}
全栈可观测服务实例ID。更多信息,请参见创建Trace实例。
test-traces
${trace_accesskey_id}
Trace AccessKey ID。
建议您使用只具备日志服务Project写入权限的RAM用户的AccessKey(包括AccessKey ID和AccessKey Secret)。授予RAM用户向指定Project写入数据权限的具体操作,请参见RAM自定义授权示例。如何获取AccessKey的具体操作,请参见访问密钥。
LA*****************
${trace_accesskey_secret}
Trace AccessKey Secret。
建议您使用只具备日志服务Project写入权限的RAM用户的AccessKey。
fR*****************
${trace_accesskey_token}
Trace AccessKey SecurityToken。仅当使用STS进行临时访问时候才需要配置的。
T****************v
${ipa_instanceId}
网络质量分析器应用ID。在网络质量分析器应用中的接入端管理页面获取。
Mq********************
Trace新增跳转配置
为了能够从Trace分析页面跳转到网络探测详情页面,需要在Trace分析中增加事件配置。
在Trace详情页签中,单击事件配置。
在Drilldown配置面板中,完成如下配置:
单击添加字段,然后选择attribute.detection.type
然后单击添加事件,并选择自定义HTTP链接
自定义名称:建议输入“网络诊断”
协议:选择HTTPS
链接地址:输入以下固定链接
是否转码:默认配置即可
打开新窗口:建议开启
cms.console.aliyun.com/ipa?hide_sldebar=true&type=sls&page=detectionDetail&detectionType=${{attribute.detection.type}}&deviceId=${{attribute.detection.deviceId }}&traceId=${{attribute.detection.traceId}}&spanId=${{attribute.detection.spanId}}
最后单击确定按钮保存配置
验证配置
按照以上方式接入数据后,您可以运行一下代码以便产生数据。数据上报成功后,在Trace详情页面看到以下页面即接入成功。
其中:玩家连接、玩家登录账号、GET /mall/api/productcategory/list 是业务Trace数据,http是网络探测数据。
在右侧面板,鼠标放在“ping”字段上,会自动弹出一个浮层,单击“网络诊断”即可。
业务链路串联示例
Trace SDK通过Span来跟踪调用链路。在Trace SDK的设计理念中,每个Span表示应用程序执行路径的一段操作。Span与Span之间通过TraceId、SpanId、ParentSpanId表示次序和依赖关系。如:
不同的Span通过同样的TraceId可以关联为一条Trace,即调用链路;
不同的Span通过ParentSpanId可以表示依赖关系,如:B的parentSpanId是A的spanId,则B是A的子操作。
这里举一个调用链路的例子,供使用Trace SDK时作为参考。有以下玩家进入游戏的过程:
以上过程有以下特性:
“开始”表示用户发起游戏连接起始,“结束”表示已经进入游戏,或游戏进入失败
进入游戏成功的条件:网络连接、资源下载、进入游戏环节都成功
每个环节都有主备链路,主链路调用失败后会发起一次网络探测
每个环节的调用顺序是串联,只有前一个环节成功后,才会调用下一个环节
每次“开始”,都是一次独立的调用链路
1. 开始
“开始”表示用户发起游戏连接,在这个业务流程中,“开始”表示一次完整业务调用的开始,因此需要针对“开始”这个操作进行埋点。
Android埋点如下:
Tracer tracer = GlobalOpenTelemetry.get().getTracer("connect"); final Span startSpan = tracer.spanBuilder("开始连接游戏").startSpan(); // 由于后续流程的操作都需要和“开始”span关联,我们需要把它设为“活跃”状态,后续流程产生的Span就会和“开始”自动关联,建议通过以下方式: try (Scope ignored = span.makeCurrent()) { // 后续流程的调用放在这里 } finally { startSpan.end(); }
iOS埋点如下:
// 构建一个tracer,该tracer为当前流程共用 let tracer = OpenTelemetry.instance.tracerProvider.get(instrumentationName: "connect", instrumentationVersion: "1.0.0") // 产生一个“开始”span let startSpan = tracer.spanBuilder(spanName: "开始连接游戏").startSpan() // 由于后续流程的操作都需要和“开始”span关联,我们需要把它设为“活跃”状态,后续流程产生的Span就会和“开始”自动关联 OpenTelemetry.instance.contextProvider.setActiveSpan(span)
2. 网关链接
网关存在主备链路,当连接失败时,需要对当前链路发起一次网络探测。
Android示例代码:
public void connectProxy() { final Span connectSpan = tracer.spanBuilder("连接网关") .startSpan() // 后续主备线路的连接产生的span都作为connectSpan的子节点存在 OpenTelemetry.instance.contextProvider.setActiveSpan(span) var success = connectProxy("主线路") // 如果连接失败,则链接备用线路 success = connectProxy("备用线路1") // 不管连接成功或失败,都需要结束connectSpan connectSpan.end() } private boolean connectProxy(String line) { final Span proxySpan = tracer.spanBuilder("连接网关") .setAttribute("proxy_line", line) .startSpan(); // 连接的具体过程省略 // ... // 如果连接失败,需要发起tcp类型的网络探测 try (Scope ignored = proxySpan.makeCurrent()) { TcpPingRequest request = new TcpPingRequest(); request.domain = "www.aliyun.com"; request.port = 8888; NetworkDiagnosis.getInstance().tcpPing(request); } finally { // 连接过程结束后,结束proxySpan proxySpan.end() } return true }
iOS:
func connectProxy() { let connectSpan = tracer.spanBuilder(spanName: "连接网关").startSpan() // 后续主备线路的连接产生的span都作为connectSpan的子节点存在 OpenTelemetry.instance.contextProvider.setActiveSpan(span) var success = connectProxy("主线路") // 如果连接失败,则链接备用线路 success = connectProxy("备用线路1") // 不管连接成功或失败,都需要结束connectSpan connectSpan.end() } func connectProxy(line: String) -> Bool { let proxySpan = tracer.spanBuilder(spanName: "连接网关") .setAttribute(key: "proxy_name", value: line) .startSpan() // 连接的具体过程省略 // ... // 如果连接失败,需要发起tcp类型的网络探测 OpenTelemetry.instance.contextProvider.setActiveSpan(proxySpan) // 可选,如果增加这一行,网络探测的Span会作为proxySpan的子span存在 let request = SLSTcpPingRequest() request.domain = "www.aliyun.com" request.port = 8888 SLSNetworkDiagnosis.sharedInstance().tcpPing2(request) // 连接过程结束后,结束proxySpan proxySpan.end() return true }
3. 资源下载、进入游戏
与网关连接的操作相似,这里省略。
4. 结束
不管整个流程成功或是失败,结束流程都需要处理。
Android已经在 finally 代码块中结束了startSpan,不需要额外处理。
iOS:
// 结束startSpan即可 startSpan.end()
- 本页导读 (1)
- 前提条件
- SDK集成
- Android
- iOS
- SDK初始化
- 网络质量探测SDK初始化
- Trace SDK初始化
- 变量说明
- Trace新增跳转配置
- 业务链路串联示例
- 1. 开始
- 2. 网关链接
- 3. 资源下载、进入游戏
- 4. 结束