-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathtcp.rb
155 lines (134 loc) · 4.44 KB
/
tcp.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# encoding: utf-8
require "logstash/outputs/base"
require "logstash/namespace"
require "thread"
# Write events over a TCP socket.
#
# Each event json is separated by a newline.
#
# Can either accept connections from clients or connect to a server,
# depending on `mode`.
class LogStash::Outputs::Tcp < LogStash::Outputs::Base
config_name "tcp"
default :codec, "json"
# When mode is `server`, the address to listen on.
# When mode is `client`, the address to connect to.
config :host, :validate => :array, :required => true
# When mode is `server`, the port to listen on.
# When mode is `client`, the port to connect to.
config :port, :validate => :number, :required => true
# When connect failed,retry interval in sec.
config :reconnect_interval, :validate => :number, :default => 10
# Mode to operate in. `server` listens for client connections,
# `client` connects to a server.
config :mode, :validate => ["server", "client"], :default => "client"
# The format to use when writing events to the file. This value
# supports any string and can include `%{name}` and other dynamic
# strings.
#
# If this setting is omitted, the full json representation of the
# event will be written as a single line.
config :message_format, :validate => :string, :deprecated => true
class Client
public
def initialize(socket, logger)
@socket = socket
@logger = logger
@queue = Queue.new
end
public
def run
loop do
begin
@socket.write(@queue.pop)
rescue => e
@logger.warn("tcp output exception", :socket => @socket,
:exception => e)
break
end
end
end # def run
public
def write(msg)
@queue.push(msg)
end # def write
end # class Client
public
def register
require "socket"
require "stud/try"
if server?
workers_not_supported
raise LogStash::ConfigurationError, "You cannot pass an array of hosts in server mode" if @host.is_a? Array and @host.count > 1
@host = @host.first
@logger.info("Starting tcp output listener", :address => "#{@host}:#{@port}")
@server_socket = TCPServer.new(@host, @port)
@client_threads = []
@accept_thread = Thread.new(@server_socket) do |server_socket|
loop do
client_thread = Thread.start(server_socket.accept) do |client_socket|
client = Client.new(client_socket, @logger)
Thread.current[:client] = client
client.run
end
@client_threads << client_thread
end
end
@codec.on_event do |event, payload|
@client_threads.each do |client_thread|
client_thread[:client].write(payload)
end
@client_threads.reject! {|t| !t.alive? }
end
else
client_socket = nil
@codec.on_event do |event, payload|
begin
client_socket = connect unless client_socket
r,w,e = IO.select([client_socket], [client_socket], [client_socket], nil)
# don't expect any reads, but a readable socket might
# mean the remote end closed, so read it and throw it away.
# we'll get an EOFError if it happens.
client_socket.sysread(16384) if r.any?
# Now send the payload
client_socket.syswrite(payload) if w.any?
rescue => e
@logger.warn("tcp output exception", :host => @host, :port => @port,
:exception => e, :backtrace => e.backtrace)
client_socket.close rescue nil
client_socket = nil
sleep @reconnect_interval
retry
end
end
end
end # def register
private
def connect
Stud::try do
@host.each do |shost|
begin
return TCPSocket.new(shost, @port)
rescue => e
@logger.warn("Connection failed", :host => @host, :port => @port,
:exception => e, :backtrace => e.backtrace)
end
end
end
@logger.error("All hosts unavailable", :hosts => @host, :port => @port)
raise StandardError, "Cannot connect - All hosts unavailable"
end # def connect
private
def server?
@mode == "server"
end # def server?
public
def receive(event)
#if @message_format
#output = event.sprintf(@message_format) + "\n"
#else
#output = event.to_hash.to_json + "\n"
#end
@codec.encode(event)
end # def receive
end # class LogStash::Outputs::Tcp