celluloid 0.18.0.pre → 0.18.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.
- checksums.yaml +5 -5
- data/CHANGES.md +258 -39
- data/CONDUCT.md +13 -0
- data/CONTRIBUTING.md +39 -0
- data/README.md +54 -165
- data/REFACTOR.md +1 -0
- data/architecture.md +120 -0
- data/examples/basic_usage.rb +1 -1
- data/examples/configurations.rb +78 -0
- data/examples/futures.rb +1 -1
- data/examples/ring.rb +5 -4
- data/examples/simple_pmap.rb +1 -1
- data/examples/stack.rb +2 -2
- data/examples/supervisors_and_registry.rb +82 -0
- data/examples/timers.rb +2 -2
- data/lib/celluloid.rb +72 -47
- data/lib/celluloid/actor.rb +27 -17
- data/lib/celluloid/actor/system.rb +13 -29
- data/lib/celluloid/autostart.rb +5 -5
- data/lib/celluloid/call/async.rb +2 -0
- data/lib/celluloid/call/sync.rb +10 -3
- data/lib/celluloid/calls.rb +5 -12
- data/lib/celluloid/cell.rb +5 -9
- data/lib/celluloid/condition.rb +3 -3
- data/lib/celluloid/core_ext.rb +0 -2
- data/lib/celluloid/debug.rb +3 -0
- data/lib/celluloid/exceptions.rb +2 -2
- data/lib/celluloid/future.rb +7 -9
- data/lib/celluloid/group.rb +12 -8
- data/lib/celluloid/group/pool.rb +1 -3
- data/lib/celluloid/group/spawner.rb +2 -6
- data/lib/celluloid/internals/call_chain.rb +15 -0
- data/lib/celluloid/internals/cpu_counter.rb +62 -0
- data/lib/celluloid/internals/handlers.rb +42 -0
- data/lib/celluloid/internals/links.rb +38 -0
- data/lib/celluloid/internals/logger.rb +104 -0
- data/lib/celluloid/internals/method.rb +34 -0
- data/lib/celluloid/internals/properties.rb +32 -0
- data/lib/celluloid/internals/receivers.rb +64 -0
- data/lib/celluloid/internals/registry.rb +102 -0
- data/lib/celluloid/internals/responses.rb +46 -0
- data/lib/celluloid/internals/signals.rb +24 -0
- data/lib/celluloid/internals/stack.rb +74 -0
- data/lib/celluloid/internals/stack/dump.rb +12 -0
- data/lib/celluloid/internals/stack/states.rb +72 -0
- data/lib/celluloid/internals/stack/summary.rb +12 -0
- data/lib/celluloid/internals/task_set.rb +51 -0
- data/lib/celluloid/internals/thread_handle.rb +52 -0
- data/lib/celluloid/internals/uuid.rb +40 -0
- data/lib/celluloid/logging/incident.rb +21 -0
- data/lib/celluloid/logging/incident_logger.rb +147 -0
- data/lib/celluloid/logging/incident_reporter.rb +49 -0
- data/lib/celluloid/logging/log_event.rb +20 -0
- data/lib/celluloid/logging/ring_buffer.rb +64 -0
- data/lib/celluloid/mailbox.rb +22 -9
- data/lib/celluloid/mailbox/evented.rb +13 -5
- data/lib/celluloid/notifications.rb +95 -0
- data/lib/celluloid/pool.rb +6 -0
- data/lib/celluloid/probe.rb +81 -0
- data/lib/celluloid/proxy/abstract.rb +9 -9
- data/lib/celluloid/proxy/async.rb +1 -1
- data/lib/celluloid/proxy/block.rb +2 -2
- data/lib/celluloid/proxy/cell.rb +1 -1
- data/lib/celluloid/proxy/future.rb +2 -4
- data/lib/celluloid/proxy/sync.rb +1 -3
- data/lib/celluloid/rspec.rb +22 -33
- data/lib/celluloid/supervision.rb +17 -0
- data/lib/celluloid/supervision/configuration.rb +169 -0
- data/lib/celluloid/supervision/configuration/injections.rb +8 -0
- data/lib/celluloid/supervision/configuration/instance.rb +113 -0
- data/lib/celluloid/supervision/constants.rb +123 -0
- data/lib/celluloid/supervision/container.rb +144 -0
- data/lib/celluloid/supervision/container/behavior.rb +89 -0
- data/lib/celluloid/supervision/container/behavior/pool.rb +71 -0
- data/lib/celluloid/supervision/container/behavior/tree.rb +23 -0
- data/lib/celluloid/supervision/container/injections.rb +8 -0
- data/lib/celluloid/supervision/container/instance.rb +116 -0
- data/lib/celluloid/supervision/container/pool.rb +210 -0
- data/lib/celluloid/supervision/service.rb +27 -0
- data/lib/celluloid/supervision/supervise.rb +34 -0
- data/lib/celluloid/supervision/validation.rb +40 -0
- data/lib/celluloid/supervision/version.rb +5 -0
- data/lib/celluloid/system_events.rb +11 -6
- data/lib/celluloid/task.rb +25 -12
- data/lib/celluloid/task/fibered.rb +2 -0
- data/lib/celluloid/task/threaded.rb +3 -3
- data/lib/celluloid/test.rb +5 -2
- data/lib/celluloid/thread.rb +0 -2
- data/lib/celluloid/version.rb +1 -1
- data/spec/celluloid/block_spec.rb +29 -32
- data/spec/celluloid/calls_spec.rb +5 -15
- data/spec/celluloid/future_spec.rb +2 -2
- data/spec/celluloid/internals/cpu_counter_spec.rb +129 -0
- data/spec/celluloid/internals/links_spec.rb +43 -0
- data/spec/celluloid/internals/properties_spec.rb +40 -0
- data/spec/celluloid/internals/registry_spec.rb +62 -0
- data/spec/celluloid/internals/stack/dump_spec.rb +4 -0
- data/spec/celluloid/internals/stack/summary_spec.rb +4 -0
- data/spec/celluloid/internals/thread_handle_spec.rb +60 -0
- data/spec/celluloid/internals/uuid_spec.rb +9 -0
- data/spec/celluloid/logging/ring_buffer_spec.rb +36 -0
- data/spec/celluloid/mailbox/evented_spec.rb +11 -22
- data/spec/celluloid/misc/leak_spec.rb +3 -4
- data/spec/celluloid/notifications_spec.rb +140 -0
- data/spec/celluloid/probe_spec.rb +102 -0
- data/spec/celluloid/proxy_spec.rb +30 -30
- data/spec/celluloid/supervision/behavior_spec.rb +74 -0
- data/spec/celluloid/supervision/configuration_spec.rb +181 -0
- data/spec/celluloid/supervision/container_spec.rb +72 -0
- data/spec/celluloid/supervision/instance_spec.rb +13 -0
- data/spec/celluloid/supervision/root_spec.rb +28 -0
- data/spec/celluloid/supervision/supervisor_spec.rb +93 -0
- data/spec/celluloid/task/fibered_spec.rb +1 -3
- data/spec/celluloid/task/threaded_spec.rb +1 -3
- data/spec/shared/actor_examples.rb +58 -33
- data/spec/shared/group_examples.rb +2 -2
- data/spec/shared/mailbox_examples.rb +1 -1
- data/spec/shared/stack_examples.rb +87 -0
- data/spec/shared/task_examples.rb +2 -3
- data/spec/spec_helper.rb +2 -4
- data/spec/support/configure_rspec.rb +2 -3
- data/spec/support/coverage.rb +2 -4
- data/spec/support/crash_checking.rb +2 -2
- data/spec/support/examples/actor_class.rb +3 -8
- data/spec/support/examples/call_class.rb +2 -2
- data/spec/support/examples/container_class.rb +35 -0
- data/spec/support/examples/evented_mailbox_class.rb +1 -2
- data/spec/support/examples/stack_classes.rb +58 -0
- data/spec/support/examples/stack_methods.rb +23 -0
- data/spec/support/examples/subordinate_class.rb +19 -0
- data/spec/support/logging.rb +2 -34
- data/spec/support/loose_threads.rb +3 -16
- data/spec/support/reset_class_variables.rb +5 -1
- data/spec/support/stubbing.rb +1 -1
- metadata +91 -323
- data/culture/CONDUCT.md +0 -38
- data/culture/GSoC/1010-why_we_will_participate.md +0 -17
- data/culture/GSoC/1020-how_mentors_stay_engaged.md +0 -7
- data/culture/GSoC/1030-keeping_students_on_schedule.md +0 -9
- data/culture/GSoC/1040-getting_students_involved.md +0 -5
- data/culture/GSoC/1050-student_involvement_after.md +0 -5
- data/culture/GSoC/README.md +0 -16
- data/culture/Gemfile +0 -9
- data/culture/LICENSE.txt +0 -22
- data/culture/README.md +0 -22
- data/culture/Rakefile +0 -5
- data/culture/SYNC.md +0 -70
- data/culture/celluloid-culture.gemspec +0 -18
- data/culture/gems/README.md +0 -39
- data/culture/gems/dependencies.yml +0 -93
- data/culture/gems/loader.rb +0 -101
- data/culture/rubocop/README.md +0 -38
- data/culture/rubocop/lint.yml +0 -8
- data/culture/rubocop/metrics.yml +0 -15
- data/culture/rubocop/perf.yml +0 -0
- data/culture/rubocop/rubocop.yml +0 -5
- data/culture/rubocop/style.yml +0 -61
- data/culture/spec/gems_spec.rb +0 -2
- data/culture/spec/spec_helper.rb +0 -0
- data/culture/spec/sync_spec.rb +0 -2
- data/culture/sync.rb +0 -56
- data/culture/tasks/rspec.rake +0 -5
- data/culture/tasks/rubocop.rake +0 -2
- data/lib/celluloid/actor/manager.rb +0 -7
- data/lib/celluloid/backported.rb +0 -2
- data/lib/celluloid/current.rb +0 -2
- data/lib/celluloid/deprecate.rb +0 -34
- data/lib/celluloid/fiber.rb +0 -32
- data/lib/celluloid/managed.rb +0 -3
- data/lib/celluloid/notices.rb +0 -15
- data/spec/deprecate/actor_system_spec.rb +0 -72
- data/spec/deprecate/block_spec.rb +0 -52
- data/spec/deprecate/calls_spec.rb +0 -39
- data/spec/deprecate/evented_mailbox_spec.rb +0 -34
- data/spec/deprecate/future_spec.rb +0 -32
- data/spec/deprecate/internal_pool_spec.rb +0 -4
- data/spec/support/env.rb +0 -21
@@ -0,0 +1,46 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
# Responses to calls
|
4
|
+
class Response
|
5
|
+
attr_reader :call, :value
|
6
|
+
|
7
|
+
def initialize(call, value)
|
8
|
+
@call = call
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def dispatch
|
13
|
+
@call.task.resume self
|
14
|
+
end
|
15
|
+
|
16
|
+
# Call completed successfully
|
17
|
+
class Success < Response; end
|
18
|
+
|
19
|
+
# Call was aborted due to sender error
|
20
|
+
class Error < Response
|
21
|
+
def value
|
22
|
+
ex = super
|
23
|
+
ex = ex.cause if ex.is_a? Celluloid::AbortError
|
24
|
+
|
25
|
+
if ex.backtrace
|
26
|
+
ex.backtrace << "(celluloid):0:in `remote procedure call'"
|
27
|
+
ex.backtrace.concat(caller)
|
28
|
+
end
|
29
|
+
|
30
|
+
raise ex
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Block
|
35
|
+
def initialize(call, result)
|
36
|
+
@call = call
|
37
|
+
@result = result
|
38
|
+
end
|
39
|
+
|
40
|
+
def dispatch
|
41
|
+
@call.task.resume(@result)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
# Event signaling between methods of the same object
|
4
|
+
class Signals
|
5
|
+
def initialize
|
6
|
+
@conditions = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Wait for the given signal and return the associated value
|
10
|
+
def wait(name)
|
11
|
+
raise "cannot wait for signals while exclusive" if Celluloid.exclusive?
|
12
|
+
|
13
|
+
@conditions[name] ||= Condition.new
|
14
|
+
@conditions[name].wait
|
15
|
+
end
|
16
|
+
|
17
|
+
# Send a signal to all method calls waiting for the given name
|
18
|
+
def broadcast(name, value = nil)
|
19
|
+
condition = @conditions.delete(name)
|
20
|
+
condition.broadcast(value) if condition
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
class Stack
|
4
|
+
attr_accessor :actors, :threads
|
5
|
+
|
6
|
+
def initialize(threads)
|
7
|
+
@group = threads
|
8
|
+
@actors = []
|
9
|
+
@threads = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def snapshot(backtrace = nil)
|
13
|
+
@group.each do |thread|
|
14
|
+
if thread.role == :actor
|
15
|
+
@actors << snapshot_actor(thread.actor, backtrace) if thread.actor
|
16
|
+
else
|
17
|
+
@threads << snapshot_thread(thread, backtrace)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def snapshot_actor(actor, backtrace = nil)
|
23
|
+
state = ActorState.new
|
24
|
+
state.id = actor.object_id
|
25
|
+
|
26
|
+
# TODO: delegate to the behavior
|
27
|
+
state.cell = snapshot_cell(actor.behavior) if actor.behavior.is_a?(Cell)
|
28
|
+
|
29
|
+
tasks = actor.tasks
|
30
|
+
if tasks.empty?
|
31
|
+
state.status = :idle
|
32
|
+
else
|
33
|
+
state.status = :running
|
34
|
+
state.tasks = tasks.to_a.map { |t| TaskState.new(t.class, t.type, t.meta, t.status, t.backtrace) }
|
35
|
+
end
|
36
|
+
|
37
|
+
state.backtrace = actor.thread.backtrace if backtrace && actor.thread
|
38
|
+
state
|
39
|
+
end
|
40
|
+
|
41
|
+
def snapshot_cell(behavior)
|
42
|
+
state = CellState.new
|
43
|
+
state.subject_id = behavior.subject.object_id
|
44
|
+
state.subject_class = behavior.subject.class
|
45
|
+
state
|
46
|
+
end
|
47
|
+
|
48
|
+
def snapshot_thread(thread, backtrace = nil)
|
49
|
+
if backtrace
|
50
|
+
backtrace = begin
|
51
|
+
thread.backtrace
|
52
|
+
rescue NoMethodError # for Rubinius < 2.5.2.c145
|
53
|
+
[]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
ThreadState.new(thread.object_id, backtrace, thread.role)
|
57
|
+
end
|
58
|
+
|
59
|
+
def print(output = STDERR)
|
60
|
+
@actors.each do |actor|
|
61
|
+
output.print actor.dump
|
62
|
+
end
|
63
|
+
|
64
|
+
@threads.each do |thread|
|
65
|
+
output.print thread.dump
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
require "celluloid/internals/stack/states"
|
73
|
+
require "celluloid/internals/stack/dump"
|
74
|
+
require "celluloid/internals/stack/summary"
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
class Stack
|
4
|
+
module DisplayBacktrace
|
5
|
+
def display_backtrace(backtrace, output, indent = nil)
|
6
|
+
backtrace ||= ["EMPTY BACKTRACE"]
|
7
|
+
backtrace.each do |line|
|
8
|
+
output << indent if indent
|
9
|
+
output << "\t" << line << "\n"
|
10
|
+
end
|
11
|
+
output << "\n\n"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class TaskState < Struct.new(:task_class, :type, :meta, :status, :backtrace); end
|
16
|
+
|
17
|
+
class CellState < Struct.new(:subject_id, :subject_class)
|
18
|
+
def dump
|
19
|
+
"Celluloid::Cell 0x#{subject_id.to_s(16)}: #{subject_class}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class ThreadState < Struct.new(:thread_id, :backtrace, :role)
|
24
|
+
include DisplayBacktrace
|
25
|
+
def dump
|
26
|
+
string = ""
|
27
|
+
string << "Thread 0x#{thread_id.to_s(16)} (#{role}):\n"
|
28
|
+
display_backtrace backtrace, string if backtrace
|
29
|
+
string
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class ActorState
|
34
|
+
include DisplayBacktrace
|
35
|
+
attr_accessor :name, :id, :cell
|
36
|
+
attr_accessor :status, :tasks
|
37
|
+
attr_accessor :backtrace
|
38
|
+
|
39
|
+
def dump
|
40
|
+
string = ""
|
41
|
+
string << "Celluloid::Actor 0x#{id.to_s(16)}"
|
42
|
+
string << " [#{name}]" if name
|
43
|
+
string << "\n"
|
44
|
+
|
45
|
+
if cell
|
46
|
+
string << cell.dump
|
47
|
+
string << "\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
if status == :idle
|
51
|
+
string << "State: Idle (waiting for messages)\n"
|
52
|
+
display_backtrace backtrace, string if backtrace
|
53
|
+
else
|
54
|
+
string << "State: Running (executing tasks)\n"
|
55
|
+
display_backtrace backtrace, string if backtrace
|
56
|
+
string << "\tTasks:\n"
|
57
|
+
|
58
|
+
tasks.each_with_index do |task, i|
|
59
|
+
string << "\t #{i + 1}) #{task.task_class}[#{task.type}]: #{task.status}\n"
|
60
|
+
if task.backtrace
|
61
|
+
string << "\t #{task.meta.inspect}\n"
|
62
|
+
display_backtrace task.backtrace, string, "\t"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
string << "\n" unless backtrace
|
67
|
+
string
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "set"
|
2
|
+
require "forwardable"
|
3
|
+
|
4
|
+
module Celluloid
|
5
|
+
module Internals
|
6
|
+
if RUBY_PLATFORM == "java"
|
7
|
+
require "jruby/synchronized"
|
8
|
+
|
9
|
+
class TaskSet
|
10
|
+
extend Forwardable
|
11
|
+
include JRuby::Synchronized
|
12
|
+
|
13
|
+
def_delegators :@tasks, :<<, :delete, :first, :empty?, :to_a
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@tasks = Set.new
|
17
|
+
end
|
18
|
+
end
|
19
|
+
elsif RUBY_ENGINE == "rbx"
|
20
|
+
class TaskSet
|
21
|
+
def initialize
|
22
|
+
@tasks = Set.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def <<(task)
|
26
|
+
Rubinius.synchronize(self) { @tasks << task }
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(task)
|
30
|
+
Rubinius.synchronize(self) { @tasks.delete task }
|
31
|
+
end
|
32
|
+
|
33
|
+
def first
|
34
|
+
Rubinius.synchronize(self) { @tasks.first }
|
35
|
+
end
|
36
|
+
|
37
|
+
def empty?
|
38
|
+
Rubinius.synchronize(self) { @tasks.empty? }
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_a
|
42
|
+
Rubinius.synchronize(self) { @tasks.to_a }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
else
|
46
|
+
# Assume we're on MRI, where we have the GIL. But what about IronRuby?
|
47
|
+
# Or MacRuby. Do people care? This will break Celluloid::Internals::StackDumps
|
48
|
+
TaskSet = Set
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
# An abstraction around threads from the InternalPool which ensures we don't
|
4
|
+
# accidentally do things to threads which have been returned to the pool,
|
5
|
+
# such as, say, killing them
|
6
|
+
class ThreadHandle
|
7
|
+
def initialize(actor_system, role = nil)
|
8
|
+
@mutex = Mutex.new
|
9
|
+
@join = ConditionVariable.new
|
10
|
+
|
11
|
+
@thread = actor_system.get_thread do
|
12
|
+
Thread.current.role = role
|
13
|
+
begin
|
14
|
+
yield
|
15
|
+
ensure
|
16
|
+
@mutex.synchronize do
|
17
|
+
@thread = nil
|
18
|
+
@join.broadcast
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Is the thread running?
|
25
|
+
def alive?
|
26
|
+
@mutex.synchronize { @thread && @thread.alive? }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Forcibly kill the thread
|
30
|
+
def kill
|
31
|
+
@mutex.synchronize { @thread && @thread.kill }
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# Join to a running thread, blocking until it terminates
|
36
|
+
def join(limit = nil)
|
37
|
+
raise ThreadError, "Target thread must not be current thread" if @thread == Thread.current
|
38
|
+
@mutex.synchronize { @join.wait(@mutex, limit) if @thread }
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
# Obtain the backtrace for this thread
|
43
|
+
def backtrace
|
44
|
+
@thread.backtrace
|
45
|
+
rescue NoMethodError
|
46
|
+
# undefined method `backtrace' for nil:NilClass
|
47
|
+
# Swallow this in case this ThreadHandle was terminated and @thread was
|
48
|
+
# set to nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "securerandom"
|
2
|
+
|
3
|
+
module Celluloid
|
4
|
+
module Internals
|
5
|
+
# Clearly Ruby doesn't have enough UUID libraries
|
6
|
+
# This one aims to be fast and simple with good support for multiple threads
|
7
|
+
# If there's a better UUID library I can use with similar multithreaded
|
8
|
+
# performance, I certainly wouldn't mind using a gem for this!
|
9
|
+
module UUID
|
10
|
+
values = SecureRandom.hex(9).match(/(.{8})(.{4})(.{3})(.{3})/)
|
11
|
+
PREFIX = "#{values[1]}-#{values[2]}-4#{values[3]}-8#{values[4]}".freeze
|
12
|
+
BLOCK_SIZE = 0x10000
|
13
|
+
|
14
|
+
@counter = 0
|
15
|
+
@counter_mutex = Mutex.new
|
16
|
+
|
17
|
+
def self.generate
|
18
|
+
thread = Thread.current
|
19
|
+
|
20
|
+
unless thread.uuid_limit
|
21
|
+
@counter_mutex.synchronize do
|
22
|
+
block_base = @counter
|
23
|
+
@counter += BLOCK_SIZE
|
24
|
+
thread.uuid_counter = block_base
|
25
|
+
thread.uuid_limit = @counter - 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
counter = thread.uuid_counter
|
30
|
+
if thread.uuid_counter >= thread.uuid_limit
|
31
|
+
thread.uuid_counter = thread.uuid_limit = nil
|
32
|
+
else
|
33
|
+
thread.uuid_counter += 1
|
34
|
+
end
|
35
|
+
|
36
|
+
"#{PREFIX}-#{format('%012x', counter)}".freeze
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
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 = $PROCESS_ID
|
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,147 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Celluloid
|
4
|
+
# A logger that holds all messages in circular buffers, then flushes the buffers
|
5
|
+
# when an event occurs at a configurable severity threshold.
|
6
|
+
#
|
7
|
+
# Unlike ruby's Logger, this class only supports a single progname.
|
8
|
+
class IncidentLogger
|
9
|
+
module Severity
|
10
|
+
include ::Logger::Severity
|
11
|
+
|
12
|
+
TRACE = -1
|
13
|
+
|
14
|
+
def severity_to_string(severity)
|
15
|
+
case severity
|
16
|
+
when TRACE then "TRACE"
|
17
|
+
when DEBUG then "DEBUG"
|
18
|
+
when INFO then "INFO"
|
19
|
+
when WARN then "WARN"
|
20
|
+
when ERROR then "ERROR"
|
21
|
+
when FATAL then "FATAL"
|
22
|
+
when UNKNOWN then "UNKNOWN"
|
23
|
+
end
|
24
|
+
end
|
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, pn|
|
53
|
+
@buffer_mutex.synchronize do
|
54
|
+
progname_hash[pn] = 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
|
+
return event.id if severity < @level
|
71
|
+
|
72
|
+
if message.nil? && !block_given?
|
73
|
+
message = progname
|
74
|
+
progname = @progname
|
75
|
+
end
|
76
|
+
|
77
|
+
event = LogEvent.new(severity, message, progname, &block)
|
78
|
+
|
79
|
+
@buffers[progname][severity] << event
|
80
|
+
|
81
|
+
if severity >= @threshold
|
82
|
+
begin
|
83
|
+
Celluloid::Notifications.notifier.async.publish(incident_topic, create_incident(event))
|
84
|
+
rescue => ex
|
85
|
+
@fallback_logger.error(ex)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
event.id
|
89
|
+
end
|
90
|
+
alias log add
|
91
|
+
|
92
|
+
# See docs for Logger#info
|
93
|
+
def trace(progname = nil, &block)
|
94
|
+
add(TRACE, nil, progname, &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
def debug(progname = nil, &block)
|
98
|
+
add(DEBUG, nil, progname, &block)
|
99
|
+
end
|
100
|
+
|
101
|
+
def info(progname = nil, &block)
|
102
|
+
add(INFO, nil, progname, &block)
|
103
|
+
end
|
104
|
+
|
105
|
+
def warn(progname = nil, &block)
|
106
|
+
add(WARN, nil, progname, &block)
|
107
|
+
end
|
108
|
+
|
109
|
+
def error(progname = nil, &block)
|
110
|
+
add(ERROR, nil, progname, &block)
|
111
|
+
end
|
112
|
+
|
113
|
+
def fatal(progname = nil, &block)
|
114
|
+
add(FATAL, nil, progname, &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
def unknown(progname = nil, &block)
|
118
|
+
add(UNKNOWN, nil, progname, &block)
|
119
|
+
end
|
120
|
+
|
121
|
+
def flush
|
122
|
+
messages = []
|
123
|
+
@buffer_mutex.synchronize do
|
124
|
+
@buffers.each do |_progname, severities|
|
125
|
+
severities.each do |_severity, buffer|
|
126
|
+
messages += buffer.flush
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
messages.sort
|
131
|
+
end
|
132
|
+
|
133
|
+
def clear
|
134
|
+
@buffer_mutex.synchronize do
|
135
|
+
@buffers.each(&:clear)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_incident(event = nil)
|
140
|
+
Incident.new(flush, event)
|
141
|
+
end
|
142
|
+
|
143
|
+
def incident_topic
|
144
|
+
"log.incident.#{@progname}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|