cabin 0.3.8 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
1
+ require "rubygems"
2
+ require "cabin"
3
+ require "logger"
4
+
5
+ # Logging::... is something I'm implemented and experimenting with.
6
+ @logger = Cabin::Channel.new
7
+
8
+ # Metrics can be subscribed-to as well.
9
+ @logger.subscribe(Logger.new(STDOUT))
10
+
11
+ counter = @logger.metrics.counter("mycounter")
12
+ counter.incr
13
+ counter.incr
14
+ counter.incr
15
+ counter.decr
16
+
17
+ meter = @logger.metrics.meter("something", "hello-world")
18
+ meter.mark
19
+ meter.mark
20
+ meter.mark
21
+
22
+ # If nil is passed as the 'instance' then the metric class name will be
23
+ # used instead.
24
+ timer = @logger.metrics.timer("ticktock")
25
+ 5.times do
26
+ timer.time do
27
+ sleep rand * 2
28
+ end
29
+ end
30
+
31
+ 3.times do
32
+ # Another way to do timing.
33
+ clock = timer.time
34
+ sleep rand * 2
35
+ clock.stop
36
+ end
37
+
38
+ # Loop through all metrics:
39
+ @logger.metrics.each do |metric|
40
+ @logger.info(metric.inspect)
41
+ end
@@ -45,6 +45,8 @@ require "logger"
45
45
  #
46
46
  class Cabin::Channel
47
47
  include Cabin::Mixins::Logger
48
+
49
+ # All channels come with a metrics provider.
48
50
  attr_accessor :metrics
49
51
 
50
52
  # Get a channel for a given identifier. If this identifier has never been
@@ -67,6 +69,7 @@ class Cabin::Channel
67
69
  @data = {}
68
70
  @level = :info
69
71
  @metrics = Cabin::Metrics.new
72
+ @metrics.channel = self
70
73
  end # def initialize
71
74
 
72
75
  # Subscribe a new input
@@ -22,7 +22,7 @@ module Cabin
22
22
  # foo = Foo.new
23
23
  # foo.inspect == '<Foo(1) @foo=123 @bar="hello" >'
24
24
  def inspect
25
- if instance_variable_defined?(:inspectables)
25
+ if instance_variable_defined?(:@inspectables)
26
26
  ivars = @inspectables
27
27
  else
28
28
  ivars = instance_variables
@@ -0,0 +1,22 @@
1
+ require "cabin/namespace"
2
+ require "cabin/publisher"
3
+ require "cabin/inspectable"
4
+
5
+ module Cabin::Metric
6
+ include Cabin::Inspectable
7
+ include Cabin::Publisher
8
+
9
+ def instance=(instance)
10
+ @instance = instance
11
+ end # def instance=
12
+
13
+ def instance
14
+ return @instance
15
+ end # def instance
16
+
17
+ def emit
18
+ if !@channel.nil?
19
+ @channel.publish({ :metric => instance }.merge(to_hash))
20
+ end
21
+ end # def emit
22
+ end # module Cabin::Metric
@@ -4,6 +4,8 @@ require "cabin/metrics/meter"
4
4
  require "cabin/metrics/counter"
5
5
  require "cabin/metrics/timer"
6
6
  require "cabin/metrics/histogram"
7
+ require "cabin/publisher"
8
+ require "cabin/channel"
7
9
 
8
10
  # What type of metrics do we want?
9
11
  #
@@ -44,13 +46,14 @@ require "cabin/metrics/histogram"
44
46
  # and management tools.
45
47
  class Cabin::Metrics
46
48
  include Enumerable
49
+ include Cabin::Publisher
47
50
 
48
51
  # Get us a new metrics container.
49
52
  public
50
53
  def initialize
51
54
  @metrics = {}
52
55
  end # def initialize
53
-
56
+
54
57
  private
55
58
  def create(instance, name, metric_object)
56
59
  if !instance.is_a?(String)
@@ -58,11 +61,22 @@ class Cabin::Metrics
58
61
  end
59
62
 
60
63
  if name.nil?
61
- metric = instance.to_s
64
+ # If no name is given, use the class name of the metric.
65
+ # For example, if we invoke Metrics#timer("foo"), the metric
66
+ # name will be "foo/timer"
67
+ metric_name = "#{instance}/#{metric_object.class.name.split("::").last.downcase}"
62
68
  else
63
- metric = "#{instance}/#{name}"
69
+ # Otherwise, use "instance/name" as the name.
70
+ metric_name = "#{instance}/#{name}"
71
+ end
72
+
73
+ metric_object.channel = @channel
74
+ metric_object.instance = metric_name
75
+
76
+ if @channel
77
+ @channel.debug("Created metric", :instance => instance, :type => metric_object.class)
64
78
  end
65
- return @metrics[metric] = metric_object
79
+ return @metrics[metric_name] = metric_object
66
80
  end # def create
67
81
 
68
82
  # Create a new Counter metric
@@ -1,9 +1,9 @@
1
1
  require "cabin/namespace"
2
- require "cabin/inspectable"
2
+ require "cabin/metric"
3
3
  require "thread"
4
4
 
5
5
  class Cabin::Metrics::Counter
6
- include Cabin::Inspectable
6
+ include Cabin::Metric
7
7
 
8
8
  # A new Counter.
9
9
  #
@@ -18,11 +18,13 @@ class Cabin::Metrics::Counter
18
18
  # increment this counter
19
19
  def incr
20
20
  @lock.synchronize { @value += 1 }
21
+ emit
21
22
  end # def incr
22
23
 
23
24
  # decrement this counter
24
25
  def decr
25
26
  @lock.synchronize { @value -= 1 }
27
+ emit
26
28
  end # def decr
27
29
 
28
30
  # Get the value of this metric.
@@ -1,8 +1,8 @@
1
1
  require "cabin/namespace"
2
- require "cabin/inspectable"
2
+ require "cabin/metric"
3
3
 
4
4
  class Cabin::Metrics::Gauge
5
- include Cabin::Inspectable
5
+ include Cabin::Metric
6
6
 
7
7
  # A new Gauge. The block given will be called every time the metric is read.
8
8
  public
@@ -1,9 +1,9 @@
1
1
  require "cabin/namespace"
2
- require "cabin/inspectable"
2
+ require "cabin/metric"
3
3
  require "thread"
4
4
 
5
5
  class Cabin::Metrics::Histogram
6
- include Cabin::Inspectable
6
+ include Cabin::Metric
7
7
 
8
8
  # A new Histogram.
9
9
  public
@@ -20,8 +20,8 @@ class Cabin::Metrics::Histogram
20
20
  #
21
21
  # Sliding values of all of these?
22
22
  @total = 0
23
- @min = 0
24
- @max = 0
23
+ @min = nil
24
+ @max = nil
25
25
  @count = 0
26
26
  @mean = 0.0
27
27
  end # def initialize
@@ -31,12 +31,17 @@ class Cabin::Metrics::Histogram
31
31
  @lock.synchronize do
32
32
  @count += 1
33
33
  @total += value
34
- @min = value if value < @min
35
- @max = value if value > @max
34
+ if @min.nil? or value < @min
35
+ @min = value
36
+ end
37
+ if @max.nil? or value > @max
38
+ @max = value
39
+ end
36
40
  @mean = @total / @count
37
41
  # TODO(sissel): median
38
42
  # TODO(sissel): percentiles
39
43
  end
44
+ emit
40
45
  end # def record
41
46
 
42
47
  # This is a very poor way to access the metric data.
@@ -1,9 +1,9 @@
1
1
  require "cabin/namespace"
2
- require "cabin/inspectable"
2
+ require "cabin/metric"
3
3
  require "thread"
4
4
 
5
5
  class Cabin::Metrics::Meter
6
- include Cabin::Inspectable
6
+ include Cabin::Metric
7
7
 
8
8
  # A new Meter
9
9
  #
@@ -21,6 +21,7 @@ class Cabin::Metrics::Meter
21
21
  @value += 1
22
22
  # TODO(sissel): Keep some moving averages?
23
23
  end
24
+ emit
24
25
  end # def mark
25
26
 
26
27
  # Get the value of this metric.
@@ -1,7 +1,5 @@
1
1
  require "cabin/namespace"
2
- require "cabin/inspectable"
3
2
  require "cabin/metrics/histogram"
4
- require "thread"
5
3
 
6
4
  class Cabin::Metrics::Timer < Cabin::Metrics::Histogram
7
5
  # Start timing something.
@@ -3,5 +3,6 @@ module Cabin
3
3
  module EM; end
4
4
  end
5
5
  module Mixins; end
6
+ module Emitters; end
6
7
  class Metrics; end
7
8
  end
@@ -0,0 +1,20 @@
1
+ require "cabin/namespace"
2
+
3
+ # This mixin allows you to easily give channel and publish features
4
+ # to a class.
5
+ module Cabin::Publisher
6
+ # Set the channel
7
+ def channel=(channel)
8
+ @channel = channel
9
+ end # def channel=
10
+
11
+ # Get the channel
12
+ def channel
13
+ return @channel
14
+ end # def channel
15
+
16
+ # Publish to the channel
17
+ def publish(object)
18
+ @channel << object
19
+ end # def publish
20
+ end # module Cabin::Publisher
@@ -44,6 +44,13 @@ describe Cabin::Metrics do
44
44
 
45
45
  test "meter time-based averages" # TODO(sissel): implement
46
46
 
47
+ test "timer first-run has max == min" do
48
+ timer = @metrics.timer(self)
49
+ timer.time { true }
50
+ assert_equal(timer.to_hash[:min], timer.to_hash[:max],
51
+ "With a single event, min and max must be equal")
52
+ end
53
+
47
54
  test "timer counter" do
48
55
  timer = @metrics.timer(self)
49
56
  30.times do |i|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cabin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.4.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-10 00:00:00.000000000 Z
12
+ date: 2012-02-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
16
- requirement: &15416680 !ruby/object:Gem::Requirement
16
+ requirement: &5144860 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *15416680
24
+ version_requirements: *5144860
25
25
  description: This is an experiment to try and make logging more flexible and more
26
26
  consumable. Plain text logs are bullshit, let's emit structured and contextual logs.
27
27
  Metrics, too!
@@ -34,6 +34,7 @@ extra_rdoc_files: []
34
34
  files:
35
35
  - lib/cabin/inspectable.rb
36
36
  - lib/cabin/context.rb
37
+ - lib/cabin/metric.rb
37
38
  - lib/cabin/timer.rb
38
39
  - lib/cabin/outputs/stdlib-logger.rb
39
40
  - lib/cabin/outputs/em/stdlib-logger.rb
@@ -47,11 +48,13 @@ files:
47
48
  - lib/cabin/metrics/meter.rb
48
49
  - lib/cabin/metrics/counter.rb
49
50
  - lib/cabin/metrics.rb
51
+ - lib/cabin/publisher.rb
50
52
  - lib/cabin/channel.rb
51
53
  - lib/cabin.rb
52
54
  - examples/fibonacci-timing.rb
53
55
  - examples/sinatra-logging.rb
54
56
  - examples/sample.rb
57
+ - examples/metrics.rb
55
58
  - test/test_logging.rb
56
59
  - test/minitest-patch.rb
57
60
  - test/test_metrics.rb