全部產品
Search
文件中心

Application Real-Time Monitoring Service:通過OpenTelemetry上報Swift應用資料

更新時間:Aug 08, 2024

通過OpenTelemetry為應用埋點並上報鏈路資料至Managed Service for OpenTelemetry後,Managed Service for OpenTelemetry即可開始監控應用,您可以查看應用拓撲、調用鏈路、異常事務、慢事務和SQL分析等一系列監控資料。本文介紹如何使用OpenTelemetry為Swift應用埋點並上報資料。

前提條件

擷取存取點資訊

  1. 登入ARMS控制台,在左側導覽列單擊接入中心

  2. 服務端應用地區單擊OpenTelemetry卡片。

  3. 在彈出的OpenTelemetry面板中選擇資料需要上報的地區。

    說明

    初次接入的地區將會自動進行資源初始化。

  4. 選擇串連方式上報方式,然後複製存取點資訊。

    • 串連方式:若您的服務部署在阿里雲上,且所屬地區與選擇的接入地區一致,推薦使用阿里雲內網方式,否則選擇公網方式。

    • 上報方式:根據用戶端支援的協議類型選擇HTTP或gRPC協議上報資料。

    image.png

樣本Demo

本文將通過具體樣本介紹如何通過OpenTelemetry上報Swift語言編寫的macOS命令列應用資料,該方法同樣適用於iOS應用。

範例程式碼倉庫地址:opentelemetry-swift-demo

步驟一:建立應用並添加依賴

  1. 選擇要建立的應用,例如macOS的Command Line Tool。

    Command Line Tool

  2. 在XCode中選擇File > Add Packages...,然後在搜尋方塊中輸入https://github.com/open-telemetry/opentelemetry-swift,選擇1.4.1版本。

    說明

    更多版本資訊,請參考opentelemetry-swift releases資訊

    opentelemetry-swift

  3. 選中所需的Package Products。

    本文中使用到的Package Products如下:Package Products

步驟二:OpenTelemetry初始化

  1. 建立用於匯出觀測資料的組件。

    從以下3種方法中選擇1種上報Trace資料。

    • 方法一:通過gRPC協議上報Trace資料

      • 請分別將<gRPC-endpoint><gRPC-port>替換為從前提條件中擷取的存取點資訊,例如:host: "http://tracing-analysis-dc-hz.aliyuncs.com", port:8090

      • 請將<your-token>替換為從前提條件中擷取的鑒權Token。

      let grpcChannel = ClientConnection(
          configuration: ClientConnection.Configuration.default(
              target: .hostAndPort("<gRPC-endpoint>", 8090), // 存取點填寫樣本:tracing-analysis-dc-hz.aliyuncs.com,不需要添加"http://"
              eventLoopGroup: MultiThreadedEventLoopGroup(numberOfThreads: 1)
          )
      )
      
      let otlpGrpcConfiguration = OtlpConfiguration(
          timeout: OtlpConfiguration.DefaultTimeoutInterval,
          headers: [
              ("Authentication","xxxxxx")
          ]
      
      )
      let otlpGrpcTraceExporter = OtlpTraceExporter(channel: grpcChannel, config: otlpGrpcConfiguration)
    • 方法二:通過HTTP協議上報Trace資料

      請將<HTTP-endpoint>替換為從前提條件中擷取的存取點資訊,例如:http://tracing-analysis-dc-hz.aliyuncs.com/adapt_xxxx@xxxx_xxxx@xxxx/api/otlp/traces

      let url = URL(string: "<HTTP-endpoint>")
      let otlpHttpTraceExporter = OtlpHttpTraceExporter(endpoint: url!)
    • 方法三:在命令列輸出Trace資料

      let consoleTraceExporter = StdoutExporter(isDebug: true)
  2. 擷取用於建立Span的Tracer。

    • 請將<your-service-name>替換為要上報的應用程式名稱,<your-host-name>替換為主機名稱。

    • 替換<trace-exporter>,根據步驟1上報Trace方法不同替換為不同值。

      • 方式一:<trace-exporter>替換為otlpGrpcTraceExporter

      • 方式二:<trace-exporter>替換為otlpHttpTraceExporter

      • 方式三:<trace-exporter>替換為consoleTraceExporter

    // 設定應用程式名稱與主機名稱
    let resource = Resource(attributes: [
        ResourceAttributes.serviceName.rawValue: AttributeValue.string("<your-service-name>"),
        ResourceAttributes.hostName.rawValue: AttributeValue.string("<your-host-name>")
    ])
    
    // 配置TracerProvider
    OpenTelemetry.registerTracerProvider(tracerProvider: TracerProviderBuilder()
                                         .add(spanProcessor: BatchSpanProcessor(spanExporter: <trace-exporter>)) // 上報至可觀測鏈路 OpenTelemetry 版
                                         .with(resource: resource)
                                         .build())
    
    // 擷取tracer,用來建立Span
    let tracer = OpenTelemetry.instance.tracerProvider.get(instrumentationName: "instrumentation-library-name", instrumentationVersion: "1.0.0")

步驟三:建立Span追蹤鏈路資料

  1. 建立Span,為Span添加屬性(Attribute)和事件(Event),並輸出當前Span的TraceId。

    let span = tracer.spanBuilder(spanName: "first span").startSpan()
    // 設定屬性
    span.setAttribute(key: "http.method", value: "GET")
    span.setAttribute(key: "http.url", value: "www.aliyun.com")
    let attributes = [
        "key": AttributeValue.string("value"),
        "result": AttributeValue.int(100)
    ]
    
    // your code...
    
    // 設定Event
    span.addEvent(name: "computation complete", attributes: attributes)
    
    // 列印TraceId
    print(span.context.traceId.hexString)
    
    // your code...
    
    // 結束當前span
    span.end()
  2. 建立嵌套的Span。

    let parentSpan = tracer.spanBuilder(spanName: "parent span").startSpan()
    
    // your code...
    
    let childSpan = tracer.spanBuilder(spanName: "child span").setParent(parentSpan).startSpan()
    
    // your code...
    
    childSpan.end()
    
    // your code...
    
    parentSpan.end()
  3. 啟動應用。

    登入ARMS控制台後,在應用監控 > 應用列表頁面選擇目標應用,查看鏈路資料。

    說明

    語言列顯示image表徵圖的應用為接入應用監控的應用,顯示-表徵圖的應用為接入可觀測鏈路 OpenTelemetry 版的應用。

步驟四:打通用戶端與服務端應用鏈路

  1. 修改Header中的Trace透傳格式。

    • 不同協議使用不同的HTTP Header向下遊傳遞Trace上下文,如OpenTelemtry預設使用W3C Trace Context格式(也支援修改為其他格式),而Zipkin使用B3或B3 Multi格式。關於透傳格式的更多資訊,請參見OpenTelemetry指定透傳Header格式

    • 根據服務端使用的協議類型,在用戶端中設定對應的透傳格式(textPropagators和baggagePropagator),以實現iOS用戶端應用與服務端應用鏈路打通:

      • 如果服務端使用OpenTelemetry預設的W3C Trace Context格式,用戶端無需設定textPropagators和baggagePropagator。

      • 如果服務端使用Zipkin的B3/B3 Multi格式,用戶端的textPropagators需設定為B3Propagator,baggagePropagator設定為ZipkinBaggagePropagator。

        // 設定B3透傳格式。
        OpenTelemetry.registerPropagators(textPropagators: [B3Propagator()],
                                          baggagePropagator: ZipkinBaggagePropagator())
      • 如果服務端使用Jaeger協議,用戶端的textPropagators需設定為JaegerPropagator,baggagePropagator設定為JaegerBaggagePropagator。

        // 設定Jaeger透傳格式。
        OpenTelemetry.registerPropagators(textPropagators: [JaegerPropagator()],
                                          baggagePropagator: JaegerBaggagePropagator())
      • 也可以同時設定多種Trace透傳格式。

        // 同時使用W3C Trace Context、B3、Jaeger三種Trace透傳格式。
        OpenTelemetry.registerPropagators(textPropagators: [W3CTraceContextPropagator(), 
                                                            B3Propagator(), 
                                                            JaegerPropagator()],
                                          baggagePropagator: W3CBaggagePropagator())
  2. 匯入URLSessionInstrumentation。

    URLSessionInstrumentation是OpenTelemetry提供的針對URLSession的自動埋點外掛程式,可以自動攔截所有通過URLSession發出的網路請求並建立調用鏈。

    import URLSessionInstrumentation
    
    ...
    let networkInstrumentation = URLSessionInstrumentation(configuration: URLSessionInstrumentationConfiguration())
  3. 使用URLSession進行網路請求,訪問服務端。

    let url = URL(string: "<服務端地址>")!
    let request = URLRequest(url: url)
    let semaphore = DispatchSemaphore(value: 0)
    
    let task = URLSession.shared.dataTask(with: request) { data, _, _ in
        if let data = data {
            let string = String(decoding: data, as: UTF8.self)
            print(string)
        }
        semaphore.signal()
    }
    task.resume()
    
    semaphore.wait()

    展開查看完整範例程式碼

    OpenTelemetryUtil.swift

    import Foundation
    
    import GRPC
    import NIO
    import OpenTelemetryApi
    import OpenTelemetrySdk
    import OpenTelemetryProtocolExporter
    import StdoutExporter
    
    class OpenTelemetryUtil {
        
        private static let tracerProvider: TracerProvider = {
            // 通過gRPC協議上報
            let grpcChannel = ClientConnection(
                configuration: ClientConnection.Configuration.default(
                    target: .hostAndPort("tracing-analysis-dc-hz.aliyuncs.com", 8090),
                    eventLoopGroup: MultiThreadedEventLoopGroup(numberOfThreads: 1)
                )
            )
            
    
            let otlpGrpcConfiguration = OtlpConfiguration(
                timeout: OtlpConfiguration.DefaultTimeoutInterval,
                headers: [
                    ("Authentication","${鑒權Token}")
                ]
    
            )
            let otlpGrpcTraceExporter = OtlpTraceExporter(channel: grpcChannel, config: otlpGrpcConfiguration)
            let consoleTraceExporter = StdoutExporter(isDebug: true)
            
            // 設定應用程式名稱與主機名稱
            let resource = Resource(attributes: [
                ResourceAttributes.serviceName.rawValue: AttributeValue.string("otel-swift-demo-grpc"),
                ResourceAttributes.hostName.rawValue: AttributeValue.string("adam.mac")
            ])
    
            let tracerProvider = TracerProviderBuilder()
                .add(spanProcessor: BatchSpanProcessor(spanExporter: otlpGrpcTraceExporter))
    //            .add(spanProcessor: BatchSpanProcessor(spanExporter: consoleTraceExporter)) // 控制台列印 Trace,調試時使用
                .with(resource: resource)
                .build()
            
            
            // 配置TracerProvider
            OpenTelemetry.registerTracerProvider(tracerProvider: tracerProvider)
            // 修改透傳格式
            OpenTelemetry.registerPropagators(textPropagators: [W3CTraceContextPropagator(),
                                                                B3Propagator(),
                                                                JaegerPropagator()],
                                              baggagePropagator: W3CBaggagePropagator())
            
            return tracerProvider
            
        }()
        
        
        // 擷取tracer,用來建立Span
        static func getTracer(name: String, version: String) -> Tracer {
            return tracerProvider.get(instrumentationName: name, instrumentationVersion: version)
        }
    
    }
    

    main.swift

    import Foundation
    
    
    import OpenTelemetryApi
    import OpenTelemetrySdk
    // 對網路請求進行自動埋點
    import URLSessionInstrumentation
    
    let tracer = OpenTelemetryUtil.getTracer(name: "ios-demo", version: "1.0.0")
    
    // 建立嵌套的Span
    let parentSpan = tracer.spanBuilder(spanName: "parent span").startSpan()
    
    let childSpan = tracer.spanBuilder(spanName: "child span").setParent(parentSpan).startSpan()
    
    // 設定屬性
    childSpan.setAttribute(key: "http.method", value: "GET")
    childSpan.setAttribute(key: "http.url", value: "www.aliyun.com")
    let attributes = [
        "stringKey": AttributeValue.string("value"),
        "intKey": AttributeValue.int(100)
    ]
    // 設定Event
    childSpan.addEvent(name: "event", attributes: attributes)
    // 列印TraceId
    print(childSpan.context.traceId.hexString)
    
    // 發送網路請求訪問服務端
    let networkInstrumentation = URLSessionInstrumentation(configuration: URLSessionInstrumentationConfiguration())
    
    let url = URL(string: "${服務端地址}")!
    let request = URLRequest(url: url)
    let semaphore = DispatchSemaphore(value: 0)
    
    let task = URLSession.shared.dataTask(with: request) { data, _, _ in
        if let data = data {
            let string = String(decoding: data, as: UTF8.self)
            print(string)
        }
        semaphore.signal()
    }
    task.resume()
    
    semaphore.wait()
    
    
    // 手動結束手動建立的 Span
    childSpan.end()
    parentSpan.end()
    
    sleep(60)
    
    print("end")
    
  4. 啟動應用,在調用鏈分析頁面查看用戶端與服務端打通的調用鏈。

    如下圖所示,HTTP GET為iOS應用,zipkin-demo-server為服務端應用。

    image