From 7d065f270f124ace5fb3ebec765ea8380a8a3234 Mon Sep 17 00:00:00 2001 From: Matthew Wear Date: Tue, 24 Dec 2019 18:34:39 -0800 Subject: [PATCH] Introduce Builder to facilitate multiple modifications to CorrelationContext This commit adds a builder to faciliate making multiple modifications to CorrelationContext without creating multiple, intermediary contexts. Manager#build_context should be used when making multiple modifications to the correlation context. When making a single modification, all other methods should be used. Some may object to the builder and might recommend a single method with kwargs, but I think this makes for a more fluent API. --- api/lib/opentelemetry/correlation_context.rb | 1 + .../correlation_context/builder.rb | 18 ++++ .../correlation_context/manager.rb | 8 ++ .../opentelemetry/sdk/correlation_context.rb | 1 + .../sdk/correlation_context/builder.rb | 40 +++++++++ .../sdk/correlation_context/manager.rb | 15 ++++ .../sdk/correlation_context/manager_test.rb | 85 +++++++++++++++++++ 7 files changed, 168 insertions(+) create mode 100644 api/lib/opentelemetry/correlation_context/builder.rb create mode 100644 sdk/lib/opentelemetry/sdk/correlation_context/builder.rb diff --git a/api/lib/opentelemetry/correlation_context.rb b/api/lib/opentelemetry/correlation_context.rb index d948703088..26fefab5ea 100644 --- a/api/lib/opentelemetry/correlation_context.rb +++ b/api/lib/opentelemetry/correlation_context.rb @@ -4,6 +4,7 @@ # # SPDX-License-Identifier: Apache-2.0 +require 'opentelemetry/correlation_context/builder' require 'opentelemetry/correlation_context/manager' require 'opentelemetry/correlation_context/propagation' diff --git a/api/lib/opentelemetry/correlation_context/builder.rb b/api/lib/opentelemetry/correlation_context/builder.rb new file mode 100644 index 0000000000..42a85495c3 --- /dev/null +++ b/api/lib/opentelemetry/correlation_context/builder.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# Copyright 2019 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module CorrelationContext + # No op implementation of CorrelationContext::Builder + class Builder + def set_value(key, value); end + + def remove_value(key); end + + def clear; end + end + end +end diff --git a/api/lib/opentelemetry/correlation_context/manager.rb b/api/lib/opentelemetry/correlation_context/manager.rb index eb82ebcbbd..dfb6dde2fb 100644 --- a/api/lib/opentelemetry/correlation_context/manager.rb +++ b/api/lib/opentelemetry/correlation_context/manager.rb @@ -8,6 +8,14 @@ module OpenTelemetry module CorrelationContext # No op implementation of CorrelationContext::Manager class Manager + NOOP_BUILDER = Builder.new + private_constant :NOOP_BUILDER + + def build(context: Context.current) + yield NOOP_BUILDER + context + end + def set_value(key, value, context: Context.current) context end diff --git a/sdk/lib/opentelemetry/sdk/correlation_context.rb b/sdk/lib/opentelemetry/sdk/correlation_context.rb index 5e5dd26088..2b0976846e 100644 --- a/sdk/lib/opentelemetry/sdk/correlation_context.rb +++ b/sdk/lib/opentelemetry/sdk/correlation_context.rb @@ -4,6 +4,7 @@ # # SPDX-License-Identifier: Apache-2.0 +require 'opentelemetry/sdk/correlation_context/builder' require 'opentelemetry/sdk/correlation_context/manager' module OpenTelemetry diff --git a/sdk/lib/opentelemetry/sdk/correlation_context/builder.rb b/sdk/lib/opentelemetry/sdk/correlation_context/builder.rb new file mode 100644 index 0000000000..de75def985 --- /dev/null +++ b/sdk/lib/opentelemetry/sdk/correlation_context/builder.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# Copyright 2019 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module CorrelationContext + # No op implementation of CorrelationContext::Builder + class Builder + attr_reader :entries + + def initialize(entries) + @entries = entries + end + + # Set key-value in the to-be-created correlation context + # + # @param [String] key The key to store this value under + # @param [String] value String value to be stored under key + def set_value(key, value) + @entries[key] = value.to_s + end + + # Removes key from the to-be-created correlation context + # + # @param [String] key The key to remove + def remove_value(key) + @entries.delete(key) + end + + # Clears all correlations from the parent context + def clear + @entries.clear + end + end + end + end +end diff --git a/sdk/lib/opentelemetry/sdk/correlation_context/manager.rb b/sdk/lib/opentelemetry/sdk/correlation_context/manager.rb index c7d1e58909..50cb0ed86b 100644 --- a/sdk/lib/opentelemetry/sdk/correlation_context/manager.rb +++ b/sdk/lib/opentelemetry/sdk/correlation_context/manager.rb @@ -13,6 +13,21 @@ class Manager EMPTY_CORRELATION_CONTEXT = {}.freeze private_constant(:CORRELATION_CONTEXT_KEY, :EMPTY_CORRELATION_CONTEXT) + # Used to chain modifications to correlation context. The result is a + # context with an updated correlation context. If only a single + # modification is being made to correlation context, use the other + # methods on +Manager+, if multiple modifications are being made, use + # this one. + # + # @param [optional Context] context The context to update with with new + # modified correlation context. Defaults to +Context.current+ + # @return [Context] + def build_context(context: Context.current) + builder = Builder.new(correlations_for(context).dup) + yield builder + context.set_value(CORRELATION_CONTEXT_KEY, builder.entries) + end + # Returns a new context with empty correlations # # @param [optional Context] context Context to clear correlations from. Defaults diff --git a/sdk/test/opentelemetry/sdk/correlation_context/manager_test.rb b/sdk/test/opentelemetry/sdk/correlation_context/manager_test.rb index 45e30d2c27..9916f7561c 100644 --- a/sdk/test/opentelemetry/sdk/correlation_context/manager_test.rb +++ b/sdk/test/opentelemetry/sdk/correlation_context/manager_test.rb @@ -112,4 +112,89 @@ end end end + + describe '.build_context' do + let(:initial_context) { manager.set_value('k1', 'v1') } + + describe 'explicit context' do + it 'sets entries' do + ctx = initial_context + ctx = manager.build_context(context: ctx) do |correlations| + correlations.set_value('k2', 'v2') + correlations.set_value('k3', 'v3') + end + _(manager.value('k1', context: ctx)).must_equal('v1') + _(manager.value('k2', context: ctx)).must_equal('v2') + _(manager.value('k3', context: ctx)).must_equal('v3') + end + + it 'removes entries' do + ctx = initial_context + ctx = manager.build_context(context: ctx) do |correlations| + correlations.remove_value('k1') + correlations.set_value('k2', 'v2') + end + _(manager.value('k1', context: ctx)).must_be_nil + _(manager.value('k2', context: ctx)).must_equal('v2') + end + + it 'clears entries' do + ctx = initial_context + ctx = manager.build_context(context: ctx) do |correlations| + correlations.clear + correlations.set_value('k2', 'v2') + end + _(manager.value('k1', context: ctx)).must_be_nil + _(manager.value('k2', context: ctx)).must_equal('v2') + end + end + + describe 'implicit context' do + it 'sets entries' do + Context.with_current(initial_context) do + ctx = manager.build_context do |correlations| + correlations.set_value('k2', 'v2') + correlations.set_value('k3', 'v3') + end + Context.with_current(ctx) do + _(manager.value('k1')).must_equal('v1') + _(manager.value('k2')).must_equal('v2') + _(manager.value('k3')).must_equal('v3') + end + end + end + + it 'removes entries' do + Context.with_current(initial_context) do + _(manager.value('k1')).must_equal('v1') + + ctx = manager.build_context do |correlations| + correlations.remove_value('k1') + correlations.set_value('k2', 'v2') + end + + Context.with_current(ctx) do + _(manager.value('k1')).must_be_nil + _(manager.value('k2')).must_equal('v2') + end + end + end + + it 'clears entries' do + Context.with_current(initial_context) do + _(manager.value('k1')).must_equal('v1') + + ctx = manager.build_context do |correlations| + correlations.clear + correlations.set_value('k2', 'v2') + end + + Context.with_current(ctx) do + _(manager.value('k1')).must_be_nil + _(manager.value('k2')).must_equal('v2') + end + end + end + end + end end