logstash-codec-joinlines 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cff8b92826a48b224f39c357774bf38c576d084fc4c367dfeab8235fb2b8df69
4
+ data.tar.gz: c3788765ed29c61f7bc3916ffdea4289e8401f9f54454b8478be467f4126482a
5
+ SHA512:
6
+ metadata.gz: 8e95490d1e38db256450e56ce14b16e3d7dfa380b8178cb2fd71d2c0af52d9039a459864d89a6980096caa3007cdb73a669f3991d87143cad6c2d6b8f527bc24
7
+ data.tar.gz: 32b1604e0071833000c9414b1654b6852a333189894c4b0a22fad9a5a5c90cdf40733268af14d4a2a22538bd96c11d23a1d903133843f4d6c38dfcfbc1217611
@@ -0,0 +1,5 @@
1
+ ## 0.1.0
2
+ - Plugin created with the logstash plugin generator
3
+ - Copied code from logstash-plugin-multiline
4
+ - Implemented multiple patterns
5
+
@@ -0,0 +1,19 @@
1
+ The following is a list of people who have contributed ideas, code, bug
2
+ reports, or in general have helped logstash along its way.
3
+
4
+ Contributors:
5
+ * Developers of logstash-codec-multiline
6
+ * Colin Surprenant (colinsurprenant)
7
+ * Jordan Sissel (jordansissel)
8
+ * João Duarte (jsvd)
9
+ * Kurt Hurtado (kurtado)
10
+ * Pier-Hugues Pellerin (ph)
11
+ * Richard Pijnenburg (electrical)
12
+ * Suyog Rao (suyograo)
13
+ * Guy Boertje (guyboertje)
14
+ * Svein L. Ellingsen (lovmoen)
15
+
16
+ Note: If you've sent us patches, bug reports, or otherwise contributed to
17
+ Logstash, and you aren't on the list above and want to be, please let us know
18
+ and we'll make sure you're here. Contributions from folks like you are what make
19
+ open source awesome.
@@ -0,0 +1,3 @@
1
+ # logstash-codec-joinlines
2
+ A codec plugin for Logstash; based on logstash-codec-multiline.
3
+ It allows for specifying multiple patterns to match for joining lines.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Licensed under the Apache License, Version 2.0 (the "License");
2
+ you may not use this file except in compliance with the License.
3
+ You may obtain a copy of the License at
4
+
5
+ http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
@@ -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-codec-awesome", :path => "/your/local/logstash-codec-awesome"
53
+ ```
54
+ - Install plugin
55
+ ```sh
56
+ bin/logstash-plugin install --no-verify
57
+ ```
58
+ - Run Logstash with your plugin
59
+ ```sh
60
+ bin/logstash -e 'codec {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-codec-awesome.gemspec
71
+ ```
72
+ - Install the plugin from the Logstash home
73
+ ```sh
74
+ bin/logstash-plugin install /your/local/plugin/logstash-codec-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,48 @@
1
+ # encoding: utf-8
2
+ require "concurrent"
3
+ require "logstash/codecs/retriggerable_task"
4
+
5
+ module LogStash module Codecs class AutoFlush
6
+ def initialize(mc, interval)
7
+ @mc, @interval = mc, interval
8
+ @stopped = Concurrent::AtomicBoolean.new # false by default
9
+ @task = RetriggerableTask.new(@interval, self)
10
+ end
11
+
12
+ def timeout
13
+ @mc.auto_flush
14
+ end
15
+
16
+ def start
17
+ # can't start if pipeline is stopping
18
+ return self if stopped?
19
+ @task.retrigger
20
+ self
21
+ end
22
+
23
+ def stopped?
24
+ @stopped.value
25
+ end
26
+
27
+ def stop
28
+ @stopped.make_true
29
+ @task.close
30
+ end
31
+ end
32
+
33
+ class AutoFlushUnset
34
+ def initialize(mc, interval)
35
+ end
36
+
37
+ def stopped?
38
+ true
39
+ end
40
+
41
+ def start
42
+ self
43
+ end
44
+
45
+ def stop
46
+ self
47
+ end
48
+ end end end
@@ -0,0 +1,347 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "thread_safe"
4
+ require "concurrent"
5
+
6
+ # This class is a Codec duck type
7
+ # Using Composition, it maps from a stream identity to
8
+ # a cloned codec instance via the same API as a Codec
9
+ # it implements the codec public API
10
+
11
+ module LogStash module Codecs class IdentityMapCodec
12
+ # subclass of Exception, LS has more than limit (20000) active streams
13
+ class IdentityMapUpperLimitException < Exception; end
14
+
15
+ module EightyPercentWarning
16
+ extend self
17
+ def visit(imc)
18
+ current_size, limit = imc.current_size_and_limit
19
+ return if current_size < (limit * 0.8)
20
+ imc.logger.warn("IdentityMapCodec has reached 80% capacity",
21
+ :current_size => current_size, :upper_limit => limit)
22
+ end
23
+ end
24
+
25
+ module UpperLimitReached
26
+ extend self
27
+ def visit(imc)
28
+ current_size, limit = imc.current_size_and_limit
29
+ return if current_size < limit
30
+ # we hit the limit
31
+ # try to clean out stale streams
32
+ current_size, limit = imc.map_cleanup
33
+ return if current_size < limit
34
+ # we are still at the limit and all streams are in use
35
+ imc.logger.error("IdentityMapCodec has reached 100% capacity",
36
+ :current_size => current_size, :upper_limit => limit)
37
+ raise IdentityMapUpperLimitException.new
38
+ end
39
+ end
40
+
41
+ class PeriodicRunner
42
+ def initialize(listener, interval, method_symbol)
43
+ @listener, @interval = listener, interval
44
+ @method_symbol = method_symbol
45
+ @running = Concurrent::AtomicBoolean.new(false)
46
+ end
47
+
48
+ def start
49
+ return self if running?
50
+ @running.make_true
51
+ @thread = Thread.new() do
52
+ while running? do
53
+ sleep @interval
54
+ break if !running?
55
+ @listener.send(@method_symbol)
56
+ end
57
+ end
58
+ self
59
+ end
60
+
61
+ def running?
62
+ @running.value
63
+ end
64
+
65
+ def stop
66
+ return if !running?
67
+ @running.make_false
68
+ if @thread.alive?
69
+ @thread.wakeup
70
+ @thread.join
71
+ end
72
+ @listener = nil
73
+ end
74
+ end
75
+
76
+ class NoopRunner
77
+ attr_reader :start, :stop
78
+ def running?() false; end
79
+ end
80
+
81
+ # A composite class to hold both the codec, the eviction_timeout and a last_used timestamp
82
+ # instances of this Value Object are stored in the mapping hash
83
+ class CodecValue
84
+ attr_reader :codec
85
+ attr_accessor :eviction_timeout, :auto_flush_timeout
86
+
87
+ def initialize(codec)
88
+ @codec = codec
89
+ end
90
+ end
91
+
92
+ #maximum size of the mapping hash
93
+ MAX_IDENTITIES = 20_000
94
+
95
+ # time after which a stream is
96
+ # considered stale
97
+ # each time a stream is accessed
98
+ # it is given a new timeout
99
+ EVICT_TIMEOUT = 60 * 60 * 1 # 1 hour
100
+
101
+ # time that the cleaner thread sleeps for
102
+ # before it tries to clean out stale mappings
103
+ CLEANER_INTERVAL = 60 * 5 # 5 minutes
104
+
105
+ attr_reader :identity_map
106
+ attr_accessor :base_codec, :cleaner, :auto_flusher
107
+
108
+ def initialize(codec)
109
+ @base_codec = codec
110
+ @base_codecs = [codec]
111
+ @identity_map = ThreadSafe::Hash.new &method(:codec_builder)
112
+ @max_identities = MAX_IDENTITIES
113
+ @evict_timeout = EVICT_TIMEOUT
114
+ cleaner_interval(CLEANER_INTERVAL)
115
+ if codec.respond_to?(:use_mapper_auto_flush) &&
116
+ (@auto_flush_interval = codec.use_mapper_auto_flush)
117
+ @auto_flusher = PeriodicRunner.new(self, 0.5, :auto_flush_mapped)
118
+ else
119
+ @auto_flusher = NoopRunner.new
120
+ end
121
+
122
+ @decode_block = lambda {|*| true }
123
+ @eviction_block = nil
124
+ end
125
+
126
+ # ==============================================
127
+ # Constructional/builder methods
128
+ # chain this method off of new
129
+ #
130
+ # used to add a non-default maximum identities
131
+ def max_identities(max)
132
+ @max_identities = max.to_i
133
+ self
134
+ end
135
+
136
+ # used to add a non-default evict timeout
137
+ def evict_timeout(timeout)
138
+ @evict_timeout = timeout.to_i
139
+ self
140
+ end
141
+
142
+ # used to add a non-default cleaner interval
143
+ def cleaner_interval(interval)
144
+ @cleaner.stop if @cleaner
145
+ @cleaner = PeriodicRunner.new(self, interval.to_i, :map_cleanup)
146
+ self
147
+ end
148
+
149
+ # used to add a non-default eviction block
150
+ def eviction_block(block)
151
+ @eviction_block = block
152
+ self
153
+ end
154
+
155
+ # end Constructional/builder methods
156
+ # ==============================================
157
+
158
+ # ==============================================
159
+ # IdentityMapCodec API
160
+ def evict(identity)
161
+ # maybe called more than once
162
+ if (compo = identity_map.delete(identity))
163
+ compo.codec.auto_flush if compo.codec.respond_to?(:auto_flush)
164
+ end
165
+ end
166
+ # end IdentityMapCodec API
167
+ # ==============================================
168
+
169
+ # ==============================================
170
+ # Codec API
171
+ def decode(data, identity = nil, &block)
172
+ @decode_block = block if @decode_block != block
173
+ stream_codec(identity).decode(data, &block)
174
+ end
175
+
176
+ def accept(listener)
177
+ stream_codec(listener.path).accept(listener)
178
+ end
179
+
180
+ alias_method :<<, :decode
181
+
182
+ def encode(event, identity = nil)
183
+ stream_codec(identity).encode(event)
184
+ end
185
+
186
+ def flush(&block)
187
+ all_codecs.each do |codec|
188
+ #let ruby do its default args thing
189
+ if block_given?
190
+ codec.flush(&block)
191
+ else
192
+ if codec.respond_to?(:auto_flush)
193
+ codec.auto_flush
194
+ else
195
+ #try this, no guarantees
196
+ codec.flush
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ def close()
203
+ cleaner.stop
204
+ auto_flusher.stop
205
+ all_codecs.each(&:close)
206
+ end
207
+ # end Codec API
208
+ # ==============================================
209
+
210
+ def auto_flush_mapped
211
+ if !identity_count.zero?
212
+ nowf = Time.now.to_f
213
+ identity_map.each do |identity, compo|
214
+ next if compo.auto_flush_timeout.zero?
215
+ next unless nowf > compo.auto_flush_timeout
216
+ compo.codec.auto_flush
217
+ # at eof (tail and read) no more lines for a while or ever
218
+ # so reset compo.auto_flush_timeout
219
+ compo.auto_flush_timeout = 0
220
+ end
221
+ end
222
+ end
223
+
224
+ def flush_mapped(listener)
225
+ listener_has_path = listener.respond_to?(:path)
226
+ identity_map.each do |identity, compo|
227
+ listener.path = identity if listener_has_path
228
+ compo.codec.auto_flush(listener)
229
+ end
230
+ end
231
+
232
+ def all_codecs
233
+ no_streams? ? @base_codecs : identity_map.values.map(&:codec)
234
+ end
235
+
236
+ def max_limit
237
+ @max_identities
238
+ end
239
+
240
+ def identity_count
241
+ identity_map.size
242
+ end
243
+
244
+ # support cleaning of stale stream/codecs
245
+ # a stream is considered stale if it has not
246
+ # been accessed in the last @evict_timeout
247
+ # period (default 1 hour)
248
+ def map_cleanup
249
+ if !identity_count.zero?
250
+ nowi = Time.now.to_i
251
+ # delete_if is atomic
252
+ # contents should not mutate during this call
253
+ identity_map.delete_if do |identity, compo|
254
+ if (flag = compo.eviction_timeout <= nowi)
255
+ evict_flush(compo.codec)
256
+ end
257
+ flag
258
+ end
259
+ end
260
+ current_size_and_limit
261
+ end
262
+
263
+ def evict_flush(codec)
264
+ if codec.respond_to?(:auto_flush)
265
+ codec.auto_flush
266
+ else
267
+ if (block = @eviction_block || @decode_block)
268
+ codec.flush(&block)
269
+ end
270
+ # all else - can't do anything
271
+ end
272
+ end
273
+
274
+ def current_size_and_limit
275
+ [identity_count, max_limit]
276
+ end
277
+
278
+ def logger
279
+ # we 'borrow' the codec's logger as we don't have our own
280
+ @base_codec.logger
281
+ end
282
+
283
+ def codec_without_usage_update(identity)
284
+ find_codec_value(identity).codec
285
+ end
286
+
287
+ def eviction_timestamp_for(identity)
288
+ find_codec_value(identity).eviction_timeout
289
+ end
290
+
291
+ private
292
+
293
+ def stream_codec(identity)
294
+ return base_codec if identity.nil?
295
+ record_codec_usage(identity) # returns codec
296
+ end
297
+
298
+ def find_codec_value(identity)
299
+ identity_map[identity]
300
+ end
301
+
302
+ # for nil stream this method is not called
303
+ def record_codec_usage(identity)
304
+ check_map_limits
305
+ # only start the cleaner if streams are in use
306
+ # continuous calls to start are OK
307
+ cleaner.start
308
+ auto_flusher.start
309
+ compo = find_codec_value(identity)
310
+ now = Time.now
311
+ compo.eviction_timeout = eviction_timestamp(now)
312
+ compo.auto_flush_timeout = auto_flush_timestamp(now)
313
+ compo.codec
314
+ end
315
+
316
+ def auto_flush_timestamp(now = Time.now)
317
+ now.to_f + @auto_flush_interval.to_f
318
+ end
319
+
320
+ def eviction_timestamp(now = Time.now)
321
+ now.to_i + @evict_timeout
322
+ end
323
+
324
+ def check_map_limits
325
+ UpperLimitReached.visit(self)
326
+ EightyPercentWarning.visit(self)
327
+ end
328
+
329
+ def codec_builder(hash, k)
330
+ codec = hash.empty? ? @base_codec : @base_codec.clone
331
+ codec.use_mapper_auto_flush if using_mapped_auto_flush?
332
+ compo = CodecValue.new(codec).tap do |o|
333
+ now = Time.now
334
+ o.eviction_timeout = eviction_timestamp(now)
335
+ o.auto_flush_timeout = auto_flush_timestamp(now)
336
+ end
337
+ hash.store(k, compo)
338
+ end
339
+
340
+ def no_streams?
341
+ identity_map.empty?
342
+ end
343
+
344
+ def using_mapped_auto_flush?
345
+ !@auto_flush_interval.nil?
346
+ end
347
+ end end end