Skip to content

Commit

Permalink
Implement Exponential Histgoram metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
jhoongo committed Mar 15, 2024
1 parent 812e3fc commit 6e5e46e
Show file tree
Hide file tree
Showing 22 changed files with 1,294 additions and 237 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,57 @@ public enum MetricsAdapter {
protoMetric.histogram.aggregationTemporality = stableMetric.data.aggregationTemporality.convertToProtoEnum()
protoMetric.histogram.dataPoints.append(protoDataPoint)
case .ExponentialHistogram:
// TODO: implement
break
guard let exponentialHistogramData = $0 as? ExponentialHistogramPointData else {
break

Check warning on line 156 in Sources/Exporters/OpenTelemetryProtocolCommon/metric/MetricsAdapter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Exporters/OpenTelemetryProtocolCommon/metric/MetricsAdapter.swift#L156

Added line #L156 was not covered by tests
}
var protoDataPoint = Opentelemetry_Proto_Metrics_V1_ExponentialHistogramDataPoint()
injectPointData(protoExponentialHistogramPoint: &protoDataPoint, pointData: exponentialHistogramData)
protoDataPoint.scale = Int32(exponentialHistogramData.scale)
protoDataPoint.sum = Double(exponentialHistogramData.sum)
protoDataPoint.count = UInt64(exponentialHistogramData.count)
protoDataPoint.zeroCount = UInt64(exponentialHistogramData.zeroCount)
protoDataPoint.max = exponentialHistogramData.max
protoDataPoint.min = exponentialHistogramData.min

var positiveBuckets = Opentelemetry_Proto_Metrics_V1_ExponentialHistogramDataPoint.Buckets()
positiveBuckets.offset = Int32(exponentialHistogramData.positiveBuckets.offset)
positiveBuckets.bucketCounts = exponentialHistogramData.positiveBuckets.bucketCounts.map { UInt64($0) }

var negativeBuckets = Opentelemetry_Proto_Metrics_V1_ExponentialHistogramDataPoint.Buckets()
negativeBuckets.offset = Int32(exponentialHistogramData.negativeBuckets.offset)
negativeBuckets.bucketCounts = exponentialHistogramData.negativeBuckets.bucketCounts.map { UInt64($0) }

protoDataPoint.positive = positiveBuckets
protoDataPoint.negative = negativeBuckets

protoMetric.exponentialHistogram.aggregationTemporality = stableMetric.data.aggregationTemporality.convertToProtoEnum()
protoMetric.exponentialHistogram.dataPoints.append(protoDataPoint)
}
}
return protoMetric
}

static func injectPointData(protoExponentialHistogramPoint protoPoint: inout Opentelemetry_Proto_Metrics_V1_ExponentialHistogramDataPoint, pointData: PointData) {
protoPoint.timeUnixNano = pointData.endEpochNanos
protoPoint.startTimeUnixNano = pointData.startEpochNanos

pointData.attributes.forEach {
protoPoint.attributes.append(CommonAdapter.toProtoAttribute(key: $0.key, attributeValue: $0.value))
}

Check warning on line 191 in Sources/Exporters/OpenTelemetryProtocolCommon/metric/MetricsAdapter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Exporters/OpenTelemetryProtocolCommon/metric/MetricsAdapter.swift#L190-L191

Added lines #L190 - L191 were not covered by tests

pointData.exemplars.forEach {
var protoExemplar = Opentelemetry_Proto_Metrics_V1_Exemplar()
protoExemplar.timeUnixNano = $0.epochNanos

$0.filteredAttributes.forEach {
protoExemplar.filteredAttributes.append(CommonAdapter.toProtoAttribute(key: $0.key, attributeValue: $0.value))
}
if let spanContext = $0.spanContext {
protoExemplar.spanID = TraceProtoUtils.toProtoSpanId(spanId: spanContext.spanId)
protoExemplar.traceID = TraceProtoUtils.toProtoTraceId(traceId: spanContext.traceId)
}
}

Check warning on line 204 in Sources/Exporters/OpenTelemetryProtocolCommon/metric/MetricsAdapter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Exporters/OpenTelemetryProtocolCommon/metric/MetricsAdapter.swift#L194-L204

Added lines #L194 - L204 were not covered by tests
}

static func injectPointData(protoHistogramPoint protoPoint: inout Opentelemetry_Proto_Metrics_V1_HistogramDataPoint, pointData: PointData) {
protoPoint.timeUnixNano = pointData.endEpochNanos
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

class AdaptingCircularBufferCounter: NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
let copy = AdaptingCircularBufferCounter(maxSize: maxSize)
copy.startIndex = startIndex
copy.endIndex = endIndex
copy.baseIndex = baseIndex
copy.backing = backing.copy() as! AdaptingIntegerArray
return copy
}

private static let NULL_INDEX: Int = Int.min
public private(set) var endIndex = NULL_INDEX
public private(set) var startIndex = NULL_INDEX
private var baseIndex = NULL_INDEX
private var backing: AdaptingIntegerArray
private let maxSize: Int

init(maxSize: Int) {
self.backing = AdaptingIntegerArray(size: maxSize)
self.maxSize = maxSize
}

@discardableResult func increment(index: Int, delta: Int64) -> Bool{
if self.baseIndex == Int.min {
self.startIndex = index
self.endIndex = index
self.baseIndex = index
self.backing.increment(index: 0, count: delta)
return true
}

if index > self.endIndex {
if (index - self.startIndex + 1) > self.backing.length() {
return false
}
self.endIndex = index
} else if index < self.startIndex {
if (self.endIndex - index + 1) > self.backing.length() {
return false

Check warning on line 46 in Sources/OpenTelemetrySdk/Metrics/Stable/Aggregation/AdaptingCircularBufferCounter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenTelemetrySdk/Metrics/Stable/Aggregation/AdaptingCircularBufferCounter.swift#L46

Added line #L46 was not covered by tests
}
self.startIndex = index
}

let realIndex = toBufferIndex(index: index)
self.backing.increment(index: realIndex, count: delta)
return true
}

func get(index: Int) -> Int64 {
if (index < self.startIndex || index > self.endIndex) {
return 0
} else {
return backing.get(index: toBufferIndex(index: index))
}
}

Check warning on line 62 in Sources/OpenTelemetrySdk/Metrics/Stable/Aggregation/AdaptingCircularBufferCounter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenTelemetrySdk/Metrics/Stable/Aggregation/AdaptingCircularBufferCounter.swift#L62

Added line #L62 was not covered by tests

func isEmpty() -> Bool {
return baseIndex == Self.NULL_INDEX
}

func getMaxSize() -> Int {
return backing.length()
}

func clear() {
self.backing.clear()
self.baseIndex = Self.NULL_INDEX
self.startIndex = Self.NULL_INDEX
self.endIndex = Self.NULL_INDEX
}

private func toBufferIndex(index: Int) -> Int {
var result = index - self.baseIndex
if (result >= backing.length()) {
result -= backing.length()

Check warning on line 82 in Sources/OpenTelemetrySdk/Metrics/Stable/Aggregation/AdaptingCircularBufferCounter.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenTelemetrySdk/Metrics/Stable/Aggregation/AdaptingCircularBufferCounter.swift#L82

Added line #L82 was not covered by tests
} else if (result < 0) {
result += backing.length()
}
return result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

class AdaptingIntegerArray: NSCopying {

func copy(with zone: NSZone? = nil) -> Any {
let copy = AdaptingIntegerArray(size: size)
copy.cellSize = cellSize
switch (cellSize) {
case .byte:
copy.byteBacking = byteBacking
case .short:
copy.shortBacking = shortBacking
case .int:
copy.intBacking = intBacking
case .long:
copy.longBacking = longBacking
}
return copy
}

var byteBacking: Array<Int8>?
var shortBacking: Array<Int16>?
var intBacking: Array<Int32>?
var longBacking: Array<Int64>?
var size: Int

enum ArrayCellSize {
case byte
case short
case int
case long
}

var cellSize: ArrayCellSize

init(size: Int) {
self.size = size
self.cellSize = ArrayCellSize.byte
self.byteBacking = Array<Int8>(repeating: Int8(0), count: size)
}

func increment(index: Int, count: Int64) {

if self.cellSize == .byte, var byteBacking = self.byteBacking {
let result = Int64(byteBacking[index]) + count
if result > Int8.max {
resizeToShort()
increment(index: index, count: count)
} else {
byteBacking[index] = Int8(result)
self.byteBacking = byteBacking
}
} else if self.cellSize == .short, var shortBacking = self.shortBacking {
let result = Int64(shortBacking[index]) + count
if result > Int16.max {
resizeToInt()
increment(index: index, count: count)
} else {
shortBacking[index] = Int16(result)
self.shortBacking = shortBacking
}
} else if self.cellSize == .int, var intBacking = self.intBacking {
let result = Int64(intBacking[index]) + count
if result > Int32.max {
resizeToLong()
increment(index: index, count: count)
} else {
intBacking[index] = Int32(result)
self.intBacking = intBacking
}
} else if self.cellSize == .long, var longBacking = self.longBacking {
let result = longBacking[index] + count
longBacking[index] = result
self.longBacking = longBacking
}
}

func get(index: Int) -> Int64 {

if self.cellSize == .byte, let byteBacking = self.byteBacking, index < byteBacking.count {
return Int64(byteBacking[index])
} else if self.cellSize == .short, let shortBacking = self.shortBacking, index < shortBacking.count {
return Int64(shortBacking[index])
} else if self.cellSize == .int, let intBacking = self.intBacking, index < intBacking.count {
return Int64(intBacking[index])
} else if self.cellSize == .long, let longBacking = self.longBacking, index < longBacking.count {
return longBacking[index]
}

return Int64(0)

Check warning on line 95 in Sources/OpenTelemetrySdk/Metrics/Stable/Aggregation/AdaptingIntegerArray.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenTelemetrySdk/Metrics/Stable/Aggregation/AdaptingIntegerArray.swift#L94-L95

Added lines #L94 - L95 were not covered by tests
}

func length() -> Int {
var length = 0

if self.cellSize == .byte, let byteBacking = self.byteBacking {
length = byteBacking.count
} else if self.cellSize == .short, let shortBacking = self.shortBacking {
length = shortBacking.count
} else if self.cellSize == .int, let intBacking = self.intBacking {
length = intBacking.count
} else if self.cellSize == .long, let longBacking = self.longBacking {
length = longBacking.count
}

return length
}

func clear() {
switch (self.cellSize) {
case .byte:
self.byteBacking = Array(repeating: Int8(0), count: self.byteBacking?.count ?? 0)
case .short:
self.shortBacking = Array(repeating: Int16(0), count: self.shortBacking?.count ?? 0)
case .int:
self.intBacking = Array(repeating: Int32(0), count: self.intBacking?.count ?? 0)
case .long:
self.longBacking = Array(repeating: Int64(0), count: self.longBacking?.count ?? 0)
}
}

private func resizeToShort() {
guard let byteBacking = self.byteBacking else { return }
var tmpShortBacking: Array<Int16> = Array<Int16>(repeating: Int16(0), count: byteBacking.count)

for (index, value) in byteBacking.enumerated() {
tmpShortBacking[index] = Int16(value)
}
self.cellSize = ArrayCellSize.short
self.shortBacking = tmpShortBacking
self.byteBacking = nil
}

private func resizeToInt() {
guard let shortBacking = self.shortBacking else { return }
var tmpIntBacking: Array<Int32> = Array<Int32>(repeating: Int32(0), count: shortBacking.count)

for (index, value) in shortBacking.enumerated() {
tmpIntBacking[index] = Int32(value)
}
self.cellSize = ArrayCellSize.int
self.intBacking = tmpIntBacking
self.shortBacking = nil
}

private func resizeToLong() {
guard let intBacking = self.intBacking else { return }
var tmpLongBacking: Array<Int64> = Array<Int64>(repeating: Int64(0), count: intBacking.count)

for (index, value) in intBacking.enumerated() {
tmpLongBacking[index] = Int64(value)
}
self.cellSize = ArrayCellSize.long
self.longBacking = tmpLongBacking
self.intBacking = nil
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ public enum Aggregations {
ExplicitBucketHistogramAggregation(bucketBoundaries: buckets)
}

static func base2ExponentialBucketHistogram() {
// todo
static func base2ExponentialBucketHistogram() -> Aggregation {
Base2ExponentialHistogramAggregation.instance
}

static func base2ExponentialBucketHistogram(maxBuckets: Int, maxScale: Int) {
// todo
static func base2ExponentialBucketHistogram(maxBuckets: Int, maxScale: Int) -> Aggregation {
Base2ExponentialHistogramAggregation(maxBuckets: maxBuckets, maxScale: maxScale)
}
}

This file was deleted.

Loading

0 comments on commit 6e5e46e

Please sign in to comment.