cabin 0.1.8 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,85 @@
1
+ require "cabin/namespace"
2
+ require "cabin/metrics/gauge"
3
+ require "cabin/metrics/meter"
4
+ require "cabin/metrics/counter"
5
+ require "cabin/metrics/timer"
6
+
7
+
8
+
9
+ # What type of metrics do we want?
10
+ #
11
+ # What metrics should come by default?
12
+ # Per-call/transaction/request metrics like:
13
+ # - hit (count++ type metrics)
14
+ # - latencies/timings
15
+ #
16
+ # Per app or generally long-lifetime metrics like:
17
+ # - "uptime"
18
+ # - cpu usage
19
+ # - memory usage
20
+ # - count of active/in-flight actions/requests/calls/transactions
21
+ # - peer metrics (number of cluster members, etc)
22
+ # ------------------------------------------------------------------
23
+ # https://github.com/codahale/metrics/tree/master/metrics-core/src/main/java/com/yammer/metrics/core
24
+ # Reading what Coda Hale's "Metrics" stuff has, here's my summary:
25
+ #
26
+ # gauges (callback to return a number)
27
+ # counters (.inc and .dec methods)
28
+ # meters (.mark to track each 'hit')
29
+ # Also exposes 1, 5, 15 minute moving averages
30
+ # histograms: (.update(value) to record a new value)
31
+ # like meter, but takes values more than simply '1'
32
+ # as a result, exposes percentiles, median, etc.
33
+ # timers
34
+ # combination of meter + histogram
35
+ # meter for invocations, histogram for duration
36
+ #
37
+ # With the exception of gauges, all the other metrics are all active/pushed.
38
+ # Gauges take callbacks, so their values are pulled, not pushed. The active
39
+ # metrics can be represented as events since they the update occurs at the
40
+ # time of the change.
41
+ #
42
+ # These active/push metrics can therefore be considered events.
43
+ #
44
+ # All metrics (active/passive) can be queried for 'current state', too,
45
+ # making this suitable for serving to interested parties like monitoring
46
+ # and management tools.
47
+ class Cabin::Metrics
48
+ include Enumerable
49
+
50
+ public
51
+ def initialize
52
+ @metrics = {}
53
+ end # def initialize
54
+
55
+ private
56
+ def create(instance, name, metric_object)
57
+ return @metrics[[instance, name]] = metric_object
58
+ end # def create
59
+
60
+ public
61
+ def counter(instance, name=nil)
62
+ return create(instance, name, Cabin::Metrics::Counter.new)
63
+ end # def counter
64
+
65
+ public
66
+ def meter(instance, name=nil)
67
+ return create(instance, name, Cabin::Metrics::Meter.new)
68
+ end # def meter
69
+
70
+ #public
71
+ #def histogram(instance, name)
72
+ #return create(instance, name, Cabin::Metrics::Histogram.new)
73
+ #end # def histogram
74
+
75
+ public
76
+ def timer(instance, name=nil)
77
+ return create(instance, name, Cabin::Metrics::Timer.new)
78
+ end # def timer
79
+
80
+ # iterate over each metric. yields identifer, metric
81
+ def each(&block)
82
+ # delegate to the @metrics hash until we need something fancier
83
+ @metrics.each(&block)
84
+ end # def each
85
+ end # class Cabin::Metrics
@@ -0,0 +1,29 @@
1
+ require "cabin/namespace"
2
+ require "thread"
3
+
4
+ class Cabin::Metrics::Counter
5
+ # A new Counter.
6
+ #
7
+ # Counters can be incremented and decremented only by 1 at a time..
8
+ public
9
+ def initialize
10
+ @value = 0
11
+ @lock = Mutex.new
12
+ end # def initialize
13
+
14
+ # increment this counter
15
+ def incr
16
+ @lock.synchronize { @value += 1 }
17
+ end # def incr
18
+
19
+ # decrement this counter
20
+ def decr
21
+ @lock.synchronize { @value -= 1 }
22
+ end # def decr
23
+
24
+ # Get the value of this metric.
25
+ public
26
+ def value
27
+ return @lock.synchronize { @value }
28
+ end # def value
29
+ end # class Cabin::Metrics::Counter
@@ -0,0 +1,15 @@
1
+ require "cabin/namespace"
2
+
3
+ class Cabin::Metrics::Gauge
4
+ # A new Gauge. The block given will be called every time the metric is read.
5
+ public
6
+ def initialize(&block)
7
+ @block = block
8
+ end # def initialize
9
+
10
+ # Get the value of this metric.
11
+ public
12
+ def value
13
+ return @block.call
14
+ end # def value
15
+ end # class Cabin::Metrics::Gauge
@@ -0,0 +1,27 @@
1
+ require "cabin/namespace"
2
+ require "thread"
3
+
4
+ class Cabin::Metrics::Meter
5
+ # A new Meter
6
+ #
7
+ # Counters can be incremented and decremented only by 1 at a time..
8
+ public
9
+ def initialize
10
+ @value = 0
11
+ @lock = Mutex.new
12
+ end # def initialize
13
+
14
+ # Mark an event
15
+ def mark
16
+ @lock.synchronize do
17
+ @value += 1
18
+ # TODO(sissel): Keep some moving averages?
19
+ end
20
+ end # def mark
21
+
22
+ # Get the value of this metric.
23
+ public
24
+ def value
25
+ return @lock.synchronize { @value }
26
+ end # def value
27
+ end # class Cabin::Metrics::Counter
@@ -0,0 +1,63 @@
1
+ require "cabin/namespace"
2
+ require "thread"
3
+
4
+ class Cabin::Metrics::Timer
5
+ # A new Timer metric
6
+ #
7
+ # Timers behave like a combination of Meter and Histogram. Every Timer
8
+ # invocation is metered and the duration of the timer is put into the
9
+ # Histogram.
10
+ public
11
+ def initialize
12
+ @invocations = 0
13
+ @lock = Mutex.new
14
+ end # def initialize
15
+
16
+ # Start timing something.
17
+ #
18
+ # If no block is given
19
+ # If a block is given, the execution of that block is timed.
20
+ #
21
+ public
22
+ def time(&block)
23
+ return time_block(&block) if block_given?
24
+
25
+ # Return an object we can .stop
26
+ return TimerContext.new(method(:record))
27
+ end # def time
28
+
29
+ private
30
+ def time_block(&block)
31
+ start = Time.now
32
+ block.call
33
+ record(Time.now - start)
34
+ end # def time_block
35
+
36
+ public
37
+ def record(duration)
38
+ @lock.synchronize do
39
+ @invocations += 1
40
+ # TODO(sissel): histogram the duration
41
+ end
42
+ end # def record
43
+
44
+ # Get the number of times this timer has been used
45
+ public
46
+ def count
47
+ return @lock.synchronize { @invocations }
48
+ end # def value
49
+
50
+ class TimerContext
51
+ public
52
+ def initialize(&stop_callback)
53
+ @start = Time.now
54
+ @callback = stop_callback
55
+ end
56
+
57
+ public
58
+ def stop
59
+ duration = Time.now - @start
60
+ @callback.call(duration)
61
+ end # def stop
62
+ end # class TimerContext
63
+ end # class Cabin::Metrics::Counter
@@ -0,0 +1,24 @@
1
+ require "cabin/namespace"
2
+
3
+ # ALL CAPS MEANS SERIOUS BUSINESS
4
+ module Cabin::Mixins::CAPSLOCK
5
+ def log(level, message, data={})
6
+ if message.is_a?(Hash)
7
+ data.merge!(message)
8
+ else
9
+ data[:message] = message
10
+ end
11
+
12
+ # CAPITALIZE ALL THE STRINGS
13
+ data.each do |key, value|
14
+ value.upcase! if value.respond_to?(:upcase!)
15
+ end
16
+
17
+ # Add extra debugging bits (file, line, method) if level is debug.
18
+ debugharder(caller.collect { |c| c.upcase }, data) if @level == :debug
19
+
20
+ data[:level] = level.upcase
21
+
22
+ publish(data)
23
+ end # def log
24
+ end # module Cabin::Mixins::CAPSLOCK
@@ -13,7 +13,11 @@ module Cabin::Mixins::Logger
13
13
  }
14
14
 
15
15
  def level=(value)
16
- @level = value.to_sym
16
+ if value.respond_to?(:downcase)
17
+ @level = value.downcase.to_sym
18
+ else
19
+ @level = value.to_sym
20
+ end
17
21
  end # def level
18
22
 
19
23
  # Define the usual log methods: info, fatal, etc.
@@ -3,4 +3,5 @@ module Cabin
3
3
  module EM; end
4
4
  end
5
5
  module Mixins; end
6
+ class Metrics; end
6
7
  end
@@ -8,15 +8,17 @@ class Cabin::Outputs::StdlibLogger
8
8
  public
9
9
  def initialize(logger)
10
10
  @logger = logger
11
+ @logger.level = logger.class::DEBUG
11
12
  end # def initialize
12
13
 
13
14
  # Receive an event
14
15
  public
15
16
  def <<(data)
16
- method = data[:level] || "info"
17
+ method = data[:level].downcase.to_sym || :info
17
18
 
18
19
  message = "#{data[:message]} #{data.to_json}"
19
20
 
21
+ #p [@logger.level, logger.class::DEBUG]
20
22
  # This will call @logger.info(data) or something similar.
21
23
  @logger.send(method, message)
22
24
  end # def <<
@@ -0,0 +1,13 @@
1
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require "rubygems"
4
+ require "minitest/autorun"
5
+ require "simplecov"
6
+
7
+ SimpleCov.start
8
+
9
+ dir = File.dirname(File.expand_path(__FILE__))
10
+ Dir.glob(File.join(dir, "**", "test_*.rb")).each do |path|
11
+ puts "Loading tests from #{path}"
12
+ require path
13
+ end
@@ -0,0 +1,54 @@
1
+ $: << File.dirname(__FILE__)
2
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
3
+
4
+ require "rubygems"
5
+ require "minitest-patch"
6
+ require "cabin"
7
+ require "cabin/metrics"
8
+ require "minitest/autorun" if __FILE__ == $0
9
+
10
+ describe Cabin::Metrics do
11
+ before do
12
+ @metrics = Cabin::Metrics.new
13
+ end
14
+
15
+ #test "gauge" do
16
+ #gauge = @metrics.gauge(self) { 3 }
17
+ #assert_equal(3, gauge.value)
18
+ ## metrics.first == [identifier, Gauge]
19
+ #assert_equal(3, @metrics.first.last.value)
20
+ #end
21
+
22
+ test "counter" do
23
+ counter = @metrics.counter(self)
24
+ 0.upto(30) do |i|
25
+ assert_equal(i, counter.value)
26
+ counter.incr
27
+ end
28
+ 31.downto(0) do |i|
29
+ assert_equal(i, counter.value)
30
+ counter.decr
31
+ end
32
+ end
33
+
34
+ test "meter counter" do
35
+ meter = @metrics.meter(self)
36
+ 30.times do |i|
37
+ assert_equal(i, meter.value)
38
+ meter.mark
39
+ end
40
+ end
41
+
42
+ test "meter time-based averages" # TODO(sissel): implement
43
+
44
+ test "timer counter" do
45
+ timer = @metrics.timer(self)
46
+ 30.times do |i|
47
+ assert_equal(i, timer.count)
48
+ timer.time { true }
49
+ end
50
+ end
51
+
52
+ test "timer histogram" # TODO(sissel): implement
53
+ test "histogram" # TODO(sissel): implement
54
+ end # describe Cabin::Channel do
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cabin
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 21
5
+ prerelease:
5
6
  segments:
6
7
  - 0
8
+ - 2
7
9
  - 1
8
- - 8
9
- version: 0.1.8
10
+ version: 0.2.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Jordan Sissel
@@ -14,7 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2012-01-13 00:00:00 -08:00
18
+ date: 2012-02-05 00:00:00 -08:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
@@ -25,12 +26,13 @@ dependencies:
25
26
  requirements:
26
27
  - - ">="
27
28
  - !ruby/object:Gem::Version
29
+ hash: 3
28
30
  segments:
29
31
  - 0
30
32
  version: "0"
31
33
  type: :runtime
32
34
  version_requirements: *id001
33
- description: This is an experiment to try and make logging more flexible and more consumable. Plain text logs are bullshit, let's emit structured and contextual logs.
35
+ description: This is an experiment to try and make logging more flexible and more consumable. Plain text logs are bullshit, let's emit structured and contextual logs. Metrics, too!
34
36
  email:
35
37
  - jls@semicomplete.com
36
38
  executables:
@@ -40,21 +42,28 @@ extensions: []
40
42
  extra_rdoc_files: []
41
43
 
42
44
  files:
43
- - lib/cabin.rb
44
- - lib/cabin/outputs/em/stdlib-logger.rb
45
- - lib/cabin/outputs/stdlib-logger.rb
46
- - lib/cabin/channel.rb
47
- - lib/cabin/namespace.rb
48
- - lib/cabin/timer.rb
49
45
  - lib/cabin/context.rb
50
- - lib/cabin/mixins/dragons.rb
51
- - lib/cabin/mixins/metrics.rb
46
+ - lib/cabin/timer.rb
47
+ - lib/cabin/outputs/stdlib-logger.rb
48
+ - lib/cabin/outputs/em/stdlib-logger.rb
52
49
  - lib/cabin/mixins/logger.rb
50
+ - lib/cabin/mixins/dragons.rb
51
+ - lib/cabin/mixins/CAPSLOCK.rb
52
+ - lib/cabin/namespace.rb
53
+ - lib/cabin/metrics/gauge.rb
54
+ - lib/cabin/metrics/timer.rb
55
+ - lib/cabin/metrics/meter.rb
56
+ - lib/cabin/metrics/counter.rb
57
+ - lib/cabin/metrics.rb
58
+ - lib/cabin/channel.rb
59
+ - lib/cabin.rb
53
60
  - examples/fibonacci-timing.rb
54
61
  - examples/sinatra-logging.rb
55
62
  - examples/sample.rb
56
- - test/minitest-patch.rb
57
63
  - test/test_logging.rb
64
+ - test/minitest-patch.rb
65
+ - test/test_metrics.rb
66
+ - test/all.rb
58
67
  - LICENSE
59
68
  - CHANGELIST
60
69
  - bin/rubygems-cabin-test
@@ -73,6 +82,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
73
82
  requirements:
74
83
  - - ">="
75
84
  - !ruby/object:Gem::Version
85
+ hash: 3
76
86
  segments:
77
87
  - 0
78
88
  version: "0"
@@ -81,13 +91,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
91
  requirements:
82
92
  - - ">="
83
93
  - !ruby/object:Gem::Version
94
+ hash: 3
84
95
  segments:
85
96
  - 0
86
97
  version: "0"
87
98
  requirements: []
88
99
 
89
100
  rubyforge_project:
90
- rubygems_version: 1.3.7
101
+ rubygems_version: 1.6.2
91
102
  signing_key:
92
103
  specification_version: 3
93
104
  summary: Experiments in structured and contextual logging
@@ -1,16 +0,0 @@
1
- require "cabin/namespace"
2
-
3
- # What kind of metrics do we want?
4
- # Per-call/transaction/request metrics like:
5
- # - hit (count++ type metrics)
6
- # - latencies/timings
7
- #
8
- # Per app or generally long-lifetime metrics like:
9
- # - "uptime"
10
- # - cpu usage
11
- # - memory usage
12
- # - count of active/in-flight actions/requests/calls/transactions
13
- # - peer metrics (number of cluster members, etc)
14
- module Cabin::Mixins::Metrics
15
-
16
- end # module Cabin::Mixins::Metrics