Class: Labkit::ExconPublisher

Inherits:
Object
  • Object
show all
Defined in:
lib/labkit/excon_publisher.rb

Overview

A middleware for Excon HTTP library to publish a notification whenever a HTTP request is triggered.

Excon supports a middleware system that allows request/response interception freely. Whenever a new Excon connection is created, a list of default middlewares is injected. This list of middlewares can be altered thanks to Excon.defaults accessor. ExconPublisher is inserted into this list. It affects all connections created in future. There is a limitation that this approach doesn’t work if a user decides to override the default middleware list. It is unlikely though, at least in the dependency tree of GitLab.

ExconPublisher instance is created once and shared between all Excon connections later. Each connection may be triggered by different threads in parallel. In such cases, a connection objects creates multiple sockets for each thread. Therfore in the implementation of this middleware, the instrumation payload for each connection is stored inside a thread-isolated storage.

For more information: github.com/excon/excon/blob/81a0130537f2f8cd00d6daafb05d02d9a90dc9f7/lib/excon/middlewares/base.rb github.com/excon/excon/blob/fa3ec51e9bb062a12846a1cfff09534e76c99f4b/lib/excon/constants.rb#L146 github.com/excon/excon/blob/fa3ec51e9bb062a12846a1cfff09534e76c99f4b/lib/excon/connection.rb#L474

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stack) ⇒ ExconPublisher

Returns a new instance of ExconPublisher.



42
43
44
45
# File 'lib/labkit/excon_publisher.rb', line 42

def initialize(stack)
  @stack = stack
  @instrumenter = ActiveSupport::Notifications.instrumenter
end

Class Method Details

.labkit_prepend!Object



31
32
33
34
35
36
37
38
39
40
# File 'lib/labkit/excon_publisher.rb', line 31

def self.labkit_prepend!
  @prepend_mutex.synchronize do
    return if !defined?(Excon) || @prepended

    defaults = Excon.defaults
    defaults[:middlewares] << ExconPublisher

    @prepended = true
  end
end

Instance Method Details

#error_call(datum) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/labkit/excon_publisher.rb', line 68

def error_call(datum)
  payload = fetch_connection_payload(datum)

  return @stack.error_call(datum) if payload.nil?

  calculate_duration(payload)

  if datum[:error].is_a?(Exception)
    payload[:exception] = [datum[:error].class.name, datum[:error].message]
    payload[:exception_object] = datum[:error]
  elsif datum[:error].is_a?(String)
    exception = StandardError.new(datum[:error])
    payload[:exception] = [exception.class.name, exception.message]
    payload[:exception_object] = exception
  end

  @instrumenter.finish(::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, payload)
  @stack.error_call(datum)
ensure
  remove_connection_payload(datum)
end

#request_call(datum) ⇒ Object



47
48
49
50
51
52
# File 'lib/labkit/excon_publisher.rb', line 47

def request_call(datum)
  payload = start_payload(datum)
  store_connection_payload(datum, payload)
  @instrumenter.start(::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, payload)
  @stack.request_call(datum)
end

#response_call(datum) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/labkit/excon_publisher.rb', line 54

def response_call(datum)
  payload = fetch_connection_payload(datum)

  return @stack.response_call(datum) if payload.nil?

  calculate_duration(payload)
  payload[:code] = datum[:response][:status].to_s

  @instrumenter.finish(::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, payload)
  @stack.response_call(datum)
ensure
  remove_connection_payload(datum)
end