fluentd-tcp-capturer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/README.md +70 -0
- data/Rakefile +2 -0
- data/bin/fm-cap +64 -0
- data/fluentd-tcp-capturer.gemspec +27 -0
- data/lib/capture.rb +160 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7ffa5c092d3847b323da8e1e6a06f07673266c6d
|
4
|
+
data.tar.gz: 6d98befca1fd50bd1ac59b8ed5aad4c508b30675
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 727f0e018fc9dd4ac3d251a9cb5fec1ccd4b97a9329ec8214cfbb08f9e2d458b625b025072c38a55018828506b96b3ed631f7c999c0b5d574c9111deda753caf
|
7
|
+
data.tar.gz: 0386bd0aafe7da24a6e34729acf5e3366f6962900ece230865b7c59c713333279396cda34802bc91fc212d88bd59d989b6173bff00b60b8e4f7ec727f4f4e57f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Fluentd TCP capturer
|
2
|
+
|
3
|
+
`fluentd-tcp-capturer` is a tool to inspect/dump/handle message to Fluentd TCP input, to:
|
4
|
+
|
5
|
+
- debug a message to fluentd from somewhere
|
6
|
+
- try other configuration on other fluentd node
|
7
|
+
|
8
|
+
without changing Fluentd configuration.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
```shell
|
13
|
+
$ gem install 'fluentd-tcp-capturer'
|
14
|
+
```
|
15
|
+
|
16
|
+
Then command `fm-cap` becomes available.
|
17
|
+
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
```shell
|
22
|
+
Usage: fm-cap [options]
|
23
|
+
-d, --device DEVICE Device name [default: eth0]
|
24
|
+
-p, --port PORT Fluentd port to capture [default: 24224]
|
25
|
+
--forward-host HOST If set, message will be forwarded to other Fluentd host
|
26
|
+
--forward-port PORT Fluentd port to forward message (used when --forward-host is set)
|
27
|
+
--debug Set loglevel DEBUG
|
28
|
+
```
|
29
|
+
|
30
|
+
### Dump mode
|
31
|
+
|
32
|
+
This mode captures tcp packet to Fluentd, dump it in the terminal.
|
33
|
+
|
34
|
+
```shell
|
35
|
+
# TODO
|
36
|
+
$ sudo fm-cap
|
37
|
+
I, [2017-03-03T22:41:31.141436 #14088] INFO -- : Start capturing lo0/port=24224
|
38
|
+
2017-03-03 13:41:34 +0000 | tag=test.20170303224134 msg={"name"=>"John", "age"=>15}
|
39
|
+
2017-03-03 13:41:46 +0000 | tag=test.20170303224145 msg={"name"=>"Michel", "age"=>16}
|
40
|
+
```
|
41
|
+
|
42
|
+
You can specify other network device, also port number of Fluentd.
|
43
|
+
|
44
|
+
```shell
|
45
|
+
$ sudo fm-cap -d lo0
|
46
|
+
$ sudo fm-cap -p 4567
|
47
|
+
```
|
48
|
+
|
49
|
+
### Transfer mode
|
50
|
+
|
51
|
+
This mode captures tcp packet, transfer it to other Fluentd tcp input.
|
52
|
+
|
53
|
+
```shell
|
54
|
+
$ sudo fm-cap --forward-host other-fluentd-node --forward-port 4567
|
55
|
+
I, [2017-03-03T22:46:31.878876 #14564] INFO -- : Start capturing lo0/port=24224
|
56
|
+
I, [2017-03-03T22:46:34.577661 #14564] INFO -- : Forwarded message to other-fluentd-node:4567
|
57
|
+
I, [2017-03-03T22:46:41.460288 #14564] INFO -- : Forwarded message to other-fluentd-node:4567
|
58
|
+
I, [2017-03-03T22:46:42.461110 #14564] INFO -- : Forwarded message to other-fluentd-node:4567
|
59
|
+
```
|
60
|
+
|
61
|
+
## TODO
|
62
|
+
|
63
|
+
- Support timezone in the dumpped message.
|
64
|
+
- Dump message over embed Fluend.
|
65
|
+
- Support other protocol, e.g. UDP
|
66
|
+
- Tests ...
|
67
|
+
|
68
|
+
## Patch
|
69
|
+
|
70
|
+
Welcome
|
data/Rakefile
ADDED
data/bin/fm-cap
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "optparse"
|
3
|
+
require "capture"
|
4
|
+
require "logger"
|
5
|
+
|
6
|
+
options = {
|
7
|
+
device: "eth0",
|
8
|
+
port: 24224,
|
9
|
+
forward_host: nil,
|
10
|
+
forward_port: 0,
|
11
|
+
output_file_path: nil
|
12
|
+
}
|
13
|
+
|
14
|
+
logger = Logger.new(STDOUT)
|
15
|
+
logger.level = Logger::INFO
|
16
|
+
|
17
|
+
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.on("-d DEVICE", "--device DEVICE", "Device name [default: eth0]") do |v|
|
20
|
+
options[:device] = v
|
21
|
+
end
|
22
|
+
opts.on("-p PORT", "--port PORT", "Fluentd port to capture [default: 24224]") do |v|
|
23
|
+
options[:port] = v.to_i
|
24
|
+
end
|
25
|
+
opts.on("--forward-host HOST", "If set, message will be forwarded to other Fluentd host") do |v|
|
26
|
+
options[:forward_host] = v
|
27
|
+
end
|
28
|
+
opts.on("--forward-port PORT", "Fluentd port to forward message (used when --forward-host is set)") do |v|
|
29
|
+
options[:forward_port] = v.to_i
|
30
|
+
end
|
31
|
+
opts.on("--debug", "Set loglevel DEBUG") do |v|
|
32
|
+
logger.level = Logger::DEBUG
|
33
|
+
end
|
34
|
+
# TODO
|
35
|
+
# opts.on("-o VALUE", "--output VALUE", "Output file path to dump message from Fluentd (used when --forward-host is set)") do |v|
|
36
|
+
# options[:output_file_path] = v
|
37
|
+
# end
|
38
|
+
# TODO max_bytes, timeout
|
39
|
+
opts.parse!(ARGV)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
exec = Capturing::Exec.new(device: options[:device], port: options[:port], logger: logger)
|
44
|
+
runner = if options[:forward_host].nil?
|
45
|
+
Capturing::PrintRunner.new(exec, writer: STDOUT)
|
46
|
+
else
|
47
|
+
# if options[:output_file_path].nil?
|
48
|
+
# STDERR.write "-o/--output must be set"
|
49
|
+
# end
|
50
|
+
Capturing::ForwardRunner.new(exec, host: options[:forward_host], port: options[:forward_port],
|
51
|
+
output: options[:output_file_path])
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
exec.logger.info "Start capturing #{options[:device]}/port=#{options[:port]}"
|
56
|
+
begin
|
57
|
+
runner.run!
|
58
|
+
rescue Interrupt
|
59
|
+
runner.destroy!
|
60
|
+
exit 0
|
61
|
+
rescue => e
|
62
|
+
exec.logger.error e
|
63
|
+
exit 1
|
64
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "fluentd-tcp-capturer"
|
7
|
+
spec.version = "0.1.0"
|
8
|
+
spec.authors = ["takumakanari"]
|
9
|
+
spec.email = ["chemtrails.t@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{Fluentd message capturer}
|
12
|
+
spec.description = %q{A tool to inspect/dump/handle message to Fluentd TCP input.}
|
13
|
+
spec.homepage = "https://github.com/takumakanari/fluentd-tcp-capturer"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
|
+
f.match(%r{^(test|spec|features)/})
|
17
|
+
end
|
18
|
+
spec.bindir = "bin"
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_runtime_dependency "ruby-pcap"
|
23
|
+
spec.add_runtime_dependency "threadpool"
|
24
|
+
spec.add_runtime_dependency "fluentd", [">= 0.12", "< 2"]
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
end
|
data/lib/capture.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
require "pcap"
|
2
|
+
require "logger"
|
3
|
+
|
4
|
+
module Capturing
|
5
|
+
|
6
|
+
class Runner
|
7
|
+
|
8
|
+
def initialize(exec)
|
9
|
+
@exec = exec
|
10
|
+
@pcaplet = Pcap::Capture.open_live(exec.device, exec.max_bytes, true, exec.timeout)
|
11
|
+
@pcaplet.setfilter(Pcap::Filter.new("port #{exec.port} and tcp", @pcaplet))
|
12
|
+
end
|
13
|
+
|
14
|
+
def run!
|
15
|
+
@pcaplet.each_packet do |packet|
|
16
|
+
if packet.tcp? && packet.tcp_data_len > 0
|
17
|
+
@exec.logger.debug "Handle message packets: #{packet}"
|
18
|
+
begin
|
19
|
+
on_packet packet.tcp_data
|
20
|
+
rescue => e
|
21
|
+
@exec.logger.error e
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_packet(data)
|
28
|
+
raise NotImplementedError.new "on_packet(packet)"
|
29
|
+
end
|
30
|
+
|
31
|
+
def destroy!
|
32
|
+
@pcaplet.close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class PrintRunner < Runner
|
37
|
+
require "fluent/engine"
|
38
|
+
require "fluent/time"
|
39
|
+
|
40
|
+
def initialize(e, writer:)
|
41
|
+
super e
|
42
|
+
@writer = writer
|
43
|
+
@unpacker = Fluent::Engine.msgpack_factory.unpacker
|
44
|
+
@time_formatter = Fluent::TimeFormatter.new("%Y-%m-%d %H:%M:%S %z", false, nil) # TODO support format
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_packet(data)
|
48
|
+
@unpacker.feed_each(data) do |msg|
|
49
|
+
tag, entries = msg
|
50
|
+
entries.each do |e|
|
51
|
+
@writer.write(format_message(tag, e))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def format_message(tag, entry)
|
57
|
+
time, record = entry
|
58
|
+
"#{@time_formatter.format(time)} | tag=#{tag} msg=#{record.inspect}\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class ForwardRunner < Runner
|
63
|
+
require "socket"
|
64
|
+
require "threadpool"
|
65
|
+
|
66
|
+
def initialize(e, host:, port:, output:)
|
67
|
+
super e
|
68
|
+
@host = host
|
69
|
+
@port = port
|
70
|
+
@output = output
|
71
|
+
@use_embed_fluentd = false # TODO support this mode
|
72
|
+
|
73
|
+
start_embed_fluentd if @use_embed_fluentd
|
74
|
+
@sock = new_socket_to_forward
|
75
|
+
@messaging_thread_pool = ThreadPool.new(4) # TODO configurable
|
76
|
+
end
|
77
|
+
|
78
|
+
def on_packet(data)
|
79
|
+
@messaging_thread_pool.process{ @sock.write data }
|
80
|
+
@exec.logger.info "Forwarded message to #{@host}:#{@port}"
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
def new_socket_to_forward
|
85
|
+
max_retry = 10
|
86
|
+
begin
|
87
|
+
TCPSocket.new(@host, @port)
|
88
|
+
rescue Errno::ECONNREFUSED
|
89
|
+
raise if max_retry == 0
|
90
|
+
sleep 1
|
91
|
+
max_retry -= 1
|
92
|
+
retry
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def start_embed_fluentd
|
97
|
+
Thread.start {
|
98
|
+
Dir.mktmpdir do |dir|
|
99
|
+
conf = File.join(dir, "fluent.conf")
|
100
|
+
output_file = File.join(dir, 'output')
|
101
|
+
@exec.logger.info "Output message to '#{output_file}'" # TODO set file path
|
102
|
+
File.write(conf, <<-EOF)
|
103
|
+
<source>
|
104
|
+
@type forward
|
105
|
+
port #{@port}
|
106
|
+
</source>
|
107
|
+
<match **>
|
108
|
+
@type file
|
109
|
+
path #{@output}
|
110
|
+
buffer_type memory
|
111
|
+
append true
|
112
|
+
flush_interval 0s
|
113
|
+
</match>
|
114
|
+
EOF
|
115
|
+
FluentdEmbed.new(conf).boot
|
116
|
+
end
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Exec
|
122
|
+
attr_reader :device, :max_bytes, :timeout, :port
|
123
|
+
|
124
|
+
DEFAULT_LOGGER ||= Logger.new(STDOUT)
|
125
|
+
|
126
|
+
def initialize(device: "eth0", max_bytes: 1460, timeout: 1000, port: 24224, logger: nil)
|
127
|
+
@device = device
|
128
|
+
@max_bytes = max_bytes
|
129
|
+
@timeout = timeout
|
130
|
+
@port = port
|
131
|
+
@logger = logger
|
132
|
+
end
|
133
|
+
|
134
|
+
def logger
|
135
|
+
@logger.nil? ? DEFAULT_LOGGER : @logger
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class FluentdEmbed
|
140
|
+
require "fluent/version"
|
141
|
+
require "fluent/supervisor"
|
142
|
+
|
143
|
+
def initialize(config_path)
|
144
|
+
@opts = {
|
145
|
+
config_path: config_path,
|
146
|
+
plugin_dirs: [],
|
147
|
+
log_level: 2,
|
148
|
+
libs: [],
|
149
|
+
suppress_repeated_stacktrace: true,
|
150
|
+
use_v1_config: true,
|
151
|
+
supervise: true,
|
152
|
+
standalone_worker: true
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
def boot
|
157
|
+
Fluent::Supervisor.new(@opts).run_worker
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluentd-tcp-capturer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- takumakanari
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ruby-pcap
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: threadpool
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: fluentd
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.12'
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '2'
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0.12'
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: bundler
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.14'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.14'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rake
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '10.0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '10.0'
|
89
|
+
description: A tool to inspect/dump/handle message to Fluentd TCP input.
|
90
|
+
email:
|
91
|
+
- chemtrails.t@gmail.com
|
92
|
+
executables:
|
93
|
+
- fm-cap
|
94
|
+
extensions: []
|
95
|
+
extra_rdoc_files: []
|
96
|
+
files:
|
97
|
+
- ".gitignore"
|
98
|
+
- Gemfile
|
99
|
+
- README.md
|
100
|
+
- Rakefile
|
101
|
+
- bin/fm-cap
|
102
|
+
- fluentd-tcp-capturer.gemspec
|
103
|
+
- lib/capture.rb
|
104
|
+
homepage: https://github.com/takumakanari/fluentd-tcp-capturer
|
105
|
+
licenses: []
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options: []
|
109
|
+
require_paths:
|
110
|
+
- lib
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
requirements: []
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 2.4.5.1
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Fluentd message capturer
|
127
|
+
test_files: []
|