http_status_agent.rb 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. module Agents
  2. class HttpStatusAgent < Agent
  3. include WebRequestConcern
  4. include FormConfigurable
  5. can_dry_run!
  6. can_order_created_events!
  7. default_schedule "every_12h"
  8. form_configurable :url
  9. form_configurable :disable_redirect_follow, type: :boolean
  10. form_configurable :changes_only, type: :boolean
  11. form_configurable :headers_to_save
  12. description <<-MD
  13. The HttpStatusAgent will check a url and emit the resulting HTTP status code with the time that it waited for a reply. Additionally, it will optionally emit the value of one or more specified headers.
  14. Specify a `Url` and the Http Status Agent will produce an event with the HTTP status code. If you specify one or more `Headers to save` (comma-delimited) as well, that header or headers' value(s) will be included in the event.
  15. The `disable redirect follow` option causes the Agent to not follow HTTP redirects. For example, setting this to `true` will cause an agent that receives a 301 redirect to `http://yahoo.com` to return a status of 301 instead of following the redirect and returning 200.
  16. The `changes only` option causes the Agent to report an event only when the status changes. If set to false, an event will be created for every check. If set to true, an event will only be created when the status changes (like if your site goes from 200 to 500).
  17. MD
  18. event_description <<-MD
  19. Events will have the following fields:
  20. {
  21. "url": "...",
  22. "status": "...",
  23. "elapsed_time": "...",
  24. "headers": {
  25. "...": "..."
  26. }
  27. }
  28. MD
  29. def working?
  30. memory['last_status'].to_i > 0
  31. end
  32. def default_options
  33. {
  34. 'url' => "http://google.com",
  35. 'disable_redirect_follow' => "true",
  36. }
  37. end
  38. def validate_options
  39. errors.add(:base, "a url must be specified") unless options['url'].present?
  40. end
  41. def header_array(str)
  42. (str || '').split(',').map(&:strip)
  43. end
  44. def check
  45. check_this_url interpolated[:url], header_array(interpolated[:headers_to_save])
  46. end
  47. def receive(incoming_events)
  48. incoming_events.each do |event|
  49. interpolate_with(event) do
  50. check_this_url interpolated[:url], header_array(interpolated[:headers_to_save])
  51. end
  52. end
  53. end
  54. private
  55. def check_this_url(url, local_headers)
  56. # Track time
  57. measured_result = TimeTracker.track { ping(url) }
  58. current_status = measured_result.result ? measured_result.status.to_s : ''
  59. return if options['changes_only'] == 'true' && current_status == memory['last_status'].to_s
  60. payload = { 'url' => url, 'response_received' => false, 'elapsed_time' => measured_result.elapsed_time }
  61. # Deal with failures
  62. if measured_result.result
  63. final_url = boolify(interpolated['disable_redirect_follow']) ? url : measured_result.result.to_hash[:url]
  64. payload.merge!({ 'final_url' => final_url, 'redirected' => (url != final_url), 'response_received' => true, 'status' => current_status })
  65. # Deal with headers
  66. if local_headers.present?
  67. header_results = measured_result.result.headers.select {|header, value| local_headers.include?(header)}
  68. # Fill in headers that we wanted, but weren't returned
  69. local_headers.each { |header| header_results[header] = nil unless header_results.has_key?(header) }
  70. payload.merge!({ 'headers' => header_results })
  71. end
  72. create_event payload: payload
  73. memory['last_status'] = measured_result.status.to_s
  74. else
  75. create_event payload: payload
  76. memory['last_status'] = nil
  77. end
  78. end
  79. def ping(url)
  80. result = faraday.get url
  81. result.status > 0 ? result : nil
  82. rescue
  83. nil
  84. end
  85. end
  86. # Clock that cannot be set and represents monotonic time since
  87. # some unspecified starting point.
  88. #
  89. # @!visibility private
  90. GLOBAL_MONOTONIC_CLOCK = class_definition.new
  91. private_constant :GLOBAL_MONOTONIC_CLOCK
  92. # @!macro [attach] monotonic_get_time
  93. #
  94. # Returns the current time a tracked by the application monotonic clock.
  95. #
  96. # @return [Float] The current monotonic time when `since` not given else
  97. # the elapsed monotonic time between `since` and the current time
  98. #
  99. # @!macro monotonic_clock_warning
  100. def monotonic_time
  101. GLOBAL_MONOTONIC_CLOCK.get_time
  102. end
  103. module_function :monotonic_time
  104. end