Class: Datadog::HTTPTransport

Inherits:
Object
  • Object
show all
Defined in:
lib/ddtrace/transport.rb

Overview

Transport class that handles the spans delivery to the local trace-agent. The class wraps a Net:HTTP instance so that the Transport is thread-safe.

Constant Summary collapse

TIMEOUT =

seconds before the transport timeout

1
TRACE_COUNT_HEADER =

header containing the number of traces in a payload

'X-Datadog-Trace-Count'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hostname, port, options = {}) ⇒ HTTPTransport

Returns a new instance of HTTPTransport.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/ddtrace/transport.rb', line 20

def initialize(hostname, port, options = {})
  @hostname = hostname
  @port = port
  @traces_endpoint = '/v0.3/traces'.freeze
  @services_endpoint = '/v0.3/services'.freeze
  @compatibility_mode = false
  @encoder = options.fetch(:encoder, Datadog::Encoding::MsgpackEncoder.new())

  # overwrite the Content-type with the one chosen in the Encoder
  @headers = options.fetch(:headers, {})
  @headers['Content-Type'] = @encoder.content_type

  # stats
  @mutex = Mutex.new
  @count_success = 0
  @count_client_error = 0
  @count_server_error = 0
  @count_internal_error = 0
end

Instance Attribute Details

#hostnameObject

Returns the value of attribute hostname.



11
12
13
# File 'lib/ddtrace/transport.rb', line 11

def hostname
  @hostname
end

#portObject

Returns the value of attribute port.



11
12
13
# File 'lib/ddtrace/transport.rb', line 11

def port
  @port
end

#services_endpointObject (readonly)

Returns the value of attribute services_endpoint.



12
13
14
# File 'lib/ddtrace/transport.rb', line 12

def services_endpoint
  @services_endpoint
end

#traces_endpointObject (readonly)

Returns the value of attribute traces_endpoint.



12
13
14
# File 'lib/ddtrace/transport.rb', line 12

def traces_endpoint
  @traces_endpoint
end

Instance Method Details

#client_error?(code) ⇒ Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/ddtrace/transport.rb', line 100

def client_error?(code)
  code.between?(400, 499)
end

#downgrade!Object

Downgrade the connection to a compatibility version of the HTTPTransport; this method should target a stable API that works whatever is the agent or the tracing client versions.



80
81
82
83
84
85
86
# File 'lib/ddtrace/transport.rb', line 80

def downgrade!
  @compatibility_mode = true
  @traces_endpoint = '/v0.2/traces'.freeze
  @services_endpoint = '/v0.2/services'.freeze
  @encoder = Datadog::Encoding::JSONEncoder.new()
  @headers['Content-Type'] = @encoder.content_type
end

#downgrade?(code) ⇒ Boolean

receiving a 404 means that we’re targeting an endpoint that is not available in the trace agent. Usually this means that we’ve an up-to-date tracing client, while running an obsolete agent. receiving a 415 means that we’re using an unsupported content-type with an existing endpoint. Usually this means that we’re using a newer encoder with a previous endpoint. In both cases, we’re going to downgrade the transporter encoder so that it will target a stable API.

Returns:

  • (Boolean)


115
116
117
# File 'lib/ddtrace/transport.rb', line 115

def downgrade?(code)
  code == 404 || code == 415
end

#handle_response(response) ⇒ Object

handles the server response; here you can log the trace-agent response or do something more complex to recover from a possible error. This function is handled within the HTTP mutex.synchronize so it’s thread-safe.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/ddtrace/transport.rb', line 122

def handle_response(response)
  status_code = response.code.to_i

  if success?(status_code)
    Datadog::Tracer.log.debug('Payload correctly sent to the trace agent.')
    @mutex.synchronize { @count_success += 1 }
  elsif downgrade?(status_code)
    Datadog::Tracer.log.debug("calling the endpoint but received #{status_code}; downgrading the API")
  elsif client_error?(status_code)
    Datadog::Tracer.log.error("Client error: #{response.message}")
    @mutex.synchronize { @count_client_error += 1 }
  elsif server_error?(status_code)
    Datadog::Tracer.log.error("Server error: #{response.message}")
    @mutex.synchronize { @count_server_error += 1 }
  end

  status_code
rescue StandardError => e
  Datadog::Tracer.log.error(e.message)
  @mutex.synchronize { @count_internal_error += 1 }
  500
end

#informational?(code) ⇒ Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/ddtrace/transport.rb', line 88

def informational?(code)
  code.between?(100, 199)
end

#post(url, data, count = nil) ⇒ Object

send data to the trace-agent; the method is thread-safe



63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ddtrace/transport.rb', line 63

def post(url, data, count = nil)
  Datadog::Tracer.log.debug("Sending data from process: #{Process.pid}")
  headers = count.nil? ? {} : { TRACE_COUNT_HEADER => count.to_s }
  headers = headers.merge(@headers)
  request = Net::HTTP::Post.new(url, headers)
  request.body = data

  response = Net::HTTP.start(@hostname, @port, read_timeout: TIMEOUT) { |http| http.request(request) }
  handle_response(response)
rescue StandardError => e
  Datadog::Tracer.log.error(e.message)
  500
end

#redirect?(code) ⇒ Boolean

Returns:

  • (Boolean)


96
97
98
# File 'lib/ddtrace/transport.rb', line 96

def redirect?(code)
  code.between?(300, 399)
end

#send(endpoint, data) ⇒ Object

route the send to the right endpoint



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/ddtrace/transport.rb', line 41

def send(endpoint, data)
  case endpoint
  when :services
    payload = @encoder.encode_services(data)
    status_code = post(@services_endpoint, payload)
  when :traces
    count = data.length
    payload = @encoder.encode_traces(data)
    status_code = post(@traces_endpoint, payload, count)
  else
    Datadog::Tracer.log.error("Unsupported endpoint: #{endpoint}")
    return nil
  end

  return status_code unless downgrade?(status_code) && !@compatibility_mode

  # the API endpoint is not available so we should downgrade the connection and re-try the call
  downgrade!
  send(endpoint, data)
end

#server_error?(code) ⇒ Boolean

Returns:

  • (Boolean)


104
105
106
# File 'lib/ddtrace/transport.rb', line 104

def server_error?(code)
  code.between?(500, 599)
end

#statsObject



145
146
147
148
149
150
151
152
153
154
# File 'lib/ddtrace/transport.rb', line 145

def stats
  @mutex.synchronize do
    {
      success: @count_success,
      client_error: @count_client_error,
      server_error: @count_server_error,
      internal_error: @count_internal_error
    }
  end
end

#success?(code) ⇒ Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/ddtrace/transport.rb', line 92

def success?(code)
  code.between?(200, 299)
end