ffwd 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/ffwd +9 -0
- data/bin/fwc +15 -0
- data/lib/em/all.rb +68 -0
- data/lib/ffwd.rb +250 -0
- data/lib/ffwd/channel.rb +62 -0
- data/lib/ffwd/circular_buffer.rb +78 -0
- data/lib/ffwd/connection.rb +40 -0
- data/lib/ffwd/core.rb +173 -0
- data/lib/ffwd/core/emitter.rb +38 -0
- data/lib/ffwd/core/interface.rb +47 -0
- data/lib/ffwd/core/processor.rb +92 -0
- data/lib/ffwd/core/reporter.rb +32 -0
- data/lib/ffwd/debug.rb +76 -0
- data/lib/ffwd/debug/connection.rb +48 -0
- data/lib/ffwd/debug/monitor_session.rb +71 -0
- data/lib/ffwd/debug/tcp.rb +82 -0
- data/lib/ffwd/event.rb +65 -0
- data/lib/ffwd/event_emitter.rb +57 -0
- data/lib/ffwd/handler.rb +43 -0
- data/lib/ffwd/lifecycle.rb +92 -0
- data/lib/ffwd/logging.rb +139 -0
- data/lib/ffwd/metric.rb +55 -0
- data/lib/ffwd/metric_emitter.rb +50 -0
- data/lib/ffwd/plugin.rb +149 -0
- data/lib/ffwd/plugin/json_line.rb +47 -0
- data/lib/ffwd/plugin/json_line/connection.rb +118 -0
- data/lib/ffwd/plugin/log.rb +35 -0
- data/lib/ffwd/plugin/log/writer.rb +42 -0
- data/lib/ffwd/plugin_channel.rb +64 -0
- data/lib/ffwd/plugin_loader.rb +121 -0
- data/lib/ffwd/processor.rb +96 -0
- data/lib/ffwd/processor/count.rb +109 -0
- data/lib/ffwd/processor/histogram.rb +200 -0
- data/lib/ffwd/processor/rate.rb +116 -0
- data/lib/ffwd/producing_client.rb +181 -0
- data/lib/ffwd/protocol.rb +28 -0
- data/lib/ffwd/protocol/tcp.rb +126 -0
- data/lib/ffwd/protocol/tcp/bind.rb +64 -0
- data/lib/ffwd/protocol/tcp/connection.rb +107 -0
- data/lib/ffwd/protocol/tcp/flushing_connect.rb +135 -0
- data/lib/ffwd/protocol/tcp/plain_connect.rb +74 -0
- data/lib/ffwd/protocol/udp.rb +48 -0
- data/lib/ffwd/protocol/udp/bind.rb +64 -0
- data/lib/ffwd/protocol/udp/connect.rb +110 -0
- data/lib/ffwd/reporter.rb +65 -0
- data/lib/ffwd/retrier.rb +72 -0
- data/lib/ffwd/schema.rb +92 -0
- data/lib/ffwd/schema/default.rb +36 -0
- data/lib/ffwd/schema/spotify100.rb +58 -0
- data/lib/ffwd/statistics.rb +29 -0
- data/lib/ffwd/statistics/collector.rb +99 -0
- data/lib/ffwd/statistics/system_statistics.rb +255 -0
- data/lib/ffwd/tunnel.rb +27 -0
- data/lib/ffwd/tunnel/plugin.rb +47 -0
- data/lib/ffwd/tunnel/tcp.rb +60 -0
- data/lib/ffwd/tunnel/udp.rb +61 -0
- data/lib/ffwd/utils.rb +46 -0
- data/lib/ffwd/version.rb +18 -0
- data/lib/fwc.rb +206 -0
- metadata +163 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
module FFWD::Debug
|
17
|
+
class Connection < EM::Connection
|
18
|
+
include FFWD::Logging
|
19
|
+
|
20
|
+
def initialize handler
|
21
|
+
@handler = handler
|
22
|
+
@peer = nil
|
23
|
+
@ip = nil
|
24
|
+
@port = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_peer
|
28
|
+
peer = get_peername
|
29
|
+
port, ip = Socket.unpack_sockaddr_in(peer)
|
30
|
+
return peer, ip, port
|
31
|
+
end
|
32
|
+
|
33
|
+
def post_init
|
34
|
+
@peer, @ip, @port = get_peer
|
35
|
+
@handler.register_client @peer, self
|
36
|
+
log.info "#{@ip}:#{@port}: Connect"
|
37
|
+
end
|
38
|
+
|
39
|
+
def unbind
|
40
|
+
@handler.unregister_client @peer, self
|
41
|
+
log.info "#{@ip}:#{@port}: Disconnect"
|
42
|
+
end
|
43
|
+
|
44
|
+
def send_line line
|
45
|
+
send_data "#{line}\n"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require_relative '../lifecycle'
|
17
|
+
|
18
|
+
module FFWD::Debug
|
19
|
+
class MonitorSession
|
20
|
+
attr_reader :id
|
21
|
+
|
22
|
+
def initialize id, channel, type
|
23
|
+
@type = type
|
24
|
+
@clients = {}
|
25
|
+
|
26
|
+
subs = []
|
27
|
+
|
28
|
+
channel.starting do
|
29
|
+
subs << channel.event_subscribe do |event|
|
30
|
+
data = @type.serialize_event event
|
31
|
+
|
32
|
+
begin
|
33
|
+
send JSON.dump(:id => @id, :type => :event, :data => data)
|
34
|
+
rescue => e
|
35
|
+
log.error "Failed to serialize event", e
|
36
|
+
return
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
subs << channel.metric_subscribe do |metric|
|
41
|
+
data = @type.serialize_metric metric
|
42
|
+
|
43
|
+
begin
|
44
|
+
send JSON.dump(:id => @id, :type => :metric, :data => data)
|
45
|
+
rescue => e
|
46
|
+
log.error "Failed to serialize metric", e
|
47
|
+
return
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
channel.stopping do
|
53
|
+
subs.each(&:unsubscribe).clear
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def register peer, client
|
58
|
+
@clients[peer] = client
|
59
|
+
end
|
60
|
+
|
61
|
+
def unregister peer, client
|
62
|
+
@clients.delete peer
|
63
|
+
end
|
64
|
+
|
65
|
+
def send line
|
66
|
+
@clients.each do |peer, client|
|
67
|
+
client.send_line line
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require_relative '../lifecycle'
|
17
|
+
require_relative '../logging'
|
18
|
+
require_relative '../retrier'
|
19
|
+
|
20
|
+
require_relative 'connection'
|
21
|
+
require_relative 'monitor_session'
|
22
|
+
|
23
|
+
module FFWD::Debug
|
24
|
+
class TCP
|
25
|
+
include FFWD::Logging
|
26
|
+
include FFWD::Lifecycle
|
27
|
+
|
28
|
+
def initialize host, port, rebind_timeout
|
29
|
+
@clients = {}
|
30
|
+
@sessions = {}
|
31
|
+
@host = host
|
32
|
+
@port = port
|
33
|
+
@peer = "#{@host}:#{@port}"
|
34
|
+
|
35
|
+
r = FFWD.retry :timeout => rebind_timeout do |attempt|
|
36
|
+
EM.start_server @host, @port, Connection, self
|
37
|
+
log.info "Bind on tcp://#{@peer} (attempt #{attempt})"
|
38
|
+
end
|
39
|
+
|
40
|
+
r.error do |a, t, e|
|
41
|
+
log.error "Failed to bind tcp://#{@peer} (attempt #{a}), retry in #{t}s", e
|
42
|
+
end
|
43
|
+
|
44
|
+
r.depend_on self
|
45
|
+
end
|
46
|
+
|
47
|
+
def register_client peer, client
|
48
|
+
@sessions.each do |id, session|
|
49
|
+
session.register peer, client
|
50
|
+
end
|
51
|
+
|
52
|
+
@clients[peer] = client
|
53
|
+
end
|
54
|
+
|
55
|
+
def unregister_client peer, client
|
56
|
+
@sessions.each do |id, session|
|
57
|
+
session.unregister peer, client
|
58
|
+
end
|
59
|
+
|
60
|
+
@clients.delete peer
|
61
|
+
end
|
62
|
+
|
63
|
+
# Setup monitor hooks for the specified input and output channel.
|
64
|
+
def monitor id, channel, type
|
65
|
+
if session = @sessions[id]
|
66
|
+
log.error "Session already monitored: #{id}"
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
session = @sessions[id] = MonitorSession.new id, channel, type
|
71
|
+
|
72
|
+
# provide the session to the already connected clients.
|
73
|
+
@clients.each do |peer, client|
|
74
|
+
session.register peer, client
|
75
|
+
end
|
76
|
+
|
77
|
+
channel.stopping do
|
78
|
+
@sessions.delete id
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/ffwd/event.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
module FFWD
|
17
|
+
# Struct used to define all fields related to an event.
|
18
|
+
EventStruct = Struct.new(
|
19
|
+
# The time at which the event was collected.
|
20
|
+
:time,
|
21
|
+
# The unique key of the event.
|
22
|
+
:key,
|
23
|
+
# A numeric value associated with the event.
|
24
|
+
:value,
|
25
|
+
# The host from which the event originated.
|
26
|
+
:host,
|
27
|
+
# The source event this event was derived from (if any).
|
28
|
+
:source,
|
29
|
+
# A state associated to the event.
|
30
|
+
:state,
|
31
|
+
# A description associated to the event.
|
32
|
+
:description,
|
33
|
+
# A time to live associated with the event.
|
34
|
+
:ttl,
|
35
|
+
# Tags associated with the event.
|
36
|
+
:tags,
|
37
|
+
# Attributes (extra fields) associated with the event.
|
38
|
+
:attributes
|
39
|
+
)
|
40
|
+
|
41
|
+
# A convenience class for each individual event.
|
42
|
+
class Event < EventStruct
|
43
|
+
def self.make opts = {}
|
44
|
+
new(opts[:time], opts[:key], opts[:value], opts[:host], opts[:source],
|
45
|
+
opts[:state], opts[:description], opts[:ttl], opts[:tags],
|
46
|
+
opts[:attributes])
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convert event to a sparse hash.
|
50
|
+
def to_h
|
51
|
+
d = {}
|
52
|
+
d[:time] = time.to_i if time
|
53
|
+
d[:key] = key if key
|
54
|
+
d[:value] = value if value
|
55
|
+
d[:host] = host if host
|
56
|
+
d[:source] = source if source
|
57
|
+
d[:state] = state if state
|
58
|
+
d[:description] = description if description
|
59
|
+
d[:ttl] = ttl if ttl
|
60
|
+
d[:tags] = tags.to_a if tags
|
61
|
+
d[:attributes] = attributes if attributes
|
62
|
+
d
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require_relative 'utils'
|
17
|
+
require_relative 'event'
|
18
|
+
require_relative 'logging'
|
19
|
+
|
20
|
+
module FFWD
|
21
|
+
# Used to emit events to an 'output' channel
|
22
|
+
#
|
23
|
+
# Can take two parts of a configuration 'base' and 'opts' to decide which
|
24
|
+
# metadata emitted events should be decorated with.
|
25
|
+
class EventEmitter
|
26
|
+
include FFWD::Logging
|
27
|
+
|
28
|
+
def self.build output, base, opts
|
29
|
+
output = output
|
30
|
+
host = opts[:host] || base[:host] || FFWD.current_host
|
31
|
+
ttl = opts[:ttl] || base[:ttl]
|
32
|
+
tags = FFWD.merge_sets base[:tags], opts[:tags]
|
33
|
+
attributes = FFWD.merge_hashes base[:attributes], opts[:attributes]
|
34
|
+
new output, host, ttl, tags, attributes
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize output, host, ttl, tags, attributes
|
38
|
+
@output = output
|
39
|
+
@host = host
|
40
|
+
@ttl = ttl
|
41
|
+
@tags = tags
|
42
|
+
@attributes = attributes
|
43
|
+
end
|
44
|
+
|
45
|
+
def emit e
|
46
|
+
e[:time] ||= Time.now
|
47
|
+
e[:host] ||= @host if @host
|
48
|
+
e[:ttl] ||= @ttl if @ttl
|
49
|
+
e[:tags] = FFWD.merge_sets @tags, e[:tags]
|
50
|
+
e[:attributes] = FFWD.merge_hashes @attributes, e[:attributes]
|
51
|
+
|
52
|
+
@output.event Event.make(e)
|
53
|
+
rescue => e
|
54
|
+
log.error "Failed to emit event", e
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/ffwd/handler.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
require_relative 'connection'
|
17
|
+
|
18
|
+
module FFWD
|
19
|
+
# Handlers are used by output plugins based of the protocol stack.
|
20
|
+
class Handler < FFWD::Connection
|
21
|
+
def self.new signature, parent, *args
|
22
|
+
instance = super(signature, *args)
|
23
|
+
|
24
|
+
instance.instance_eval do
|
25
|
+
@parent = parent
|
26
|
+
end
|
27
|
+
|
28
|
+
instance
|
29
|
+
end
|
30
|
+
|
31
|
+
def unbind
|
32
|
+
@parent.unbind
|
33
|
+
end
|
34
|
+
|
35
|
+
def connection_completed
|
36
|
+
@parent.connection_completed
|
37
|
+
end
|
38
|
+
|
39
|
+
def send_all events, metrics; end
|
40
|
+
def send_event event; end
|
41
|
+
def send_metric metric; end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# $LICENSE
|
2
|
+
# Copyright 2013-2014 Spotify AB. All rights reserved.
|
3
|
+
#
|
4
|
+
# The contents of this file are licensed under the Apache License, Version 2.0
|
5
|
+
# (the "License"); you may not use this file except in compliance with the
|
6
|
+
# License. You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations under
|
14
|
+
# the License.
|
15
|
+
|
16
|
+
# Lifecycle management module.
|
17
|
+
#
|
18
|
+
# Any class and module including this will allow other components to subscribe
|
19
|
+
# to their state changes (starting, stopping).
|
20
|
+
module FFWD
|
21
|
+
module Lifecycle
|
22
|
+
def stopping_hooks
|
23
|
+
@stopping_hooks ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def starting_hooks
|
27
|
+
@starting_hooks ||= []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Register a callback to be executed when the Stoppable is to be stopped.
|
31
|
+
#
|
32
|
+
# This will only be called once.
|
33
|
+
def stopping &block
|
34
|
+
if stopped?
|
35
|
+
block.call
|
36
|
+
else
|
37
|
+
stopping_hooks << block
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def starting &block
|
42
|
+
if started?
|
43
|
+
block.call
|
44
|
+
else
|
45
|
+
starting_hooks << block
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def start
|
50
|
+
return if started?
|
51
|
+
starting_hooks.each(&:call)
|
52
|
+
starting_hooks.clear
|
53
|
+
@state = :started
|
54
|
+
end
|
55
|
+
|
56
|
+
def stop
|
57
|
+
return if stopped?
|
58
|
+
stopping_hooks.each(&:call)
|
59
|
+
stopping_hooks.clear
|
60
|
+
@state = :stopped
|
61
|
+
end
|
62
|
+
|
63
|
+
def started?
|
64
|
+
(@state ||= :none) == :started
|
65
|
+
end
|
66
|
+
|
67
|
+
def stopped?
|
68
|
+
(@state ||= :none) == :stopped
|
69
|
+
end
|
70
|
+
|
71
|
+
def depend_on other_lifecycle
|
72
|
+
if other_lifecycle.nil?
|
73
|
+
raise "Other lifecycle must not be nil"
|
74
|
+
end
|
75
|
+
|
76
|
+
if (@depends ||= nil)
|
77
|
+
raise "This component already depends on #{@depends}"
|
78
|
+
end
|
79
|
+
|
80
|
+
@depends = other_lifecycle
|
81
|
+
|
82
|
+
other_lifecycle.starting do
|
83
|
+
start
|
84
|
+
end
|
85
|
+
|
86
|
+
other_lifecycle.stopping do
|
87
|
+
stop
|
88
|
+
@depends = nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|