Instrumenting an iOS application for tracing in OCI Application Performance Monitoring (APM) with the opentelemetry-swift libbrary


CONTEXT

Application Performance Monitoring (APM) provides a comprehensive set of features to monitor applications and diagnose performance issues. APM tracing is compatible with JSON-encoded Zipkin v2 format. among others.

Opentelemetry-swift is a set of libraries for iOS for tracing purposes. It provides exporters for several technologies and/or de facto standrads such opentracing (deprecated), opentelemetry, zipkin

HANDS ON

GIT CLONE OPENTELEMETRY-SWIFT

git clone https://github.com/open-telemetry/opentelemetry-swift.git

CREATE NEW APP PROJECT

ADD LIBRARIES TO WORKSPACE AND RELATIVES

After dependency checking libraries appear in the workspace:

Add libraries to project:

Select libraries:

INSTRUMENT THE APPLICATION CODE

IMPORT SECTION

import Foundation
import OpenTelemetrySdk
import StdoutExporter
import URLSessionInstrumentation
import JaegerExporter
import OpenTelemetryApi
import OpenTelemetrySdk
import ResourceExtension
import ZipkinExporter
import SignPostIntegration

ADD COMMON VARIABLES AND ENDPOINTS

The APM trace collector endpoint url format is explained here and here.

// instrumentation vars
let resources = DefaultResources().get()
let instrumentationLibraryName = "SimpleExporter"
let instrumentationLibraryVersion = "semver:0.1.0"
var instrumentationLibraryInfo = InstrumentationLibraryInfo(name: instrumentationLibraryName, version: instrumentationLibraryVersion)
let stdoutExporter = StdoutExporter()
// apm endpoint
let tracesEndpoint = "https://aaa...wq.apm-agt.eu-frankfurt-1.oci.oraclecloud.com/20200101/observations/public-span?dataFormat=zipkin&dataFormatVersion=2&dataKey=FE...YF"
let serviceName = "servicename"
let zipkinExporterOptions = ZipkinTraceExporterOptions(endpoint: tracesEndpoint , serviceName: serviceName )
let zipkinExporter = ZipkinTraceExporter(options: zipkinExporterOptions)
let spanExporter = MultiSpanExporter(spanExporters: [/*jaegerExporter, stdoutExporter,*/ zipkinExporter  ])
let spanProcessor = SimpleSpanProcessor(spanExporter: spanExporter)
//let spanProcessor = BatchSpanProcessor(spanExporter: spanExporter)
var tracer: TracerSdk = OpenTelemetrySDK.instance.tracerProvider.get(instrumentationName: instrumentationLibraryName, instrumentationVersion: instrumentationLibraryVersion) as! TracerSdk

ADD AN INIT METHOD

    init(){
        
        tracer = OpenTelemetrySDK.instance.tracerProvider.get(instrumentationName: instrumentationLibraryName, instrumentationVersion: instrumentationLibraryVersion) as! TracerSdk
        OpenTelemetrySDK.instance.tracerProvider.addSpanProcessor(spanProcessor)
        if #available(macOS 10.14, *) {
            OpenTelemetrySDK.instance.tracerProvider.addSpanProcessor(SignPostIntegration())
        }
    }

INSTRUMENT SOME CODE

 private func addItem() {
        
        withAnimation {
            
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date()
    
            let span = tracer.spanBuilder(spanName: "addItem").setSpanKind(spanKind: .server).setActive(true).startSpan()
            span.setAttribute(key: "op", value: "new item added")

            do {
                
                let childSpan = tracer.spanBuilder(spanName: "save data").setSpanKind(spanKind: .client).startSpan()
                
                try viewContext.save()
                
                childSpan.setAttribute(key: "op", value: "data saved")

                childSpan.end()
                span.end()
                
                
            } catch {
                
                let childSpan = tracer.spanBuilder(spanName: "save data").setSpanKind(spanKind: .client).startSpan()
                childSpan.setAttribute(key: "op", value: "Unresolved error")
                childSpan.end()
                span.end()
                
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }

CONFIGURE TRANSPORT SECURUTY EXCEPTIONS

Add endpoint connectivity exceptions in the info.plist file (in xCode 13+ the Info.plist mechanism has changed a little bit) to all the endpoints that your app is connecting and are raising security exceptions:

Example:

<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSAllowsArbitraryLoads</key>
		<true/>
		<key>NSExceptionDomains</key>
		<dict>
			<key>bi...tq.apigateway.eu-frankfurt-1.oci.customer-oci.com</key>
			<dict>
				<key>NSExceptionAllowsInsecureHTTPLoads</key>
				<true/>
			</dict>
			<key>*.oraclecloud.com</key>
			<dict>
				<key>NSExceptionAllowsInsecureHTTPLoads</key>
				<true/>
			</dict>
            <key>t.....s.moskis.org</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
		</dict>
	</dict>

TEST THE APP

RUN ON THE SIMULATOR

OR RUN ON THE DEVICE

SEE WHAT HAPPENS

truobleshooting iOS

nw_proxy_resolver_create_parsed_array [C4 proxy pac] Evaluation error: NSURLErrorDomain: -1003

If you have following error, it seems is realted with iOS emulator, change network proxy settings and good luck!!

[boringssl] boringssl_metrics_log_metric_block_invoke(151) Failed to log metrics

It seems you can forget it…

https://developer.apple.com/forums/thread/697026

That’s all, hope it helps!! 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.