fluent-plugin-heroku-syslog 0.0.1 → 0.1.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
  SHA1:
3
- metadata.gz: c1ba40b8e6dea0a28ffed82b25787897c0ead301
4
- data.tar.gz: e2e430813032e6445a4bf0344dca1f746580ca09
3
+ metadata.gz: 45464deb9237a40a07e441e968af9c5b70fa3bce
4
+ data.tar.gz: e4308639e7ccb688d490d81f6e2d47ff11b125fa
5
5
  SHA512:
6
- metadata.gz: cc75128973dfcb608908f1f930e9cf4e6eb436457a12d1cbfddc1308c902e8e56d77df21f34b153c9f32c3c9a2c3b3c47e6490eb31df67cb1c3c699d35dfbf53
7
- data.tar.gz: 4743b87ccc0fec77eb82613a49308700995aa305a133ed68c55c39631339b4b356e14c6b1dbabe0475491ee69dddbfea839cd21cd2404aa8f4a44cae51f5f98e
6
+ metadata.gz: 7f5051f8e44f95e6f6597e9a6c2c3be526396c0bf97e146d0d310d857f2d4676b524636209cb2b1df00376f44779fc1d6569547de5ec91320438b3cbb7432bc1
7
+ data.tar.gz: a2037e66e3937285987c138091a7959e16e807ad328120d81d61a9f794d47c5a0dcd58b5d359a33ab98ce27eca09647294060e7d1b6d763b7ab60433f16edb48
@@ -3,3 +3,4 @@ rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
5
  - 2.1.0
6
+ - 2.2.0
data/README.md CHANGED
@@ -1,16 +1,28 @@
1
1
  # fluent-plugin-heroku-syslog
2
2
 
3
- fluent plugin to drain heroku syslog.
3
+ [fluent](http://fluentd.org) plugin to drain heroku syslog.
4
4
 
5
5
  [![Build Status](https://travis-ci.org/hakobera/fluent-plugin-heroku-syslog.png?branch=master)](https://travis-ci.org/hakobera/fluent-plugin-heroku-syslog)
6
6
 
7
+ ## Installation
8
+
9
+ Install with gem or fluent-gem command as:
10
+
11
+ ```
12
+ # for fluentd
13
+ $ gem install fluent-plugin-heroku-syslog
14
+
15
+ # for td-agent
16
+ $ sudo /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-heroku-syslog
17
+ ```
18
+
7
19
  ## Component
8
20
 
9
21
  ### HerokuSyslogInput
10
22
 
11
- Plugin to accept syslog input from [heroku syslog drains](https://devcenter.heroku.com/articles/logging#syslog-drains).
23
+ Plugin to accept syslog input from [heroku syslog drains](https://devcenter.heroku.com/articles/log-drains#syslog-drains).
12
24
 
13
- ## Configuration
25
+ #### Configuration
14
26
 
15
27
  ```
16
28
  <source>
@@ -21,9 +33,34 @@ Plugin to accept syslog input from [heroku syslog drains](https://devcenter.hero
21
33
  </source>
22
34
  ```
23
35
 
24
- ## TODO
36
+ ### HerokuSyslogHttpInput
25
37
 
26
- - Implement authentication logic or filter like HTTP basic auth.
38
+ Plugin to accept syslog input from [heroku http(s) drains](https://devcenter.heroku.com/articles/log-drains#http-s-drains).
39
+
40
+ #### Configuration
41
+
42
+ ##### Basic
43
+
44
+ ```
45
+ <source>
46
+ type heroku_syslog_http
47
+ port 9880
48
+ bind 0.0.0.0
49
+ tag heroku
50
+ </source>
51
+ ```
52
+
53
+ ##### Filter by drain_ids
54
+
55
+ ```
56
+ <source>
57
+ type heroku_syslog_http
58
+ port 9880
59
+ bind 0.0.0.0
60
+ tag heroku
61
+ drain_ids ["YOUR-HEROKU-DRAIN-ID"]
62
+ </source>
63
+ ```
27
64
 
28
65
  ## Copyright
29
66
 
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
3
  gem.name = "fluent-plugin-heroku-syslog"
4
- gem.version = "0.0.1"
4
+ gem.version = "0.1.0"
5
5
  gem.authors = ["Kazuyuki Honda"]
6
6
  gem.email = ["hakobera@gmail.com"]
7
7
  gem.description = %q{fluent plugin to drain heroku syslog}
@@ -14,6 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
15
  gem.require_paths = ["lib"]
16
16
 
17
- gem.add_runtime_dependency "fluentd"
17
+ gem.add_runtime_dependency "fluentd", ">= 0.10.55"
18
18
  gem.add_development_dependency "rake"
19
+ gem.add_development_dependency("test-unit", ["~> 3.0.2"])
19
20
  end
@@ -1,221 +1,35 @@
1
+ require 'fluent/plugin/in_tcp'
2
+ require_relative 'logplex'
3
+
1
4
  module Fluent
2
- class HerokuSyslogInput < Input
5
+ class HerokuSyslogInput < TcpInput
3
6
  Plugin.register_input('heroku_syslog', self)
7
+ include Logplex
4
8
 
5
- OCTET_COUNTING_REGEXP = /^([0-9]+)\s+(.*)/
6
- SYSLOG_REGEXP = /^\<([0-9]+)\>[0-9]*(.*)/
7
- SYSLOG_ALL_REGEXP = /^\<(?<pri>[0-9]+)\>[0-9]* (?<time>[^ ]*) (?<drain_id>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*) (?<pid>[a-zA-Z0-9\.]+)? *(?<message>.*)$/
8
- TIME_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
9
-
10
- FACILITY_MAP = {
11
- 0 => 'kern',
12
- 1 => 'user',
13
- 2 => 'mail',
14
- 3 => 'daemon',
15
- 4 => 'auth',
16
- 5 => 'syslog',
17
- 6 => 'lpr',
18
- 7 => 'news',
19
- 8 => 'uucp',
20
- 9 => 'cron',
21
- 10 => 'authpriv',
22
- 11 => 'ftp',
23
- 12 => 'ntp',
24
- 13 => 'audit',
25
- 14 => 'alert',
26
- 15 => 'at',
27
- 16 => 'local0',
28
- 17 => 'local1',
29
- 18 => 'local2',
30
- 19 => 'local3',
31
- 20 => 'local4',
32
- 21 => 'local5',
33
- 22 => 'local6',
34
- 23 => 'local7'
35
- }
36
-
37
- PRIORITY_MAP = {
38
- 0 => 'emerg',
39
- 1 => 'alert',
40
- 2 => 'crit',
41
- 3 => 'err',
42
- 4 => 'warn',
43
- 5 => 'notice',
44
- 6 => 'info',
45
- 7 => 'debug'
46
- }
47
-
48
- def initialize
49
- super
50
- require 'cool.io'
51
- require 'fluent/plugin/socket_util'
52
- end
53
-
54
- config_param :port, :integer, :default => 5140
55
- config_param :bind, :string, :default => '0.0.0.0'
56
- config_param :tag, :string
57
-
58
- def configure(conf)
59
- super
60
-
61
- parser = TextParser.new
62
- if parser.configure(conf, false)
63
- @parser = parser
64
- else
65
- @parser = nil
66
- @time_parser = TextParser::TimeParser.new(TIME_FORMAT)
67
- end
68
- end
69
-
70
- def start
71
- if @parser
72
- callback = method(:receive_data_parser)
73
- else
74
- callback = method(:receive_data)
75
- end
76
-
77
- @loop = Coolio::Loop.new
78
- @handler = listen(callback)
79
- @loop.attach(@handler)
80
-
81
- @thread = Thread.new(&method(:run))
82
- end
83
-
84
- def shutdown
85
- @loop.watchers.each {|w| w.detach }
86
- @loop.stop
87
- @handler.close
88
- @thread.join
89
- end
90
-
91
- def run
92
- @loop.run
93
- rescue
94
- $log.error "unexpected error", :error=>$!.to_s
95
- $log.error_backtrace
96
- end
97
-
98
- protected
99
- def receive_data_parser(data)
100
- m = SYSLOG_REGEXP.match(data)
101
- unless m
102
- $log.debug "invalid syslog message: #{data.dump}"
103
- return
104
- end
105
- pri = m[1].to_i
106
- text = m[2]
107
-
108
- time, record = @parser.parse(text)
109
- unless time && record
110
- return
111
- end
112
-
113
- emit(pri, time, record)
114
-
115
- rescue
116
- $log.warn data.dump, :error=>$!.to_s
117
- $log.debug_backtrace
118
- end
119
-
120
- def receive_data(data)
121
- m = SYSLOG_ALL_REGEXP.match(data)
122
- unless m
123
- $log.debug "invalid syslog message", :data=>data
124
- return
125
- end
126
-
127
- pri = nil
128
- time = nil
129
- record = {}
130
-
131
- m.names.each {|name|
132
- if value = m[name]
133
- case name
134
- when "pri"
135
- pri = value.to_i
136
- when "time"
137
- time = @time_parser.parse(value.gsub(/ +/, ' ').gsub(/\.[0-9]+/, ''))
138
- else
139
- record[name] = value
140
- end
141
- end
142
- }
143
-
144
- time ||= Engine.now
145
-
146
- emit(pri, time, record)
147
-
148
- rescue
149
- $log.warn data.dump, :error=>$!.to_s
150
- $log.debug_backtrace
151
- end
9
+ config_param :format, :string, :default => SYSLOG_REGEXP
10
+ config_param :drain_ids, :array, :default => nil
152
11
 
153
12
  private
154
13
 
155
- def listen(callback)
156
- $log.debug "listening heroku syslog socket on #{@bind}:#{@port}"
157
- Coolio::TCPServer.new(@bind, @port, TcpHandler, callback)
158
- end
159
-
160
- def emit(pri, time, record)
161
- facility = FACILITY_MAP[pri >> 3]
162
- priority = PRIORITY_MAP[pri & 0b111]
163
-
164
- tag = "#{@tag}.#{facility}.#{priority}"
165
-
166
- Engine.emit(tag, time, record)
167
- rescue => e
168
- $log.error "syslog failed to emit", :error => e.to_s, :error_class => e.class.to_s, :tag => tag, :record => Yajl.dump(record)
169
- end
170
-
171
- class TcpHandler < Coolio::Socket
172
- def initialize(io, on_message)
173
- super(io)
174
- if io.is_a?(TCPSocket)
175
- opt = [1, @timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
176
- io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
14
+ def on_message(msg, addr)
15
+ @parser.parse(msg) { |time, record|
16
+ unless time && record
17
+ log.warn "pattern not match: #{msg.inspect}"
18
+ return
177
19
  end
178
- $log.trace { "accepted fluent socket object_id=#{self.object_id}" }
179
- @on_message = on_message
180
- @buffer = "".force_encoding('ASCII-8BIT')
181
- end
182
-
183
- def on_connect
184
- end
185
20
 
186
- def on_read(data)
187
- @buffer << data
188
- pos = 0
189
-
190
- # syslog family add "\n" to each message and this seems only way to split messages in tcp stream
191
- while i = @buffer.index("\n", pos)
192
- msg = @buffer[pos..i]
193
-
194
- # Support Octet Counting
195
- # https://tools.ietf.org/html/rfc6587#section-3.4.1
196
- m = OCTET_COUNTING_REGEXP.match(msg)
197
- valid = true
198
- if m
199
- msg_len = m[1].to_i - 1
200
- msg = m[2]
201
-
202
- if msg_len != msg.length
203
- $log.debug "invalid syslog message length", :expected => msg_len, :actual => msg.length, :data => msg
204
- valid = false
205
- end
206
- end
207
- @on_message.call(msg) if valid
208
- pos = i + 1
21
+ unless @drain_ids.nil? || @drain_ids.include?(record['drain_id'])
22
+ log.warn "drain_id not match: #{msg.inspect}"
23
+ return
209
24
  end
210
- @buffer.slice!(0, pos) if pos > 0
211
- rescue => e
212
- $log.error "syslog error", :error => e, :error_class => e.class
213
- close
214
- end
215
25
 
216
- def on_close
217
- $log.trace { "closed fluent socket object_id=#{self.object_id}" }
218
- end
26
+ record[@source_host_key] = addr[3] if @source_host_key
27
+ parse_logplex(record)
28
+ router.emit(@tag, time, record)
29
+ }
30
+ rescue => e
31
+ log.error msg.dump, :error => e, :error_class => e.class, :host => addr[3]
32
+ log.error_backtrace
219
33
  end
220
34
  end
221
35
  end
@@ -0,0 +1,37 @@
1
+ require 'fluent/plugin/in_http'
2
+ require_relative 'logplex'
3
+
4
+ module Fluent
5
+ class HerokuSyslogHttpInput < HttpInput
6
+ Plugin.register_input('heroku_syslog_http', self)
7
+ include Logplex
8
+
9
+ config_param :format, :string, :default => SYSLOG_REGEXP
10
+ config_param :drain_ids, :array, :default => nil
11
+
12
+ private
13
+
14
+ def parse_params_with_parser(params)
15
+ if content = params[EVENT_RECORD_PARAMETER]
16
+ records = []
17
+ messages = content.split("\n")
18
+ messages.each do |msg|
19
+ @parser.parse(msg) { |time, record|
20
+ raise "Received event is not #{@format}: #{content}" if record.nil?
21
+
22
+ record["time"] ||= time
23
+ parse_logplex(record, params)
24
+ unless @drain_ids.nil? || @drain_ids.include?(record['drain_id'])
25
+ log.warn "drain_id not match: #{msg.inspect}"
26
+ next
27
+ end
28
+ records << record
29
+ }
30
+ end
31
+ return nil, records
32
+ else
33
+ raise "'#{EVENT_RECORD_PARAMETER}' parameter is required"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,55 @@
1
+ module Fluent
2
+ module Logplex
3
+ SYSLOG_REGEXP = '/^([0-9]+)\\s+\\<(?<pri>[0-9]+)\\>[0-9]* (?<time>[^ ]*) (?<drain_id>[^ ]*) (?<ident>[a-zA-Z0-9_\\/\\.\\-]*) (?<pid>[a-zA-Z0-9\\.]+)? *(?<message>.*)$/'
4
+
5
+ FACILITY_MAP = {
6
+ 0 => 'kern',
7
+ 1 => 'user',
8
+ 2 => 'mail',
9
+ 3 => 'daemon',
10
+ 4 => 'auth',
11
+ 5 => 'syslog',
12
+ 6 => 'lpr',
13
+ 7 => 'news',
14
+ 8 => 'uucp',
15
+ 9 => 'cron',
16
+ 10 => 'authpriv',
17
+ 11 => 'ftp',
18
+ 12 => 'ntp',
19
+ 13 => 'audit',
20
+ 14 => 'alert',
21
+ 15 => 'at',
22
+ 16 => 'local0',
23
+ 17 => 'local1',
24
+ 18 => 'local2',
25
+ 19 => 'local3',
26
+ 20 => 'local4',
27
+ 21 => 'local5',
28
+ 22 => 'local6',
29
+ 23 => 'local7'
30
+ }
31
+
32
+ PRIORITY_MAP = {
33
+ 0 => 'emerg',
34
+ 1 => 'alert',
35
+ 2 => 'crit',
36
+ 3 => 'err',
37
+ 4 => 'warn',
38
+ 5 => 'notice',
39
+ 6 => 'info',
40
+ 7 => 'debug'
41
+ }
42
+
43
+ def parse_logplex(record, params=nil)
44
+ pri = record['pri'].to_i
45
+ record['facility'] = FACILITY_MAP[pri >> 3]
46
+ record['priority'] = PRIORITY_MAP[pri & 0b111]
47
+
48
+ if params
49
+ record['drain_id'] = params['HTTP_LOGPLEX_DRAIN_TOKEN']
50
+ end
51
+
52
+ record
53
+ end
54
+ end
55
+ end
@@ -42,6 +42,7 @@ def ipv6_enabled?
42
42
  end
43
43
 
44
44
  require 'fluent/plugin/in_heroku_syslog'
45
+ require 'fluent/plugin/in_heroku_syslog_http'
45
46
 
46
47
  class Test::Unit::TestCase
47
48
  end
@@ -15,7 +15,7 @@ class HerokuSyslogInputTest < Test::Unit::TestCase
15
15
  IPv6_CONFIG = %[
16
16
  port #{PORT}
17
17
  bind ::1
18
- tag syslog
18
+ tag heroku.syslog
19
19
  ]
20
20
 
21
21
  def create_driver(conf=CONFIG)
@@ -43,11 +43,13 @@ class HerokuSyslogInputTest < Test::Unit::TestCase
43
43
  tests = [
44
44
  {
45
45
  'msg' => "92 <13>1 2014-01-29T06:25:52.589365+00:00 d.916a3e50-efa1-4754-aded-ffffffffffff app web.1 foo\n",
46
- 'expected' => Time.strptime('2014-01-29T06:25:52+00:00', '%Y-%m-%dT%H:%M:%S%z').to_i
46
+ 'expected' => 'foo',
47
+ 'expected_time' => Time.strptime('2014-01-29T06:25:52+00:00', '%Y-%m-%dT%H:%M:%S%z').to_i
47
48
  },
48
49
  {
49
50
  'msg' => "92 <13>1 2014-01-30T07:35:00.123456+09:00 d.916a3e50-efa1-4754-aded-ffffffffffff app web.1 bar\n",
50
- 'expected' => Time.strptime('2014-01-30T07:35:00+09:00', '%Y-%m-%dT%H:%M:%S%z').to_i
51
+ 'expected' => 'bar',
52
+ 'expected_time' => Time.strptime('2014-01-30T07:35:00+09:00', '%Y-%m-%dT%H:%M:%S%z').to_i
51
53
  }
52
54
  ]
53
55
 
@@ -60,12 +62,7 @@ class HerokuSyslogInputTest < Test::Unit::TestCase
60
62
  sleep 1
61
63
  end
62
64
 
63
- emits = d.emits
64
- $log.debug emits
65
- emits.each_index {|i|
66
- $log.debug emits[i][1]
67
- assert_equal(tests[i]['expected'], emits[i][1])
68
- }
65
+ compare_test_result(d.emits, tests)
69
66
  }
70
67
  end
71
68
 
@@ -101,6 +98,54 @@ class HerokuSyslogInputTest < Test::Unit::TestCase
101
98
  compare_test_result(d.emits, tests)
102
99
  end
103
100
 
101
+ def test_accept_matched_drain_id
102
+ d = create_driver(CONFIG + "\ndrain_ids [\"d.916a3e50-efa1-4754-aded-ffffffffffff\"]")
103
+ tests = create_test_case
104
+
105
+ d.run do
106
+ TCPSocket.open('127.0.0.1', PORT) do |s|
107
+ tests.each {|test|
108
+ s.send(test['msg'], 0)
109
+ }
110
+ end
111
+ sleep 1
112
+ end
113
+
114
+ compare_test_result(d.emits, tests)
115
+ end
116
+
117
+ def test_accept_matched_drain_id_multiple
118
+ d = create_driver(CONFIG + "\ndrain_ids [\"abc\",\"d.916a3e50-efa1-4754-aded-ffffffffffff\"]")
119
+ tests = create_test_case
120
+
121
+ d.run do
122
+ TCPSocket.open('127.0.0.1', PORT) do |s|
123
+ tests.each {|test|
124
+ s.send(test['msg'], 0)
125
+ }
126
+ end
127
+ sleep 1
128
+ end
129
+
130
+ compare_test_result(d.emits, tests)
131
+ end
132
+
133
+ def test_ignore_unmatched_drain_id
134
+ d = create_driver(CONFIG + "\ndrain_ids [\"abc\"]")
135
+ tests = create_test_case
136
+
137
+ d.run do
138
+ TCPSocket.open('127.0.0.1', PORT) do |s|
139
+ tests.each {|test|
140
+ s.send(test['msg'], 0)
141
+ }
142
+ end
143
+ sleep 1
144
+ end
145
+
146
+ assert_equal(0, d.emits.length)
147
+ end
148
+
104
149
  def create_test_case
105
150
  # actual syslog message has "\n"
106
151
  msgs = [
@@ -116,11 +161,16 @@ class HerokuSyslogInputTest < Test::Unit::TestCase
116
161
  end
117
162
 
118
163
  def compare_test_result(emits, tests)
164
+ assert_equal(tests.length, emits.length)
119
165
  emits.each_index {|i|
120
- assert_equal(tests[i]['expected'], emits[i][2]['message'])
166
+ assert_equal('heroku.syslog', emits[i][0])
167
+ assert_equal(tests[i]['expected_time'], emits[i][1]) if tests[i]['expected_time']
168
+ assert_equal(tests[i]['expected'], emits[i][2]['message']) if tests[i]['expected']
121
169
  assert_equal('d.916a3e50-efa1-4754-aded-ffffffffffff', emits[i][2]['drain_id'])
122
170
  assert_equal('app', emits[i][2]['ident'])
123
171
  assert_equal('web.1', emits[i][2]['pid'])
172
+ assert_equal('user', emits[i][2]['facility'])
173
+ assert_equal('notice', emits[i][2]['priority'])
124
174
  }
125
175
  end
126
176
 
@@ -0,0 +1,181 @@
1
+ require 'helper'
2
+ require 'net/http'
3
+
4
+ class HerokuSyslogHttpInputTest < Test::Unit::TestCase
5
+ def setup
6
+ Fluent::Test.setup
7
+ end
8
+
9
+ PORT = unused_port
10
+ CONFIG = %[
11
+ port #{PORT}
12
+ bind 127.0.0.1
13
+ body_size_limit 10m
14
+ keepalive_timeout 5
15
+ ]
16
+
17
+ def create_driver(conf=CONFIG)
18
+ Fluent::Test::InputTestDriver.new(Fluent::HerokuSyslogHttpInput).configure(conf)
19
+ end
20
+
21
+ def test_configure
22
+ d = create_driver
23
+ assert_equal PORT, d.instance.port
24
+ assert_equal '127.0.0.1', d.instance.bind
25
+ assert_equal 10*1024*1024, d.instance.body_size_limit
26
+ assert_equal 5, d.instance.keepalive_timeout
27
+ end
28
+
29
+ def test_time_format
30
+ d = create_driver
31
+
32
+ tests = [
33
+ {
34
+ 'msg' => "<13>1 2014-01-29T06:25:52.589365+00:00 host app web.1 foo",
35
+ 'expected' => 'foo',
36
+ 'expected_time' => Time.strptime('2014-01-29T06:25:52+00:00', '%Y-%m-%dT%H:%M:%S%z').to_i
37
+ },
38
+ {
39
+ 'msg' => "<13>1 2014-01-30T07:35:00.123456+09:00 host app web.1 bar",
40
+ 'expected' => 'bar',
41
+ 'expected_time' => Time.strptime('2014-01-30T07:35:00+09:00', '%Y-%m-%dT%H:%M:%S%z').to_i
42
+ }
43
+ ]
44
+
45
+ tests.each do |msg|
46
+ msg['msg'] = "#{msg['msg'].length} #{msg['msg']}"
47
+ end
48
+
49
+ d.expect_emit 'heroku', tests[0]['expected_time'], {
50
+ "drain_id" => "d.fc6b856b-3332-4546-93de-7d0ee272c3bd",
51
+ "ident"=>"app",
52
+ "pid"=>"web.1",
53
+ "message"=> "foo",
54
+ "pri" => "13",
55
+ "facility" => "user",
56
+ "priority" => "notice"
57
+ }
58
+
59
+ d.expect_emit 'heroku', tests[1]['expected_time'], {
60
+ "drain_id" => "d.fc6b856b-3332-4546-93de-7d0ee272c3bd",
61
+ "ident"=>"app",
62
+ "pid"=>"web.1",
63
+ "message"=> "bar",
64
+ "pri" => "13",
65
+ "facility" => "user",
66
+ "priority" => "notice"
67
+ }
68
+
69
+ d.run do
70
+ res = post(tests)
71
+ assert_equal "200", res.code
72
+ end
73
+ end
74
+
75
+ def test_msg_size
76
+ d = create_driver
77
+ tests = create_test_case
78
+
79
+ d.expect_emit 'heroku', tests[0]['expected_time'], {
80
+ "drain_id" => "d.fc6b856b-3332-4546-93de-7d0ee272c3bd",
81
+ "ident" => "app",
82
+ "pid" => "web.1",
83
+ "message" => "x" * 100,
84
+ "pri" => "13",
85
+ "facility" => "user",
86
+ "priority" => "notice"
87
+ }
88
+ d.expect_emit 'heroku', tests[1]['expected_time'], {
89
+ "drain_id" => "d.fc6b856b-3332-4546-93de-7d0ee272c3bd",
90
+ "ident" => "app",
91
+ "pid" => "web.1",
92
+ "message" => "x" * 1024,
93
+ "pri" => "13",
94
+ "facility" => "user",
95
+ "priority" => "notice"
96
+ }
97
+
98
+ d.run do
99
+ res = post(tests)
100
+ assert_equal "200", res.code
101
+ end
102
+ end
103
+
104
+ def test_accept_matched_drain_id_multiple
105
+ d = create_driver(CONFIG + "\ndrain_ids [\"abc\", \"d.fc6b856b-3332-4546-93de-7d0ee272c3bd\"]")
106
+ tests = create_test_case
107
+
108
+ d.expect_emit 'heroku', tests[0]['expected_time'], {
109
+ "drain_id" => "d.fc6b856b-3332-4546-93de-7d0ee272c3bd",
110
+ "ident" => "app",
111
+ "pid" => "web.1",
112
+ "message" => "x" * 100,
113
+ "pri" => "13",
114
+ "facility" => "user",
115
+ "priority" => "notice"
116
+ }
117
+ d.expect_emit 'heroku', tests[1]['expected_time'], {
118
+ "drain_id" => "d.fc6b856b-3332-4546-93de-7d0ee272c3bd",
119
+ "ident" => "app",
120
+ "pid" => "web.1",
121
+ "message" => "x" * 1024,
122
+ "pri" => "13",
123
+ "facility" => "user",
124
+ "priority" => "notice"
125
+ }
126
+
127
+ d.run do
128
+ res = post(tests)
129
+ assert_equal "200", res.code
130
+ end
131
+ end
132
+
133
+ def test_ignore_unmatched_drain_id
134
+ d = create_driver(CONFIG + "\ndrain_ids [\"abc\"]")
135
+ tests = create_test_case
136
+
137
+ d.run do
138
+ res = post(tests)
139
+ assert_equal "200", res.code
140
+ end
141
+
142
+ assert_equal(0, d.emits.length)
143
+ end
144
+
145
+ def create_test_case
146
+ # actual syslog message has "\n"
147
+ msgs = [
148
+ {
149
+ 'msg' => '<13>1 2014-01-01T01:23:45.123456+00:00 host app web.1 ' + 'x' * 100,
150
+ 'expected' => 'x' * 100,
151
+ 'expected_time' => Time.parse("2014-01-01T01:23:45 UTC").to_i
152
+ },
153
+ {
154
+ 'msg' => '<13>1 2014-01-01T01:23:45.123456+00:00 host app web.1 ' + 'x' * 1024,
155
+ 'expected' => 'x' * 1024,
156
+ 'expected_time' => Time.parse("2014-01-01T01:23:45 UTC").to_i
157
+ }
158
+ ]
159
+
160
+ msgs.each do |msg|
161
+ msg['msg'] = "#{msg['msg'].length} #{msg['msg']}"
162
+ end
163
+
164
+ msgs
165
+ end
166
+
167
+ def post(messages)
168
+ # https://github.com/heroku/logplex/blob/master/doc/README.http_drains.md
169
+ http = Net::HTTP.new("127.0.0.1", PORT)
170
+ req = Net::HTTP::Post.new('/heroku', {
171
+ "Content-Type" => "application/logplex-1",
172
+ "Logplex-Msg-Count" => messages.length.to_s,
173
+ "Logplex-Frame-Id" => "09C557EAFCFB6CF2740EE62F62971098",
174
+ "Logplex-Drain-Token" => "d.fc6b856b-3332-4546-93de-7d0ee272c3bd",
175
+ "User-Agent" => "Logplex/v49"
176
+ })
177
+ req.body = messages.map {|msg| msg['msg']}.join("\n")
178
+ http.request(req)
179
+ end
180
+
181
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-heroku-syslog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuyuki Honda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-30 00:00:00.000000000 Z
11
+ date: 2015-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.10.55
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.10.55
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.0.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.0.2
41
55
  description: fluent plugin to drain heroku syslog
42
56
  email:
43
57
  - hakobera@gmail.com
@@ -53,8 +67,11 @@ files:
53
67
  - Rakefile
54
68
  - fluent-plugin-heroku-syslog.gemspec
55
69
  - lib/fluent/plugin/in_heroku_syslog.rb
70
+ - lib/fluent/plugin/in_heroku_syslog_http.rb
71
+ - lib/fluent/plugin/logplex.rb
56
72
  - test/helper.rb
57
73
  - test/plugin/test_in_heroku_syslog.rb
74
+ - test/plugin/test_in_heroku_syslog_http.rb
58
75
  homepage: https://github.com/hakobera/fluent-plugin-heroku-syslog
59
76
  licenses:
60
77
  - APLv2
@@ -75,10 +92,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
92
  version: '0'
76
93
  requirements: []
77
94
  rubyforge_project:
78
- rubygems_version: 2.2.0
95
+ rubygems_version: 2.2.2
79
96
  signing_key:
80
97
  specification_version: 4
81
98
  summary: fluent plugin to drain heroku syslog
82
99
  test_files:
83
100
  - test/helper.rb
84
101
  - test/plugin/test_in_heroku_syslog.rb
102
+ - test/plugin/test_in_heroku_syslog_http.rb