cabin 0.4.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -51,6 +51,7 @@ class Cabin::Metrics
51
51
  # Get us a new metrics container.
52
52
  public
53
53
  def initialize
54
+ @metrics_lock = Mutex.new
54
55
  @metrics = {}
55
56
  end # def initialize
56
57
 
@@ -76,7 +77,9 @@ class Cabin::Metrics
76
77
  if @channel
77
78
  @channel.debug("Created metric", :instance => instance, :type => metric_object.class)
78
79
  end
79
- return @metrics[metric_name] = metric_object
80
+ return @metrics_lock.synchronize do
81
+ @metrics[metric_name] = metric_object
82
+ end
80
83
  end # def create
81
84
 
82
85
  # Create a new Counter metric
@@ -114,6 +117,8 @@ class Cabin::Metrics
114
117
  # iterate over each metric. yields identifer, metric
115
118
  def each(&block)
116
119
  # delegate to the @metrics hash until we need something fancier
117
- @metrics.each(&block)
120
+ @metrics_lock.synchronize do
121
+ @metrics.each(&block)
122
+ end
118
123
  end # def each
119
124
  end # class Cabin::Metrics
@@ -1,11 +1,8 @@
1
1
  require "cabin"
2
- require "json"
3
2
  require "eventmachine"
4
3
 
5
4
  # Wrap Ruby stdlib's logger and make it EventMachine friendly. This
6
- # allows you to output to a normal ruby logger with Cabin. Since
7
- # Ruby's Logger has a love for strings alone, this wrapper will
8
- # convert the data/event to json before sending it to Logger.
5
+ # allows you to output to a normal ruby logger with Cabin.
9
6
  class Cabin::Outputs::EM::StdlibLogger
10
7
  public
11
8
  def initialize(logger)
@@ -33,7 +30,7 @@ class Cabin::Outputs::EM::StdlibLogger
33
30
  def <<(data)
34
31
  line = Hash.new
35
32
  line[:method] = data[:level] || "info"
36
- line[:message] = "#{data[:message]} #{data.to_json}"
33
+ line[:message] = "#{data[:message]} #{data.inspect}"
37
34
  if EM::reactor_running?
38
35
  # Push line onto queue for later sending
39
36
  @logger_queue.push(line)
@@ -1,17 +1,16 @@
1
1
  require "cabin"
2
- require "json"
3
2
  require "thread"
4
3
 
5
4
  # Wrap IO objects with a reasonable log output.
6
5
  #
7
6
  # If the IO is *not* attached to a tty (io#tty? returns false), then
8
- # the event will be written in json format terminated by a newline:
7
+ # the event will be written in ruby inspect format terminated by a newline:
9
8
  #
10
- # { "timestamp": ..., "message": message, ... }
9
+ # { "timestamp" => ..., "message" => message, ... }
11
10
  #
12
11
  # If the IO is attached to a TTY, there are # human-friendly in this format:
13
12
  #
14
- # message {json data}
13
+ # message { event data }
15
14
  #
16
15
  # Additionally, colorized output may be available. If the event has :level,
17
16
  # :color, or :bold. Any of the Cabin::Mixins::Logger methods (info, error, etc)
@@ -50,7 +49,7 @@ class Cabin::Outputs::IO
50
49
  def <<(event)
51
50
  @lock.synchronize do
52
51
  if !@io.tty?
53
- @io.puts(event.to_json)
52
+ @io.puts(event.inspect)
54
53
  else
55
54
  tty_write(event)
56
55
  end
@@ -77,7 +76,7 @@ class Cabin::Outputs::IO
77
76
  if data.empty?
78
77
  message = [event[:message]]
79
78
  else
80
- message = ["#{event[:message]} #{data.to_json}"]
79
+ message = ["#{event[:message]} #{data.inspect}"]
81
80
  end
82
81
  message.unshift("\e[#{CODEMAP[color.to_sym]}m") if !color.nil?
83
82
  message.unshift("\e[#{CODEMAP[bold]}m") if !bold.nil?
@@ -1,9 +1,9 @@
1
1
  require "cabin"
2
- require "json"
3
2
 
4
3
  # Wrap Ruby stdlib's logger. This allows you to output to a normal ruby logger
5
4
  # with Cabin. Since Ruby's Logger has a love for strings alone, this
6
- # wrapper will convert the data/event to json before sending it to Logger.
5
+ # wrapper will convert the data/event to ruby inspect format before sending it
6
+ # to Logger.
7
7
  class Cabin::Outputs::StdlibLogger
8
8
  public
9
9
  def initialize(logger)
@@ -24,7 +24,7 @@ class Cabin::Outputs::StdlibLogger
24
24
  # delete things from the 'data' portion that's not really data.
25
25
  data.delete(:message)
26
26
  data.delete(:timestamp)
27
- message = "#{event[:message]} #{data.to_json}"
27
+ message = "#{event[:message]} #{data.inspect}"
28
28
 
29
29
  #p [@logger.level, logger.class::DEBUG]
30
30
  # This will call @logger.info(data) or something similar.
@@ -0,0 +1,96 @@
1
+ require 'cabin'
2
+ require 'ffi-rzmq'
3
+
4
+ # Output to a zeromq socket.
5
+ class Cabin::Outputs::ZeroMQ
6
+ DEFAULTS = {
7
+ :topology => "pushpull",
8
+ :hwm => 0, # zeromq default: no limit
9
+ :linger => -1, # zeromq default: wait until all messages are sent.
10
+ :topic => ""
11
+ }
12
+
13
+ CONTEXT = ZMQ::Context.new
14
+
15
+ attr_reader :socket, :topology, :topic
16
+
17
+ # Create a new ZeroMQ output.
18
+ #
19
+ # arguments:
20
+ # addresses A list of addresses to connect to. These are round-robined by zeromq.
21
+ #
22
+ # :topology Either 'pushpull' or 'pubsub'. Specifies which zeromq socket type to use. Default pushpull.
23
+ # :hwm Specifies the High Water Mark for the socket. Default 0, which means there is none.
24
+ # :linger Specifies the linger time in milliseconds for the socket. Default -1, meaning wait forever for the socket to close.
25
+ # :topic Specifies the topic for a pubsub topology. This can be a string or a proc with the event as the only argument.
26
+ def initialize(addresses, options={})
27
+ options = DEFAULTS.merge(options)
28
+
29
+ @topology = options[:topology].to_s
30
+ case @topology
31
+ when "pushpull"
32
+ socket_type = ZMQ::PUSH
33
+ when "pubsub"
34
+ socket_type = ZMQ::PUB
35
+ end
36
+
37
+ @topic = options[:topic]
38
+ @socket = CONTEXT.socket(socket_type)
39
+
40
+ Array(addresses).each do |address|
41
+ error_check @socket.connect(address), "connecting to #{address}"
42
+ end
43
+
44
+ error_check @socket.setsockopt(ZMQ::LINGER, options[:linger]), "while setting ZMQ::LINGER to #{options[:linger]}"
45
+ error_check @socket.setsockopt(ZMQ::HWM, options[:hwm]), "while setting ZMQ::HWM to #{options[:hwm]}"
46
+
47
+ #TODO use cabin's teardown when it exists
48
+ at_exit do
49
+ teardown
50
+ end
51
+
52
+ #define_finalizer
53
+ end
54
+
55
+ def linger
56
+ array = []
57
+ error_check @socket.getsockopt(ZMQ::LINGER, array), "while getting ZMQ::LINGER"
58
+ array.first
59
+ end
60
+
61
+ def hwm
62
+ array = []
63
+ error_check @socket.getsockopt(ZMQ::HWM, array), "while getting ZMQ::HWM"
64
+ array.first
65
+ end
66
+
67
+ def <<(event)
68
+ if @socket.name == "PUB"
69
+ topic = @topic.is_a?(Proc) ? @topic.call(event) : @topic
70
+ error_check @socket.send_string(topic, ZMQ::SNDMORE), "in topic send_string"
71
+ end
72
+ error_check @socket.send_string(event.inspect), "in send_string"
73
+ end
74
+
75
+ def teardown
76
+ @socket.close if @socket
77
+ end
78
+
79
+ private
80
+ def error_check(rc, doing)
81
+ unless ZMQ::Util.resultcode_ok?(rc)
82
+ raise "ZeroMQ Error while #{doing}"
83
+ end
84
+ end
85
+
86
+ # This causes the following message on exit:
87
+ # File exists (epoll.cpp:69)
88
+ # [1] 26175 abort bundle exec irb
89
+ # def define_finalizer
90
+ # ObjectSpace.define_finalizer(self, self.class.finalize(@socket))
91
+ # end
92
+
93
+ # def self.finalize(socket)
94
+ # Proc.new { puts "finalizing"; socket.close unless socket.nil?; puts "done" }
95
+ # end
96
+ end
@@ -9,5 +9,9 @@ SimpleCov.start
9
9
  dir = File.dirname(File.expand_path(__FILE__))
10
10
  Dir.glob(File.join(dir, "**", "test_*.rb")).each do |path|
11
11
  puts "Loading tests from #{path}"
12
+ if path =~ /test_zeromq/
13
+ puts "Skipping zeromq tests because they force ruby to exit if libzmq is not found"
14
+ next
15
+ end
12
16
  require path
13
17
  end
@@ -0,0 +1,99 @@
1
+ $: << File.dirname(__FILE__)
2
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
3
+
4
+ require "rubygems"
5
+ require "minitest-patch"
6
+ require "cabin/outputs/zeromq"
7
+ require "minitest/autorun" if __FILE__ == $0
8
+
9
+ describe Cabin::Outputs::ZeroMQ do
10
+
11
+ def error_check(rc, doing)
12
+ unless ZMQ::Util.resultcode_ok?(rc)
13
+ raise "ZeroMQ Error while #{doing}"
14
+ end
15
+ end
16
+
17
+ NonBlockingFlag = (ZMQ::LibZMQ.version2? ? ZMQ::NOBLOCK : ZMQ::DONTWAIT) unless defined?(NonBlockingFlag)
18
+ def receive(socket)
19
+ received = ""
20
+ error_check socket.recv_string(received, NonBlockingFlag), "receiving"
21
+ received
22
+ end
23
+
24
+ before do
25
+ @logger = Cabin::Channel.new
26
+ @address = "inproc://zeromq-output"
27
+ @pull = Cabin::Outputs::ZeroMQ::CONTEXT.socket(ZMQ::PULL)
28
+ @sub = Cabin::Outputs::ZeroMQ::CONTEXT.socket(ZMQ::SUB)
29
+ end
30
+
31
+ after do
32
+ @pull.close
33
+ @sub.close
34
+ @output.teardown if @output
35
+ end
36
+
37
+ test 'push messages' do
38
+ @pull.bind(@address); sleep 0.1 # sleeps are necessary for inproc transport
39
+ @output = Cabin::Outputs::ZeroMQ.new(@address)
40
+ @logger.subscribe(@output)
41
+ @logger.info("hello")
42
+ @logger.info("hello2")
43
+ assert_equal "hello", JSON.parse(receive(@pull))['message']
44
+ assert_equal "hello2", JSON.parse(receive(@pull))['message']
45
+ end
46
+
47
+ test "pub messages" do
48
+ @sub.bind(@address); sleep 0.1
49
+ error_check @sub.setsockopt(ZMQ::SUBSCRIBE, ""), "subscribing"
50
+ @output = Cabin::Outputs::ZeroMQ.new(@address, :topology => "pubsub")
51
+ @logger.subscribe(@output)
52
+ @logger.info("hi")
53
+ assert_equal "", receive(@sub)
54
+ assert_equal "hi", JSON.parse(receive(@sub))['message']
55
+ end
56
+
57
+ test "pub messages on a topic" do
58
+ @sub.bind(@address); sleep 0.1
59
+ error_check @sub.setsockopt(ZMQ::SUBSCRIBE, "topic"), "subscribing"
60
+ @output = Cabin::Outputs::ZeroMQ.new(@address, :topology => "pubsub", :topic => "topic")
61
+ @logger.subscribe(@output)
62
+ @logger.info("hi")
63
+ assert_equal "topic", receive(@sub)
64
+ assert_equal "hi", JSON.parse(receive(@sub))['message']
65
+ end
66
+
67
+ test "topic proc" do
68
+ @sub.bind(@address); sleep 0.1
69
+ error_check @sub.setsockopt(ZMQ::SUBSCRIBE, "topic2"), "subscribing"
70
+ @output = Cabin::Outputs::ZeroMQ.new(@address, :topology => "pubsub", :topic => Proc.new { |event| event[:message] })
71
+ @logger.subscribe(@output)
72
+ @logger.info("topic1")
73
+ @logger.info("topic2")
74
+ assert_equal "topic2", receive(@sub)
75
+ assert_equal "topic2", JSON.parse(receive(@sub))['message']
76
+ end
77
+
78
+ test "multiple addresses" do
79
+ @pull.bind(@address); sleep 0.1
80
+ @pull2 = Cabin::Outputs::ZeroMQ::CONTEXT.socket(ZMQ::PULL)
81
+ @pull2.bind(@address.succ); sleep 0.1
82
+
83
+ @output = Cabin::Outputs::ZeroMQ.new([@address, @address.succ])
84
+ @logger.subscribe(@output)
85
+ @logger.info("yo")
86
+ @logger.info("yo")
87
+
88
+ assert_equal "yo", JSON.parse(receive(@pull))['message']
89
+ assert_equal "yo", JSON.parse(receive(@pull2))['message']
90
+ end
91
+
92
+ test "options" do
93
+ @pull.bind(@address); sleep 0.1
94
+ @output = Cabin::Outputs::ZeroMQ.new(@address, :hwm => 10, :linger => 100)
95
+
96
+ assert_equal 10, @output.hwm
97
+ assert_equal 100, @output.linger
98
+ end
99
+ end
metadata CHANGED
@@ -1,35 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cabin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
5
- prerelease:
4
+ prerelease:
5
+ version: 0.5.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jordan Sissel
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-23 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: json
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: '0'
30
- description: This is an experiment to try and make logging more flexible and more
31
- consumable. Plain text logs are bullshit, let's emit structured and contextual logs.
32
- Metrics, too!
12
+ date: 2012-11-09 00:00:00.000000000 Z
13
+ dependencies: []
14
+ 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!
33
15
  email:
34
16
  - jls@semicomplete.com
35
17
  executables:
@@ -37,65 +19,101 @@ executables:
37
19
  extensions: []
38
20
  extra_rdoc_files: []
39
21
  files:
40
- - lib/cabin/metrics/histogram.rb
41
- - lib/cabin/metrics/timer.rb
42
- - lib/cabin/metrics/gauge.rb
43
- - lib/cabin/metrics/meter.rb
44
- - lib/cabin/metrics/counter.rb
45
- - lib/cabin/publisher.rb
46
- - lib/cabin/channel.rb
47
- - lib/cabin/mixins/logger.rb
48
- - lib/cabin/mixins/colors.rb
49
- - lib/cabin/mixins/dragons.rb
50
- - lib/cabin/mixins/CAPSLOCK.rb
51
- - lib/cabin/mixins/timer.rb
52
- - lib/cabin/mixins/timestamp.rb
53
- - lib/cabin/timer.rb
54
- - lib/cabin/metric.rb
55
- - lib/cabin/metrics.rb
56
- - lib/cabin/namespace.rb
57
- - lib/cabin/inspectable.rb
58
- - lib/cabin/outputs/io.rb
59
- - lib/cabin/outputs/stdlib-logger.rb
60
- - lib/cabin/outputs/em/stdlib-logger.rb
61
- - lib/cabin/context.rb
62
- - lib/cabin.rb
63
- - examples/fibonacci-timing.rb
64
- - examples/sinatra-logging.rb
65
- - examples/sample.rb
66
- - examples/metrics.rb
67
- - test/test_logging.rb
68
- - test/test_metrics.rb
69
- - test/minitest-patch.rb
70
- - test/all.rb
22
+ - !binary |-
23
+ bGliL2NhYmluLnJi
24
+ - !binary |-
25
+ bGliL2NhYmluL2NoYW5uZWwucmI=
26
+ - !binary |-
27
+ bGliL2NhYmluL2NvbnRleHQucmI=
28
+ - !binary |-
29
+ bGliL2NhYmluL2luc3BlY3RhYmxlLnJi
30
+ - !binary |-
31
+ bGliL2NhYmluL21ldHJpYy5yYg==
32
+ - !binary |-
33
+ bGliL2NhYmluL21ldHJpY3MucmI=
34
+ - !binary |-
35
+ bGliL2NhYmluL25hbWVzcGFjZS5yYg==
36
+ - !binary |-
37
+ bGliL2NhYmluL3B1Ymxpc2hlci5yYg==
38
+ - !binary |-
39
+ bGliL2NhYmluL3RpbWVyLnJi
40
+ - !binary |-
41
+ bGliL2NhYmluL21ldHJpY3MvY291bnRlci5yYg==
42
+ - !binary |-
43
+ bGliL2NhYmluL21ldHJpY3MvZ2F1Z2UucmI=
44
+ - !binary |-
45
+ bGliL2NhYmluL21ldHJpY3MvaGlzdG9ncmFtLnJi
46
+ - !binary |-
47
+ bGliL2NhYmluL21ldHJpY3MvbWV0ZXIucmI=
48
+ - !binary |-
49
+ bGliL2NhYmluL21ldHJpY3MvdGltZXIucmI=
50
+ - !binary |-
51
+ bGliL2NhYmluL21peGlucy9DQVBTTE9DSy5yYg==
52
+ - !binary |-
53
+ bGliL2NhYmluL21peGlucy9jb2xvcnMucmI=
54
+ - !binary |-
55
+ bGliL2NhYmluL21peGlucy9kcmFnb25zLnJi
56
+ - !binary |-
57
+ bGliL2NhYmluL21peGlucy9sb2dnZXIucmI=
58
+ - !binary |-
59
+ bGliL2NhYmluL21peGlucy90aW1lci5yYg==
60
+ - !binary |-
61
+ bGliL2NhYmluL21peGlucy90aW1lc3RhbXAucmI=
62
+ - !binary |-
63
+ bGliL2NhYmluL291dHB1dHMvaW8ucmI=
64
+ - !binary |-
65
+ bGliL2NhYmluL291dHB1dHMvc3RkbGliLWxvZ2dlci5yYg==
66
+ - !binary |-
67
+ bGliL2NhYmluL291dHB1dHMvemVyb21xLnJi
68
+ - !binary |-
69
+ bGliL2NhYmluL291dHB1dHMvZW0vc3RkbGliLWxvZ2dlci5yYg==
70
+ - !binary |-
71
+ ZXhhbXBsZXMvZmlib25hY2NpLXRpbWluZy5yYg==
72
+ - !binary |-
73
+ ZXhhbXBsZXMvbWV0cmljcy5yYg==
74
+ - !binary |-
75
+ ZXhhbXBsZXMvc2FtcGxlLnJi
76
+ - !binary |-
77
+ ZXhhbXBsZXMvc2luYXRyYS1sb2dnaW5nLnJi
78
+ - !binary |-
79
+ dGVzdC9hbGwucmI=
80
+ - !binary |-
81
+ dGVzdC9taW5pdGVzdC1wYXRjaC5yYg==
82
+ - !binary |-
83
+ dGVzdC90ZXN0X2xvZ2dpbmcucmI=
84
+ - !binary |-
85
+ dGVzdC90ZXN0X21ldHJpY3MucmI=
86
+ - !binary |-
87
+ dGVzdC90ZXN0X3plcm9tcS5yYg==
71
88
  - LICENSE
72
89
  - CHANGELIST
73
90
  - bin/rubygems-cabin-test
74
91
  homepage: https://github.com/jordansissel/ruby-cabin
75
92
  licenses:
76
93
  - Apache License (2.0)
77
- post_install_message:
94
+ post_install_message:
78
95
  rdoc_options: []
79
96
  require_paths:
80
97
  - lib
81
98
  - lib
82
99
  required_ruby_version: !ruby/object:Gem::Requirement
83
- none: false
84
100
  requirements:
85
101
  - - ! '>='
86
102
  - !ruby/object:Gem::Version
87
- version: '0'
88
- required_rubygems_version: !ruby/object:Gem::Requirement
103
+ version: !binary |-
104
+ MA==
89
105
  none: false
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
107
  requirements:
91
108
  - - ! '>='
92
109
  - !ruby/object:Gem::Version
93
- version: '0'
110
+ version: !binary |-
111
+ MA==
112
+ none: false
94
113
  requirements: []
95
- rubyforge_project:
96
- rubygems_version: 1.8.18
97
- signing_key:
114
+ rubyforge_project:
115
+ rubygems_version: 1.8.24
116
+ signing_key:
98
117
  specification_version: 3
99
118
  summary: Experiments in structured and contextual logging
100
119
  test_files: []
101
- has_rdoc: