diff --git a/Gemfile b/Gemfile index f1218c6..a8f63ff 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,7 @@ gem "secure_headers" gem "heroku-env" gem "activesupport" gem "nokogiri" +gem "prometheus-client", require: "prometheus/middleware/exporter" # dilbert feed gem "feedjira" diff --git a/Gemfile.lock b/Gemfile.lock index 0991dc3..4fb33aa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -48,6 +48,8 @@ GEM nokogiri powder (0.4.0) thor (>= 0.11.5) + prometheus-client (0.8.0) + quantile (~> 0.2.1) pry (0.12.2) coderay (~> 1.1.0) method_source (~> 0.9.0) @@ -56,6 +58,7 @@ GEM slop (~> 3.0) public_suffix (3.0.3) puma (3.12.0) + quantile (0.2.1) rack (2.0.6) rack-protection (2.0.4) rack @@ -102,6 +105,7 @@ DEPENDENCIES nokogiri opengraph_parser powder + prometheus-client pry-remote puma rack diff --git a/app/twitter.rb b/app/twitter.rb index 51a3a43..ba04110 100644 --- a/app/twitter.rb +++ b/app/twitter.rb @@ -9,6 +9,14 @@ class Twitter < HTTP "Authorization" => "Bearer #{ENV["TWITTER_ACCESS_TOKEN"]}", } ERROR_CLASS = TwitterError + + def self.get(*args, &block) + response = super(*args, &block) + if response.headers.key?("x-rate-limit-remaining") + $metrics[:ratelimit].set({ service: "twitter" }, response.headers["x-rate-limit-remaining"][0].to_i) + end + return response + end end error TwitterError do |e| diff --git a/config/application.rb b/config/application.rb index 2e36105..f29be54 100644 --- a/config/application.rb +++ b/config/application.rb @@ -12,6 +12,8 @@ use Rack::Deflater use Rack::SslEnforcer, only_hosts: (ENV["SSL_ENFORCER_HOST"] || /\.herokuapp\.com$/) use SecureHeaders::Middleware + use Prometheus::Middleware::Exporter + set :protection, :except => [:frame_options] # Disable things that secure_headers handles set :erb, trim: "-" # Look up Rack::Mime::MIME_TYPES to see rack defaults diff --git a/config/initializers/40-prometheus.rb b/config/initializers/40-prometheus.rb new file mode 100644 index 0000000..243ab3d --- /dev/null +++ b/config/initializers/40-prometheus.rb @@ -0,0 +1,7 @@ +$prometheus = Prometheus::Client.registry + +$metrics = { + ratelimit: $prometheus.gauge(:ratelimit, docstring: "Remaining ratelimit for external services.", labels: [:service]), + requests: $prometheus.counter(:requests, docstring: "Number of requests made to external services.", labels: [:service, :response_code]), + urls: $prometheus.counter(:urls, docstring: "Number of URLs resolved."), +} diff --git a/docker-compose.yml b/docker-compose.yml index 8e20dca..86ef33a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,5 @@ +# Grafana is available at http://localhost:3001/. Login with admin/admin. Configure a Prometheus data source to pull metrics from http://prometheus:9090. + version: "3" services: web: @@ -10,13 +12,34 @@ services: ports: - "3000:80" networks: - - rssbox + - backend + - monitoring depends_on: - redis + redis: image: redis networks: - - rssbox + - backend + + prometheus: + image: prom/prometheus + ports: + - "9090:9090" + volumes: + - ${PWD}/prometheus.yml:/etc/prometheus/prometheus.yml + networks: + - monitoring + + grafana: + image: grafana/grafana + ports: + - "3001:3000" + networks: + - monitoring + depends_on: + - prometheus networks: - rssbox: + backend: + monitoring: diff --git a/lib/http.rb b/lib/http.rb index b3e24fb..a278e11 100644 --- a/lib/http.rb +++ b/lib/http.rb @@ -36,6 +36,7 @@ def self.get(url, options={headers: nil, query: nil}) headers.merge!(self::HEADERS) if defined?(self::HEADERS) && relative_url headers.merge!(options[:headers]) if options[:headers] response = http.request_get(uri.request_uri, headers) + $metrics[:requests].increment({ service: self.to_s.downcase, response_code: response.code }) return HTTPResponse.new(response, uri.to_s) end rescue Net::OpenTimeout, Net::ReadTimeout, SocketError, Errno::ECONNREFUSED, Errno::ECONNRESET, OpenSSL::SSL::SSLError, Zlib::BufError, EOFError diff --git a/lib/url.rb b/lib/url.rb index 1f10e2c..7c849f6 100644 --- a/lib/url.rb +++ b/lib/url.rb @@ -131,5 +131,7 @@ def self.resolve_url(url) dest = "" if url == dest @@cache[url] = dest $redis.set("url:#{url}", dest) + + $metrics[:urls].increment end end diff --git a/prometheus.yml b/prometheus.yml new file mode 100644 index 0000000..f0ded92 --- /dev/null +++ b/prometheus.yml @@ -0,0 +1,13 @@ +global: + scrape_interval: "15s" + scrape_timeout: "10s" + evaluation_interval: "15s" + +scrape_configs: +- job_name: "rssbox" + metrics_path: "/metrics" + scheme: "http" + static_configs: + - targets: + - "web:80" + # - "host.docker.internal:3000"