cabin 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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