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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc18f5099028958215b671685ea41ebcd5c6754c0b1ee0eb46b3fc87f0b08226
4
- data.tar.gz: a0d5bbe77d1ccd49d9c5c7a452a3702397fdfd9c41da6f81fd7a35e509b43ba5
3
+ metadata.gz: e730565e1f497509cb2d5e579527d5b500ba3240312fa0bb6527972db7e57ff4
4
+ data.tar.gz: 9d4a45f7a0c3cbf6b30b00be745dc624ed79d5b7e10f9753c7af2a5728148cca
5
5
  SHA512:
6
- metadata.gz: f28da19e7f6291fad09fd1a19373d241db41c9d29eedc53b79e0f6432b7d9ad499dea0d5ba5d65440a2f1030e88888f63cbe3086f94eff21334c40811c2fb5c2
7
- data.tar.gz: 22b091803d9db3ae70d1df0fcf3d9a2e7077bbf83b62274b54512a3c9b9900f304e75308396e8f3742536f15dfac540a489da2f1c575c74d7734c389d9c00658
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**.
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "fluent-plugin-unix-client"
6
- spec.version = "0.1.0"
6
+ spec.version = "1.0.0"
7
7
  spec.authors = ["daipom"]
8
8
  spec.email = ["reangoapyththeorem@gmail.com"]
9
9
 
@@ -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(@path, delimiter: @delimiter, log: log)
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
- router.emit(@tag, time, record)
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
- records << @buf[pos_read...pos_next_delimiter]
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
- @thread = nil
12
+ @server_thread = nil
13
13
  end
14
14
 
15
15
  def teardown
16
- @thread.kill if @thread
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
- @thread = Thread.new do
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: 0.1.0
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: 2021-05-31 00:00:00.000000000 Z
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.2.15
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.