celluloid 0.12.0 → 0.13.0
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.
- data/README.md +36 -23
- data/lib/celluloid/actor.rb +74 -47
- data/lib/celluloid/autostart.rb +16 -0
- data/lib/celluloid/calls.rb +52 -52
- data/lib/celluloid/condition.rb +70 -0
- data/lib/celluloid/core_ext.rb +1 -1
- data/lib/celluloid/cpu_counter.rb +7 -1
- data/lib/celluloid/fsm.rb +49 -16
- data/lib/celluloid/future.rb +5 -2
- data/lib/celluloid/internal_pool.rb +62 -44
- data/lib/celluloid/legacy.rb +46 -0
- data/lib/celluloid/links.rb +0 -5
- data/lib/celluloid/logger.rb +14 -4
- data/lib/celluloid/logging/incident.rb +21 -0
- data/lib/celluloid/logging/incident_logger.rb +129 -0
- data/lib/celluloid/logging/incident_reporter.rb +48 -0
- data/lib/celluloid/logging/log_event.rb +20 -0
- data/lib/celluloid/logging/ring_buffer.rb +65 -0
- data/lib/celluloid/logging.rb +5 -0
- data/lib/celluloid/mailbox.rb +2 -1
- data/lib/celluloid/method.rb +5 -0
- data/lib/celluloid/pool_manager.rb +21 -8
- data/lib/celluloid/proxies/actor_proxy.rb +11 -11
- data/lib/celluloid/proxies/async_proxy.rb +5 -1
- data/lib/celluloid/responses.rb +8 -6
- data/lib/celluloid/stack_dump.rb +94 -0
- data/lib/celluloid/supervision_group.rb +5 -3
- data/lib/celluloid/system_events.rb +11 -0
- data/lib/celluloid/tasks/task_fiber.rb +17 -9
- data/lib/celluloid/tasks/task_thread.rb +24 -14
- data/lib/celluloid/tasks.rb +59 -0
- data/lib/celluloid/thread_handle.rb +10 -1
- data/lib/celluloid/version.rb +1 -1
- data/lib/celluloid.rb +146 -87
- data/spec/support/actor_examples.rb +242 -114
- data/spec/support/example_actor_class.rb +17 -5
- data/spec/support/task_examples.rb +11 -2
- metadata +58 -14
- data/lib/celluloid/boot.rb +0 -9
- data/lib/celluloid/task.rb +0 -22
data/lib/celluloid/fsm.rb
CHANGED
|
@@ -84,30 +84,71 @@ module Celluloid
|
|
|
84
84
|
#
|
|
85
85
|
# Note: making additional state transitions will cancel delayed transitions
|
|
86
86
|
def transition(state_name, options = {})
|
|
87
|
+
new_state = validate_and_sanitize_new_state(state_name)
|
|
88
|
+
return unless new_state
|
|
89
|
+
|
|
90
|
+
if handle_delayed_transitions(new_state, options[:delay])
|
|
91
|
+
return @delayed_transition
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
transition_with_callbacks!(new_state)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Immediate state transition with no sanity checks, or callbacks. "Dangerous!"
|
|
98
|
+
def transition!(state_name)
|
|
99
|
+
@state = state_name
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
protected
|
|
103
|
+
|
|
104
|
+
def validate_and_sanitize_new_state(state_name)
|
|
87
105
|
state_name = state_name.to_sym
|
|
88
|
-
current_state = self.class.states[@state]
|
|
89
106
|
|
|
90
|
-
return if
|
|
107
|
+
return if current_state_name == state_name
|
|
91
108
|
|
|
92
109
|
if current_state and not current_state.valid_transition? state_name
|
|
93
110
|
valid = current_state.transitions.map(&:to_s).join(", ")
|
|
94
111
|
raise ArgumentError, "#{self.class} can't change state from '#{@state}' to '#{state_name}', only to: #{valid}"
|
|
95
112
|
end
|
|
96
113
|
|
|
97
|
-
new_state =
|
|
114
|
+
new_state = states[state_name]
|
|
98
115
|
|
|
99
116
|
unless new_state
|
|
100
|
-
return if state_name ==
|
|
117
|
+
return if state_name == default_state
|
|
101
118
|
raise ArgumentError, "invalid state for #{self.class}: #{state_name}"
|
|
102
119
|
end
|
|
103
120
|
|
|
104
|
-
|
|
121
|
+
new_state
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def transition_with_callbacks!(state_name)
|
|
125
|
+
transition! state_name.name
|
|
126
|
+
state_name.call(self)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def states
|
|
130
|
+
self.class.states
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def default_state
|
|
134
|
+
self.class.default_state
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def current_state
|
|
138
|
+
states[@state]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def current_state_name
|
|
142
|
+
current_state && current_state.name || ''
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def handle_delayed_transitions(new_state, delay)
|
|
146
|
+
if delay
|
|
105
147
|
raise UnattachedError, "can't delay unless attached" unless @actor
|
|
106
148
|
@delayed_transition.cancel if @delayed_transition
|
|
107
149
|
|
|
108
|
-
@delayed_transition = @actor.after(
|
|
109
|
-
|
|
110
|
-
new_state.call(self)
|
|
150
|
+
@delayed_transition = @actor.after(delay) do
|
|
151
|
+
transition_with_callbacks!(new_state)
|
|
111
152
|
end
|
|
112
153
|
|
|
113
154
|
return @delayed_transition
|
|
@@ -117,14 +158,6 @@ module Celluloid
|
|
|
117
158
|
@delayed_transition.cancel
|
|
118
159
|
@delayed_transition = nil
|
|
119
160
|
end
|
|
120
|
-
|
|
121
|
-
transition! new_state.name
|
|
122
|
-
new_state.call(self)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Immediate state transition with no sanity checks. "Dangerous!"
|
|
126
|
-
def transition!(state_name)
|
|
127
|
-
@state = state_name
|
|
128
161
|
end
|
|
129
162
|
|
|
130
163
|
# FSM states as declared by Celluloid::FSM.state
|
data/lib/celluloid/future.rb
CHANGED
|
@@ -4,8 +4,11 @@ module Celluloid
|
|
|
4
4
|
# Celluloid::Future objects allow methods and blocks to run in the
|
|
5
5
|
# background, their values requested later
|
|
6
6
|
class Future
|
|
7
|
+
attr_reader :address
|
|
8
|
+
|
|
7
9
|
# Create a future bound to a given receiver, or with a block to compute
|
|
8
10
|
def initialize(*args, &block)
|
|
11
|
+
@address = Celluloid.uuid
|
|
9
12
|
@mutex = Mutex.new
|
|
10
13
|
@ready = false
|
|
11
14
|
@result = nil
|
|
@@ -13,7 +16,7 @@ module Celluloid
|
|
|
13
16
|
|
|
14
17
|
if block
|
|
15
18
|
@call = SyncCall.new(self, :call, args)
|
|
16
|
-
|
|
19
|
+
Celluloid.internal_pool.get do
|
|
17
20
|
begin
|
|
18
21
|
@call.dispatch(block)
|
|
19
22
|
rescue
|
|
@@ -74,7 +77,7 @@ module Celluloid
|
|
|
74
77
|
if result
|
|
75
78
|
result.value
|
|
76
79
|
else
|
|
77
|
-
raise "Timed out"
|
|
80
|
+
raise TimeoutError, "Timed out"
|
|
78
81
|
end
|
|
79
82
|
end
|
|
80
83
|
alias_method :call, :value
|
|
@@ -2,61 +2,79 @@ require 'thread'
|
|
|
2
2
|
|
|
3
3
|
module Celluloid
|
|
4
4
|
# Maintain a thread pool FOR SPEED!!
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@mutex = Mutex.new
|
|
5
|
+
class InternalPool
|
|
6
|
+
attr_accessor :busy_size, :idle_size, :max_idle
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
def initialize
|
|
9
|
+
@pool = []
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
@busy_size = @idle_size = 0
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# Get a thread from the pool, running the given block
|
|
16
|
-
def get(&block)
|
|
17
|
-
@mutex.synchronize do
|
|
18
|
-
begin
|
|
19
|
-
if @pool.empty?
|
|
20
|
-
thread = create
|
|
21
|
-
else
|
|
22
|
-
thread = @pool.shift
|
|
23
|
-
end
|
|
24
|
-
end until thread.status # handle crashed threads
|
|
25
|
-
|
|
26
|
-
thread[:queue] << block
|
|
27
|
-
thread
|
|
28
|
-
end
|
|
29
|
-
end
|
|
13
|
+
# TODO: should really adjust this based on usage
|
|
14
|
+
@max_idle = 16
|
|
15
|
+
end
|
|
30
16
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
17
|
+
# Get a thread from the pool, running the given block
|
|
18
|
+
def get(&block)
|
|
19
|
+
@mutex.synchronize do
|
|
20
|
+
begin
|
|
21
|
+
if @pool.empty?
|
|
22
|
+
thread = create
|
|
36
23
|
else
|
|
37
|
-
@pool
|
|
24
|
+
thread = @pool.shift
|
|
25
|
+
@idle_size -= 1
|
|
38
26
|
end
|
|
27
|
+
end until thread.status # handle crashed threads
|
|
28
|
+
|
|
29
|
+
@busy_size += 1
|
|
30
|
+
thread[:celluloid_queue] << block
|
|
31
|
+
thread
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Return a thread to the pool
|
|
36
|
+
def put(thread)
|
|
37
|
+
@mutex.synchronize do
|
|
38
|
+
if @pool.size >= @max_idle
|
|
39
|
+
thread[:celluloid_queue] << nil
|
|
40
|
+
else
|
|
41
|
+
clean_thread_locals(thread)
|
|
42
|
+
@pool << thread
|
|
43
|
+
@idle_size += 1
|
|
44
|
+
@busy_size -= 1
|
|
39
45
|
end
|
|
40
46
|
end
|
|
47
|
+
end
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
put thread
|
|
49
|
+
# Create a new thread with an associated queue of procs to run
|
|
50
|
+
def create
|
|
51
|
+
queue = Queue.new
|
|
52
|
+
thread = Thread.new do
|
|
53
|
+
while proc = queue.pop
|
|
54
|
+
begin
|
|
55
|
+
proc.call
|
|
56
|
+
rescue => ex
|
|
57
|
+
Logger.crash("thread crashed", ex)
|
|
54
58
|
end
|
|
59
|
+
|
|
60
|
+
put thread
|
|
55
61
|
end
|
|
62
|
+
end
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
thread[:celluloid_queue] = queue
|
|
65
|
+
thread
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Clean the thread locals of an incoming thread
|
|
69
|
+
def clean_thread_locals(thread)
|
|
70
|
+
thread.keys.each do |key|
|
|
71
|
+
next if key == :celluloid_queue
|
|
72
|
+
|
|
73
|
+
# Ruby seems to lack an API for deleting thread locals. WTF, Ruby?
|
|
74
|
+
thread[key] = nil
|
|
59
75
|
end
|
|
60
76
|
end
|
|
61
77
|
end
|
|
62
|
-
|
|
78
|
+
|
|
79
|
+
self.internal_pool = InternalPool.new
|
|
80
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
class ActorProxy
|
|
3
|
+
# method_missing black magic to call bang predicate methods asynchronously
|
|
4
|
+
def method_missing(meth, *args, &block)
|
|
5
|
+
# bang methods are async calls
|
|
6
|
+
if meth.match(/!$/)
|
|
7
|
+
Logger.deprecate("'bang method'-style async syntax is deprecated and will be removed in Celluloid 1.0." +
|
|
8
|
+
"Call async methods with 'actor.async.method'.")
|
|
9
|
+
|
|
10
|
+
unbanged_meth = meth.to_s
|
|
11
|
+
unbanged_meth.slice!(-1, 1)
|
|
12
|
+
Actor.async @mailbox, unbanged_meth, *args, &block
|
|
13
|
+
else
|
|
14
|
+
Actor.call @mailbox, meth, *args, &block
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module InstanceMethods
|
|
20
|
+
# Process async calls via method_missing
|
|
21
|
+
def method_missing(meth, *args, &block)
|
|
22
|
+
# bang methods are async calls
|
|
23
|
+
if meth.to_s.match(/!$/)
|
|
24
|
+
Logger.deprecate("'bang method'-style async syntax is deprecated and will be removed in Celluloid 1.0." +
|
|
25
|
+
"Call async methods with 'actor.async.method'.")
|
|
26
|
+
|
|
27
|
+
unbanged_meth = meth.to_s.sub(/!$/, '')
|
|
28
|
+
args.unshift unbanged_meth
|
|
29
|
+
|
|
30
|
+
call = AsyncCall.new(:__send__, args, block)
|
|
31
|
+
begin
|
|
32
|
+
Thread.current[:celluloid_actor].mailbox << call
|
|
33
|
+
rescue MailboxError
|
|
34
|
+
# Silently swallow asynchronous calls to dead actors. There's no way
|
|
35
|
+
# to reliably generate DeadActorErrors for async calls, so users of
|
|
36
|
+
# async calls should find other ways to deal with actors dying
|
|
37
|
+
# during an async call (i.e. linking/supervisors)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
super
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/celluloid/links.rb
CHANGED
|
@@ -27,11 +27,6 @@ module Celluloid
|
|
|
27
27
|
@links.each { |_, actor| yield(actor) }
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
# Send an event message to all actors
|
|
31
|
-
def send_event(event)
|
|
32
|
-
each { |actor| actor.mailbox << event }
|
|
33
|
-
end
|
|
34
|
-
|
|
35
30
|
# Generate a string representation
|
|
36
31
|
def inspect
|
|
37
32
|
links = self.map(&:inspect).join(',')
|
data/lib/celluloid/logger.rb
CHANGED
|
@@ -37,10 +37,10 @@ module Celluloid
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
#
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
# Note a deprecation
|
|
41
|
+
def deprecate(message)
|
|
42
|
+
trace = caller.join("\n\t")
|
|
43
|
+
warn "DEPRECATION WARNING: #{message}\n\t#{trace}"
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Define an exception handler
|
|
@@ -49,5 +49,15 @@ module Celluloid
|
|
|
49
49
|
@exception_handlers << block
|
|
50
50
|
nil
|
|
51
51
|
end
|
|
52
|
+
|
|
53
|
+
# Format an exception message
|
|
54
|
+
def format_exception(exception)
|
|
55
|
+
str = "#{exception.class}: #{exception.to_s}\n\t"
|
|
56
|
+
if exception.backtrace
|
|
57
|
+
str << exception.backtrace.join("\n\t")
|
|
58
|
+
else
|
|
59
|
+
str << "EMPTY BACKTRACE\n\t"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
52
62
|
end
|
|
53
63
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
# Wraps all events and context for a single incident.
|
|
3
|
+
class Incident
|
|
4
|
+
attr_accessor :pid
|
|
5
|
+
attr_accessor :events, :triggering_event
|
|
6
|
+
|
|
7
|
+
def initialize(events, triggering_event=nil)
|
|
8
|
+
@events = events
|
|
9
|
+
@triggering_event = triggering_event
|
|
10
|
+
@pid = $$
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Merge two incidents together. This may be useful if two incidents occur at the same time.
|
|
14
|
+
def merge(*other_incidents)
|
|
15
|
+
merged_events = other_incidents.flatten.inject(events) do |events, incident|
|
|
16
|
+
events += incident.events
|
|
17
|
+
end
|
|
18
|
+
Incident.new(merged_events.sort, triggering_event)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
module Celluloid
|
|
3
|
+
# A logger that holds all messages in circular buffers, then flushes the buffers
|
|
4
|
+
# when an event occurs at a configurable severity threshold.
|
|
5
|
+
#
|
|
6
|
+
# Unlike ruby's Logger, this class only supports a single progname.
|
|
7
|
+
class IncidentLogger
|
|
8
|
+
module Severity
|
|
9
|
+
include ::Logger::Severity
|
|
10
|
+
|
|
11
|
+
TRACE = -1
|
|
12
|
+
|
|
13
|
+
def severity_to_string(severity)
|
|
14
|
+
case severity
|
|
15
|
+
when TRACE then 'TRACE'
|
|
16
|
+
when DEBUG then 'DEBUG'
|
|
17
|
+
when INFO then 'INFO'
|
|
18
|
+
when WARN then 'WARN'
|
|
19
|
+
when ERROR then 'ERROR'
|
|
20
|
+
when FATAL then 'FATAL'
|
|
21
|
+
when UNKNOWN then 'UNKNOWN'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
include Severity
|
|
27
|
+
|
|
28
|
+
# The progname (facility) for this instance.
|
|
29
|
+
attr_accessor :progname
|
|
30
|
+
|
|
31
|
+
# The logging level. Messages below this severity will not be logged at all.
|
|
32
|
+
attr_accessor :level
|
|
33
|
+
|
|
34
|
+
# The incident threshold. Messages at or above this severity will generate an
|
|
35
|
+
# incident and be published to incident reporters.
|
|
36
|
+
attr_accessor :threshold
|
|
37
|
+
|
|
38
|
+
# The buffer size limit. Each log level will retain this number of messages
|
|
39
|
+
# at maximum.
|
|
40
|
+
attr_accessor :sizelimit
|
|
41
|
+
|
|
42
|
+
attr_accessor :buffers
|
|
43
|
+
|
|
44
|
+
# Create a new IncidentLogger.
|
|
45
|
+
def initialize(progname=nil, options={})
|
|
46
|
+
@progname = progname || "default"
|
|
47
|
+
@level = options[:level] || DEBUG
|
|
48
|
+
@threshold = options[:threshold] || ERROR
|
|
49
|
+
@sizelimit = options[:sizelimit] || 100
|
|
50
|
+
|
|
51
|
+
@buffer_mutex = Mutex.new
|
|
52
|
+
@buffers = Hash.new do |progname_hash, progname|
|
|
53
|
+
@buffer_mutex.synchronize do
|
|
54
|
+
progname_hash[progname] = Hash.new do |severity_hash, severity|
|
|
55
|
+
severity_hash[severity] = RingBuffer.new(@sizelimit)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# When the IncidentLogger itself encounters an error, it falls back to logging to stderr
|
|
61
|
+
@fallback_logger = ::Logger.new(STDERR)
|
|
62
|
+
@fallback_logger.progname = "FALLBACK"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# add an event.
|
|
66
|
+
def add(severity, message=nil, progname=nil, &block)
|
|
67
|
+
progname ||= @progname
|
|
68
|
+
severity ||= UNKNOWN
|
|
69
|
+
|
|
70
|
+
if severity < @level
|
|
71
|
+
return event.id
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if message.nil? && !block_given?
|
|
75
|
+
message = progname
|
|
76
|
+
progname = @progname
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
event = LogEvent.new(severity, message, progname, &block)
|
|
80
|
+
|
|
81
|
+
@buffers[progname][severity] << event
|
|
82
|
+
|
|
83
|
+
if severity >= @threshold
|
|
84
|
+
begin
|
|
85
|
+
Celluloid::Notifications.notifier.async.publish(incident_topic, create_incident(event))
|
|
86
|
+
rescue => ex
|
|
87
|
+
@fallback_logger.error(ex)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
event.id
|
|
91
|
+
end
|
|
92
|
+
alias :log :add
|
|
93
|
+
|
|
94
|
+
# See docs for Logger#info
|
|
95
|
+
def trace (progname=nil, &block); add(TRACE, nil, progname, &block); end
|
|
96
|
+
def debug (progname=nil, &block); add(DEBUG, nil, progname, &block); end
|
|
97
|
+
def info (progname=nil, &block); add(INFO, nil, progname, &block); end
|
|
98
|
+
def warn (progname=nil, &block); add(WARN, nil, progname, &block); end
|
|
99
|
+
def error (progname=nil, &block); add(ERROR, nil, progname, &block); end
|
|
100
|
+
def fatal (progname=nil, &block); add(FATAL, nil, progname, &block); end
|
|
101
|
+
def unknown (progname=nil, &block); add(UNKNOWN, nil, progname, &block); end
|
|
102
|
+
|
|
103
|
+
def flush
|
|
104
|
+
messages = []
|
|
105
|
+
@buffer_mutex.synchronize do
|
|
106
|
+
@buffers.each do |progname, severities|
|
|
107
|
+
severities.each do |severity, buffer|
|
|
108
|
+
messages += buffer.flush
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
messages.sort
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def clear
|
|
116
|
+
@buffer_mutex.synchronize do
|
|
117
|
+
@buffers.each { |buffer| buffer.clear }
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def create_incident(event=nil)
|
|
122
|
+
Incident.new(flush, event)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def incident_topic
|
|
126
|
+
"log.incident.#{@progname}"
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
module Celluloid
|
|
3
|
+
# Subscribes to log incident topics to report on them.
|
|
4
|
+
class IncidentReporter
|
|
5
|
+
include Celluloid
|
|
6
|
+
include Celluloid::Notifications
|
|
7
|
+
|
|
8
|
+
# get the time from the event
|
|
9
|
+
class Formatter < ::Logger::Formatter
|
|
10
|
+
def call(severity, time, progname, msg)
|
|
11
|
+
super(severity, msg.time, progname, msg.message)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(*args)
|
|
16
|
+
subscribe(/log\.incident/, :report)
|
|
17
|
+
@logger = ::Logger.new(*args)
|
|
18
|
+
@logger.formatter = Formatter.new
|
|
19
|
+
@silenced = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def report(topic, incident)
|
|
23
|
+
return if @silenced
|
|
24
|
+
|
|
25
|
+
header = "INCIDENT"
|
|
26
|
+
header << " AT #{incident.triggering_event.time}" if incident.triggering_event
|
|
27
|
+
@logger << header
|
|
28
|
+
@logger << "\n"
|
|
29
|
+
@logger << "====================\n"
|
|
30
|
+
incident.events.each do |event|
|
|
31
|
+
@logger.add(event.severity, event, event.progname)
|
|
32
|
+
end
|
|
33
|
+
@logger << "====================\n"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def silence
|
|
37
|
+
@silenced = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def unsilence
|
|
41
|
+
@silenced = false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def silenced?
|
|
45
|
+
@silenced
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
# Wraps a single log event.
|
|
3
|
+
class LogEvent
|
|
4
|
+
attr_accessor :id, :severity, :message, :progname, :time
|
|
5
|
+
|
|
6
|
+
def initialize(severity, message, progname, time=Time.now, &block)
|
|
7
|
+
# This id should be ordered. For now relies on Celluloid::UUID to be ordered.
|
|
8
|
+
# May want to use a generation/counter strategy for independence of uuid.
|
|
9
|
+
@id = Celluloid::UUID.generate
|
|
10
|
+
@severity = severity
|
|
11
|
+
@message = block_given? ? yield : message
|
|
12
|
+
@progname = progname
|
|
13
|
+
@time = time
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def <=>(other)
|
|
17
|
+
@id <=> other.id
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
class RingBuffer
|
|
3
|
+
def initialize(size)
|
|
4
|
+
@size = size
|
|
5
|
+
@start = 0
|
|
6
|
+
@count = 0
|
|
7
|
+
@buffer = Array.new(size)
|
|
8
|
+
@mutex = Mutex.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def full?
|
|
12
|
+
@count == @size
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def empty?
|
|
16
|
+
@count == 0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def push(value)
|
|
20
|
+
@mutex.synchronize do
|
|
21
|
+
stop = (@start + @count) % @size
|
|
22
|
+
@buffer[stop] = value
|
|
23
|
+
if full?
|
|
24
|
+
@start = (@start + 1) % @size
|
|
25
|
+
else
|
|
26
|
+
@count += 1
|
|
27
|
+
end
|
|
28
|
+
value
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
alias :<< :push
|
|
32
|
+
|
|
33
|
+
def shift
|
|
34
|
+
@mutex.synchronize do
|
|
35
|
+
remove_element
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def flush
|
|
40
|
+
values = []
|
|
41
|
+
@mutex.synchronize do
|
|
42
|
+
while !empty?
|
|
43
|
+
values << remove_element
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
values
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def clear
|
|
50
|
+
@buffer = Array.new(@size)
|
|
51
|
+
@start = 0
|
|
52
|
+
@count = 0
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def remove_element
|
|
58
|
+
return nil if empty?
|
|
59
|
+
value, @buffer[@start] = @buffer[@start], nil
|
|
60
|
+
@start = (@start + 1) % @size
|
|
61
|
+
@count -= 1
|
|
62
|
+
value
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/celluloid/mailbox.rb
CHANGED
|
@@ -10,9 +10,10 @@ module Celluloid
|
|
|
10
10
|
include Enumerable
|
|
11
11
|
|
|
12
12
|
# A unique address at which this mailbox can be found
|
|
13
|
-
|
|
13
|
+
attr_reader :address
|
|
14
14
|
|
|
15
15
|
def initialize
|
|
16
|
+
@address = Celluloid.uuid
|
|
16
17
|
@messages = []
|
|
17
18
|
@mutex = Mutex.new
|
|
18
19
|
@dead = false
|
data/lib/celluloid/method.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module Celluloid
|
|
2
2
|
# Method handles that route through an actor proxy
|
|
3
3
|
class Method
|
|
4
|
+
|
|
4
5
|
def initialize(actor, name)
|
|
5
6
|
raise NameError, "undefined method `#{name}'" unless actor.respond_to? name
|
|
6
7
|
|
|
@@ -8,6 +9,10 @@ module Celluloid
|
|
|
8
9
|
@klass = @actor.class
|
|
9
10
|
end
|
|
10
11
|
|
|
12
|
+
def arity
|
|
13
|
+
@actor._send_(:method, @name).arity
|
|
14
|
+
end
|
|
15
|
+
|
|
11
16
|
def call(*args, &block)
|
|
12
17
|
@actor._send_(@name, *args, &block)
|
|
13
18
|
end
|