opentelemetry-api 0.16.0 → 1.0.0.rc3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -0
  3. data/README.md +3 -3
  4. data/lib/opentelemetry.rb +21 -28
  5. data/lib/opentelemetry/baggage.rb +95 -2
  6. data/lib/opentelemetry/baggage/builder.rb +30 -4
  7. data/lib/opentelemetry/baggage/entry.rb +20 -0
  8. data/lib/opentelemetry/baggage/propagation.rb +9 -18
  9. data/lib/opentelemetry/baggage/propagation/text_map_propagator.rb +111 -0
  10. data/lib/opentelemetry/context.rb +48 -33
  11. data/lib/opentelemetry/context/propagation.rb +35 -5
  12. data/lib/opentelemetry/context/propagation/composite_text_map_propagator.rb +105 -0
  13. data/lib/opentelemetry/context/propagation/noop_text_map_propagator.rb +27 -0
  14. data/lib/opentelemetry/context/propagation/{propagator.rb → text_map_propagator.rb} +23 -16
  15. data/lib/opentelemetry/internal.rb +17 -0
  16. data/lib/opentelemetry/internal/proxy_tracer.rb +38 -0
  17. data/lib/opentelemetry/internal/proxy_tracer_provider.rb +59 -0
  18. data/lib/opentelemetry/trace.rb +10 -0
  19. data/lib/opentelemetry/trace/propagation/trace_context.rb +7 -18
  20. data/lib/opentelemetry/trace/propagation/trace_context/text_map_propagator.rb +73 -0
  21. data/lib/opentelemetry/trace/status.rb +34 -5
  22. data/lib/opentelemetry/trace/tracer.rb +8 -9
  23. data/lib/opentelemetry/version.rb +1 -1
  24. metadata +16 -25
  25. data/lib/opentelemetry/baggage/manager.rb +0 -41
  26. data/lib/opentelemetry/baggage/propagation/text_map_extractor.rb +0 -57
  27. data/lib/opentelemetry/baggage/propagation/text_map_injector.rb +0 -52
  28. data/lib/opentelemetry/context/propagation/composite_propagator.rb +0 -72
  29. data/lib/opentelemetry/context/propagation/noop_extractor.rb +0 -26
  30. data/lib/opentelemetry/context/propagation/noop_injector.rb +0 -26
  31. data/lib/opentelemetry/instrumentation.rb +0 -15
  32. data/lib/opentelemetry/instrumentation/base.rb +0 -307
  33. data/lib/opentelemetry/instrumentation/registry.rb +0 -86
  34. data/lib/opentelemetry/metrics.rb +0 -16
  35. data/lib/opentelemetry/metrics/handles.rb +0 -44
  36. data/lib/opentelemetry/metrics/instruments.rb +0 -105
  37. data/lib/opentelemetry/metrics/meter.rb +0 -72
  38. data/lib/opentelemetry/metrics/meter_provider.rb +0 -22
  39. data/lib/opentelemetry/trace/propagation/trace_context/text_map_extractor.rb +0 -52
  40. data/lib/opentelemetry/trace/propagation/trace_context/text_map_injector.rb +0 -49
  41. data/lib/opentelemetry/trace/util/http_to_status.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2044ecfc3776d70aa668bcbd537f9df904f3a48a19b3b1daa67d0632686b3358
4
- data.tar.gz: 996749298853bb15d62cadff700e06a1f3bcb505f5b49786b713c1daa1796165
3
+ metadata.gz: b873eaad4492b7cefaebdf1a9e5e9998f8af3192340d0d4eae0d2e7c32964e16
4
+ data.tar.gz: a7a4c5c20d299ac33d0cae853104ee8f33a2f8e4436888e5259a706b0061d6cb
5
5
  SHA512:
6
- metadata.gz: 12c9863e4b90e34f5b4d6e2e3e5b97244d609e5381d554b39d461e77cfff5e7f8130b1389c68743d6fb7ea3080321aa4b12dc56e3ca78cac20fda1cc0100e7b7
7
- data.tar.gz: 990f49fa7ef5e2990283cf4b42af9a6ecaa4989078a7b084568806675a9a2d3ae635bb830bccc9626b51869e3f0bfc9df5406f2cdbfda6c33576e4fd92c26716
6
+ metadata.gz: a9689040364ba9f92dbd9b4af6a95c5d9e0adbffb805ed79f3d0e4a7e2061fd6ac0d5dec6ec50ef5e4fe290dfd74c3779a0331145b9ebee5fe83dc9551bdf58a
7
+ data.tar.gz: 0127d9a7168c4f275a132b63f038d0e4f58105d72475e70ab2db03f3a868dc85d2252e5c50bf5b52af713b0018c33a4d8366a872ba4d1522304e2d8f5e8d5360
data/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # Release History: opentelemetry-api
2
2
 
3
+ ### v1.0.0.rc3 / 2021-08-12
4
+
5
+ * BREAKING CHANGE: Remove optional parent_context from in_span
6
+ * BREAKING CHANGE: Refactor Baggage to remove Noop*
7
+ * BREAKING CHANGE: Total order constraint on span.status=
8
+
9
+ * ADDED: Add Tracer.non_recording_span to API
10
+ * ADDED: Make API's NoopTextMapPropagator private
11
+ * FIXED: Remove optional parent_context from in_span
12
+ * FIXED: Reduce span allocation in API
13
+ * FIXED: Refactor Baggage to remove Noop*
14
+ * FIXED: Total order constraint on span.status=
15
+ * FIXED: Return early if carrier is nil
16
+ * FIXED: Update context to match spec
17
+ * FIXED: Return the original context if the baggage header value is empty
18
+ * DOCS: Update docs to rely more on environment variable configuration
19
+
20
+ ### v1.0.0.rc2 / 2021-06-23
21
+
22
+ * BREAKING CHANGE: Remove optional parent_context from in_span [729](https://github.com/open-telemetry/opentelemetry-ruby/pull/729)
23
+ * BREAKING CHANGE: Refactor Baggage to remove Noop* [800](https://github.com/open-telemetry/opentelemetry-ruby/pull/800)
24
+ - The noop baggage manger has been removed.
25
+ - The baggage management methods are now available through OpenTelemetry::Baggage#method, previously OpenTelemetry.baggage#method
26
+ * BREAKING CHANGE: Total order constraint on span.status= [805](https://github.com/open-telemetry/opentelemetry-ruby/pull/805)
27
+ - The OpenTelemetry::Trace::Util::HttpToStatus module has been removed as it was incorrectly setting the span status to OK for codes codes in the range 100..399
28
+ - The HttpToStatus module can be replaced inline as follows `span.status = OpenTelemetry::Trace::Status.error unless (100..399).include?(response_code.to_i)`
29
+ - The `Status.new(code, description:)` initializer has been hidden in favour of simpler constructors for each status code: `Status.ok`, `Status.error` and `Status.unset`. Each constructor takes an optional description.
30
+
31
+ * ADDED: Add Tracer.non_recording_span to API [799](https://github.com/open-telemetry/opentelemetry-ruby/pull/799)
32
+ * FIXED: Reduce span allocation in API [795](https://github.com/open-telemetry/opentelemetry-ruby/pull/795)
33
+ * FIXED: Return early if carrier is nil [835](https://github.com/open-telemetry/opentelemetry-ruby/pull/835)
34
+ * FIXED: Update context to match spec [807](https://github.com/open-telemetry/opentelemetry-ruby/pull/807)
35
+ - The `Context.current` setter has been removed and the previously private attach/detach methods are now available as class methods on the context module.
36
+
37
+ ### v1.0.0.rc1 / 2021-05-21
38
+
39
+ * BREAKING CHANGE: Remove optional parent_context from in_span
40
+
41
+ * FIXED: Remove optional parent_context from in_span
42
+
43
+ ### v0.17.0 / 2021-04-22
44
+
45
+ * BREAKING CHANGE: Replace TextMapInjector/TextMapExtractor pairs with a TextMapPropagator.
46
+
47
+ [Check the propagator documentation](https://open-telemetry.github.io/opentelemetry-ruby/) for the new usage.
48
+ * BREAKING CHANGE: Remove metrics API.
49
+
50
+ `OpenTelemetry::Metrics` and all of its behavior removed until spec stabilizes.
51
+ * BREAKING CHANGE: Extract instrumentation base from api (#698).
52
+
53
+ To take advantage of a base instrumentation class to create your own auto-instrumentation, require and use the `opentelemetry-instrumentation-base` gem.
54
+
55
+ * ADDED: Default noop tracer for instrumentation
56
+ * FIXED: Refactor propagators to add #fields
57
+ * FIXED: Remove metrics API
58
+ * FIXED: Dynamically upgrade global tracer provider
59
+
3
60
  ### v0.16.0 / 2021-03-17
4
61
 
5
62
  * ADDED: Span#add_attributes
data/README.md CHANGED
@@ -29,11 +29,11 @@ Then, use the OpenTelemetry interfaces to produces traces and other telemetry da
29
29
  ```ruby
30
30
  require 'opentelemetry'
31
31
 
32
- # Obtain the current default tracer factory
33
- factory = OpenTelemetry.tracer_factory
32
+ # Obtain the current default tracer provider
33
+ provider = OpenTelemetry.tracer_provider
34
34
 
35
35
  # Create a trace
36
- tracer = factory.tracer('my_app', '1.0')
36
+ tracer = provider.tracer('my_app', '1.0')
37
37
 
38
38
  # Record spans
39
39
  tracer.in_span('my_task') do |task_span|
data/lib/opentelemetry.rb CHANGED
@@ -9,9 +9,8 @@ require 'logger'
9
9
  require 'opentelemetry/error'
10
10
  require 'opentelemetry/context'
11
11
  require 'opentelemetry/baggage'
12
- require_relative './opentelemetry/instrumentation'
13
- require 'opentelemetry/metrics'
14
12
  require 'opentelemetry/trace'
13
+ require 'opentelemetry/internal'
15
14
  require 'opentelemetry/version'
16
15
 
17
16
  # OpenTelemetry is an open source observability framework, providing a
@@ -22,8 +21,10 @@ require 'opentelemetry/version'
22
21
  module OpenTelemetry
23
22
  extend self
24
23
 
25
- attr_writer :tracer_provider, :meter_provider, :propagation, :baggage,
26
- :logger, :error_handler
24
+ @mutex = Mutex.new
25
+ @tracer_provider = Internal::ProxyTracerProvider.new
26
+
27
+ attr_writer :propagation, :logger, :error_handler
27
28
 
28
29
  # @return [Object, Logger] configured Logger or a default STDOUT Logger.
29
30
  def logger
@@ -44,36 +45,28 @@ module OpenTelemetry
44
45
  error_handler.call(exception: exception, message: message)
45
46
  end
46
47
 
48
+ # Register the global tracer provider.
49
+ #
50
+ # @param [TracerProvider] provider A tracer provider to register as the
51
+ # global instance.
52
+ def tracer_provider=(provider)
53
+ @mutex.synchronize do
54
+ if @tracer_provider.instance_of? Internal::ProxyTracerProvider
55
+ logger.debug("Upgrading default proxy tracer provider to #{provider.class}")
56
+ @tracer_provider.delegate = provider
57
+ end
58
+ @tracer_provider = provider
59
+ end
60
+ end
61
+
47
62
  # @return [Object, Trace::TracerProvider] registered tracer provider or a
48
63
  # default no-op implementation of the tracer provider.
49
64
  def tracer_provider
50
- @tracer_provider ||= Trace::TracerProvider.new
51
- end
52
-
53
- # @return [Object, Metrics::MeterProvider] registered meter provider or a
54
- # default no-op implementation of the meter provider.
55
- def meter_provider
56
- @meter_provider ||= Metrics::MeterProvider.new
57
- end
58
-
59
- # @return [Instrumentation::Registry] registry containing all known
60
- # instrumentation
61
- def instrumentation_registry
62
- @instrumentation_registry ||= Instrumentation::Registry.new
63
- end
64
-
65
- # @return [Object, Baggage::Manager] registered
66
- # baggage manager or a default no-op implementation of the
67
- # manager.
68
- def baggage
69
- @baggage ||= Baggage::Manager.new
65
+ @mutex.synchronize { @tracer_provider }
70
66
  end
71
67
 
72
68
  # @return [Context::Propagation::Propagator] a propagator instance
73
69
  def propagation
74
- @propagation ||= Context::Propagation::Propagator.new(
75
- Context::Propagation::NoopInjector.new,
76
- Context::Propagation::NoopExtractor.new
77
- )
70
+ @propagation ||= Context::Propagation::NoopTextMapPropagator.new
78
71
  end
79
72
  end
@@ -4,13 +4,106 @@
4
4
  #
5
5
  # SPDX-License-Identifier: Apache-2.0
6
6
 
7
- require 'opentelemetry/baggage/builder'
8
- require 'opentelemetry/baggage/manager'
9
7
  require 'opentelemetry/baggage/propagation'
8
+ require 'opentelemetry/baggage/builder'
9
+ require 'opentelemetry/baggage/entry'
10
10
 
11
11
  module OpenTelemetry
12
12
  # The Baggage module provides functionality to record and propagate
13
13
  # baggage in a distributed trace
14
14
  module Baggage
15
+ extend self
16
+
17
+ BAGGAGE_KEY = OpenTelemetry::Baggage::Propagation::ContextKeys.baggage_key
18
+ EMPTY_BAGGAGE = {}.freeze
19
+ private_constant(:BAGGAGE_KEY, :EMPTY_BAGGAGE)
20
+
21
+ # Used to chain modifications to baggage. The result is a
22
+ # context with an updated baggage. If only a single
23
+ # modification is being made to baggage, use the other
24
+ # methods on +Baggage+, if multiple modifications are being made, use
25
+ # this one.
26
+ #
27
+ # @param [optional Context] context The context to update with with new
28
+ # modified baggage. Defaults to +Context.current+
29
+ # @return [Context]
30
+ def build(context: Context.current)
31
+ builder = Builder.new(baggage_for(context).dup)
32
+ yield builder
33
+ context.set_value(BAGGAGE_KEY, builder.entries)
34
+ end
35
+
36
+ # Returns a new context with empty baggage
37
+ #
38
+ # @param [optional Context] context Context to clear baggage from. Defaults
39
+ # to +Context.current+
40
+ # @return [Context]
41
+ def clear(context: Context.current)
42
+ context.set_value(BAGGAGE_KEY, EMPTY_BAGGAGE)
43
+ end
44
+
45
+ # Returns the corresponding baggage.entry (or nil) for key
46
+ #
47
+ # @param [String] key The lookup key
48
+ # @param [optional Context] context The context from which to retrieve
49
+ # the key.
50
+ # Defaults to +Context.current+
51
+ # @return [String]
52
+ def value(key, context: Context.current)
53
+ baggage_for(context)[key]&.value
54
+ end
55
+
56
+ # Returns the baggage
57
+ #
58
+ # @param [optional Context] context The context from which to retrieve
59
+ # the baggage.
60
+ # Defaults to +Context.current+
61
+ # @return [Hash]
62
+ def values(context: Context.current)
63
+ baggage_for(context).transform_values(&:value)
64
+ end
65
+
66
+ # @api private
67
+ def raw_entries(context: Context.current)
68
+ baggage_for(context).dup.freeze
69
+ end
70
+
71
+ # Returns a new context with new key-value pair
72
+ #
73
+ # @param [String] key The key to store this value under
74
+ # @param [String] value String value to be stored under key
75
+ # @param [optional String] metadata This is here to store properties
76
+ # received from other W3C Baggage impelmentations but is not exposed in
77
+ # OpenTelemetry. This is condsidered private API and not for use by
78
+ # end-users.
79
+ # @param [optional Context] context The context to update with new
80
+ # value. Defaults to +Context.current+
81
+ # @return [Context]
82
+ def set_value(key, value, metadata: nil, context: Context.current)
83
+ new_baggage = baggage_for(context).dup
84
+ new_baggage[key] = Entry.new(value, metadata)
85
+ context.set_value(BAGGAGE_KEY, new_baggage)
86
+ end
87
+
88
+ # Returns a new context with value at key removed
89
+ #
90
+ # @param [String] key The key to remove
91
+ # @param [optional Context] context The context to remove baggage
92
+ # from. Defaults to +Context.current+
93
+ # @return [Context]
94
+ def remove_value(key, context: Context.current)
95
+ baggage = baggage_for(context)
96
+ return context unless baggage.key?(key)
97
+
98
+ new_baggage = baggage.dup
99
+ new_baggage.delete(key)
100
+ context.set_value(BAGGAGE_KEY, new_baggage)
101
+ end
102
+
103
+ private
104
+
105
+ def baggage_for(context)
106
+ context.value(BAGGAGE_KEY) || EMPTY_BAGGAGE
107
+ end
15
108
  end
16
109
  end
@@ -6,13 +6,39 @@
6
6
 
7
7
  module OpenTelemetry
8
8
  module Baggage
9
- # No op implementation of Baggage::Builder
9
+ # Operational implementation of Baggage::Builder
10
10
  class Builder
11
- def set_value(key, value); end
11
+ # @api private
12
+ attr_reader :entries
12
13
 
13
- def remove_value(key); end
14
+ # @api private
15
+ def initialize(entries)
16
+ @entries = entries
17
+ end
14
18
 
15
- def clear; end
19
+ # Set key-value in the to-be-created baggage
20
+ #
21
+ # @param [String] key The key to store this value under
22
+ # @param [String] value String value to be stored under key
23
+ # @param [optional String] metadata This is here to store properties
24
+ # received from other W3C Baggage impelmentations but is not exposed in
25
+ # OpenTelemetry. This is condsidered private API and not for use by
26
+ # end-users.
27
+ def set_value(key, value, metadata: nil)
28
+ @entries[key] = OpenTelemetry::Baggage::Entry.new(value, metadata)
29
+ end
30
+
31
+ # Removes key from the to-be-created baggage
32
+ #
33
+ # @param [String] key The key to remove
34
+ def remove_value(key)
35
+ @entries.delete(key)
36
+ end
37
+
38
+ # Clears all baggage from the to-be-created baggage
39
+ def clear
40
+ @entries.clear
41
+ end
16
42
  end
17
43
  end
18
44
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ # OpenTelemetry Baggage Implementation
9
+ module Baggage
10
+ # Read-only representation of a baggage entry
11
+ class Entry
12
+ attr_reader :value, :metadata
13
+
14
+ def initialize(value, metadata = nil)
15
+ @value = value
16
+ @metadata = metadata
17
+ end
18
+ end
19
+ end
20
+ end
@@ -5,32 +5,23 @@
5
5
  # SPDX-License-Identifier: Apache-2.0
6
6
 
7
7
  require 'opentelemetry/baggage/propagation/context_keys'
8
- require 'opentelemetry/baggage/propagation/text_map_injector'
9
- require 'opentelemetry/baggage/propagation/text_map_extractor'
8
+ require 'opentelemetry/baggage/propagation/text_map_propagator'
10
9
 
11
10
  module OpenTelemetry
12
11
  module Baggage
13
- # The Baggage::Propagation module contains injectors and
14
- # extractors for sending and receiving baggage over the wire
12
+ # The Baggage::Propagation module contains a text map propagator for
13
+ # sending and receiving baggage over the wire.
15
14
  module Propagation
16
15
  extend self
17
16
 
18
- BAGGAGE_KEY = 'baggage'
19
- TEXT_MAP_EXTRACTOR = TextMapExtractor.new
20
- TEXT_MAP_INJECTOR = TextMapInjector.new
17
+ TEXT_MAP_PROPAGATOR = TextMapPropagator.new
21
18
 
22
- private_constant :BAGGAGE_KEY, :TEXT_MAP_INJECTOR, :TEXT_MAP_EXTRACTOR
19
+ private_constant :TEXT_MAP_PROPAGATOR
23
20
 
24
- # Returns an extractor that extracts context using the W3C Baggage
25
- # format
26
- def text_map_injector
27
- TEXT_MAP_INJECTOR
28
- end
29
-
30
- # Returns an injector that injects context using the W3C Baggage
31
- # format
32
- def text_map_extractor
33
- TEXT_MAP_EXTRACTOR
21
+ # Returns a text map propagator that propagates context using the
22
+ # W3C Baggage format.
23
+ def text_map_propagator
24
+ TEXT_MAP_PROPAGATOR
34
25
  end
35
26
  end
36
27
  end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright The OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ require 'cgi'
8
+
9
+ module OpenTelemetry
10
+ module Baggage
11
+ module Propagation
12
+ # Propagates baggage using the W3C Baggage format
13
+ class TextMapPropagator
14
+ # Maximums according to W3C Baggage spec
15
+ MAX_ENTRIES = 180
16
+ MAX_ENTRY_LENGTH = 4096
17
+ MAX_TOTAL_LENGTH = 8192
18
+
19
+ BAGGAGE_KEY = 'baggage'
20
+ FIELDS = [BAGGAGE_KEY].freeze
21
+
22
+ private_constant :BAGGAGE_KEY, :FIELDS, :MAX_ENTRIES, :MAX_ENTRY_LENGTH, :MAX_TOTAL_LENGTH
23
+
24
+ # Inject in-process baggage into the supplied carrier.
25
+ #
26
+ # @param [Carrier] carrier The mutable carrier to inject baggage into
27
+ # @param [Context] context The context to read baggage from
28
+ # @param [optional Setter] setter If the optional setter is provided, it
29
+ # will be used to write context into the carrier, otherwise the default
30
+ # text map setter will be used.
31
+ def inject(carrier, context: Context.current, setter: Context::Propagation.text_map_setter)
32
+ baggage = OpenTelemetry::Baggage.raw_entries(context: context)
33
+
34
+ return if baggage.nil? || baggage.empty?
35
+
36
+ encoded_baggage = encode(baggage)
37
+ setter.set(carrier, BAGGAGE_KEY, encoded_baggage) unless encoded_baggage&.empty?
38
+ nil
39
+ end
40
+
41
+ # Extract remote baggage from the supplied carrier.
42
+ # If extraction fails or there is no baggage to extract,
43
+ # then the original context will be returned
44
+ #
45
+ # @param [Carrier] carrier The carrier to get the header from
46
+ # @param [optional Context] context Context to be updated with the baggage
47
+ # extracted from the carrier. Defaults to +Context.current+.
48
+ # @param [optional Getter] getter If the optional getter is provided, it
49
+ # will be used to read the header from the carrier, otherwise the default
50
+ # text map getter will be used.
51
+ #
52
+ # @return [Context] context updated with extracted baggage, or the original context
53
+ # if extraction fails
54
+ def extract(carrier, context: Context.current, getter: Context::Propagation.text_map_getter)
55
+ header = getter.get(carrier, BAGGAGE_KEY)
56
+ return context if header.nil? || header.empty?
57
+
58
+ entries = header.gsub(/\s/, '').split(',')
59
+
60
+ OpenTelemetry::Baggage.build(context: context) do |builder|
61
+ entries.each do |entry|
62
+ # Note metadata is currently unused in OpenTelemetry, but is part
63
+ # the W3C spec where it's referred to as properties. We preserve
64
+ # the properties (as-is) so that they can be propagated elsewhere.
65
+ kv, meta = entry.split(';', 2)
66
+ k, v = kv.split('=').map!(&CGI.method(:unescape))
67
+ builder.set_value(k, v, metadata: meta)
68
+ end
69
+ end
70
+ rescue StandardError => e
71
+ OpenTelemetry.logger.debug "Error extracting W3C baggage: #{e.message}"
72
+ context
73
+ end
74
+
75
+ # Returns the predefined propagation fields. If your carrier is reused, you
76
+ # should delete the fields returned by this method before calling +inject+.
77
+ #
78
+ # @return [Array<String>] a list of fields that will be used by this propagator.
79
+ def fields
80
+ FIELDS
81
+ end
82
+
83
+ private
84
+
85
+ def encode(baggage)
86
+ result = +''
87
+ encoded_count = 0
88
+ baggage.each_pair do |key, entry|
89
+ break unless encoded_count < MAX_ENTRIES
90
+
91
+ encoded_entry = encode_value(key, entry)
92
+ next unless encoded_entry.size <= MAX_ENTRY_LENGTH &&
93
+ encoded_entry.size + result.size <= MAX_TOTAL_LENGTH
94
+
95
+ result << encoded_entry << ','
96
+ encoded_count += 1
97
+ end
98
+ result.chop!
99
+ end
100
+
101
+ def encode_value(key, entry)
102
+ result = +"#{CGI.escape(key.to_s)}=#{CGI.escape(entry.value.to_s)}"
103
+ # We preserve metadata recieved on extract and assume it's already formatted
104
+ # for transport. It's sent as-is without further processing.
105
+ result << ";#{entry.metadata}" if entry.metadata
106
+ result
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end