From 784a4e64f250d3702fa10fde47a0d3489d189cc1 Mon Sep 17 00:00:00 2001 From: mamunto Date: Wed, 6 Nov 2024 09:08:47 -0500 Subject: [PATCH 1/6] compression support for HTTP exporter --- Package.swift | 10 +- Package@swift-5.6.swift | 10 +- Package@swift-5.9.swift | 10 +- .../DataCompression/DataCompression.swift | 6 +- .../Upload/RequestBuilder.swift | 1 + .../common/OtlpConfiguration.swift | 14 +- .../OtlpHttpExporterBase.swift | 48 +++++-- .../OtlpHttpExporterBaseTests.swift | 130 ++++++++++++++++++ 8 files changed, 206 insertions(+), 23 deletions(-) rename Sources/Exporters/{DatadogExporter/Utils => }/DataCompression/DataCompression.swift (99%) create mode 100644 Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpExporterBaseTests.swift diff --git a/Package.swift b/Package.swift index 7bb43a26..15a43e33 100644 --- a/Package.swift +++ b/Package.swift @@ -29,6 +29,7 @@ let package = Package( .library(name: "DatadogExporter", type: .static, targets: ["DatadogExporter"]), .library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]), .library(name: "OTelSwiftLog", type: .static, targets: ["OTelSwiftLog"]), + .library(name: "DataCompression", type: .static, targets: ["DataCompression"]), .executable(name: "simpleExporter", targets: ["SimpleExporter"]), .executable(name: "OTLPExporter", targets: ["OTLPExporter"]), .executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]), @@ -110,6 +111,9 @@ let package = Package( "OpenTelemetryProtocolExporterCommon", .product(name: "GRPC", package: "grpc-swift")], path: "Sources/Exporters/OpenTelemetryProtocolGrpc"), + .target(name: "DataCompression", + dependencies: [], + path: "Sources/Exporters/DataCompression"), .target(name: "StdoutExporter", dependencies: ["OpenTelemetrySdk"], path: "Sources/Exporters/Stdout"), @@ -117,7 +121,8 @@ let package = Package( dependencies: ["OpenTelemetrySdk"], path: "Sources/Exporters/InMemory"), .target(name: "DatadogExporter", - dependencies: ["OpenTelemetrySdk"], + dependencies: ["OpenTelemetrySdk", + "DataCompression"], path: "Sources/Exporters/DatadogExporter", exclude: ["NOTICE", "README.md"]), .target(name: "PersistenceExporter", @@ -163,6 +168,7 @@ let package = Package( .testTarget(name: "OpenTelemetryProtocolExporterTests", dependencies: ["OpenTelemetryProtocolExporterGrpc", "OpenTelemetryProtocolExporterHttp", + "DataCompression", .product(name: "NIO", package: "swift-nio"), .product(name: "NIOHTTP1", package: "swift-nio"), .product(name: "NIOTestUtils", package: "swift-nio")], @@ -190,7 +196,7 @@ let package = Package( path: "Examples/OTLP Exporter", exclude: ["README.md"]), .target(name: "OTLPHTTPExporter", - dependencies: ["OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"], + dependencies: ["OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration", "DataCompression"], path: "Examples/OTLP HTTP Exporter", exclude: ["README.md"]), .target(name: "PrometheusSample", diff --git a/Package@swift-5.6.swift b/Package@swift-5.6.swift index 64fd9aca..2d25e584 100644 --- a/Package@swift-5.6.swift +++ b/Package@swift-5.6.swift @@ -30,6 +30,7 @@ let package = Package( .library(name: "DatadogExporter", type: .static, targets: ["DatadogExporter"]), .library(name: "NetworkStatus", type: .static, targets: ["NetworkStatus"]), .library(name: "OTelSwiftLog", type: .static, targets: ["OTelSwiftLog"]), + .library(name: "DataCompression", type: .static, targets: ["DataCompression"]), .executable(name: "simpleExporter", targets: ["SimpleExporter"]), .executable(name: "OTLPExporter", targets: ["OTLPExporter"]), .executable(name: "OTLPHTTPExporter", targets: ["OTLPHTTPExporter"]), @@ -115,6 +116,9 @@ let package = Package( "OpenTelemetryProtocolExporterCommon", .product(name: "GRPC", package: "grpc-swift")], path: "Sources/Exporters/OpenTelemetryProtocolGrpc"), + .target(name: "DataCompression", + dependencies: [], + path: "Sources/Exporters/DataCompression"), .target(name: "StdoutExporter", dependencies: ["OpenTelemetrySdk"], path: "Sources/Exporters/Stdout"), @@ -122,7 +126,8 @@ let package = Package( dependencies: ["OpenTelemetrySdk"], path: "Sources/Exporters/InMemory"), .target(name: "DatadogExporter", - dependencies: ["OpenTelemetrySdk"], + dependencies: ["OpenTelemetrySdk", + "DataCompression"], path: "Sources/Exporters/DatadogExporter", exclude: ["NOTICE", "README.md"]), .target(name: "PersistenceExporter", @@ -173,6 +178,7 @@ let package = Package( .testTarget(name: "OpenTelemetryProtocolExporterTests", dependencies: ["OpenTelemetryProtocolExporterGrpc", "OpenTelemetryProtocolExporterHttp", + "DataCompression", .product(name: "NIO", package: "swift-nio"), .product(name: "NIOHTTP1", package: "swift-nio"), .product(name: "NIOTestUtils", package: "swift-nio")], @@ -207,7 +213,7 @@ let package = Package( ), .executableTarget( name: "OTLPHTTPExporter", - dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"], + dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration", "DataCompression"], path: "Examples/OTLP HTTP Exporter", exclude: ["README.md"] ), diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift index e15fa60c..49ff7747 100644 --- a/Package@swift-5.9.swift +++ b/Package@swift-5.9.swift @@ -23,6 +23,7 @@ let package = Package( .library(name: "PersistenceExporter", targets: ["PersistenceExporter"]), .library(name: "InMemoryExporter", targets: ["InMemoryExporter"]), .library(name: "OTelSwiftLog", targets: ["OTelSwiftLog"]), + .library(name: "DataCompression", type: .static, targets: ["DataCompression"]), .executable(name: "ConcurrencyContext", targets: ["ConcurrencyContext"]), .executable(name: "loggingTracer", targets: ["LoggingTracer"]), ], @@ -70,6 +71,9 @@ let package = Package( "OpenTelemetryProtocolExporterCommon", .product(name: "GRPC", package: "grpc-swift")], path: "Sources/Exporters/OpenTelemetryProtocolGrpc"), + .target(name: "DataCompression", + dependencies: [], + path: "Sources/Exporters/DataCompression"), .target(name: "StdoutExporter", dependencies: ["OpenTelemetrySdk"], path: "Sources/Exporters/Stdout"), @@ -100,6 +104,7 @@ let package = Package( .testTarget(name: "OpenTelemetryProtocolExporterTests", dependencies: ["OpenTelemetryProtocolExporterGrpc", "OpenTelemetryProtocolExporterHttp", + "DataCompression", .product(name: "NIO", package: "swift-nio"), .product(name: "NIOHTTP1", package: "swift-nio"), .product(name: "NIOTestUtils", package: "swift-nio")], @@ -240,7 +245,7 @@ extension Package { ), .executableTarget( name: "OTLPHTTPExporter", - dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration"], + dependencies: ["OpenTelemetrySdk", "OpenTelemetryProtocolExporterHttp", "StdoutExporter", "ZipkinExporter", "ResourceExtension", "SignPostIntegration", "DataCompression"], path: "Examples/OTLP HTTP Exporter", exclude: ["README.md"] ), @@ -256,7 +261,8 @@ extension Package { dependencies: ["ResourceExtension", "OpenTelemetrySdk"], path: "Tests/InstrumentationTests/SDKResourceExtensionTests"), .target(name: "DatadogExporter", - dependencies: ["OpenTelemetrySdk"], + dependencies: ["OpenTelemetrySdk", + "DataCompression"], path: "Sources/Exporters/DatadogExporter", exclude: ["NOTICE", "README.md"]), .testTarget(name: "DatadogExporterTests", diff --git a/Sources/Exporters/DatadogExporter/Utils/DataCompression/DataCompression.swift b/Sources/Exporters/DataCompression/DataCompression.swift similarity index 99% rename from Sources/Exporters/DatadogExporter/Utils/DataCompression/DataCompression.swift rename to Sources/Exporters/DataCompression/DataCompression.swift index 07c5a2f1..8cda5df3 100644 --- a/Sources/Exporters/DatadogExporter/Utils/DataCompression/DataCompression.swift +++ b/Sources/Exporters/DataCompression/DataCompression.swift @@ -30,7 +30,7 @@ import Foundation import Compression -extension Data +public extension Data { /// Compresses the data. /// - parameter withAlgorithm: Compression algorithm to use. See the `CompressionAlgorithm` type @@ -247,7 +247,7 @@ extension Data /// Calculate the Adler32 checksum of the data. /// - returns: Adler32 checksum type. Can still be further advanced. - func adler32() -> Adler32 + internal func adler32() -> Adler32 { var res = Adler32() res.advance(withChunk: self) @@ -256,7 +256,7 @@ extension Data /// Calculate the Crc32 checksum of the data. /// - returns: Crc32 checksum type. Can still be further advanced. - func crc32() -> Crc32 + internal func crc32() -> Crc32 { var res = Crc32() res.advance(withChunk: self) diff --git a/Sources/Exporters/DatadogExporter/Upload/RequestBuilder.swift b/Sources/Exporters/DatadogExporter/Upload/RequestBuilder.swift index b88c8536..20d9cadb 100644 --- a/Sources/Exporters/DatadogExporter/Upload/RequestBuilder.swift +++ b/Sources/Exporters/DatadogExporter/Upload/RequestBuilder.swift @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import DataCompression import Foundation /// Builds `URLRequest` for sending data to Datadog. diff --git a/Sources/Exporters/OpenTelemetryProtocolCommon/common/OtlpConfiguration.swift b/Sources/Exporters/OpenTelemetryProtocolCommon/common/OtlpConfiguration.swift index 32863848..6a0f715a 100644 --- a/Sources/Exporters/OpenTelemetryProtocolCommon/common/OtlpConfiguration.swift +++ b/Sources/Exporters/OpenTelemetryProtocolCommon/common/OtlpConfiguration.swift @@ -5,6 +5,12 @@ import Foundation +public enum CompressionType { + case gzip + case deflate + case none +} + public struct OtlpConfiguration { public static let DefaultTimeoutInterval : TimeInterval = TimeInterval(10) @@ -23,9 +29,15 @@ public struct OtlpConfiguration { // let compression public let headers : [(String,String)]? public let timeout : TimeInterval + public let compression: CompressionType - public init(timeout : TimeInterval = OtlpConfiguration.DefaultTimeoutInterval, headers: [(String,String)]? = nil) { + public init( + timeout : TimeInterval = OtlpConfiguration.DefaultTimeoutInterval, + compression: CompressionType = .gzip, + headers: [(String,String)]? = nil + ) { self.headers = headers self.timeout = timeout + self.compression = compression } } diff --git a/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift b/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift index d1471964..df8144c0 100644 --- a/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift +++ b/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // +import DataCompression import Foundation import SwiftProtobuf import OpenTelemetryProtocolExporterCommon @@ -28,20 +29,41 @@ public class OtlpHttpExporterBase { } } - public func createRequest(body: Message, endpoint: URL) -> URLRequest { - var request = URLRequest(url: endpoint) - - do { - request.httpMethod = "POST" - request.httpBody = try body.serializedData() - request.setValue(Headers.getUserAgentHeader(), forHTTPHeaderField: Constants.HTTP.userAgent) - request.setValue("application/x-protobuf", forHTTPHeaderField: "Content-Type") - } catch { - print("Error serializing body: \(error)") + public func createRequest(body: Message, endpoint: URL) -> URLRequest { + var request = URLRequest(url: endpoint) + + do { + let rawData = try body.serializedData() + request.httpMethod = "POST" + request.setValue(Headers.getUserAgentHeader(), forHTTPHeaderField: Constants.HTTP.userAgent) + request.setValue("application/x-protobuf", forHTTPHeaderField: "Content-Type") + + var compressedData = rawData + switch config.compression { + case .gzip: + if let data = rawData.gzip() { + compressedData = data + request.setValue("gzip", forHTTPHeaderField: "Content-Encoding") + } + + case .deflate: + if let data = rawData.deflate() { + compressedData = data + request.setValue("deflate", forHTTPHeaderField: "Content-Encoding") + } + + case .none: + break + } + // Apply final data. Could be compressed or raw + // but it doesn't matter here + request.httpBody = compressedData + } catch { + print("Error serializing body: \(error)") + } + + return request } - - return request - } public func shutdown(explicitTimeout: TimeInterval? = nil) { diff --git a/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpExporterBaseTests.swift b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpExporterBaseTests.swift new file mode 100644 index 00000000..cb8b40e2 --- /dev/null +++ b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpExporterBaseTests.swift @@ -0,0 +1,130 @@ +// +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif +import Logging +import NIO +import NIOHTTP1 +import NIOTestUtils +import OpenTelemetryApi +import OpenTelemetryProtocolExporterCommon +import DataCompression +@testable import OpenTelemetryProtocolExporterHttp +@testable import OpenTelemetrySdk +import XCTest + +class OtlpHttpExporterBaseTests: XCTestCase { + + var exporter: OtlpHttpExporterBase! + var spans: [SpanData] = [] + + override func setUp() { + super.setUp() + + spans = [] + let endpointName1 = "/api/foo" + String(Int.random(in: 1...100)) + let endpointName2 = "/api/bar" + String(Int.random(in: 100...500)) + spans.append(generateFakeSpan(endpointName: endpointName1)) + spans.append(generateFakeSpan(endpointName: endpointName2)) + } + + // Test for .gzip compression + func testCreateRequestWithGzipCompression() { + let config = OtlpConfiguration(compression: .gzip) + + exporter = OtlpHttpExporterBase( + endpoint: URL(string: "http://example.com")!, + config: config + ) + + let body = Opentelemetry_Proto_Collector_Trace_V1_ExportTraceServiceRequest.with { + $0.resourceSpans = SpanAdapter.toProtoResourceSpans(spanDataList: spans) + } + + let request = exporter.createRequest(body: body, endpoint: URL(string: "http://example.com")!) + + /// gzip + let data = try! body.serializedData().gzip() + + // Verify Content-Encoding header is set to "gzip" + XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Encoding"), "gzip") + XCTAssertNotNil(request.httpBody) + XCTAssertEqual(request.httpBody!.count, data!.count) + } + + // Test for .deflate compression + func testCreateRequestWithDeflateCompression() { + let config = OtlpConfiguration(compression: .deflate) + + exporter = OtlpHttpExporterBase( + endpoint: URL(string: "http://example.com")!, + config: config + ) + + let body = Opentelemetry_Proto_Collector_Trace_V1_ExportTraceServiceRequest.with { + $0.resourceSpans = SpanAdapter.toProtoResourceSpans(spanDataList: spans) + } + + let request = exporter.createRequest(body: body, endpoint: URL(string: "http://example.com")!) + + /// deflate + let data = try! body.serializedData().deflate() + + // Verify Content-Encoding header is set to "deflate" + XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Encoding"), "deflate") + XCTAssertNotNil(request.httpBody) + XCTAssertEqual(request.httpBody!.count, data!.count) + } + + // Test for .none compression (no compression) + func testCreateRequestWithNoCompression() { + let config = OtlpConfiguration(compression: .none) + + exporter = OtlpHttpExporterBase( + endpoint: URL(string: "http://example.com")!, + config: config + ) + + let body = Opentelemetry_Proto_Collector_Trace_V1_ExportTraceServiceRequest.with { + $0.resourceSpans = SpanAdapter.toProtoResourceSpans(spanDataList: spans) + } + + let request = exporter.createRequest(body: body, endpoint: URL(string: "http://example.com")!) + + let data = try! body.serializedData() + + // Verify Content-Encoding header is set to "deflate" + XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Encoding"), nil) + XCTAssertNotNil(request.httpBody) + XCTAssertEqual(request.httpBody!.count, data.count) + } + + private func generateFakeSpan(endpointName: String = "/api/endpoint") -> SpanData { + let duration = 0.9 + let start = Date() + let end = start.addingTimeInterval(duration) + let testattributes: [String: AttributeValue] = ["foo": AttributeValue("bar")!, "fizz": AttributeValue("buzz")!] + + var testData = SpanData(traceId: TraceId.random(), + spanId: SpanId.random(), + name: "GET " + endpointName, + kind: SpanKind.server, + startTime: start, + endTime: end, + totalAttributeCount: 2) + testData.settingAttributes(testattributes) + testData.settingTotalAttributeCount(2) + testData.settingHasEnded(true) + testData.settingTotalRecordedEvents(0) + testData.settingLinks([SpanData.Link]()) + testData.settingTotalRecordedLinks(0) + testData.settingStatus(.ok) + + return testData + } +} From 09e14b1639a518286b1f58f518154d6c5f2f5faf Mon Sep 17 00:00:00 2001 From: mamunto Date: Wed, 6 Nov 2024 11:06:53 -0500 Subject: [PATCH 2/6] added http metric compression --- .../StableOtlpHTTPExporterBase.swift | 25 +++- .../StableOtlpHTTPExporterBaseTests.swift | 130 ++++++++++++++++++ 2 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPExporterBaseTests.swift diff --git a/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift b/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift index 2b7db4f9..dad17503 100644 --- a/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift +++ b/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // +import DataCompression import Foundation import OpenTelemetryProtocolExporterCommon import SwiftProtobuf @@ -44,14 +45,34 @@ public class StableOtlpHTTPExporterBase { } do { + let rawData = try body.serializedData() request.httpMethod = "POST" - request.httpBody = try body.serializedData() request.setValue(Headers.getUserAgentHeader(), forHTTPHeaderField: Constants.HTTP.userAgent) request.setValue("application/x-protobuf", forHTTPHeaderField: "Content-Type") + + var compressedData = rawData + switch config.compression { + case .gzip: + if let data = rawData.gzip() { + compressedData = data + request.setValue("gzip", forHTTPHeaderField: "Content-Encoding") + } + + case .deflate: + if let data = rawData.deflate() { + compressedData = data + request.setValue("deflate", forHTTPHeaderField: "Content-Encoding") + } + + case .none: + break + } + // Apply final data. Could be compressed or raw + // but it doesn't matter here + request.httpBody = compressedData } catch { print("Error serializing body: \(error)") } - return request } diff --git a/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPExporterBaseTests.swift b/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPExporterBaseTests.swift new file mode 100644 index 00000000..98ee71e1 --- /dev/null +++ b/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPExporterBaseTests.swift @@ -0,0 +1,130 @@ +// +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif +import Logging +import NIO +import NIOHTTP1 +import NIOTestUtils +import OpenTelemetryApi +import OpenTelemetryProtocolExporterCommon +import DataCompression +@testable import OpenTelemetryProtocolExporterHttp +@testable import OpenTelemetrySdk +import XCTest + +class StableOtlpHTTPExporterBaseTests: XCTestCase { + + var exporter: StableOtlpHTTPExporterBase! + var spans: [SpanData] = [] + + override func setUp() { + super.setUp() + + spans = [] + let endpointName1 = "/api/foo" + String(Int.random(in: 1...100)) + let endpointName2 = "/api/bar" + String(Int.random(in: 100...500)) + spans.append(generateFakeSpan(endpointName: endpointName1)) + spans.append(generateFakeSpan(endpointName: endpointName2)) + } + + // Test for .gzip compression + func testCreateRequestWithGzipCompression() { + let config = OtlpConfiguration(compression: .gzip) + + exporter = StableOtlpHTTPExporterBase( + endpoint: URL(string: "http://example.com")!, + config: config + ) + + let body = Opentelemetry_Proto_Collector_Trace_V1_ExportTraceServiceRequest.with { + $0.resourceSpans = SpanAdapter.toProtoResourceSpans(spanDataList: spans) + } + + let request = exporter.createRequest(body: body, endpoint: URL(string: "http://example.com")!) + + /// gzip + let data = try! body.serializedData().gzip() + + // Verify Content-Encoding header is set to "gzip" + XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Encoding"), "gzip") + XCTAssertNotNil(request.httpBody) + XCTAssertEqual(request.httpBody!.count, data!.count) + } + + // Test for .deflate compression + func testCreateRequestWithDeflateCompression() { + let config = OtlpConfiguration(compression: .deflate) + + exporter = StableOtlpHTTPExporterBase( + endpoint: URL(string: "http://example.com")!, + config: config + ) + + let body = Opentelemetry_Proto_Collector_Trace_V1_ExportTraceServiceRequest.with { + $0.resourceSpans = SpanAdapter.toProtoResourceSpans(spanDataList: spans) + } + + let request = exporter.createRequest(body: body, endpoint: URL(string: "http://example.com")!) + + /// deflate + let data = try! body.serializedData().deflate() + + // Verify Content-Encoding header is set to "deflate" + XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Encoding"), "deflate") + XCTAssertNotNil(request.httpBody) + XCTAssertEqual(request.httpBody!.count, data!.count) + } + + // Test for .none compression (no compression) + func testCreateRequestWithNoCompression() { + let config = OtlpConfiguration(compression: .none) + + exporter = StableOtlpHTTPExporterBase( + endpoint: URL(string: "http://example.com")!, + config: config + ) + + let body = Opentelemetry_Proto_Collector_Trace_V1_ExportTraceServiceRequest.with { + $0.resourceSpans = SpanAdapter.toProtoResourceSpans(spanDataList: spans) + } + + let request = exporter.createRequest(body: body, endpoint: URL(string: "http://example.com")!) + + let data = try! body.serializedData() + + // Verify Content-Encoding header is set to "deflate" + XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Encoding"), nil) + XCTAssertNotNil(request.httpBody) + XCTAssertEqual(request.httpBody!.count, data.count) + } + + private func generateFakeSpan(endpointName: String = "/api/endpoint") -> SpanData { + let duration = 0.9 + let start = Date() + let end = start.addingTimeInterval(duration) + let testattributes: [String: AttributeValue] = ["foo": AttributeValue("bar")!, "fizz": AttributeValue("buzz")!] + + var testData = SpanData(traceId: TraceId.random(), + spanId: SpanId.random(), + name: "GET " + endpointName, + kind: SpanKind.server, + startTime: start, + endTime: end, + totalAttributeCount: 2) + testData.settingAttributes(testattributes) + testData.settingTotalAttributeCount(2) + testData.settingHasEnded(true) + testData.settingTotalRecordedEvents(0) + testData.settingLinks([SpanData.Link]()) + testData.settingTotalRecordedLinks(0) + testData.settingStatus(.ok) + + return testData + } +} From 950b431de98f25c20885b62e8796270a55bbff65 Mon Sep 17 00:00:00 2001 From: mamunto Date: Wed, 6 Nov 2024 13:16:05 -0500 Subject: [PATCH 3/6] iOS Test fix --- .../OtlpHttpLogRecordExporterTests.swift | 2 +- .../OpenTelemetryProtocol/OtlpHttpMetricsExporterTest.swift | 4 ++-- .../OpenTelemetryProtocol/OtlpHttpTraceExporterTests.swift | 2 +- .../StableOtlpHTTPMetricsExporterTest.swift | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpLogRecordExporterTests.swift b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpLogRecordExporterTests.swift index a46cb3b1..be36cf66 100644 --- a/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpLogRecordExporterTests.swift +++ b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpLogRecordExporterTests.swift @@ -45,7 +45,7 @@ class OtlpHttpLogRecordExporterTests: XCTestCase { attributes: ["event.name":AttributeValue.string("name"), "event.domain": AttributeValue.string("domain")]) let endpoint = URL(string: "http://localhost:\(testServer.serverPort)")! - let exporter = OtlpHttpLogExporter(endpoint: endpoint) + let exporter = OtlpHttpLogExporter(endpoint: endpoint, config: .init(compression: .none)) let _ = exporter.export(logRecords: [logRecord]) // TODO: Use protobuf to verify that we have received the correct Log records diff --git a/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpMetricsExporterTest.swift b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpMetricsExporterTest.swift index 852fbd82..b9ca07c1 100644 --- a/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpMetricsExporterTest.swift +++ b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpMetricsExporterTest.swift @@ -50,7 +50,7 @@ class OtlpHttpMetricsExporterTest: XCTestCase { } let endpoint = URL(string: "http://localhost:\(testServer.serverPort)")! - let exporter = OtlpHttpMetricExporter(endpoint: endpoint) + let exporter = OtlpHttpMetricExporter(endpoint: endpoint, config: .init(compression: .none)) let result = exporter.export(metrics: metrics) { () -> Bool in false } @@ -83,7 +83,7 @@ class OtlpHttpMetricsExporterTest: XCTestCase { } let endpoint = URL(string: "http://localhost:\(testServer.serverPort)")! - let exporter = OtlpHttpMetricExporter(endpoint: endpoint) + let exporter = OtlpHttpMetricExporter(endpoint: endpoint, config: .init(compression: .none)) let result = exporter.export(metrics: metrics) { () -> Bool in false diff --git a/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpTraceExporterTests.swift b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpTraceExporterTests.swift index b88403b4..982593da 100644 --- a/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpTraceExporterTests.swift +++ b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpTraceExporterTests.swift @@ -37,7 +37,7 @@ class OtlpHttpTraceExporterTests: XCTestCase { // It should ideally turn that body into [SpanData] using protobuf and then confirm content func testExport() { let endpoint = URL(string: "http://localhost:\(testServer.serverPort)/v1/traces")! - let exporter = OtlpHttpTraceExporter(endpoint: endpoint) + let exporter = OtlpHttpTraceExporter(endpoint: endpoint, config: .init(compression: .none)) var spans: [SpanData] = [] let endpointName1 = "/api/foo" + String(Int.random(in: 1...100)) diff --git a/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPMetricsExporterTest.swift b/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPMetricsExporterTest.swift index 9566a89c..201cd12c 100644 --- a/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPMetricsExporterTest.swift +++ b/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPMetricsExporterTest.swift @@ -63,7 +63,7 @@ class StableOtlpHttpMetricsExporterTest: XCTestCase { } let endpoint = URL(string: "http://localhost:\(testServer.serverPort)")! - let exporter = StableOtlpHTTPMetricExporter(endpoint: endpoint) + let exporter = StableOtlpHTTPMetricExporter(endpoint: endpoint, config: .init(compression: .none)) let result = exporter.export(metrics: metrics) XCTAssertEqual(result, ExportResult.success) @@ -95,7 +95,7 @@ class StableOtlpHttpMetricsExporterTest: XCTestCase { } let endpoint = URL(string: "http://localhost:\(testServer.serverPort)")! - let exporter = StableOtlpHTTPMetricExporter(endpoint: endpoint) + let exporter = StableOtlpHTTPMetricExporter(endpoint: endpoint, config: .init(compression: .none)) let result = exporter.export(metrics: metrics) XCTAssertEqual(result, ExportResult.success) From d367c21d17a273a7298735be50cbfedf0c347a3b Mon Sep 17 00:00:00 2001 From: mamunto Date: Thu, 7 Nov 2024 12:52:16 -0500 Subject: [PATCH 4/6] Added condition to avoid compression for Linux --- Sources/Exporters/DataCompression/DataCompression.swift | 3 +++ .../OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift | 4 ++++ .../StableOtlpHTTPExporterBase.swift | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/Sources/Exporters/DataCompression/DataCompression.swift b/Sources/Exporters/DataCompression/DataCompression.swift index 8cda5df3..e91146f5 100644 --- a/Sources/Exporters/DataCompression/DataCompression.swift +++ b/Sources/Exporters/DataCompression/DataCompression.swift @@ -28,6 +28,8 @@ import Foundation +// Excluding linux +#if canImport(Compression) import Compression public extension Data @@ -514,3 +516,4 @@ fileprivate func perform(_ config: Config, source: UnsafePointer, sourceS } } } +#endif diff --git a/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift b/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift index df8144c0..42ed5c65 100644 --- a/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift +++ b/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift @@ -39,6 +39,9 @@ public class OtlpHttpExporterBase { request.setValue("application/x-protobuf", forHTTPHeaderField: "Content-Type") var compressedData = rawData + + // Skip for linux +#if canImport(Compression) switch config.compression { case .gzip: if let data = rawData.gzip() { @@ -55,6 +58,7 @@ public class OtlpHttpExporterBase { case .none: break } +#endif // Apply final data. Could be compressed or raw // but it doesn't matter here request.httpBody = compressedData diff --git a/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift b/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift index dad17503..83324c07 100644 --- a/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift +++ b/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift @@ -51,6 +51,9 @@ public class StableOtlpHTTPExporterBase { request.setValue("application/x-protobuf", forHTTPHeaderField: "Content-Type") var compressedData = rawData + + // Skip for linux +#if canImport(Compression) switch config.compression { case .gzip: if let data = rawData.gzip() { @@ -67,6 +70,8 @@ public class StableOtlpHTTPExporterBase { case .none: break } +#endif + // Apply final data. Could be compressed or raw // but it doesn't matter here request.httpBody = compressedData From 7f8d941ccd67fd04bdbefbcd755e539558960156 Mon Sep 17 00:00:00 2001 From: mamunto Date: Thu, 7 Nov 2024 13:29:20 -0500 Subject: [PATCH 5/6] trying with explicit traget --- Sources/Exporters/DataCompression/DataCompression.swift | 2 +- .../OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift | 3 ++- .../OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Sources/Exporters/DataCompression/DataCompression.swift b/Sources/Exporters/DataCompression/DataCompression.swift index e91146f5..5289ec96 100644 --- a/Sources/Exporters/DataCompression/DataCompression.swift +++ b/Sources/Exporters/DataCompression/DataCompression.swift @@ -28,7 +28,7 @@ import Foundation -// Excluding linux + #if canImport(Compression) import Compression diff --git a/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift b/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift index 42ed5c65..99c7fa1b 100644 --- a/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift +++ b/Sources/Exporters/OpenTelemetryProtocolHttp/OtlpHttpExporterBase.swift @@ -3,7 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 // +#if canImport(Compression) import DataCompression +#endif import Foundation import SwiftProtobuf import OpenTelemetryProtocolExporterCommon @@ -40,7 +42,6 @@ public class OtlpHttpExporterBase { var compressedData = rawData - // Skip for linux #if canImport(Compression) switch config.compression { case .gzip: diff --git a/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift b/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift index 83324c07..fe19eb9c 100644 --- a/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift +++ b/Sources/Exporters/OpenTelemetryProtocolHttp/StableOtlpHTTPExporterBase.swift @@ -3,7 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 // +#if canImport(Compression) import DataCompression +#endif import Foundation import OpenTelemetryProtocolExporterCommon import SwiftProtobuf @@ -52,7 +54,6 @@ public class StableOtlpHTTPExporterBase { var compressedData = rawData - // Skip for linux #if canImport(Compression) switch config.compression { case .gzip: From 30f0b153e03eb6cc95257c6c389b06c42d600b50 Mon Sep 17 00:00:00 2001 From: mamunto Date: Fri, 8 Nov 2024 09:32:05 -0500 Subject: [PATCH 6/6] adding more conditional import --- .../Exporters/DatadogExporter/Upload/RequestBuilder.swift | 2 ++ .../OpenTelemetryProtocol/OtlpHttpExporterBaseTests.swift | 4 +++- .../StableOtlpHTTPExporterBaseTests.swift | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Sources/Exporters/DatadogExporter/Upload/RequestBuilder.swift b/Sources/Exporters/DatadogExporter/Upload/RequestBuilder.swift index 20d9cadb..8b686746 100644 --- a/Sources/Exporters/DatadogExporter/Upload/RequestBuilder.swift +++ b/Sources/Exporters/DatadogExporter/Upload/RequestBuilder.swift @@ -3,7 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +#if canImport(Compression) import DataCompression +#endif import Foundation /// Builds `URLRequest` for sending data to Datadog. diff --git a/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpExporterBaseTests.swift b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpExporterBaseTests.swift index cb8b40e2..53f84cdb 100644 --- a/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpExporterBaseTests.swift +++ b/Tests/ExportersTests/OpenTelemetryProtocol/OtlpHttpExporterBaseTests.swift @@ -3,6 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#if canImport(Compression) +import DataCompression import Foundation #if canImport(FoundationNetworking) import FoundationNetworking @@ -13,7 +15,6 @@ import NIOHTTP1 import NIOTestUtils import OpenTelemetryApi import OpenTelemetryProtocolExporterCommon -import DataCompression @testable import OpenTelemetryProtocolExporterHttp @testable import OpenTelemetrySdk import XCTest @@ -128,3 +129,4 @@ class OtlpHttpExporterBaseTests: XCTestCase { return testData } } +#endif diff --git a/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPExporterBaseTests.swift b/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPExporterBaseTests.swift index 98ee71e1..2d687929 100644 --- a/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPExporterBaseTests.swift +++ b/Tests/ExportersTests/OpenTelemetryProtocol/StableOtlpHTTPExporterBaseTests.swift @@ -3,6 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 // +#if canImport(Compression) +import DataCompression + import Foundation #if canImport(FoundationNetworking) import FoundationNetworking @@ -13,7 +16,6 @@ import NIOHTTP1 import NIOTestUtils import OpenTelemetryApi import OpenTelemetryProtocolExporterCommon -import DataCompression @testable import OpenTelemetryProtocolExporterHttp @testable import OpenTelemetrySdk import XCTest @@ -128,3 +130,4 @@ class StableOtlpHTTPExporterBaseTests: XCTestCase { return testData } } +#endif