dogstatsd-ruby 4.0.0 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +186 -57
- data/lib/datadog/statsd/connection.rb +60 -0
- data/lib/datadog/statsd/connection_cfg.rb +76 -0
- data/lib/datadog/statsd/forwarder.rb +133 -0
- data/lib/datadog/statsd/message_buffer.rb +97 -0
- data/lib/datadog/statsd/sender.rb +181 -0
- data/lib/datadog/statsd/serialization/event_serializer.rb +71 -0
- data/lib/datadog/statsd/serialization/serializer.rb +41 -0
- data/lib/datadog/statsd/serialization/service_check_serializer.rb +60 -0
- data/lib/datadog/statsd/serialization/stat_serializer.rb +55 -0
- data/lib/datadog/statsd/serialization/tag_serializer.rb +96 -0
- data/lib/datadog/statsd/serialization.rb +15 -0
- data/lib/datadog/statsd/single_thread_sender.rb +68 -0
- data/lib/datadog/statsd/telemetry.rb +117 -0
- data/lib/datadog/statsd/timer.rb +61 -0
- data/lib/datadog/statsd/udp_connection.rb +46 -0
- data/lib/datadog/statsd/uds_connection.rb +49 -0
- data/lib/datadog/statsd/version.rb +9 -0
- data/lib/datadog/statsd.rb +209 -333
- metadata +38 -11
@@ -0,0 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
# Sender is using a companion thread to flush and pack messages
|
6
|
+
# in a `MessageBuffer`.
|
7
|
+
# The communication with this thread is done using a `Queue`.
|
8
|
+
# If the thread is dead, it is starting a new one to avoid having a blocked
|
9
|
+
# Sender with no companion thread to communicate with (most of the time, having
|
10
|
+
# a dead companion thread means that a fork just happened and that we are
|
11
|
+
# running in the child process).
|
12
|
+
class Sender
|
13
|
+
CLOSEABLE_QUEUES = Queue.instance_methods.include?(:close)
|
14
|
+
|
15
|
+
def initialize(message_buffer, telemetry: nil, queue_size: UDP_DEFAULT_BUFFER_SIZE, logger: nil, flush_interval: nil, queue_class: Queue, thread_class: Thread)
|
16
|
+
@message_buffer = message_buffer
|
17
|
+
@telemetry = telemetry
|
18
|
+
@queue_size = queue_size
|
19
|
+
@logger = logger
|
20
|
+
@mx = Mutex.new
|
21
|
+
@queue_class = queue_class
|
22
|
+
@thread_class = thread_class
|
23
|
+
@flush_timer = if flush_interval
|
24
|
+
Datadog::Statsd::Timer.new(flush_interval) { flush(sync: true) }
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def flush(sync: false)
|
31
|
+
# keep a copy around in case another thread is calling #stop while this method is running
|
32
|
+
current_message_queue = message_queue
|
33
|
+
|
34
|
+
# don't try to flush if there is no message_queue instantiated or
|
35
|
+
# no companion thread running
|
36
|
+
if !current_message_queue
|
37
|
+
@logger.debug { "Statsd: can't flush: no message queue ready" } if @logger
|
38
|
+
return
|
39
|
+
end
|
40
|
+
if !sender_thread.alive?
|
41
|
+
@logger.debug { "Statsd: can't flush: no sender_thread alive" } if @logger
|
42
|
+
return
|
43
|
+
end
|
44
|
+
|
45
|
+
current_message_queue.push(:flush)
|
46
|
+
rendez_vous if sync
|
47
|
+
end
|
48
|
+
|
49
|
+
def rendez_vous
|
50
|
+
# could happen if #start hasn't be called
|
51
|
+
return unless message_queue
|
52
|
+
|
53
|
+
# Initialize and get the thread's sync queue
|
54
|
+
queue = (@thread_class.current[:statsd_sync_queue] ||= @queue_class.new)
|
55
|
+
# tell sender-thread to notify us in the current
|
56
|
+
# thread's queue
|
57
|
+
message_queue.push(queue)
|
58
|
+
# wait for the sender thread to send a message
|
59
|
+
# once the flush is done
|
60
|
+
queue.pop
|
61
|
+
end
|
62
|
+
|
63
|
+
def add(message)
|
64
|
+
raise ArgumentError, 'Start sender first' unless message_queue
|
65
|
+
|
66
|
+
# if the thread does not exist, we assume we are running in a forked process,
|
67
|
+
# empty the message queue and message buffers (these messages belong to
|
68
|
+
# the parent process) and spawn a new companion thread.
|
69
|
+
if !sender_thread.alive?
|
70
|
+
@mx.synchronize {
|
71
|
+
# a call from another thread has already re-created
|
72
|
+
# the companion thread before this one acquired the lock
|
73
|
+
break if sender_thread.alive?
|
74
|
+
@logger.debug { "Statsd: companion thread is dead, re-creating one" } if @logger
|
75
|
+
|
76
|
+
message_queue.close if CLOSEABLE_QUEUES
|
77
|
+
@message_queue = nil
|
78
|
+
message_buffer.reset
|
79
|
+
start
|
80
|
+
@flush_timer.start if @flush_timer && @flush_timer.stop?
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
if message_queue.length <= @queue_size
|
85
|
+
message_queue << message
|
86
|
+
else
|
87
|
+
@telemetry.dropped_queue(packets: 1, bytes: message.bytesize) if @telemetry
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def start
|
92
|
+
raise ArgumentError, 'Sender already started' if message_queue
|
93
|
+
|
94
|
+
# initialize a new message queue for the background thread
|
95
|
+
@message_queue = @queue_class.new
|
96
|
+
# start background thread
|
97
|
+
@sender_thread = @thread_class.new(&method(:send_loop))
|
98
|
+
@sender_thread.name = "Statsd Sender" unless Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3')
|
99
|
+
@flush_timer.start if @flush_timer
|
100
|
+
end
|
101
|
+
|
102
|
+
if CLOSEABLE_QUEUES
|
103
|
+
# when calling stop, make sure that no other threads is trying
|
104
|
+
# to close the sender nor trying to continue to `#add` more message
|
105
|
+
# into the sender.
|
106
|
+
def stop(join_worker: true)
|
107
|
+
@flush_timer.stop if @flush_timer
|
108
|
+
|
109
|
+
message_queue = @message_queue
|
110
|
+
message_queue.close if message_queue
|
111
|
+
|
112
|
+
sender_thread = @sender_thread
|
113
|
+
sender_thread.join if sender_thread && join_worker
|
114
|
+
end
|
115
|
+
else
|
116
|
+
# when calling stop, make sure that no other threads is trying
|
117
|
+
# to close the sender nor trying to continue to `#add` more message
|
118
|
+
# into the sender.
|
119
|
+
def stop(join_worker: true)
|
120
|
+
@flush_timer.stop if @flush_timer
|
121
|
+
|
122
|
+
message_queue = @message_queue
|
123
|
+
message_queue << :close if message_queue
|
124
|
+
|
125
|
+
sender_thread = @sender_thread
|
126
|
+
sender_thread.join if sender_thread && join_worker
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
attr_reader :message_buffer
|
133
|
+
attr_reader :message_queue
|
134
|
+
attr_reader :sender_thread
|
135
|
+
|
136
|
+
if CLOSEABLE_QUEUES
|
137
|
+
def send_loop
|
138
|
+
until (message = message_queue.pop).nil? && message_queue.closed?
|
139
|
+
# skip if message is nil, e.g. when message_queue
|
140
|
+
# is empty and closed
|
141
|
+
next unless message
|
142
|
+
|
143
|
+
case message
|
144
|
+
when :flush
|
145
|
+
message_buffer.flush
|
146
|
+
when @queue_class
|
147
|
+
message.push(:go_on)
|
148
|
+
else
|
149
|
+
message_buffer.add(message)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
@message_queue = nil
|
154
|
+
@sender_thread = nil
|
155
|
+
end
|
156
|
+
else
|
157
|
+
def send_loop
|
158
|
+
loop do
|
159
|
+
message = message_queue.pop
|
160
|
+
|
161
|
+
next unless message
|
162
|
+
|
163
|
+
case message
|
164
|
+
when :close
|
165
|
+
break
|
166
|
+
when :flush
|
167
|
+
message_buffer.flush
|
168
|
+
when @queue_class
|
169
|
+
message.push(:go_on)
|
170
|
+
else
|
171
|
+
message_buffer.add(message)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
@message_queue = nil
|
176
|
+
@sender_thread = nil
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
module Serialization
|
6
|
+
class EventSerializer
|
7
|
+
EVENT_BASIC_OPTIONS = {
|
8
|
+
date_happened: 'd:',
|
9
|
+
hostname: 'h:',
|
10
|
+
aggregation_key: 'k:',
|
11
|
+
priority: 'p:',
|
12
|
+
source_type_name: 's:',
|
13
|
+
alert_type: 't:',
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize(global_tags: [])
|
17
|
+
@tag_serializer = TagSerializer.new(global_tags)
|
18
|
+
end
|
19
|
+
|
20
|
+
def format(title, text, options = EMPTY_OPTIONS)
|
21
|
+
title = escape(title)
|
22
|
+
text = escape(text)
|
23
|
+
|
24
|
+
String.new.tap do |event|
|
25
|
+
event << '_e{'
|
26
|
+
event << title.bytesize.to_s
|
27
|
+
event << ','
|
28
|
+
event << text.bytesize.to_s
|
29
|
+
event << '}:'
|
30
|
+
event << title
|
31
|
+
event << '|'
|
32
|
+
event << text
|
33
|
+
|
34
|
+
# we are serializing the generic service check options
|
35
|
+
# before serializing specialized options that need edge-cases
|
36
|
+
EVENT_BASIC_OPTIONS.each do |option_key, shortcut|
|
37
|
+
if value = options[option_key]
|
38
|
+
event << '|'
|
39
|
+
event << shortcut
|
40
|
+
event << value.to_s.delete('|')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# also returns the global tags from serializer
|
45
|
+
if tags = tag_serializer.format(options[:tags])
|
46
|
+
event << '|#'
|
47
|
+
event << tags
|
48
|
+
end
|
49
|
+
|
50
|
+
if event.bytesize > MAX_EVENT_SIZE
|
51
|
+
if options[:truncate_if_too_long]
|
52
|
+
event.slice!(MAX_EVENT_SIZE..event.length)
|
53
|
+
else
|
54
|
+
raise "Event #{title} payload is too big (more that 8KB), event discarded"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
attr_reader :tag_serializer
|
62
|
+
|
63
|
+
def escape(text)
|
64
|
+
text.delete('|').tap do |t|
|
65
|
+
t.gsub!("\n", '\n')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require 'forwardable'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
class Statsd
|
7
|
+
module Serialization
|
8
|
+
class Serializer
|
9
|
+
def initialize(prefix: nil, global_tags: [])
|
10
|
+
@stat_serializer = StatSerializer.new(prefix, global_tags: global_tags)
|
11
|
+
@service_check_serializer = ServiceCheckSerializer.new(global_tags: global_tags)
|
12
|
+
@event_serializer = EventSerializer.new(global_tags: global_tags)
|
13
|
+
end
|
14
|
+
|
15
|
+
# using *args would make new allocations
|
16
|
+
def to_stat(name, delta, type, tags: [], sample_rate: 1)
|
17
|
+
stat_serializer.format(name, delta, type, tags: tags, sample_rate: sample_rate)
|
18
|
+
end
|
19
|
+
|
20
|
+
# using *args would make new allocations
|
21
|
+
def to_service_check(name, status, options = EMPTY_OPTIONS)
|
22
|
+
service_check_serializer.format(name, status, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
# using *args would make new allocations
|
26
|
+
def to_event(title, text, options = EMPTY_OPTIONS)
|
27
|
+
event_serializer.format(title, text, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def global_tags
|
31
|
+
stat_serializer.global_tags
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
attr_reader :stat_serializer
|
36
|
+
attr_reader :service_check_serializer
|
37
|
+
attr_reader :event_serializer
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
module Serialization
|
6
|
+
class ServiceCheckSerializer
|
7
|
+
SERVICE_CHECK_BASIC_OPTIONS = {
|
8
|
+
timestamp: 'd:',
|
9
|
+
hostname: 'h:',
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def initialize(global_tags: [])
|
13
|
+
@tag_serializer = TagSerializer.new(global_tags)
|
14
|
+
end
|
15
|
+
|
16
|
+
def format(name, status, options = EMPTY_OPTIONS)
|
17
|
+
String.new.tap do |service_check|
|
18
|
+
# line basics
|
19
|
+
service_check << "_sc"
|
20
|
+
service_check << "|"
|
21
|
+
service_check << name.to_s
|
22
|
+
service_check << "|"
|
23
|
+
service_check << status.to_s
|
24
|
+
|
25
|
+
# we are serializing the generic service check options
|
26
|
+
# before serializing specialized options that need edge-cases
|
27
|
+
SERVICE_CHECK_BASIC_OPTIONS.each do |option_key, shortcut|
|
28
|
+
if value = options[option_key]
|
29
|
+
service_check << '|'
|
30
|
+
service_check << shortcut
|
31
|
+
service_check << value.to_s.delete('|')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if message = options[:message]
|
36
|
+
service_check << '|m:'
|
37
|
+
service_check << escape_message(message)
|
38
|
+
end
|
39
|
+
|
40
|
+
# also returns the global tags from serializer
|
41
|
+
if tags = tag_serializer.format(options[:tags])
|
42
|
+
service_check << '|#'
|
43
|
+
service_check << tags
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
attr_reader :tag_serializer
|
50
|
+
|
51
|
+
def escape_message(message)
|
52
|
+
message.delete('|').tap do |m|
|
53
|
+
m.gsub!("\n", '\n')
|
54
|
+
m.gsub!('m:', 'm\:')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
module Serialization
|
6
|
+
class StatSerializer
|
7
|
+
def initialize(prefix, global_tags: [])
|
8
|
+
@prefix = prefix
|
9
|
+
@prefix_str = prefix.to_s
|
10
|
+
@tag_serializer = TagSerializer.new(global_tags)
|
11
|
+
end
|
12
|
+
|
13
|
+
def format(name, delta, type, tags: [], sample_rate: 1)
|
14
|
+
name = formated_name(name)
|
15
|
+
|
16
|
+
if sample_rate != 1
|
17
|
+
if tags_list = tag_serializer.format(tags)
|
18
|
+
"#{@prefix_str}#{name}:#{delta}|#{type}|@#{sample_rate}|##{tags_list}"
|
19
|
+
else
|
20
|
+
"#{@prefix_str}#{name}:#{delta}|#{type}|@#{sample_rate}"
|
21
|
+
end
|
22
|
+
else
|
23
|
+
if tags_list = tag_serializer.format(tags)
|
24
|
+
"#{@prefix_str}#{name}:#{delta}|#{type}|##{tags_list}"
|
25
|
+
else
|
26
|
+
"#{@prefix_str}#{name}:#{delta}|#{type}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def global_tags
|
32
|
+
tag_serializer.global_tags
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :prefix
|
38
|
+
attr_reader :tag_serializer
|
39
|
+
|
40
|
+
def formated_name(name)
|
41
|
+
if name.is_a?(String)
|
42
|
+
# DEV: gsub is faster than dup.gsub!
|
43
|
+
formated = name.gsub('::', '.')
|
44
|
+
else
|
45
|
+
formated = name.to_s
|
46
|
+
formated.gsub!('::', '.')
|
47
|
+
end
|
48
|
+
|
49
|
+
formated.tr!(':|@', '_')
|
50
|
+
formated
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
module Serialization
|
6
|
+
class TagSerializer
|
7
|
+
def initialize(global_tags = [], env = ENV)
|
8
|
+
# Convert to hash
|
9
|
+
global_tags = to_tags_hash(global_tags)
|
10
|
+
|
11
|
+
# Merge with default tags
|
12
|
+
global_tags = default_tags(env).merge(global_tags)
|
13
|
+
|
14
|
+
# Convert to tag list and set
|
15
|
+
@global_tags = to_tags_list(global_tags)
|
16
|
+
if @global_tags.any?
|
17
|
+
@global_tags_formatted = @global_tags.join(',')
|
18
|
+
else
|
19
|
+
@global_tags_formatted = nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def format(message_tags)
|
24
|
+
if !message_tags || message_tags.empty?
|
25
|
+
return @global_tags_formatted
|
26
|
+
end
|
27
|
+
|
28
|
+
tags = if @global_tags_formatted
|
29
|
+
[@global_tags_formatted, to_tags_list(message_tags)]
|
30
|
+
else
|
31
|
+
to_tags_list(message_tags)
|
32
|
+
end
|
33
|
+
|
34
|
+
tags.join(',')
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :global_tags
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def to_tags_hash(tags)
|
42
|
+
case tags
|
43
|
+
when Hash
|
44
|
+
tags.dup
|
45
|
+
when Array
|
46
|
+
Hash[
|
47
|
+
tags.map do |string|
|
48
|
+
tokens = string.split(':')
|
49
|
+
tokens << nil if tokens.length == 1
|
50
|
+
tokens.length == 2 ? tokens : nil
|
51
|
+
end.compact
|
52
|
+
]
|
53
|
+
else
|
54
|
+
{}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_tags_list(tags)
|
59
|
+
case tags
|
60
|
+
when Hash
|
61
|
+
tags.map do |name, value|
|
62
|
+
if value
|
63
|
+
escape_tag_content("#{name}:#{value}")
|
64
|
+
else
|
65
|
+
escape_tag_content(name)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
when Array
|
69
|
+
tags.map { |tag| escape_tag_content(tag) }
|
70
|
+
else
|
71
|
+
[]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def escape_tag_content(tag)
|
76
|
+
tag.to_s.delete('|,')
|
77
|
+
end
|
78
|
+
|
79
|
+
def dd_tags(env = ENV)
|
80
|
+
return {} unless dd_tags = env['DD_TAGS']
|
81
|
+
|
82
|
+
to_tags_hash(dd_tags.split(','))
|
83
|
+
end
|
84
|
+
|
85
|
+
def default_tags(env = ENV)
|
86
|
+
dd_tags(env).tap do |tags|
|
87
|
+
tags['dd.internal.entity_id'] = env['DD_ENTITY_ID'] if env.key?('DD_ENTITY_ID')
|
88
|
+
tags['env'] = env['DD_ENV'] if env.key?('DD_ENV')
|
89
|
+
tags['service'] = env['DD_SERVICE'] if env.key?('DD_SERVICE')
|
90
|
+
tags['version'] = env['DD_VERSION'] if env.key?('DD_VERSION')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
module Serialization
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative 'serialization/tag_serializer'
|
11
|
+
require_relative 'serialization/service_check_serializer'
|
12
|
+
require_relative 'serialization/event_serializer'
|
13
|
+
require_relative 'serialization/stat_serializer'
|
14
|
+
|
15
|
+
require_relative 'serialization/serializer'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
class Statsd
|
5
|
+
# The SingleThreadSender is a sender synchronously buffering messages
|
6
|
+
# in a `MessageBuffer`.
|
7
|
+
# It is using current Process.PID to check it is the result of a recent fork
|
8
|
+
# and it is reseting the MessageBuffer if that's the case.
|
9
|
+
class SingleThreadSender
|
10
|
+
def initialize(message_buffer, logger: nil, flush_interval: nil)
|
11
|
+
@message_buffer = message_buffer
|
12
|
+
@logger = logger
|
13
|
+
@mx = Mutex.new
|
14
|
+
@flush_timer = if flush_interval
|
15
|
+
Datadog::Statsd::Timer.new(flush_interval) { flush }
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
# store the pid for which this sender has been created
|
20
|
+
update_fork_pid
|
21
|
+
end
|
22
|
+
|
23
|
+
def add(message)
|
24
|
+
@mx.synchronize {
|
25
|
+
# we have just forked, meaning we have messages in the buffer that we should
|
26
|
+
# not send, they belong to the parent process, let's clear the buffer.
|
27
|
+
if forked?
|
28
|
+
@message_buffer.reset
|
29
|
+
@flush_timer.start if @flush_timer && @flush_timer.stop?
|
30
|
+
update_fork_pid
|
31
|
+
end
|
32
|
+
@message_buffer.add(message)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def flush(*)
|
37
|
+
@mx.synchronize {
|
38
|
+
@message_buffer.flush()
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def start()
|
43
|
+
@flush_timer.start if @flush_timer
|
44
|
+
end
|
45
|
+
|
46
|
+
def stop()
|
47
|
+
@flush_timer.stop if @flush_timer
|
48
|
+
end
|
49
|
+
|
50
|
+
# Compatibility with `Sender`
|
51
|
+
def rendez_vous()
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# below are "fork management" methods to be able to clean the MessageBuffer
|
57
|
+
# if it detects that it is running in a unknown PID.
|
58
|
+
|
59
|
+
def forked?
|
60
|
+
Process.pid != @fork_pid
|
61
|
+
end
|
62
|
+
|
63
|
+
def update_fork_pid
|
64
|
+
@fork_pid = Process.pid
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|