fluent-tail 0.0.1
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 +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +3 -0
- data/README.md +71 -0
- data/Rakefile +6 -0
- data/bin/fluent-tail +6 -0
- data/fluent-tail.gemspec +24 -0
- data/lib/fluent/tail/version.rb +5 -0
- data/lib/fluent/tail.rb +131 -0
- data/spec/fluent_tail/fluent.conf +11 -0
- data/spec/fluent_tail/tail_spec.rb +54 -0
- data/spec/spec_helper.rb +23 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 13b33dca730d228105b39647de7cf4810d485faa
|
4
|
+
data.tar.gz: 8fcbe2060535a0d84a0efe6fce02b17c3c38d12f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 87cbc96521d825e47a4a4d16d6c9e6a8fb6dca34f07b79f9caee632cff6d2d565e82e6af13eca52e100e72cb5d699dd82913ddf6733b5188f3f83995e3fc22b9
|
7
|
+
data.tar.gz: 6fbfd0a972be084ee6ff4c0409ad6cedd6d0aa2b7e3690cd8607ef923ebc4190d1283fdf255faaa3e14629abd80c832c7907187d68f1a1600ddeca2a90a305f9
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Fluent::Tail
|
2
|
+
|
3
|
+
Using fluent-tail, you can tail [fluentd](http://fluentd.org/) event stream without any configuration changes.
|
4
|
+
|
5
|
+
## Caution
|
6
|
+
|
7
|
+
Because tool modify the running fluentd process using `drb` and `instance_eval', there is a potential risk that the process could be broken unexpectedly.
|
8
|
+
|
9
|
+
In addition, this tool might degrade the perfermance of the fluentd process.
|
10
|
+
|
11
|
+
*Use this tool at your own risk.*
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
```
|
16
|
+
$ fluent-gem install fluent-tail
|
17
|
+
```
|
18
|
+
|
19
|
+
## Prerequisite
|
20
|
+
|
21
|
+
`in_debug_agent` plugin is required to be enabled.
|
22
|
+
|
23
|
+
```
|
24
|
+
<source>
|
25
|
+
type debug_agent
|
26
|
+
</source>
|
27
|
+
```
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
```
|
32
|
+
$ fluent-tail <tag_pattern>
|
33
|
+
```
|
34
|
+
|
35
|
+
You can specify a pattern of tag with the same format as fluentd match tag.
|
36
|
+
|
37
|
+
e.g.
|
38
|
+
|
39
|
+
|
40
|
+
```
|
41
|
+
$ fluent-tail foo.**
|
42
|
+
```
|
43
|
+
|
44
|
+
then events with tag, such as "foo" , "foo.bar" and "foo.bar.foo" etc., will be shown in your console.
|
45
|
+
|
46
|
+
```
|
47
|
+
2014-03-06 14:22:21 +0900 foo: {"hoge":"fuga"}
|
48
|
+
2014-03-06 14:22:23 +0900 foo.bar: {"hoge":"fuga"}
|
49
|
+
2014-03-06 14:22:27 +0900 foo.bar.foo: {"hoge":"fuga"}
|
50
|
+
```
|
51
|
+
|
52
|
+
## Option
|
53
|
+
|
54
|
+
|parameter|description|default|
|
55
|
+
|---|---|---|
|
56
|
+
|-h, --host HOST|fluent host|127.0.0.1|
|
57
|
+
|-p, --port PORT|debug_agent|24230|
|
58
|
+
|-u, --unix PATH|use unix socket instead of tcp||
|
59
|
+
|-t, --output-type TYPE|output format of record. available types are 'json' or 'hash'.|json|
|
60
|
+
|
61
|
+
## Copyright
|
62
|
+
|
63
|
+
See LICENSE.txt
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
1. Fork it ( http://github.com/choplin/fluent-tail/fork )
|
68
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
69
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
70
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
71
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/fluent-tail
ADDED
data/fluent-tail.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fluent/tail/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "fluent-tail"
|
8
|
+
spec.version = Fluent::Tail::VERSION
|
9
|
+
spec.authors = ["OKUNO Akihiro"]
|
10
|
+
spec.email = ["okuno.akihiro@gmail.com"]
|
11
|
+
spec.summary = %q{Tools for tailing fluentd stream events}
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/choplin/fluent-tail"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
end
|
data/lib/fluent/tail.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'drb/drb'
|
3
|
+
require 'fluent/load'
|
4
|
+
|
5
|
+
def parse_options
|
6
|
+
op = OptionParser.new
|
7
|
+
op.banner += ' <pattern>'
|
8
|
+
|
9
|
+
(class<<self;self;end).module_eval do
|
10
|
+
define_method(:usage) do |msg|
|
11
|
+
puts op.to_s
|
12
|
+
puts "error: #{msg}" if msg
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
opts = {
|
18
|
+
host: '127.0.0.1',
|
19
|
+
port: 24230,
|
20
|
+
unix: nil,
|
21
|
+
pattern: nil,
|
22
|
+
output_type: :json,
|
23
|
+
}
|
24
|
+
|
25
|
+
op.on('-h', '--host HOST', "fluent host (default: #{opts[:host]})") {|v|
|
26
|
+
opts[:host] = v
|
27
|
+
}
|
28
|
+
|
29
|
+
op.on('-p', '--port PORT', "debug_agent tcp port (default: #{opts[:host]})", Integer) {|v|
|
30
|
+
opts[:port] = v
|
31
|
+
}
|
32
|
+
|
33
|
+
op.on('-u', '--unix PATH', "use unix socket instead of tcp") {|v|
|
34
|
+
opts[:unix] = b
|
35
|
+
}
|
36
|
+
|
37
|
+
op.on('-t', '--output-type TYPE', "output format of record. available types are 'json' or 'hash'. (default: #{opts[:output_type]})") {|v|
|
38
|
+
case v.downcase
|
39
|
+
when 'json'
|
40
|
+
opts[:output_type] = :json
|
41
|
+
when 'hash'
|
42
|
+
opts[:output_type] = :hash
|
43
|
+
else
|
44
|
+
raise ConfigError, "output_type must be 'json' or 'hash'"
|
45
|
+
end
|
46
|
+
}
|
47
|
+
|
48
|
+
begin
|
49
|
+
op.parse!(ARGV)
|
50
|
+
opts[:pattern] = ARGV.shift
|
51
|
+
|
52
|
+
if opts[:pattern].nil?
|
53
|
+
usage "a pattern must be specified"
|
54
|
+
end
|
55
|
+
rescue
|
56
|
+
usage $!.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
opts
|
60
|
+
end
|
61
|
+
|
62
|
+
def format(tag, time, record)
|
63
|
+
"#{Time.at(time).localtime} #{tag}: #{@output_proc.call(record)}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def main
|
67
|
+
opts = parse_options
|
68
|
+
|
69
|
+
@output_proc = case opts[:output_type]
|
70
|
+
when :json then Proc.new {|record| Yajl.dump(record) }
|
71
|
+
when :hash then Proc.new {|record| record.to_s }
|
72
|
+
end
|
73
|
+
|
74
|
+
unless opts[:unix].nil?
|
75
|
+
uri = "drbunix:#{opts[:unix]}"
|
76
|
+
else
|
77
|
+
uri = "druby://#{opts[:host]}:#{opts[:port]}"
|
78
|
+
end
|
79
|
+
|
80
|
+
$remote_engine = DRb::DRbObject.new_with_uri(uri)
|
81
|
+
|
82
|
+
remote_code = <<-CODE
|
83
|
+
alias :original_emit_staream :emit_stream
|
84
|
+
@fluent_tail_queue = Queue.new
|
85
|
+
@fluent_tail_match_pattern = Fluent::MatchPattern.create("#{opts[:pattern]}")
|
86
|
+
@fluent_tail_match_cache = {}
|
87
|
+
|
88
|
+
def emit_stream(tag, es)
|
89
|
+
matched = @fluent_tail_match_cache[tag]
|
90
|
+
|
91
|
+
if matched.nil?
|
92
|
+
matched = @fluent_tail_match_pattern.match(tag)
|
93
|
+
@fluent_tail_match_cache[tag] = matched
|
94
|
+
end
|
95
|
+
|
96
|
+
@fluent_tail_queue.push([tag, es.dup]) if matched
|
97
|
+
|
98
|
+
original_emit_staream(tag, es)
|
99
|
+
end
|
100
|
+
|
101
|
+
def pop
|
102
|
+
@fluent_tail_queue.pop
|
103
|
+
end
|
104
|
+
CODE
|
105
|
+
|
106
|
+
if $remote_engine.respond_to?(:original_emit_staream)
|
107
|
+
abort 'another client has already connected to the server. abort.'
|
108
|
+
end
|
109
|
+
|
110
|
+
begin
|
111
|
+
$remote_engine.method_missing(:instance_eval, remote_code)
|
112
|
+
|
113
|
+
while e = $remote_engine.pop
|
114
|
+
tag, es = e
|
115
|
+
es.each do |time,record|
|
116
|
+
STDOUT.puts format(tag, time, record)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
ensure
|
120
|
+
if not $remote_engine.nil? and $remote_engine.respond_to?(:original_emit_staream)
|
121
|
+
remote_code = <<-CODE
|
122
|
+
@fluent_tail_queue = nil
|
123
|
+
alias :emit_stream :original_emit_staream
|
124
|
+
undef :original_emit_staream
|
125
|
+
CODE
|
126
|
+
$remote_engine.method_missing(:instance_eval, remote_code)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
main
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'fluent/tail/version'
|
4
|
+
|
5
|
+
describe Fluent::Tail do
|
6
|
+
TAG_PATTER = 'foo.**'
|
7
|
+
CONFIG_PATH = File.join(File.dirname(__FILE__), 'fluent.conf')
|
8
|
+
BIN_DIR = File.join(ROOT, 'bin')
|
9
|
+
|
10
|
+
LOG = File.join(File.dirname(__FILE__), 'test.log')
|
11
|
+
|
12
|
+
before :all do
|
13
|
+
@fluentd_pid = spawn('fluentd', '-c', CONFIG_PATH, out: '/dev/null')
|
14
|
+
sleep 2
|
15
|
+
|
16
|
+
@r,w = IO.pipe
|
17
|
+
@fluent_tail_pid = spawn("#{File.join(BIN_DIR, 'fluent-tail')} #{TAG_PATTER}", out: w)
|
18
|
+
sleep 1
|
19
|
+
end
|
20
|
+
|
21
|
+
after :all do
|
22
|
+
Process.kill(:TERM, @fluent_tail_pid)
|
23
|
+
sleep 1
|
24
|
+
Process.kill(:TERM, @fluentd_pid)
|
25
|
+
Process.waitall
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'have a version number' do
|
29
|
+
expect(Fluent::Tail::VERSION).not_to be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'show matched events' do
|
33
|
+
tag = 'foo.bar'
|
34
|
+
time = Time.now
|
35
|
+
event = {'foo' => 'bar'}
|
36
|
+
|
37
|
+
client = FluentdClient.connect
|
38
|
+
client.write(tag, time.to_i, event)
|
39
|
+
|
40
|
+
line = @r.gets
|
41
|
+
expect(line).to eq("#{time.localtime} #{tag}: #{event.to_json}\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does not show matched events' do
|
45
|
+
tag = 'hoge'
|
46
|
+
time = Time.now
|
47
|
+
event = {'foo' => 'bar'}
|
48
|
+
|
49
|
+
client = FluentdClient.connect
|
50
|
+
client.write(tag, time.to_i, event)
|
51
|
+
|
52
|
+
expect {@r.read_nonblock(10)}.to raise_error(Errno::EAGAIN)
|
53
|
+
end
|
54
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
ROOT = File.expand_path('../../', __FILE__)
|
2
|
+
$LOAD_PATH.unshift File.join(ROOT, 'lib')
|
3
|
+
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
|
7
|
+
class FluentdClient
|
8
|
+
def self.connect
|
9
|
+
conn = TCPSocket.open('127.0.0.1', 24224)
|
10
|
+
self.new(conn)
|
11
|
+
end
|
12
|
+
|
13
|
+
def write(tag, time, event)
|
14
|
+
@conn.write [tag, time, event].to_json
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def initialize(conn)
|
20
|
+
@conn = conn
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-tail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- OKUNO Akihiro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
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: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Tools for tailing fluentd stream events
|
56
|
+
email:
|
57
|
+
- okuno.akihiro@gmail.com
|
58
|
+
executables:
|
59
|
+
- fluent-tail
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- .rspec
|
65
|
+
- .travis.yml
|
66
|
+
- Gemfile
|
67
|
+
- LICENSE.txt
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- bin/fluent-tail
|
71
|
+
- fluent-tail.gemspec
|
72
|
+
- lib/fluent/tail.rb
|
73
|
+
- lib/fluent/tail/version.rb
|
74
|
+
- spec/fluent_tail/fluent.conf
|
75
|
+
- spec/fluent_tail/tail_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
homepage: https://github.com/choplin/fluent-tail
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.0.14
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Tools for tailing fluentd stream events
|
101
|
+
test_files:
|
102
|
+
- spec/fluent_tail/fluent.conf
|
103
|
+
- spec/fluent_tail/tail_spec.rb
|
104
|
+
- spec/spec_helper.rb
|