Skip to content

Commit

Permalink
Add TraceFlags class (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
fbogsany authored Sep 5, 2019
1 parent 8d3cec2 commit c1a609e
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def to_bytes(span_context)
def from_bytes(bytes)
raise ArgumentError if bytes.nil?

SpanContext.INVALID
Trace::SpanContext::INVALID
end
end
end
Expand Down
40 changes: 39 additions & 1 deletion api/lib/opentelemetry/trace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,47 @@
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
# The Trace API allows recording a set of events, triggered as a result of a
# single logical operation, consolidated across various components of an
# application.
module Trace
# An invalid trace identifier, a 16-byte array with all zero bytes, encoded
# as a hexadecimal string.
INVALID_TRACE_ID = ('0' * 32).freeze

# An invalid span identifier, an 8-byte array with all zero bytes, encoded
# as a hexadecimal string.
INVALID_SPAN_ID = ('0' * 16).freeze

# Generates a valid trace identifier, a 16-byte array with at least one
# non-zero byte, encoded as a hexadecimal string.
#
# @return [String] a hexadecimal string encoding of a valid trace ID.
def self.generate_trace_id
loop do
id = Random::DEFAULT.bytes(16).unpack1('H*')
return id unless id == INVALID_TRACE_ID
end
end

# Generates a valid span identifier, an 8-byte array with at least one
# non-zero byte, encoded as a hexadecimal string.
#
# @return [String] a hexadecimal string encoding of a valid span ID.
def self.generate_span_id
loop do
id = Random::DEFAULT.bytes(8).unpack1('H*')
return id unless id == INVALID_SPAN_ID
end
end
end
end

require 'opentelemetry/trace/trace_flags'
require 'opentelemetry/trace/samplers'
require 'opentelemetry/trace/span_context'
require 'opentelemetry/trace/span_kind'
require 'opentelemetry/trace/span'
require 'opentelemetry/trace/status'
require 'opentelemetry/trace/tracer'
require 'opentelemetry/trace/samplers'
20 changes: 11 additions & 9 deletions api/lib/opentelemetry/trace/span_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,27 @@

module OpenTelemetry
module Trace
# A SpanContext contains the state that must propagate to child @see Spans and across process boundaries.
# It contains the identifiers (a @see TraceId and @see SpanId) associated with the @see Span and a set of
# @see TraceOptions.
# A SpanContext contains the state that must propagate to child {Span}s and across process boundaries.
# It contains the identifiers (a trace ID and span ID) associated with the {Span} and a set of
# {TraceFlags}.
class SpanContext
attr_reader :trace_id, :span_id, :trace_options
attr_reader :trace_id, :span_id, :trace_flags

def initialize(
trace_id: generate_trace_id,
span_id: generate_span_id,
trace_options: TraceOptions::DEFAULT
trace_id: Trace.generate_trace_id,
span_id: Trace.generate_span_id,
trace_flags: TraceFlags::DEFAULT
)
@trace_id = trace_id
@span_id = span_id
@trace_options = trace_options
@trace_flags = trace_flags
end

def valid?
!(@trace_id.zero? || @span_id.zero?)
@trace_id != INVALID_TRACE_ID && @span_id != INVALID_SPAN_ID
end

INVALID = new(trace_id: INVALID_TRACE_ID, span_id: INVALID_SPAN_ID)
end
end
end
50 changes: 50 additions & 0 deletions api/lib/opentelemetry/trace/trace_flags.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

# Copyright 2019 OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
module Trace
# TraceFlags contain details about the trace. Unlike Tracestate values,
# TraceFlags are present in all traces. Currently, the only TraceFlag is a
# boolean {sampled?} {https://www.w3.org/TR/trace-context/#trace-flags flag}.
class TraceFlags
class << self
private :new # rubocop:disable Style/AccessModifierDeclarations

# Returns a newly created {TraceFlags} with the specified flags.
#
# @param [Integer] flags 8-bit byte of bit flags
# @raise [ArgumentError] If flags is not an 8-bit byte
# @return [TraceFlags]
def from_byte(flags)
raise ArgumentError, 'flags must be an 8-bit byte' unless flags & ~0xFF == 0 # rubocop:disable Style/NumericPredicate

new(flags)
end
end

# @api private
# The constructor is private and only for use internally by the class.
# Users should use the {from_byte} factory method to obtain a {TraceFlags}
# instance.
#
# @param [Integer] flags 8-bit byte of bit flags
# @return [TraceFlags]
def initialize(flags)
@flags = flags
end

# Returns whether the caller may have recorded trace data. When false,
# the caller did not record trace data out-of-band.
#
# @return [Boolean]
def sampled?
(@flags & 1) != 0
end

DEFAULT = from_byte(0)
end
end
end
6 changes: 3 additions & 3 deletions api/lib/opentelemetry/trace/tracer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ def start_span(name, with_parent: nil, with_parent_context: nil, attributes: nil
raise ArgumentError if name.nil?

span_context = with_parent&.context || with_parent_context || current_span.context
if SpanContext.INVALID == span_context
Span.create_random
else
if span_context.valid?
Span.new(span_context)
else
Span.create_random
end
end

Expand Down
62 changes: 62 additions & 0 deletions api/test/opentelemetry/trace/span_context_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

require 'test_helper'

describe OpenTelemetry::Trace::SpanContext do
let(:span_context) { OpenTelemetry::Trace::SpanContext.new }
let(:invalid_context) { OpenTelemetry::Trace::SpanContext::INVALID }

describe '#initialize' do
it 'must generate valid span_id and trace_id by default' do
span_context.trace_id.wont_equal(OpenTelemetry::Trace::INVALID_TRACE_ID)
span_context.span_id.wont_equal(OpenTelemetry::Trace::INVALID_SPAN_ID)
end
end

describe '#valid?' do
it 'is true by default' do
span_context.must_be(:valid?)
end

it 'is false for invalid context' do
invalid_context.wont_be(:valid?)
end
end

describe '#trace_id' do
it 'reflects the value passed in' do
trace_id = OpenTelemetry::Trace.generate_trace_id
context = OpenTelemetry::Trace::SpanContext.new(trace_id: trace_id)
context.trace_id.must_equal(trace_id)
end

it 'is invalid for invalid context' do
invalid_context.trace_id
.must_equal(OpenTelemetry::Trace::INVALID_TRACE_ID)
end
end

describe '#span_id' do
it 'reflects the value passed in' do
span_id = OpenTelemetry::Trace.generate_span_id
context = OpenTelemetry::Trace::SpanContext.new(span_id: span_id)
context.span_id.must_equal(span_id)
end

it 'is invalid for invalid context' do
invalid_context.span_id.must_equal(OpenTelemetry::Trace::INVALID_SPAN_ID)
end
end

describe '#trace_flags' do
it 'is unsampled by default' do
span_context.trace_flags.wont_be(:sampled?)
end

it 'reflects the value passed in' do
flags = OpenTelemetry::Trace::TraceFlags.from_byte(1)
context = OpenTelemetry::Trace::SpanContext.new(trace_flags: flags)
context.trace_flags.must_equal(flags)
end
end
end
33 changes: 33 additions & 0 deletions api/test/opentelemetry/trace/trace_flags_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

require 'test_helper'

describe OpenTelemetry::Trace::TraceFlags do
describe '.new' do
it 'is private' do
-> { OpenTelemetry::Trace::TraceFlags.new(0) }\
.must_raise(NoMethodError)
end
end
describe '.from_byte' do
it 'can be initialized with a byte' do
flags = OpenTelemetry::Trace::TraceFlags.from_byte(0)
flags.sampled?.must_equal(false)
end

it 'enforces flags is an 8-bit byte' do
-> { OpenTelemetry::Trace::TraceFlags.from_byte(256) }\
.must_raise(ArgumentError)
end
end

describe '#sampled?' do
it 'reflects the least-significant bit in the flags' do
sampled = OpenTelemetry::Trace::TraceFlags.from_byte(1)
not_sampled = OpenTelemetry::Trace::TraceFlags.from_byte(0)

sampled.sampled?.must_equal(true)
not_sampled.sampled?.must_equal(false)
end
end
end

0 comments on commit c1a609e

Please sign in to comment.