fluentd-tcp-capturer 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/.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: []
|