AWS X-Ray SDK for Swift

Build codecov

非官方的 AWS X-Ray SDK for Swift。

项目状态

功能性 Beta 版。

目前 SDK 尚不支持采样规则,跟踪功能只能启用或禁用(问题 57)。

AWS X-Ray SDK for Swift 遵循 SemVer 版本规范。在 1.0.0 版本之前,次要版本号的变更可能会引入破坏性更改。

文档

入门指南

添加依赖项

将包依赖项添加到您的 Swift Package Manager 包清单文件 Package.swift

.package(url: "https://github.com/pokryfka/aws-xray-sdk-swift.git", upToNextMinor(from: "0.7.1"))

并将 AWSXRaySDK 库添加到您的目标(此处为 AWSXRaySDKExample

.target(name: "AWSXRaySDKExample", dependencies: [
    .product(name: "AWSXRaySDK", package: "aws-xray-sdk-swift"),
])

记录

创建 XRayRecorder 的实例和新的上下文

import AWSXRaySDK

let recorder = XRayRecorder()

let context = XRayContext()

显式地开始和结束(子)段

let segment = recorder.beginSegment(name: "Segment 1", context: context)
usleep(100_000)
segment.end()

为了方便起见,可以使用闭包

recorder.segment(name: "Segment 2", context: context) { segment in
    try? segment.subsegment(name: "Subsegment 2.1") { segment in
        _ = segment.subsegment(name: "Subsegment 2.1.1 with Result") { _ -> String in
            usleep(100_000)
            return "Result"
        }
        try segment.subsegment(name: "Subsegment 2.1.2 with Error") { _ in
            usleep(200_000)
            throw ExampleError.test
        }
    }
}

错误和异常

您可以记录 错误和异常

segment.addError(ExampleError.test)
segment.addException(message: "Test Exception")

请注意,闭包中抛出的 Error 会被记录下来。

HTTP 请求数据

您可以记录有关您的应用程序服务或向下游 HTTP API 发出的 HTTP 请求的详细信息,请参阅 HTTP 请求数据

segment.setHTTPRequest(method: .POST, url: "http://www.example.com/api/user")
segment.setHTTPResponse(status: .ok)

注释和元数据

段和子段可以包含 注释

segment.setAnnotation(98101, forKey: "zip_code")

元数据

segment.setMetadata(["debug": ["test": "Metadata string"]])

发射

事件会在结束后立即发射。

子段必须在父段结束之前创建。

子段可能在父段结束后结束,在这种情况下,它们将被显示为待处理,直到它们结束。

确保在程序退出前关闭记录器

recorder.shutdown()

您可以在关闭之前刷新它

recorder.wait()

或者,如果使用 SwiftNIO,则在提供的 EventLoop 上刷新

try recorder.flush(on: eventLoop).wait()

AWS X-Ray 控制台中查看结果

Screenshot of the AWS X-Ray console

有关完整示例,请参阅 AWSXRaySDKExample/main.swift

自定义发射器

默认情况下,事件以 UDP 形式发送到 AWS X-Ray 守护程序,该守护程序会缓冲这些事件并将它们中继到 AWS X-Ray API

自定义发射器必须实现 XRayEmitter 协议

public protocol XRayEmitter {
    func send(_ segment: XRayRecorder.Segment)
    func flush(_ callback: @escaping (Error?) -> Void)
    func shutdown(_ callback: @escaping (Error?) -> Void)
}

它也可以实现 XRayNIOEmitter

public protocol XRayNIOEmitter: XRayEmitter {
    func flush(on eventLoop: EventLoop?) -> EventLoopFuture<Void>
}

必须在创建 XRayRecorder 实例时提供发射器

let recorder = XRayRecorder(emitter: XRayNoOpEmitter())

上下文传播

与其他 X-Ray SDK 不同,AWS X-Ray SDK for Swift 中的 XRayRecorder 不会公开(线程本地)当前的 Segment

上下文(广义上包括但不限于跟踪上下文)应在 BaggageContext 中显式传递

// the baggage should contain trace context
var baggage: BaggageContext

let segment = recorder.beginSegment(name: "Segment 1", baggage: baggage)

// create subsegment by passing the parent segment baggage
let subsegment = recorder.beginSegment(name: "Subsegment 1.1", baggage: segment.baggage)

// or using segment function
let subsegment2 = segment.beginSubsegment(name: "Subsegment 1.2")

您可以从跟踪标头创建新的 X-Ray 上下文

let context = try XRayContext(tracingHeader:  "Root=1-5759e988-bd862e3fe1be46a994272793")

或者使用提供的(或生成的)TraceID、父段和采样决策

let newContext = XRayContext(traceId: .init(), parentId: nil, sampled: true)

您可以在 baggage 中更新 X-Ray 上下文

// empty baggage
var baggage = BaggageContext()
// create new X-Ray context
baggage.xRayContext = XRayContext()

请注意,该主题目前正在 swift-server 社区中讨论

XRayInstrument (WIP)

swift-server 生态系统中的库的集成正在(将要)使用 AWSXRayInstrument 完成,该工具实现了 swift-tracing 库中定义的 TracingInstrument

由于 TracingInstrument 的 API 不稳定,XRayInstrument 的 PoC 实现位于 feature/instrument 分支上。

示例

import AWSXRayInstrument
import TracingInstrumentation

// create and bootstrap the tracer
let instrument = XRayRecorder()
InstrumentationSystem.bootstrap(instrument)

// get the tracer
let tracer = InstrumentationSystem.tracer

// extract the context from HTTP headers
let headers = HTTPHeaders([
     ("X-Amzn-Trace-Id", "Root=1-5759e988-bd862e3fe1be46a994272793"),
 ])
var baggage = BaggageContext()
tracer.extract(headers, into: &baggage, using: HTTPHeadersExtractor())

// create new span (aka segment)
var span = tracer.startSpan(named: "Span 1", context: baggage)

配置

库的行为可以使用环境变量进行配置

或者,可以使用 XRayRecorder.Config 配置 XRayRecorder,这将覆盖环境变量

let recorder = XRayRecorder(config: .init(enabled: true, logLevel: .debug))              

测试

您可以在本地或 Docker 容器中运行 AWS X-Ray 守护程序,请参阅本地运行 X-Ray 守护程序

您可以使用 AWSXRayTesting 中的 XRayLogEmitter 将段“发射”到控制台

import AWSXRaySDK
import AWSXRayTesting

let recorder = XRayRecorder(emitter: XRayLogEmitter())

贡献

代码格式化

使用 swiftformat 格式化代码

swiftformat .

考虑创建 Git pre-commit hook

echo 'swiftformat --lint .' > .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

示例

使用 Swift AWS Lambda Runtime 的 AWS Lambda

按照“将 AWS Lambda 与 AWS X-Ray 结合使用”中的说明启用跟踪。

注意

当为采样的请求调用函数时,Lambda 会自动运行守护程序。

确保在每次调用中刷新记录器

private struct ExampleLambdaHandler: EventLoopLambdaHandler {
    typealias In = Cloudwatch.ScheduledEvent
    typealias Out = Void

    private let recorder = XRayRecorder()

    private func doWork(on eventLoop: EventLoop) -> EventLoopFuture<Void> {
        eventLoop.submit { usleep(100_000) }.map { _ in }
    }

    func handle(context: Lambda.Context, event: In) -> EventLoopFuture<Void> {
        recorder.segment(name: "ExampleLambdaHandler", context: context) {
            self.doWork(on: context.eventLoop)
        }.flatMap {
            self.recorder.flush(on: context.eventLoop)
        }
    }
}

有关完整示例,请参阅 AWSXRaySDKExampleLambda/main.swift

请注意,当 Swift AWS Lambda Runtime 被 instrumentation 时,将不需要创建记录器或刷新它,请参阅 PoC

许可证

AWS X-Ray SDK for Swift 在 Apache 2.0 许可证下获得许可。有关更多信息,请参阅 LICENSE.txt。