Class: Gruf::Zipkin::Trace

Inherits:
Object
  • Object
show all
Defined in:
lib/gruf/zipkin/trace.rb

Overview

Represents a trace through Gruf and gRPC

Constant Summary collapse

METADATA_KEYS =
{
  error: 'error',
  grpc: {
    method: 'grpc.method',
    request_class: 'grpc.request_class',
    error: 'grpc.error',
    error_code: 'grpc.error_code',
    error_class: 'grpc.error_class',
    success: 'grpc.success'
  }
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(method, service_key, options = {}) ⇒ Trace

Returns a new instance of Trace

Parameters:


42
43
44
45
46
# File 'lib/gruf/zipkin/trace.rb', line 42

def initialize(method, service_key, options = {})
  @method = method
  @service_key = service_key.to_s
  @options = options
end

Instance Attribute Details

#methodObject (readonly)

Returns the value of attribute method


23
24
25
# File 'lib/gruf/zipkin/trace.rb', line 23

def method
  @method
end

#service_keyObject (readonly)

Returns the value of attribute service_key


23
24
25
# File 'lib/gruf/zipkin/trace.rb', line 23

def service_key
  @service_key
end

Instance Method Details

#componentString

Returns:

  • (String)

144
145
146
# File 'lib/gruf/zipkin/trace.rb', line 144

def component
  "#{span_prefix}#{@service_key}.#{@method.signature}"
end

#header_value(key) ⇒ String|NilClass

Delegator to headers object to get value of a B3 header

Parameters:

  • key (Symbol)

Returns:

  • (String|NilClass)

120
121
122
# File 'lib/gruf/zipkin/trace.rb', line 120

def header_value(key)
  @method.headers.value(key)
end

#sampled?Boolean

Returning whether or not this trace is sampled, which is based on either:the sample 1) The X-B3-Sampled header, if present 2) The sample rate set in the zipkin configuration …in that order.

Returns:

  • (Boolean)

132
133
134
135
136
137
138
139
# File 'lib/gruf/zipkin/trace.rb', line 132

def sampled?
  sampled = header_value(:sampled)
  if sampled && sampled.to_s != ''
    [1, '1', 'true', true].include?(sampled)
  else
    trace_id.sampled?
  end
end

#span_prefixString

Returns:

  • (String)

151
152
153
154
# File 'lib/gruf/zipkin/trace.rb', line 151

def span_prefix
  prefix = @options.fetch(:span_prefix, '').to_s
  prefix.empty? ? '' : "#{prefix}."
end

#trace!(tracer, &block) ⇒ Object

Trace the request

Parameters:

  • The (::Trace::Tracer)

    tracing service to use

Returns:

  • (Object)

Raises:

  • (ArgumentError)

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/gruf/zipkin/trace.rb', line 54

def trace!(tracer, &block)
  raise ArgumentError, 'no block given' unless block_given?
  # If for some reason we don't have a tracer, let's just proceed as normal
  # and not cause the request to fail
  unless tracer
    Gruf.logger.warn "Failed to log trace for #{method.request_class}.#{method.signature.classify} because Tracer was not found!" if Gruf.logger
    return block.call(method.request, method.active_call)
  end

  result = nil

  tracer.with_new_span(trace_id, component) do |span|
    span.record(::Trace::Annotation::SERVER_RECV)
    span.record_local_component(component)
    span.record_tag(METADATA_KEYS[:grpc][:method], method.signature.classify)
    span.record_tag(METADATA_KEYS[:grpc][:request_class], method.request_class)

    begin
      result = block.call(method.request, method.active_call)
      span.record(::Trace::Annotation::SERVER_SEND)
    rescue => e
      if e.is_a?(::GRPC::BadStatus)
        span.record_tag(METADATA_KEYS[:error], true)
        span.record_tag(METADATA_KEYS[:grpc][:error], true)
        span.record_tag(METADATA_KEYS[:grpc][:error_code], e.code.to_s)
        span.record_tag(METADATA_KEYS[:grpc][:error_class], e.class.to_s)
      end
      span.record(::Trace::Annotation::SERVER_SEND)
      tracer.end_span(span) # manually end here as the raise prevents this
      raise # passthrough, we just want the annotations
    end
  end
  result
end

#trace_id::Trace::TraceId

Memoize build of a new trace_id based on propagation headers, or generator if no headers are present

Returns:

  • (::Trace::TraceId)

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/gruf/zipkin/trace.rb', line 95

def trace_id
  unless @trace_id
    tid = header_value(:trace_id)
    span_id = header_value(:span_id)
    # both trace ID and span ID are required for propagation
    if !tid.to_s.empty? && !span_id.to_s.empty?
      # we have a propagated trace, let's carry over the information
      parent_id = header_value(:parent_span_id)
      sampled = header_value(:sampled)
      flags = header_value(:flags)
      @trace_id = ::Trace::TraceId.new(tid, parent_id, span_id, sampled, flags)
    else
      # if trace_id/span_id are not present, generate a new trace
      @trace_id = ::ZipkinTracer::TraceGenerator.new.next_trace_id
    end
  end
  @trace_id
end