Skip to content

Commit

Permalink
Update attribute processor for stable metrics (#524)
Browse files Browse the repository at this point in the history
Previously AttribtueProcessor is a class and not open. It can't be
customized externally. Now it's changed to procotol and its static
functions are moved into SimpleAttributeProcessor (now it's public).

Also duplicated NoopAttributesProcessor is removed and more tests are
added.
  • Loading branch information
yangjian committed Mar 21, 2024
1 parent 4d1502b commit 1535a05
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 70 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,87 +6,80 @@
import Foundation
import OpenTelemetryApi

public protocol AttributeProcessorProtocol {
public protocol AttributeProcessor {
func process(incoming : [String: AttributeValue]) -> [String: AttributeValue]
}
public class AttributeProcessor : AttributeProcessorProtocol {
public func then(other : AttributeProcessor) -> AttributeProcessor {

public extension AttributeProcessor {
func then(other : AttributeProcessor) -> AttributeProcessor {
if type(of: other) == NoopAttributeProcessor.self {
return self
}
if type(of: self) == NoopAttributeProcessor.self {
return other
}

if type(of: other) == JoinedAttributeProcessor.self {
return (other as! JoinedAttributeProcessor).prepend(processor:self)
if let joined = self as? JoinedAttributeProcessor {
return joined.append(processor:other)
}
if let joined = other as? JoinedAttributeProcessor {
return joined.prepend(processor:self)
}

return JoinedAttributeProcessor([self, other])
}
}

public class SimpleAttributeProcessor : AttributeProcessor {
let attributeProcessor : ([String: AttributeValue]) -> [String:AttributeValue]

init(attributeProcessor : @escaping ([String: AttributeValue]) -> [String: AttributeValue]) {
self.attributeProcessor = attributeProcessor
}

public func process(incoming: [String : AttributeValue]) -> [String : AttributeValue] {
return incoming
return attributeProcessor(incoming)
}

public static func filterByKeyName( nameFilter : @escaping (String) -> Bool) -> AttributeProcessor {
static func filterByKeyName( nameFilter : @escaping (String) -> Bool) -> AttributeProcessor {
return SimpleAttributeProcessor { attributes in
attributes.filter { key, value in
nameFilter(key)
}
}
}

public static func append(attributes: [String : AttributeValue]) -> AttributeProcessor {
static func append(attributes: [String : AttributeValue]) -> AttributeProcessor {
SimpleAttributeProcessor { incoming in
incoming.merging(attributes) { a, b in
b
}
}
}


}

internal class SimpleAttributeProcessor : AttributeProcessor {

let attributeProcessor : ([String: AttributeValue]) -> [String:AttributeValue]

init(attributeProcessor : @escaping ([String: AttributeValue]) -> [String: AttributeValue]) {
self.attributeProcessor = attributeProcessor

}

override func process(incoming: [String : OpenTelemetryApi.AttributeValue]) -> [String : OpenTelemetryApi.AttributeValue] {
return attributeProcessor(incoming)
}


}


public class JoinedAttributeProcessor : AttributeProcessor {

override public func process(incoming: [String : OpenTelemetryApi.AttributeValue]) -> [String : OpenTelemetryApi.AttributeValue] {
public func process(incoming: [String : AttributeValue]) -> [String : AttributeValue] {
var result = incoming
for processor in processors {
result = processor.process(incoming: result)
}
return result
}

override public func then(other: AttributeProcessor) -> AttributeProcessor {
public func append(processor: AttributeProcessor) -> JoinedAttributeProcessor {
var newList = processors
if let otherJoined = other as? JoinedAttributeProcessor {
newList.append(contentsOf: otherJoined.processors)
if let joinedProcessor = processor as? JoinedAttributeProcessor {
newList.append(contentsOf: joinedProcessor.processors)
} else {
newList.append(other)
newList.append(processor)
}
return JoinedAttributeProcessor(newList)
}

public func prepend(processor: AttributeProcessor) -> AttributeProcessor {
public func prepend(processor: AttributeProcessor) -> JoinedAttributeProcessor {
var newProcessors = [processor]
newProcessors.append(contentsOf: processors)
return JoinedAttributeProcessor(newProcessors)
Expand All @@ -101,8 +94,8 @@ public class JoinedAttributeProcessor : AttributeProcessor {

public class NoopAttributeProcessor : AttributeProcessor {
static let noop = NoopAttributeProcessor()
private override init() {}
override public func process(incoming: [String : AttributeValue]) -> [String : AttributeValue] {
private init() {}
public func process(incoming: [String : AttributeValue]) -> [String : AttributeValue] {
return incoming
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,13 @@ public class ViewBuilder {
return self
}

public func withAttributeProcessor(processor: AttributeProcessor) -> Self {
self.processor = processor
return self
}

public func addAttributeFilter(keyFilter: @escaping (String) -> Bool) -> Self {
addAttributeProcessor(processor: AttributeProcessor.filterByKeyName(nameFilter: keyFilter))
addAttributeProcessor(processor: SimpleAttributeProcessor.filterByKeyName(nameFilter: keyFilter))
}

public func addAttributeProcessor(processor: AttributeProcessor) -> Self {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

import OpenTelemetryApi
@testable import OpenTelemetrySdk
import XCTest

class AttributeProcessorTests : XCTestCase {
func testStaticNoop() {
XCTAssertNotNil(NoopAttributeProcessor.noop)
XCTAssertEqual(NoopAttributeProcessor.noop.process(incoming: ["hello": AttributeValue.string("world")]), ["hello" : AttributeValue.string("world")])
}

func testSimpleProcessor() {
let incoming = ["hello": AttributeValue("world")]

var processor: AttributeProcessor = SimpleAttributeProcessor.append(attributes: ["foo" : .string("bar")])
XCTAssertEqual(processor.process(incoming: incoming), ["hello" : .string("world"), "foo": .string("bar")])

processor = processor.then(other: SimpleAttributeProcessor.filterByKeyName(nameFilter: { $0 == "foo" }))
XCTAssertEqual(processor.process(incoming: incoming), ["foo": .string("bar")])
}

func testJoinedProcessor() {
let incoming = ["hello": AttributeValue("world")]

let processor0 = SimpleAttributeProcessor.append(attributes: ["foo" : .string("bar0")])
let processor1 = SimpleAttributeProcessor.append(attributes: ["foo" : .string("bar1")])
let processor2 = SimpleAttributeProcessor.append(attributes: ["foo" : .string("bar2")])

var processor = JoinedAttributeProcessor([processor0])
XCTAssertEqual(processor.process(incoming: incoming), ["hello" : .string("world"), "foo": .string("bar0")])

processor = processor.prepend(processor: processor1)
XCTAssertEqual(processor.process(incoming: incoming), ["hello" : .string("world"), "foo": .string("bar0")])

processor = processor.append(processor: processor2)
XCTAssertEqual(processor.process(incoming: incoming), ["hello" : .string("world"), "foo": .string("bar2")])
}
}

This file was deleted.

0 comments on commit 1535a05

Please sign in to comment.