logstash-input-varnishncsa 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 92cfd4712bce564f03d74d66698d594756468938
4
+ data.tar.gz: f61dbfeeafd939de2256f3be27b0d56b99f53115
5
+ SHA512:
6
+ metadata.gz: 6326e0364864b985d44927e649c7f2408436f06202e46c5a6ce64c308730d71560bff2df5728c81b71d27e791d60f591942a0a19a9ff29d8d629573362d2ed96
7
+ data.tar.gz: b9718c14c33946c52be7b0ffc8e9e5bf42477eb01b30bd84da59144bb8900d333b954d0931f0373de73db9492f402d27bdfe0370cf3ac03d1396ce50879f178f
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2012–2015 Francetelevisions <http://www.francetelevisions.fr>
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Logstash Plugin
2
+
3
+ This is a plugin for [Logstash](https://github.com/elastic/logstash).
4
+
5
+ It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
6
+
7
+ ## Documentation
8
+
9
+ Logstash provides infrastructure to automatically generate documentation for this plugin. We use the asciidoc format to write documentation so any comments in the source code will be first converted into asciidoc and then into html. All plugin documentation are placed under one [central location](http://www.elastic.co/guide/en/logstash/current/).
10
+
11
+ - For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
12
+ - For more asciidoc formatting tips, see the excellent reference here https://github.com/elastic/docs#asciidoc-guide
13
+
14
+ ## Need Help?
15
+
16
+ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
17
+
18
+ ## Developing
19
+
20
+ ### 1. Plugin Developement and Testing
21
+
22
+ #### Code
23
+ - To get started, you'll need JRuby with the Bundler gem installed.
24
+
25
+ - Create a new plugin or clone and existing from the GitHub [logstash-plugins](https://github.com/logstash-plugins) organization. We also provide [example plugins](https://github.com/logstash-plugins?query=example).
26
+
27
+ - Install dependencies
28
+ ```sh
29
+ bundle install
30
+ ```
31
+
32
+ #### Test
33
+
34
+ - Update your dependencies
35
+
36
+ ```sh
37
+ bundle install
38
+ ```
39
+
40
+ - Run tests
41
+
42
+ ```sh
43
+ bundle exec rspec
44
+ ```
45
+
46
+ ### 2. Running your unpublished Plugin in Logstash
47
+
48
+ #### 2.1 Run in a local Logstash clone
49
+
50
+ - Edit Logstash `Gemfile` and add the local plugin path, for example:
51
+ ```ruby
52
+ gem "logstash-filter-awesome", :path => "/your/local/logstash-filter-awesome"
53
+ ```
54
+ - Install plugin
55
+ ```sh
56
+ bin/plugin install --no-verify
57
+ ```
58
+ - Run Logstash with your plugin
59
+ ```sh
60
+ bin/logstash -e 'filter {awesome {}}'
61
+ ```
62
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
63
+
64
+ #### 2.2 Run in an installed Logstash
65
+
66
+ You can use the same **2.1** method to run your plugin in an installed Logstash by editing its `Gemfile` and pointing the `:path` to your local plugin development directory or you can build the gem and install it using:
67
+
68
+ - Build your plugin gem
69
+ ```sh
70
+ gem build logstash-filter-awesome.gemspec
71
+ ```
72
+ - Install the plugin from the Logstash home
73
+ ```sh
74
+ bin/plugin install /your/local/plugin/logstash-filter-awesome.gem
75
+ ```
76
+ - Start Logstash and proceed to test the plugin
77
+
78
+ ## Contributing
79
+
80
+ All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
81
+
82
+ Programming is not a required skill. Whatever you've seen about open source and maintainers or community members saying "send patches or die" - you will not see that here.
83
+
84
+ It is more important to the community that you are able to contribute.
85
+
86
+ For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
@@ -0,0 +1,336 @@
1
+ # encoding: utf-8
2
+ require "scanf"
3
+ require "logstash/inputs/threadable"
4
+ require "logstash/namespace"
5
+ require "socket" # for Socket.gethostname
6
+
7
+ # Read from varnish cache's shared memory log
8
+ class LogStash::Inputs::Varnishncsa < LogStash::Inputs::Threadable
9
+ config_name "varnishncsa"
10
+
11
+ config :instance, :validate => :string, :default => "default"
12
+
13
+ trap("SIGINT") { @@reopen = 1}
14
+
15
+ Logline = Struct.new(
16
+ :df_H, # %H, Protocol version
17
+ :df_U, # %U, URL path
18
+ :df_q, # %q, query string
19
+ :df_b, # %b, Bytes
20
+ :df_h, # %h (host name / IP adress)
21
+ :df_m, # %m, Request metho
22
+ :df_s, # %s, Status
23
+ :df_t, # %t, Date and time, received
24
+ :df_u, # %u, Remote user
25
+ :df_ttfb, # Time to first byte
26
+ :df_D, # %D, time taken to serve the request,
27
+ # in microseconds, also used for %T
28
+ :df_hitmiss, # Whether this is a hit or miss
29
+ :df_handling, # How the request was handled (hit/miss/pass/pipe)
30
+ :active, # Is log line in an active trans
31
+ :complete, # Is log line complete
32
+ :bitmap, # Bitmap for regex matches
33
+ :req_headers, # Request headers
34
+ :resp_headers, # Response headers
35
+ :vcl_log, # VLC_Log entries
36
+ )
37
+
38
+ public
39
+ def register
40
+ require 'varnish'
41
+ @lp = Logline.new
42
+ clean_logline
43
+ @lp[:df_hitmiss] = nil
44
+ @lp[:df_handling] = nil
45
+ @lp[:active] = false
46
+ @lp[:complete] = false
47
+ @vd = Varnish::VSM.VSM_New
48
+ @@reopen = 0
49
+ Varnish::VSL.VSL_Setup(@vd)
50
+ if Varnish::VSM.VSM_n_Arg(@vd, @instance) != 1
51
+ @logger.warn("varnishlog exception: #{e.inspect}")
52
+ end
53
+ Varnish::VSL.VSL_Open(@vd, 1)
54
+ end # def register
55
+
56
+ def run(queue)
57
+ @q = queue
58
+ @hostname = Socket.gethostname
59
+ # callback = Proc.new { |*args| h_ncsa(*args) }
60
+ Varnish::VSL.VSL_Dispatch(@vd, self.method(:h_ncsa).to_proc, FFI::MemoryPointer.new(:pointer))
61
+ end # def run
62
+
63
+ private
64
+ def clean_logline(tag = '')
65
+ @lp[:df_H] = nil
66
+ @lp[:df_U] = nil
67
+ @lp[:df_q] = nil
68
+ @lp[:df_b] = nil
69
+ @lp[:df_h] = nil
70
+ @lp[:df_m] = nil
71
+ @lp[:df_s] = nil
72
+ @lp[:df_u] = nil
73
+ @lp[:df_ttfb] = nil
74
+ @lp[:req_headers] = []
75
+ @lp[:resp_headers] = []
76
+ @lp[:vcl_log] = []
77
+ end
78
+
79
+ def collect_client(tag, str)
80
+ case tag
81
+ # Collect Client
82
+ when :reqstart
83
+ if @lp[:active] || @lp[:df_h] != nil
84
+ clean_logline(tag)
85
+ end
86
+ @lp[:active] = true;
87
+ @lp[:df_h] = str.split(' ').first
88
+ when :rxrequest
89
+ if @lp[:active]
90
+ if @lp[:df_m] != nil
91
+ clean_logline(tag)
92
+ else
93
+ @lp[:df_m] = str
94
+ end
95
+ end
96
+ when :rxurl
97
+ if @lp[:active]
98
+ if @lp[:df_U] != nil || @lp[:df_q] != nil
99
+ clean_logline(tag)
100
+ else
101
+ qs = str.index('?')
102
+ if qs
103
+ @lp[:df_U] = str[0, qs]
104
+ @lp[:df_q] = str[qs, str.length]
105
+ else
106
+ @lp[:df_U] = str
107
+ end
108
+ end
109
+ end
110
+ when :rxprotocol
111
+ if @lp[:active]
112
+ if @lp[:df_H] != nil
113
+ clean_logline(tag)
114
+ else
115
+ @lp[:df_H] = str
116
+ end
117
+ end
118
+ when :txstatus
119
+ if @lp[:active]
120
+ if @lp[:df_s] != nil
121
+ clean_logline(tag)
122
+ else
123
+ @lp[:df_s] = str
124
+ end
125
+ end
126
+ when :rxheader || :txheader
127
+ if @lp[:active]
128
+ split = str.index(':')
129
+ if !split == nil
130
+ if tag == :rxheader && str[0, split] == "Authorization" && str[split+2, str.length] == "basic"
131
+ @lp[:df_u] = str
132
+ end
133
+ else
134
+ if tag == :rxheader
135
+ @lp[:req_headers].push({str[0, split] => str[split+2, str.length]})
136
+ else
137
+ @lp[:resp_headers].push({str[0, split] => str[split+2, str.length]})
138
+ end
139
+ end
140
+ end
141
+ when :vcl_log
142
+ if @lp[:active]
143
+ split = str.index(':')
144
+ if !split == nil
145
+ @lp[:vcl_log].push({str[0, split] => str[split+2, str.length]})
146
+ end
147
+ end
148
+ when :vcl_call
149
+ if @lp[:active]
150
+ if str == "hit"
151
+ @lp[:df_hitmiss] = "hit"
152
+ @lp[:df_handling] = "hit"
153
+ elsif str == "miss"
154
+ @lp[:df_hitmiss] = "miss"
155
+ @lp[:df_handling] = "miss"
156
+ elsif str == "pass"
157
+ @lp[:df_hitmiss] = "miss"
158
+ @lp[:df_handling] = "pass"
159
+ elsif str == "pipe"
160
+ clean_logline(tag)
161
+ end
162
+ end
163
+ when :length
164
+ if @lp[:active]
165
+ if @lp[:df_b] != nil
166
+ clean_logline(tag)
167
+ else
168
+ @lp[:df_b] = str
169
+ end
170
+ end
171
+ when :sessionclose
172
+ if @lp[:active]
173
+ if str == "pipe" || str == "error"
174
+ clean_logline(tag)
175
+ end
176
+ end
177
+ when :reqend
178
+ t_req = str.scanf("%*u %f %f %*u.%*u %s")
179
+ t_start = t_req[0]
180
+ t_end = t_req[1]
181
+ ttfb = t_req[2]
182
+ if @lp[:active]
183
+ @lp[:df_ttfb] = ttfb
184
+ @lp[:df_D] = t_end - t_start
185
+ @lp[:df_t] = Time.at(t_start)
186
+ @lp[:complete] = true
187
+ end
188
+ end
189
+ end
190
+
191
+ def collect_backend(tag, str)
192
+ case tag
193
+ # Collect Backend
194
+ when :backendopen
195
+ if @lp[:active] || @lp[:df_h] != nil
196
+ clean_logline(tag)
197
+ end
198
+ @lp[:active] = true;
199
+ @lp[:df_h] = str.split(' ')[1]
200
+ when :txrequest
201
+ if @lp[:active]
202
+ if @lp[:df_m] != nil
203
+ clean_logline(tag)
204
+ else
205
+ @lp[:df_m] = str
206
+ end
207
+ end
208
+ when :txurl
209
+ if @lp[:active]
210
+ if @lp[:df_U] != nil || @lp[:df_q] != nil
211
+ clean_logline(tag)
212
+ else
213
+ qs = str.index('?')
214
+ if qs
215
+ @lp[:df_U] = str[0, qs]
216
+ @lp[:df_q] = str[qs, str.length]
217
+ else
218
+ @lp[:df_U] = str
219
+ end
220
+ end
221
+ end
222
+ when :txprotocol
223
+ if @lp[:active]
224
+ if @lp[:df_H] != nil
225
+ clean_logline(tag)
226
+ else
227
+ @lp[:df_H] = str
228
+ end
229
+ end
230
+ when :rxstatus
231
+ if @lp[:active]
232
+ if @lp[:df_s] != nil
233
+ clean_logline(tag)
234
+ else
235
+ @lp[:df_s] = str
236
+ end
237
+ end
238
+ when :rxheader
239
+ if @lp[:active]
240
+ split = str.index(':')
241
+ if str[0, split] == 'Content-Length'
242
+ @lp[:df_b] = str[split+2, str.length]
243
+ elsif str[0, split] == 'Date'
244
+ @lp[:df_t] = DateTime.strptime(str[split+2, str.length], "%a, %d %b %Y %T %Z")
245
+ if @lp[:df_t] == nil
246
+ clean_logline(tag)
247
+ end
248
+ end
249
+ end
250
+ when :txheader
251
+ if @lp[:active]
252
+ split = str.index(':')
253
+ @lp[:req_headers].push({str[0, split] => str[split+2, str.length]})
254
+ end
255
+ when :backendclose || :backendreuse
256
+ if @lp[:active]
257
+ @lp[:complete] = true
258
+ end
259
+ end
260
+ end
261
+
262
+ def h_ncsa(priv, tag, fd, len, spec, ptr, bitmap)
263
+ begin
264
+ str = ptr.read_string(len)
265
+ if spec == :spec_client
266
+ collect_client(tag, str)
267
+ elsif spec == :spec_backend
268
+ collect_backend(tag, str)
269
+ else
270
+ return @@reopen
271
+ end
272
+ if !@lp[:complete]
273
+ return @@reopen
274
+ end
275
+ # We have a complete data set - log a line
276
+ headers = Hash.new
277
+ if @lp[:resp_headers] != []
278
+ headers = @lp[:resp_headers].reduce Hash.new, :merge
279
+ elsif @lp[:req_headers] != []
280
+ headers = @lp[:req_headers].reduce Hash.new, :merge
281
+ end
282
+ if !@lp[:df_h].nil?
283
+ event = LogStash::Event.new("message" => "[#{@lp[:df_t].to_s}] http://#{headers['Host'].to_s}#{@lp[:df_U].to_s}#{@lp[:df_q]} #{@lp[:df_h]}", "host" => @hostname)
284
+ decorate(event)
285
+
286
+ event["varnish_spec"] = spec.to_s
287
+ event["clientip"] = headers['X-Client-IP'].nil? ? headers['X-Forwarded-For'].nil? ? '' : headers['X-Forwarded-For'].split(',').first : headers['X-Client-IP']
288
+ event["timestamp"] = @lp[:df_t].to_s
289
+ event["vhost"] = headers['Host']
290
+ event["ident"] = '-'
291
+ event["auth"] = @lp[:df_u].to_s
292
+ event["verb"] = @lp[:df_m].to_s
293
+ event["request"] = "#{@lp[:df_U].to_s}#{@lp[:df_q]}"
294
+ event["httpversion"] = @lp[:df_H].to_s
295
+ event["rawrequest"] = "#{@lp[:df_m]} #{@lp[:df_U].to_s}#{@lp[:df_q]} #{@lp[:df_s]}"
296
+ event["response"] = @lp[:df_s].to_s
297
+ event["bytes"] = @lp[:df_b].to_s
298
+ event["referrer"] = headers['Referer']
299
+ event["agent"] = headers['User-Agent']
300
+ event["X-Forwarded-For"] = headers['X-Forwarded-For']
301
+ event["hitmiss"] = @lp[:df_hitmiss]
302
+ event["handling"] = @lp[:df_handling]
303
+ event["headers"] = headers.map {|header| header.lowercase}
304
+ begin
305
+ payload = event.to_json
306
+ rescue Encoding::UndefinedConversionError, ArgumentError
307
+ puts "FAILUREENCODING : #{@lp[:df_m]} #{@lp[:df_U].to_s}#{@lp[:df_q]} #{@lp[:df_s]}"
308
+ @logger.error("Failed to convert event to JSON. Invalid UTF-8, maybe?",
309
+ :event => event.inspect)
310
+ return @@reopen
311
+ end
312
+
313
+ @q << event
314
+
315
+ end
316
+ # clean up
317
+ clean_logline
318
+ @lp[:df_hitmiss] = nil
319
+ @lp[:df_handling] = nil
320
+ @lp[:active] = false
321
+ @lp[:complete] = false
322
+ rescue => e
323
+ @logger.warn("varnishlog exception: #{e.inspect} #{e.backtrace}")
324
+ ensure
325
+ return @@reopen
326
+ end
327
+ end
328
+
329
+ public
330
+ def close
331
+ finished
332
+ end # def close
333
+ def teardown
334
+ finished
335
+ end # def teardown
336
+ end # class LogStash::Inputs::Stdin
@@ -0,0 +1,29 @@
1
+ Gem::Specification.new do |s|
2
+
3
+ s.name = 'logstash-input-varnishncsa'
4
+ s.version = '2.0.2'
5
+ s.licenses = ['Apache License (2.0)']
6
+ s.summary = "Read from varnish cache's shared memory log and return all value in a logstash field (rewrite of varnishlog in ruby)"
7
+ s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
8
+ s.authors = ["hans moulron"]
9
+ s.email = 'hans.moulron@francetv.fr'
10
+ s.homepage = "https://github.com/francetv/logstash-input-varnishncsa"
11
+ s.require_paths = ["lib"]
12
+
13
+ # Files
14
+ s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','Gemfile','LICENSE']
15
+
16
+ # Tests
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+
19
+ # Special flag to let us know this is actually a logstash plugin
20
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
21
+
22
+ # Gem dependencies
23
+ s.add_runtime_dependency "logstash-core", ">= 2.1.0", "< 3.0.0"
24
+
25
+ s.add_runtime_dependency 'varnish-wrapper'
26
+
27
+ s.add_development_dependency 'logstash-devutils'
28
+ end
29
+
@@ -0,0 +1 @@
1
+ require "logstash/devutils/rspec/spec_helper"
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-input-varnishncsa
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.2
5
+ platform: ruby
6
+ authors:
7
+ - hans moulron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logstash-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 3.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 2.1.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: varnish-wrapper
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: logstash-devutils
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ description: This gem is a logstash plugin required to be installed on top of the
62
+ Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not
63
+ a stand-alone program
64
+ email: hans.moulron@francetv.fr
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - Gemfile
70
+ - LICENSE
71
+ - README.md
72
+ - lib/logstash/inputs/varnishncsa.rb
73
+ - logstash-input-varnishncsa.gemspec
74
+ - spec/inputs/varnishlog_spec.rb
75
+ homepage: https://github.com/francetv/logstash-input-varnishncsa
76
+ licenses:
77
+ - Apache License (2.0)
78
+ metadata:
79
+ logstash_plugin: 'true'
80
+ logstash_group: input
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.4.6
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Read from varnish cache's shared memory log and return all value in a logstash
101
+ field (rewrite of varnishlog in ruby)
102
+ test_files:
103
+ - spec/inputs/varnishlog_spec.rb