Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dashboards With Dashing

Jon McCartie
September 25, 2014

Dashboards With Dashing

Lightning Talk From Rocky Mountain Ruby 2014
Video: http://youtu.be/WBvB0PZcxLg

Longer version given at OKC Ruby November 2014

Jon McCartie

September 25, 2014
Tweet

More Decks by Jon McCartie

Other Decks in Technology

Transcript

  1. Point your browser at localhost:3030 and have fun! Install $

    gem install dashing $ dashing new sweet_dashboard_project $ bundle $ dashing start
  2. Number Widget <h1 class="title" data-bind="title"></h1> <h2 class="value" data-bind="current | shortenedNumber

    | prepend prefix"></h2> <p class="change-rate"> <i data-bind-class="arrow"></i><span data-bind="difference"></span> </p> <p class="more-info" data-bind="moreinfo | raw"></p> <p class="updated-at" data-bind="updatedAtMessage"></p> number.html
  3. Number Widget class Dashing.Number extends Dashing.Widget ready: -> # This

    is fired when the widget is done being rendered onData: (data) -> # Fired when you receive data # You could do something like have the widget flash each time data comes in by doing: # $(@node).fadeOut().fadeIn() # Any attribute that has the 'Dashing.AnimatedValue' will cause the number to animate when it changes. @accessor 'current', Dashing.AnimatedValue # Calculates the % difference between current & last values. @accessor 'difference', -> if @get('last') last = parseInt(@get('last')) current = parseInt(@get('current')) if last != 0 diff = Math.abs(Math.round((current - last) / last * 100)) "#{diff}%" else "" # Picks the direction of the arrow based on whether the current value is higher or lower than the last @accessor 'arrow', -> if @get('last') if parseInt(@get('current')) > parseInt(@get('last')) then 'icon-arrow-up' else 'icon-arrow-down' number.coffee
  4. Get That Data SCHEDULER.every '1m', :first_in => 0 do |job|

    send_event('karma', { current: rand(1000) }) end sample_job.rb This job will run every minute, and will send a random number to ALL widgets that have data-id set to 'karma'.
  5. JavaScript Alerts class Dashing.NewRelic extends Dashing.Widget @FAILED_JOB_ALERT_THRESHOLD: 10 onData: (data)

    => super if (@get('current_failed') > Dashing.Metrics.FAILED_JOB_ALERT_THRESHOLD) $(@node).addClass('status-danger') $(@node).closest('li').css('z-index',10) else $(@node).removeClass('status-danger') $(@node).closest('li').css('z-index',2) new_relic.coffee
  6. Our Dashboard 1. Code Climate 2. Deploys 3. Desk 4.

    Github 5. New Relic 6. Pingdom 7. Rdio 8. Trello
  7. Github require 'octokit' SCHEDULER.every '60s', :first_in => 0 do |job|

    client = Octokit::Client.new(:login => "login", :password => "password") %w[churchmetrics open chop develop-dot-me].each do |repo| current = client.pull_requests("lifechurch/#{repo}").size send_event("#{repo}_pull_requests", current: current) end end
  8. Code Climate require 'httparty' projects = { my_project: "repo", }

    SCHEDULER.every ‘3m' do |job| projects.each do |name, repo| response = HTTParty.get("https://codeclimate.com/api/repos/#{repo}", body: {api_token: API_TOKEN} ) data = { current: response[:last_snapshot]["gpa"].to_s, last: (response[:previous_snapshot]["gpa"].to_s rescue "---") } send_event("codeclimate_#{name}", data ) end end
  9. Deploys user_email = `git config --global user.email`.chomp() data = {

    message: message, at: Time.now.to_i, who: user_email } redis = Redis.new redis.sadd("#{product}_deploys", data.to_json) inside our deployment script SCHEDULER.every '10s', :first_in => 0 do |job| redis = Redis.new %w[churchmetrics open chop devdotme].each do |product| data = redis.smembers("#{product}_deploys").map {|x| JSON.parse x}.sort_by { |k| k["at"] } items = data.pop(2).reverse! items.each {|i| i["at"] = relative_time_ago(Time.at(i["at"].to_i))+" ago"} items.each {|i| i["who"] = gravatar_url(i["who"])} send_event("#{product}_deploys", items: items) end end deploys_job.rb
  10. Rdio require 'rdio' SCHEDULER.every ‘20s' do |job| user_id = "blahblah"

    rdio = Rdio::BaseApi.new(“app_key", “app_secret") response = JSON.parse rdio.call("get", {"keys" => user_id, "extras" => "lastSongPlayed,lastSongPlayTime"}) if response["status"] == "ok" res = response["result"][user_id] song = res["lastSongPlayed"] data = { album: song["album"], artist: song["albumArtist"], cover: song["icon"], title: song["name"] } send_event("now_playing", data) end end
  11. New Relic api_url = 'https://api.newrelic.com/accounts/%s/applications/%s/%s.xml' action = :threshold_values apps =

    [] SCHEDULER.every ‘30s' do |job| global_metrics = [] apps.each do |app| metrics = {} url = api_url % [app[:account_id], app[:app_id], action] data = { headers: { "x-api-key" => app[:api_key]} } response = HTTParty.post(url,data).symbolize_keys! response[:threshold_values].each do |metric| metric.symbolize_keys! slug = metric[:name].downcase.gsub(/ /,'_') metrics[slug.to_sym] = metric[:formatted_metric_value] metrics[slug.to_sym] = metrics[slug.to_sym].gsub(/[^\d\.]/,'').to_i.ceil if slug == 'response_time' metrics[slug.to_sym] = metrics[slug.to_sym].gsub(/rpm/,'') if slug == 'throughput' end send_event("#{app[:name]}_product_health", {response_time: metrics[:response_time]}) send_event("#{app[:name]}_product_status", metrics) end end