fluent-plugin-td 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ FURUHASHI Sadayuki <frsyuki _at_ gmail.com>
@@ -0,0 +1,7 @@
1
+ = Treasure Data output plugin for Fluent
2
+
3
+ == Copyright
4
+
5
+ Copyright:: Copyright (c) 2011 Treasure Data, Inc.
6
+ License:: Apache License, Version 2.0
7
+
@@ -0,0 +1,50 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gemspec|
8
+ gemspec.name = "fluent-plugin-td"
9
+ gemspec.summary = "Treasure Data plugin for Fluent event collector"
10
+ gemspec.author = "Sadayuki Furuhashi"
11
+ #gemspec.email = "frsyuki@gmail.com"
12
+ #gemspec.homepage = "http://fluent.github.com/"
13
+ gemspec.has_rdoc = false
14
+ gemspec.require_paths = ["lib"]
15
+ gemspec.add_dependency "fluent", "~> 0.9.7"
16
+ gemspec.test_files = Dir["test/**/*.rb"]
17
+ gemspec.files = Dir["bin/**/*", "lib/**/*", "test/**/*.rb"] +
18
+ %w[example.conf VERSION AUTHORS Rakefile fluent-plugin-td.gemspec]
19
+ gemspec.executables = []
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler not available. Install it with: gem install jeweler"
24
+ end
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.test_files = Dir['test/*_test.rb']
28
+ t.ruby_opts = ['-rubygems'] if defined? Gem
29
+ t.ruby_opts << '-I.'
30
+ end
31
+
32
+ #VERSION_FILE = "lib/fluent/version.rb"
33
+ #
34
+ #file VERSION_FILE => ["VERSION"] do |t|
35
+ # version = File.read("VERSION").strip
36
+ # File.open(VERSION_FILE, "w") {|f|
37
+ # f.write <<EOF
38
+ #module Fluent
39
+ #
40
+ #VERSION = '#{version}'
41
+ #
42
+ #end
43
+ #EOF
44
+ # }
45
+ #end
46
+ #
47
+ #task :default => [VERSION_FILE, :build]
48
+
49
+ task :default => [:build]
50
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.9.7
@@ -0,0 +1,78 @@
1
+
2
+ ## match tag=apache.access and upload to Treasure Data
3
+ #<match apache.access>
4
+ # type tdlog
5
+ # apikey APIKEY
6
+ #</match>
7
+
8
+
9
+ ## built-in TCP input
10
+ <source>
11
+ type tcp
12
+ </source>
13
+
14
+ ## built-in UNIX socket input
15
+ #<source>
16
+ # type unix
17
+ #</source>
18
+
19
+ # HTTP input
20
+ # http://localhost:8888/<tag>?json=<json>
21
+ <source>
22
+ type http
23
+ port 8888
24
+ </source>
25
+
26
+ ## File input
27
+ ## read apache logs with tag=apache.access
28
+ #<source>
29
+ # type tail
30
+ # format apache
31
+ # path /var/log/httpd-access.log
32
+ # tag apache.access
33
+ #</source>
34
+
35
+
36
+ ## match tag=apache.access and write to file
37
+ #<match apache.access>
38
+ # type file
39
+ # path /var/log/td-agent/access
40
+ #</match>
41
+
42
+ ## match tag=debug.** and dump to console
43
+ <match debug.**>
44
+ type stdout
45
+ </match>
46
+
47
+ ## match tag=system.** and forward to another td-agent server
48
+ #<match system.**>
49
+ # type tcp
50
+ # host 192.168.0.11
51
+ # <secondary>
52
+ # host 192.168.0.12
53
+ # </secondary>
54
+ #</match>
55
+
56
+ ## match tag=myapp.** and forward and write to file
57
+ #<match myapp.**>
58
+ # type copy
59
+ # <store>
60
+ # type tcp
61
+ # host 192.168.0.13
62
+ # buffer_type file
63
+ # buffer_path /var/log/td-agent/myapp-forward
64
+ # retry_limit 50
65
+ # flush_interval 10s
66
+ # </store>
67
+ # <store>
68
+ # type file
69
+ # path /var/log/td-agent/myapp
70
+ # </store>
71
+ #</match>
72
+
73
+ ## match not matched logs and write to file
74
+ #<match **>
75
+ # type file
76
+ # path /var/log/td-agent/else/%Y-%m-%d/%H.log
77
+ #</match>
78
+
@@ -0,0 +1,42 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{fluent-plugin-td}
8
+ s.version = "0.9.7"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Sadayuki Furuhashi"]
12
+ s.date = %q{2011-08-06}
13
+ s.extra_rdoc_files = [
14
+ "README.rdoc"
15
+ ]
16
+ s.files = [
17
+ "AUTHORS",
18
+ "Rakefile",
19
+ "VERSION",
20
+ "example.conf",
21
+ "fluent-plugin-td.gemspec",
22
+ "lib/fluent/plugin/out_tdlog.rb"
23
+ ]
24
+ s.rdoc_options = ["--charset=UTF-8"]
25
+ s.require_paths = ["lib"]
26
+ s.rubygems_version = %q{1.3.7}
27
+ s.summary = %q{Treasure Data plugin for Fluent event collector}
28
+
29
+ if s.respond_to? :specification_version then
30
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
31
+ s.specification_version = 3
32
+
33
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
34
+ s.add_runtime_dependency(%q<fluent>, ["~> 0.9.7"])
35
+ else
36
+ s.add_dependency(%q<fluent>, ["~> 0.9.7"])
37
+ end
38
+ else
39
+ s.add_dependency(%q<fluent>, ["~> 0.9.7"])
40
+ end
41
+ end
42
+
@@ -0,0 +1,209 @@
1
+ module Fluent
2
+
3
+
4
+ class TreasureDataLogOutput < BufferedOutput
5
+ Plugin.register_output('tdlog', self)
6
+
7
+ HOST = ENV['TD_API_SERVER'] || 'api.treasure-data.com'
8
+ PORT = 80
9
+ USE_SSL = false
10
+
11
+ def initialize
12
+ require 'fileutils'
13
+ require 'tempfile'
14
+ require 'zlib'
15
+ require 'net/http'
16
+ require 'json'
17
+ require 'cgi' # CGI.escape
18
+ require 'time' # Time#rfc2822
19
+ super
20
+ @tmpdir = '/tmp/fluent/tdlog'
21
+ @apikey = nil
22
+ @key = nil
23
+ @table_list = []
24
+ end
25
+
26
+ def configure(conf)
27
+ super
28
+
29
+ @tmpdir = conf['tmpdir'] || @tmpdir
30
+ FileUtils.mkdir_p(@tmpdir)
31
+
32
+ @apikey = conf['apikey']
33
+ unless @apikey
34
+ raise ConfigError, "'apikey' parameter is required on tdlog output"
35
+ end
36
+
37
+ database = conf['database']
38
+ table = conf['table']
39
+ if database && table
40
+ if !validate_name(database)
41
+ raise ConfigError, "Invalid database name #{database.inspect}: #{conf}"
42
+ end
43
+ if !validate_name(table)
44
+ raise ConfigError, "Invalid table name #{table.inspect}: #{conf}"
45
+ end
46
+ @key = "#{database}.#{table}"
47
+ elsif (database && !table) || (!database && table)
48
+ raise ConfigError, "'database' and 'table' parameter are required on tdlog output"
49
+ end
50
+
51
+ @table_list = get_table_list
52
+
53
+ if @key && !@table_list.include?(@key)
54
+ raise ConfigError, "Table #{@key.inspect} does not exist on Treasure Data. Use 'td create-log-table #{database} #{table}' to create it."
55
+ end
56
+ end
57
+
58
+ def emit(tag, es, chain)
59
+ if @key
60
+ key = @key
61
+ else
62
+ database, table = tag.split('.')[-2,2]
63
+ if !validate_name(database) || !validate_name(table)
64
+ $log.debug { "Invalid tag #{tag.inspect}" }
65
+ return
66
+ end
67
+ key = "#{database}.#{table}"
68
+ end
69
+
70
+ # check the table exists
71
+ unless @table_list.include?(key)
72
+ begin
73
+ @table_list = get_table_list
74
+ rescue
75
+ $log.warn "failed to update table list on Treasure Data", :error=>$!.to_s
76
+ $log.debug_backtrace $!
77
+ end
78
+ unless @table_list.include?(key)
79
+ database, table = key.split('.',2)
80
+ raise "Table #{key.inspect} does not exist on Treasure Data. Use 'td create-log-table #{database} #{table}' to create it."
81
+ end
82
+ end
83
+
84
+ super(tag, es, chain, key)
85
+ end
86
+
87
+ def validate_name(name)
88
+ true
89
+ end
90
+
91
+ def format_stream(tag, es)
92
+ out = ''
93
+ es.each {|event|
94
+ record = event.record
95
+ record['time'] = event.time
96
+ record.to_msgpack(out)
97
+ }
98
+ out
99
+ end
100
+
101
+ def write(chunk)
102
+ database, table = chunk.key.split('.',2)
103
+ if !validate_name(database) || !validate_name(table)
104
+ $log.error "Invalid key name #{chunk.key.inspect}"
105
+ return
106
+ end
107
+
108
+ f = Tempfile.new("tdlog-", @tmpdir)
109
+ w = Zlib::GzipWriter.new(f)
110
+
111
+ chunk.write_to(w)
112
+ w.finish
113
+ w = nil
114
+
115
+ size = f.pos
116
+ f.pos = 0
117
+ upload(database, table, f, size)
118
+
119
+ ensure
120
+ w.close if w
121
+ f.close if f
122
+ end
123
+
124
+ def upload(database, table, io, size)
125
+ http, header = new_http
126
+ header['Content-Length'] = size.to_s
127
+ header['Content-Type'] = 'application/octet-stream'
128
+
129
+ url = "/v3/table/import/#{e database}/#{e table}/msgpack.gz"
130
+
131
+ req = Net::HTTP::Put.new(url, header)
132
+ if req.respond_to?(:body_stream=)
133
+ req.body_stream = io
134
+ else # Ruby 1.8
135
+ req.body = io.read
136
+ end
137
+
138
+ $log.trace { "uploading logs to Treasure Data database=#{database} table=#{table} (#{size}bytes)" }
139
+
140
+ response = http.request(req)
141
+
142
+ if response.code[0] != ?2
143
+ raise "Treasure Data upload failed: #{response.body}"
144
+ end
145
+ end
146
+
147
+ def get_table_list
148
+ $log.info "updating table list from Treasure Data"
149
+ list = []
150
+ api_list_database.each {|db|
151
+ api_list_table(db).each {|t|
152
+ list << "#{db}.#{t}"
153
+ }
154
+ }
155
+ list
156
+ end
157
+
158
+ def api_list_database
159
+ body = get("/v3/database/list")
160
+ js = JSON.load(body)
161
+ return js["databases"].map {|m| m['name'] }
162
+ end
163
+
164
+ def api_list_table(db)
165
+ body = get("/v3/table/list/#{e db}")
166
+ js = JSON.load(body)
167
+ return js["tables"].map {|m| m['name'] }
168
+ end
169
+
170
+ def get(path)
171
+ http, header = new_http
172
+
173
+ request = Net::HTTP::Get.new(path, header)
174
+
175
+ response = http.request(request)
176
+
177
+ if response.code[0] != ?2
178
+ raise "Treasure Data API failed: #{response.body}"
179
+ end
180
+
181
+ return response.body
182
+ end
183
+
184
+ def new_http
185
+ http = Net::HTTP.new(HOST, PORT)
186
+ if USE_SSL
187
+ http.use_ssl = true
188
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
189
+ store = OpenSSL::X509::Store.new
190
+ http.cert_store = store
191
+ end
192
+
193
+ # TODO read_timeout
194
+ #http.read_timeout = options[:read_timeout]
195
+
196
+ header = {}
197
+ header['Authorization'] = "TD1 #{@apikey}"
198
+ header['Date'] = Time.now.rfc2822
199
+
200
+ return http, header
201
+ end
202
+
203
+ def e(s)
204
+ CGI.escape(s.to_s)
205
+ end
206
+ end
207
+
208
+
209
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-td
3
+ version: !ruby/object:Gem::Version
4
+ hash: 53
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 7
10
+ version: 0.9.7
11
+ platform: ruby
12
+ authors:
13
+ - Sadayuki Furuhashi
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-06 00:00:00 +09:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: fluent
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 53
30
+ segments:
31
+ - 0
32
+ - 9
33
+ - 7
34
+ version: 0.9.7
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description:
38
+ email:
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - README.rdoc
45
+ files:
46
+ - AUTHORS
47
+ - Rakefile
48
+ - VERSION
49
+ - example.conf
50
+ - fluent-plugin-td.gemspec
51
+ - lib/fluent/plugin/out_tdlog.rb
52
+ - README.rdoc
53
+ has_rdoc: true
54
+ homepage:
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --charset=UTF-8
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.7
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Treasure Data plugin for Fluent event collector
87
+ test_files: []
88
+