fluent-plugin-unix-client 0.1.0 → 1.0.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 +59 -0
- data/fluent-plugin-unix-client.gemspec +1 -1
- data/lib/fluent/plugin/in_unix_client.rb +65 -6
- data/test/plugin/test_in_unix_client.rb +97 -10
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e730565e1f497509cb2d5e579527d5b500ba3240312fa0bb6527972db7e57ff4
|
4
|
+
data.tar.gz: 9d4a45f7a0c3cbf6b30b00be745dc624ed79d5b7e10f9753c7af2a5728148cca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a186a0de2333d95f32162f54b38fcab619b2e113122d0e08c7d10338aa9e3b64eb6c838ef34828772fec956d4c255bfd11be5c84af5aa703a631381bb7800b5
|
7
|
+
data.tar.gz: dfd15243ad040f815bd5c9c5dccb24c72bc57cadcbacc118cad2f4a1f0a597bfcbcc4d801e3294d63af04fbd440555d972165a33a08a88ef07a20a19e0c894a9
|
data/README.md
CHANGED
@@ -48,8 +48,20 @@ The payload is read up to this character.
|
|
48
48
|
|
49
49
|
Default value: `"\n"` (newline).
|
50
50
|
|
51
|
+
### format_json (bool) (optional)
|
52
|
+
|
53
|
+
When recieved JSON data splitted by the delimiter is not completed, like '[{...},', trim '[', ']' and ',' characters to format.
|
54
|
+
|
55
|
+
Please see `Sample` below for details.
|
56
|
+
|
57
|
+
Default value: false.
|
58
|
+
|
51
59
|
## Sample
|
52
60
|
|
61
|
+
### For JSON
|
62
|
+
|
63
|
+
Config:
|
64
|
+
|
53
65
|
```
|
54
66
|
<source>
|
55
67
|
@type unix_client
|
@@ -66,6 +78,53 @@ Default value: `"\n"` (newline).
|
|
66
78
|
</match>
|
67
79
|
```
|
68
80
|
|
81
|
+
Assumed Data:
|
82
|
+
|
83
|
+
* ndjson
|
84
|
+
```
|
85
|
+
{"key":0}\n
|
86
|
+
{"key":0}\n
|
87
|
+
...
|
88
|
+
```
|
89
|
+
|
90
|
+
* JSON list
|
91
|
+
```
|
92
|
+
[{"key":0}, {"key":0}, ...]\n
|
93
|
+
[{"key":0}, {"key":0}, ...]\n
|
94
|
+
...
|
95
|
+
```
|
96
|
+
|
97
|
+
### Use `format_json`
|
98
|
+
|
99
|
+
Config:
|
100
|
+
|
101
|
+
```
|
102
|
+
<source>
|
103
|
+
@type unix_client
|
104
|
+
tag debug.unix_client
|
105
|
+
path /tmp/unix.sock
|
106
|
+
<parse>
|
107
|
+
@type json
|
108
|
+
</parse>
|
109
|
+
delimiter "\n"
|
110
|
+
format_json true
|
111
|
+
</source>
|
112
|
+
|
113
|
+
<match debug.**>
|
114
|
+
@type stdout
|
115
|
+
</match>
|
116
|
+
```
|
117
|
+
|
118
|
+
Assumed Data:
|
119
|
+
|
120
|
+
```
|
121
|
+
[{"key":0},\n
|
122
|
+
{"key":0},\n
|
123
|
+
...
|
124
|
+
{"key":0}]\n
|
125
|
+
...
|
126
|
+
```
|
127
|
+
|
69
128
|
## Specification
|
70
129
|
|
71
130
|
* This recieves data from UNIX domain socket which **is opened by another application**.
|
@@ -15,6 +15,7 @@
|
|
15
15
|
|
16
16
|
require "fluent/plugin/input"
|
17
17
|
require 'socket'
|
18
|
+
require "json"
|
18
19
|
|
19
20
|
module Fluent
|
20
21
|
module Plugin
|
@@ -29,11 +30,19 @@ module Fluent
|
|
29
30
|
config_param :path, :string
|
30
31
|
desc 'The payload is read up to this character.'
|
31
32
|
config_param :delimiter, :string, default: "\n"
|
33
|
+
desc "When recieved JSON data splitted by the delimiter is not completed, like '[{...},'," \
|
34
|
+
" trim '[', ']' and ',' characters to format."
|
35
|
+
config_param :format_json, :bool, default: false
|
32
36
|
|
33
37
|
def configure(conf)
|
34
38
|
super
|
35
39
|
@parser = parser_create
|
36
|
-
@socket_handler = SocketHandler.new(
|
40
|
+
@socket_handler = SocketHandler.new(
|
41
|
+
@path,
|
42
|
+
delimiter: @delimiter,
|
43
|
+
format_json: @format_json,
|
44
|
+
log: log,
|
45
|
+
)
|
37
46
|
end
|
38
47
|
|
39
48
|
def start
|
@@ -60,21 +69,34 @@ module Fluent
|
|
60
69
|
|
61
70
|
raw_records.each do |raw_record|
|
62
71
|
@parser.parse(raw_record) do |time, record|
|
63
|
-
|
72
|
+
emit_one_parsed(time, record)
|
64
73
|
end
|
65
74
|
end
|
66
75
|
end
|
76
|
+
|
77
|
+
def emit_one_parsed(time, record)
|
78
|
+
case record
|
79
|
+
when Array
|
80
|
+
es = Fluent::MultiEventStream.new
|
81
|
+
record.each do |e|
|
82
|
+
es.add(time, e)
|
83
|
+
end
|
84
|
+
router.emit_stream(@tag, es)
|
85
|
+
else
|
86
|
+
router.emit(@tag, time, record)
|
87
|
+
end
|
88
|
+
end
|
67
89
|
end
|
68
90
|
|
69
91
|
|
70
92
|
class SocketHandler
|
71
93
|
MAX_LENGTH_RECEIVE_ONCE = 10000
|
72
94
|
|
73
|
-
def initialize(path, delimiter: "\n", log: nil)
|
95
|
+
def initialize(path, delimiter: "\n", format_json: false, log: nil)
|
74
96
|
@path = path
|
75
97
|
@log = log
|
76
98
|
@socket = nil
|
77
|
-
@buf = Buffer.new(delimiter)
|
99
|
+
@buf = Buffer.new(delimiter, format_json: format_json)
|
78
100
|
end
|
79
101
|
|
80
102
|
def connected?
|
@@ -142,9 +164,10 @@ module Fluent
|
|
142
164
|
|
143
165
|
|
144
166
|
class Buffer
|
145
|
-
def initialize(delimiter)
|
167
|
+
def initialize(delimiter, format_json: false)
|
146
168
|
@buf = ""
|
147
169
|
@delimiter = delimiter
|
170
|
+
@format_json = format_json
|
148
171
|
end
|
149
172
|
|
150
173
|
def add(data)
|
@@ -160,7 +183,8 @@ module Fluent
|
|
160
183
|
|
161
184
|
pos_read = 0
|
162
185
|
while pos_next_delimiter = @buf.index(@delimiter, pos_read)
|
163
|
-
|
186
|
+
fixed = fix_format(@buf[pos_read...pos_next_delimiter])
|
187
|
+
records << fixed unless fixed.empty?
|
164
188
|
pos_read = pos_next_delimiter + @delimiter.size
|
165
189
|
end
|
166
190
|
|
@@ -168,6 +192,41 @@ module Fluent
|
|
168
192
|
|
169
193
|
records
|
170
194
|
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def fix_format(record)
|
199
|
+
return record if record.empty?
|
200
|
+
return record unless @format_json
|
201
|
+
|
202
|
+
fix_uncompleted_json(record)
|
203
|
+
end
|
204
|
+
|
205
|
+
def fix_uncompleted_json(record)
|
206
|
+
return record if is_correct_json(record)
|
207
|
+
|
208
|
+
# Assume uncompleted JSON such as "[{...},", "{...},", or "{...}]"
|
209
|
+
|
210
|
+
if record[0] == "["
|
211
|
+
record.slice!(0)
|
212
|
+
return record if record.empty?
|
213
|
+
end
|
214
|
+
|
215
|
+
if record[-1] == "," || record[-1] == "]"
|
216
|
+
record.slice!(-1)
|
217
|
+
return record if record.empty?
|
218
|
+
end
|
219
|
+
|
220
|
+
record
|
221
|
+
end
|
222
|
+
|
223
|
+
def is_correct_json(record)
|
224
|
+
# Just to check the format
|
225
|
+
JSON.parse(record)
|
226
|
+
return true
|
227
|
+
rescue JSON::ParserError
|
228
|
+
return false
|
229
|
+
end
|
171
230
|
end
|
172
231
|
end
|
173
232
|
end
|
@@ -9,12 +9,11 @@ require_relative "./unix_server.rb"
|
|
9
9
|
class UnixClientInputTest < Test::Unit::TestCase
|
10
10
|
def setup
|
11
11
|
Fluent::Test.setup
|
12
|
-
@
|
12
|
+
@server_thread = nil
|
13
13
|
end
|
14
14
|
|
15
15
|
def teardown
|
16
|
-
|
17
|
-
@thread = nil
|
16
|
+
stop_server
|
18
17
|
FileUtils.rm_rf(TMP_DIR)
|
19
18
|
end
|
20
19
|
|
@@ -32,6 +31,14 @@ class UnixClientInputTest < Test::Unit::TestCase
|
|
32
31
|
d = create_driver(config_with_json_parser)
|
33
32
|
assert_equal "unix_client", d.instance.tag
|
34
33
|
assert_equal "#{TMP_DIR}/socket.sock", d.instance.path
|
34
|
+
assert_equal false, d.instance.format_json
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_configure_with_format_json
|
38
|
+
d = create_driver(config_with_json_parser_and_format_json)
|
39
|
+
assert_equal "unix_client", d.instance.tag
|
40
|
+
assert_equal "#{TMP_DIR}/socket.sock", d.instance.path
|
41
|
+
assert_equal true, d.instance.format_json
|
35
42
|
end
|
36
43
|
|
37
44
|
def test_receive_json
|
@@ -100,6 +107,67 @@ class UnixClientInputTest < Test::Unit::TestCase
|
|
100
107
|
end
|
101
108
|
end
|
102
109
|
|
110
|
+
def test_receive_json_list
|
111
|
+
d = create_driver(config_with_json_parser_and_format_json)
|
112
|
+
path = d.instance.path
|
113
|
+
delimiter = "\n"
|
114
|
+
|
115
|
+
msgs = ["hoge", "fuga", "foo"]
|
116
|
+
|
117
|
+
start_server(path)
|
118
|
+
|
119
|
+
d.run(expect_records: 3, timeout: 10) do
|
120
|
+
sleep 1
|
121
|
+
UNIXSocket.open(path) do |sock|
|
122
|
+
sock.write("[")
|
123
|
+
sock.write(JSON.generate(raw_data(msg: msgs[0])))
|
124
|
+
sock.write(",")
|
125
|
+
sock.write(delimiter)
|
126
|
+
|
127
|
+
sock.write(JSON.generate(raw_data(msg: msgs[1])))
|
128
|
+
sock.write(",")
|
129
|
+
sock.write(delimiter)
|
130
|
+
|
131
|
+
sock.write(JSON.generate(raw_data(msg: msgs[2])))
|
132
|
+
sock.write(delimiter)
|
133
|
+
sock.write("]")
|
134
|
+
sock.write(delimiter)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
assert_equal 3, d.events.length
|
139
|
+
|
140
|
+
d.events.each_with_index do |event, i|
|
141
|
+
assert_equal msgs[i], event[2]["msg"]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_receive_json_list_with_one_delimiter
|
146
|
+
d = create_driver(config_with_json_parser_and_format_json)
|
147
|
+
path = d.instance.path
|
148
|
+
delimiter = "\n"
|
149
|
+
|
150
|
+
msgs = ["hoge", "fuga", "foo"]
|
151
|
+
|
152
|
+
start_server(path)
|
153
|
+
|
154
|
+
d.run(expect_records: 3, timeout: 10) do
|
155
|
+
sleep 1
|
156
|
+
data = msgs.map {|msg| raw_data(msg: msg)}
|
157
|
+
|
158
|
+
UNIXSocket.open(path) do |sock|
|
159
|
+
sock.write(JSON.generate(data))
|
160
|
+
sock.write(delimiter)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
assert_equal 3, d.events.length
|
165
|
+
|
166
|
+
d.events.each_with_index do |event, i|
|
167
|
+
assert_equal msgs[i], event[2]["msg"]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
103
171
|
private
|
104
172
|
|
105
173
|
def create_driver(conf)
|
@@ -124,24 +192,43 @@ class UnixClientInputTest < Test::Unit::TestCase
|
|
124
192
|
!
|
125
193
|
end
|
126
194
|
|
195
|
+
def config_with_json_parser_and_format_json
|
196
|
+
BASE_CONFIG + %!
|
197
|
+
format_json true
|
198
|
+
<parse>
|
199
|
+
@type json
|
200
|
+
</parse>
|
201
|
+
!
|
202
|
+
end
|
203
|
+
|
127
204
|
def start_server(path)
|
128
|
-
@
|
205
|
+
@server_thread = Thread.new do
|
129
206
|
server = UnixBroadcastServer.new(path)
|
130
207
|
server.run
|
131
208
|
end
|
132
209
|
sleep 1
|
133
210
|
end
|
134
211
|
|
212
|
+
def stop_server
|
213
|
+
if @server_thread
|
214
|
+
@server_thread.kill if @server_thread
|
215
|
+
@server_thread.join
|
216
|
+
@server_thread = nil
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
135
220
|
def send_json(path, time: nil, msg: DEFAULT_MSG, delimiter: "\n")
|
136
|
-
msg = JSON.generate(
|
137
|
-
{
|
138
|
-
"time" => time.nil? ? Time.now.to_i : time,
|
139
|
-
"msg" => msg
|
140
|
-
}
|
141
|
-
)
|
221
|
+
msg = JSON.generate(raw_data(time: time, msg: msg))
|
142
222
|
UNIXSocket.open(path) do |sock|
|
143
223
|
sock.write(msg)
|
144
224
|
sock.write(delimiter)
|
145
225
|
end
|
146
226
|
end
|
227
|
+
|
228
|
+
def raw_data(time: nil, msg: DEFAULT_MSG)
|
229
|
+
{
|
230
|
+
"time" => time.nil? ? Time.now.to_i : time,
|
231
|
+
"msg" => msg
|
232
|
+
}
|
233
|
+
end
|
147
234
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-unix-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- daipom
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -72,7 +72,7 @@ dependencies:
|
|
72
72
|
- - "<"
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '2'
|
75
|
-
description:
|
75
|
+
description:
|
76
76
|
email:
|
77
77
|
- reangoapyththeorem@gmail.com
|
78
78
|
executables: []
|
@@ -93,7 +93,7 @@ homepage: https://github.com/daipom/fluent-plugin-unix-client
|
|
93
93
|
licenses:
|
94
94
|
- Apache-2.0
|
95
95
|
metadata: {}
|
96
|
-
post_install_message:
|
96
|
+
post_install_message:
|
97
97
|
rdoc_options: []
|
98
98
|
require_paths:
|
99
99
|
- lib
|
@@ -108,8 +108,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: '0'
|
110
110
|
requirements: []
|
111
|
-
rubygems_version: 3.
|
112
|
-
signing_key:
|
111
|
+
rubygems_version: 3.1.6
|
112
|
+
signing_key:
|
113
113
|
specification_version: 4
|
114
114
|
summary: Fluentd Input plugin to receive data from UNIX domain socket. This is a client
|
115
115
|
version of the default `unix` input plugin.
|