Class: Crystil::Collector

Inherits:
Object
  • Object
show all
Defined in:
lib/crystil/collector.rb,
sig/crystil/collector.rbs

Overview

Handles asynchronous submission of analytics to Crystil collector

Constant Summary collapse

DEFAULT_MAX_RETRIES =

Returns:

  • (Integer)
3
RETRY_DELAY =

seconds

Returns:

  • (Integer)
1

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Collector

Returns a new instance of Collector.

Parameters:



14
15
16
17
18
19
20
21
22
# File 'lib/crystil/collector.rb', line 14

def initialize(config)
  @config = config
  @executor = Concurrent::ThreadPoolExecutor.new(
    min_threads: 1,
    max_threads: 5,
    max_queue: 100,
    fallback_policy: :discard
  )
end

Instance Method Details

#post_to_collector(payload) ⇒ Object

Parameters:

  • payload (Hash[Symbol, untyped])

Returns:

  • (Object)


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/crystil/collector.rb', line 65

def post_to_collector(payload)
  uri = URI.parse(@config.collector_url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = uri.scheme == "https"

  # Configure SSL to use system certificates
  if http.use_ssl?
    http.verify_mode = OpenSSL::SSL::VERIFY_PEER
    http.cert_store = OpenSSL::X509::Store.new
    http.cert_store.set_default_paths
  end

  http.open_timeout = @config.timeout
  http.read_timeout = @config.timeout

  request = Net::HTTP::Post.new("/rec")
  request["Content-Type"] = "application/json"
  request["User-Agent"] = "crystil-ruby/#{@config.version}"
  request.body = JSON.generate(payload)

  http.request(request)
end

#shutdownvoid

This method returns an undefined value.

Gracefully shutdown the collector



39
40
41
42
# File 'lib/crystil/collector.rb', line 39

def shutdown
  @executor.shutdown
  @executor.wait_for_termination(5)
end

#submit(payload) ⇒ Object

Submit analytics payload synchronously (for testing)

Parameters:

  • payload (Hash[Symbol, untyped])

Returns:

  • (Object)


34
35
36
# File 'lib/crystil/collector.rb', line 34

def submit(payload)
  submit_with_retry(payload)
end

#submit_async(payload) ⇒ void

This method returns an undefined value.

Submit analytics payload asynchronously

Parameters:

  • payload (Hash[Symbol, untyped])


25
26
27
28
29
30
31
# File 'lib/crystil/collector.rb', line 25

def submit_async(payload)
  @executor.post do
    submit_with_retry(payload)
  rescue StandardError => e
    warn "Crystil: Failed to submit analytics: #{e.message}"
  end
end

#submit_with_retry(payload, attempt = 1) ⇒ Object

Parameters:

  • payload (Hash[Symbol, untyped])
  • attempt (Integer) (defaults to: 1)

Returns:

  • (Object)


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/crystil/collector.rb', line 46

def submit_with_retry(payload, attempt = 1)
  response = post_to_collector(payload)

  unless response.is_a?(Net::HTTPSuccess)
    raise APIError.new(
      "Collector request failed: #{response.code} #{response.message}",
      status_code: response.code.to_i,
      response_body: response.body
    )
  end

  response
rescue StandardError => e
  raise e unless attempt < DEFAULT_MAX_RETRIES

  sleep(RETRY_DELAY * attempt)
  submit_with_retry(payload, attempt + 1)
end