logstash-event 1.1.5 → 1.2.02

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2009-2011 Jordan Sissel, Pete Fritchman, and contributors.
1
+ Copyright 2009-2013 Jordan Sissel, Pete Fritchman, and contributors.
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
@@ -1,3 +1 @@
1
1
  require "logstash/event"
2
- require "logstash/version"
3
-
@@ -1,45 +1,85 @@
1
1
  require "json"
2
- require "logstash/time"
3
- require "logstash/namespace"
4
- require "uri"
5
2
  require "time"
3
+ require "date"
4
+ require "logstash/namespace"
5
+ require "logstash/util/fieldreference"
6
+
7
+ # Use a custom serialization for jsonifying Time objects.
8
+ # TODO(sissel): Put this in a separate file.
9
+ class Time
10
+ def to_json(*args)
11
+ return iso8601(3).to_json(*args)
12
+ end
13
+
14
+ def inspect
15
+ return to_json
16
+ end
17
+ end
6
18
 
7
- # General event type.
8
- # Basically a light wrapper on top of a hash.
19
+ # the logstash event object.
20
+ #
21
+ # An event is simply a tuple of (timestamp, data).
22
+ # The 'timestamp' is an ISO8601 timestamp. Data is anything - any message,
23
+ # context, references, etc that are relevant to this event.
24
+ #
25
+ # Internally, this is represented as a hash with only two guaranteed fields.
26
+ #
27
+ # * "@timestamp" - an ISO8601 timestamp representing the time the event
28
+ # occurred at.
29
+ # * "@version" - the version of the schema. Currently "1"
30
+ #
31
+ # They are prefixed with an "@" symbol to avoid clashing with your
32
+ # own custom fields.
33
+ #
34
+ # When serialized, this is represented in JSON. For example:
9
35
  #
10
- # TODO(sissel): properly handle lazy properties like parsed time formats, urls,
11
- # etc, as necessary.
36
+ # {
37
+ # "@timestamp": "2013-02-09T20:39:26.234Z",
38
+ # "@version": "1",
39
+ # message: "hello world"
40
+ # }
12
41
  class LogStash::Event
42
+ class DeprecatedMethod < StandardError; end
43
+
13
44
  public
14
- def initialize(data=nil)
45
+ def initialize(data={})
15
46
  @cancelled = false
16
47
 
17
- @data = {
18
- "@source" => "unknown",
19
- "@tags" => [],
20
- "@fields" => {},
21
- }
22
- @data.merge!(data) unless data.nil?
23
- @data["@timestamp"] ||= LogStash::Time.now
48
+ @data = data
49
+ if data.include?("@timestamp")
50
+ t = data["@timestamp"]
51
+ if t.is_a?(String)
52
+ data["@timestamp"] = Time.parse(t).gmtime
53
+ end
54
+ else
55
+ data["@timestamp"] = ::Time.now.utc
56
+ end
57
+ data["@version"] = "1" if !@data.include?("@version")
24
58
  end # def initialize
25
59
 
26
- if RUBY_ENGINE == "jruby"
27
- @@date_parser = Java::org.joda.time.format.ISODateTimeFormat.dateTimeParser.withOffsetParsed
28
- else
29
- # TODO(sissel): LOGSTASH-217
30
- @@date_parser ||= nil
31
- end
32
-
60
+ # Add class methods on inclusion.
33
61
  public
34
- def self.from_json(json)
35
- return LogStash::Event.new(JSON.parse(json))
36
- end # def self.from_json
62
+ def self.included(klass)
63
+ klass.extend(ClassMethods)
64
+ end # def included
65
+
66
+ module ClassMethods
67
+ public
68
+ def from_json(json)
69
+ return self.new(JSON.parse(json))
70
+ end # def from_json
71
+ end
37
72
 
38
73
  public
39
74
  def cancel
40
75
  @cancelled = true
41
76
  end # def cancel
42
77
 
78
+ public
79
+ def uncancel
80
+ @cancelled = false
81
+ end # def uncancel
82
+
43
83
  public
44
84
  def cancelled?
45
85
  return @cancelled
@@ -48,95 +88,90 @@ class LogStash::Event
48
88
  # Create a deep-ish copy of this event.
49
89
  public
50
90
  def clone
51
- newdata = @data.clone
52
- newdata["@fields"] = {}
53
- fields.each do |k,v|
54
- newdata["@fields"][k] = v.clone
91
+ copy = {}
92
+ @data.each do |k,v|
93
+ # TODO(sissel): Recurse if this is a hash/array?
94
+ copy[k] = v.clone
55
95
  end
56
- return LogStash::Event.new(newdata)
96
+ return self.class.new(copy)
57
97
  end # def clone
58
98
 
59
- public
60
- def to_s
61
- return self.sprintf("%{@timestamp} %{@source}: %{@message}")
62
- end # def to_s
63
-
64
- public
65
- def timestamp; @data["@timestamp"]; end # def timestamp
66
- def timestamp=(val); @data["@timestamp"] = val; end # def timestamp=
67
-
68
- public
69
- def unix_timestamp
70
- if RUBY_ENGINE != "jruby"
71
- # This is really slow. See LOGSTASH-217
72
- return Time.parse(timestamp).to_f
73
- else
74
- time = @@date_parser.parseDateTime(timestamp)
75
- return time.getMillis.to_f / 1000
76
- end
99
+ if RUBY_ENGINE == "jruby"
100
+ public
101
+ def to_s
102
+ return self.sprintf("%{+yyyy-MM-dd'T'HH:mm:ss.SSSZ} %{host} %{message}")
103
+ end # def to_s
104
+ else
105
+ public
106
+ def to_s
107
+ return self.sprintf("#{self["@timestamp"].iso8601} %{host} %{message}")
108
+ end # def to_s
77
109
  end
78
110
 
79
111
  public
80
- def source; @data["@source"]; end # def source
81
- def source=(val)
82
- uri = URI.parse(val) rescue nil
83
- val = uri if uri
84
- if val.is_a?(URI)
85
- @data["@source"] = val.to_s
86
- @data["@source_host"] = val.host
87
- @data["@source_path"] = val.path
88
- else
89
- @data["@source"] = val
90
- @data["@source_host"] = val
91
- end
92
- end # def source=
93
-
94
- public
95
- def source_host; @data["@source_host"]; end # def source_host
96
- def source_host=(val); @data["@source_host"] = val; end # def source_host=
97
-
98
- public
99
- def source_path; @data["@source_path"]; end # def source_path
100
- def source_path=(val); @data["@source_path"] = val; end # def source_path=
101
-
102
- public
103
- def message; @data["@message"]; end # def message
104
- def message=(val); @data["@message"] = val; end # def message=
105
-
106
- public
107
- def type; @data["@type"]; end # def type
108
- def type=(val); @data["@type"] = val; end # def type=
112
+ def timestamp; return @data["@timestamp"]; end # def timestamp
113
+ def timestamp=(val); return @data["@timestamp"] = val; end # def timestamp=
109
114
 
110
- public
111
- def tags; @data["@tags"]; end # def tags
112
- def tags=(val); @data["@tags"] = val; end # def tags=
115
+ def unix_timestamp
116
+ raise DeprecatedMethod
117
+ end # def unix_timestamp
113
118
 
119
+ def ruby_timestamp
120
+ raise DeprecatedMethod
121
+ end # def unix_timestamp
122
+
114
123
  # field-related access
115
124
  public
116
- def [](key)
117
- # If the key isn't in fields and it starts with an "@" sign, get it out of data instead of fields
118
- if ! @data["@fields"].has_key?(key) and key.slice(0,1) == "@"
119
- return @data[key]
120
- # Exists in @fields (returns value) or doesn't start with "@" (return null)
125
+ def [](str)
126
+ if str[0,1] == "+"
121
127
  else
122
- return @data["@fields"][key]
128
+ return LogStash::Util::FieldReference.exec(str, @data)
123
129
  end
124
130
  end # def []
125
131
 
126
132
  public
127
- def []=(key, value)
128
- if @data.has_key?(key)
129
- @data[key] = value
130
- else
131
- @data["@fields"][key] = value
133
+ def []=(str, value)
134
+ r = LogStash::Util::FieldReference.exec(str, @data) do |obj, key|
135
+ obj[key] = value
132
136
  end
137
+
138
+ # The assignment can fail if the given field reference (str) does not exist
139
+ # In this case, we'll want to set the value manually.
140
+ if r.nil?
141
+ # TODO(sissel): Implement this in LogStash::Util::FieldReference
142
+ if str[0,1] != "["
143
+ return @data[str] = value
144
+ end
145
+
146
+ # No existing element was found, so let's set one.
147
+ *parents, key = str.scan(/(?<=\[)[^\]]+(?=\])/)
148
+ obj = @data
149
+ parents.each do |p|
150
+ if obj.include?(p)
151
+ obj = obj[p]
152
+ else
153
+ obj[p] = {}
154
+ obj = obj[p]
155
+ end
156
+ end
157
+ obj[key] = value
158
+ end
159
+ return value
133
160
  end # def []=
134
161
 
135
- def fields; return @data["@fields"] end # def fields
162
+ public
163
+ def fields
164
+ raise DeprecatedMethod
165
+ end
136
166
 
137
167
  public
138
- def to_json(*args); return @data.to_json(*args) end # def to_json
139
- def to_hash; return @data end # def to_hash
168
+ def to_json(*args)
169
+ return @data.to_json(*args)
170
+ end # def to_json
171
+
172
+ def to_hash
173
+ return @data
174
+ end # def to_hash
140
175
 
141
176
  public
142
177
  def overwrite(event)
@@ -145,45 +180,22 @@ class LogStash::Event
145
180
 
146
181
  public
147
182
  def include?(key)
148
- return (@data.include?(key) or @data["@fields"].include?(key))
183
+ return !self[key].nil?
149
184
  end # def include?
150
185
 
151
186
  # Append an event to this one.
152
187
  public
153
188
  def append(event)
154
- if event.message
155
- if self.message
156
- self.message += "\n" + event.message
157
- else
158
- self.message = event.message
159
- end
160
- end
161
- self.tags |= event.tags
189
+ # non-destructively merge that event with ourselves.
190
+ LogStash::Util.hash_merge(@data, event.to_hash)
191
+ end # append
162
192
 
163
- # Append all fields
164
- event.fields.each do |name, value|
165
- if self.fields.include?(name)
166
- if !self.fields[name].is_a?(Array)
167
- self.fields[name] = [self.fields[name]]
168
- end
169
- if value.is_a?(Array)
170
- self.fields[name] |= value
171
- else
172
- self.fields[name] << value unless self.fields[name].include?(value)
173
- end
174
- else
175
- self.fields[name] = value
176
- end
177
- end # event.fields.each
178
- end # def append
179
-
180
- # Remove a field
193
+ # Remove a field or field reference. Returns the value of that field when
194
+ # deleted
181
195
  public
182
- def remove(field)
183
- if @data.has_key?(field)
184
- @data.delete(field)
185
- else
186
- @data["@fields"].delete(field)
196
+ def remove(str)
197
+ return LogStash::Util::FieldReference.exec(str, @data) do |obj, key|
198
+ next obj.delete(key)
187
199
  end
188
200
  end # def remove
189
201
 
@@ -192,9 +204,9 @@ class LogStash::Event
192
204
  # any format values, delimited by %{foo} where 'foo' is a field or
193
205
  # metadata member.
194
206
  #
195
- # For example, if the event has @type == "foo" and @source == "bar"
207
+ # For example, if the event has type == "foo" and source == "bar"
196
208
  # then this string:
197
- # "type is %{@type} and source is %{@source}"
209
+ # "type is %{type} and source is %{host}"
198
210
  # will return
199
211
  # "type is foo and source is bar"
200
212
  #
@@ -205,6 +217,7 @@ class LogStash::Event
205
217
  # is an array (or hash?) should be. Join by comma? Something else?
206
218
  public
207
219
  def sprintf(format)
220
+ format = format.to_s
208
221
  if format.index("%").nil?
209
222
  return format
210
223
  end
@@ -215,69 +228,45 @@ class LogStash::Event
215
228
 
216
229
  if key == "+%s"
217
230
  # Got %{+%s}, support for unix epoch time
218
- if RUBY_ENGINE != "jruby"
219
- # This is really slow. See LOGSTASH-217
220
- Date.parse(self.timestamp).to_i
221
- else
222
- datetime = @@date_parser.parseDateTime(self.timestamp)
223
- (datetime.getMillis / 1000).to_i
224
- end
231
+ next @data["@timestamp"].to_i
225
232
  elsif key[0,1] == "+"
226
- # We got a %{+TIMEFORMAT} so use joda to format it.
227
- if RUBY_ENGINE != "jruby"
228
- # This is really slow. See LOGSTASH-217
229
- datetime = Date.parse(self.timestamp)
230
- format = key[1 .. -1]
231
- datetime.strftime(format)
232
- else
233
- datetime = @@date_parser.parseDateTime(self.timestamp)
234
- format = key[1 .. -1]
235
- datetime.toString(format) # return requested time format
236
- end
233
+ t = @data["@timestamp"]
234
+ formatter = org.joda.time.format.DateTimeFormat.forPattern(key[1 .. -1])\
235
+ .withZone(org.joda.time.DateTimeZone::UTC)
236
+ #next org.joda.time.Instant.new(t.tv_sec * 1000 + t.tv_usec / 1000).toDateTime.toString(formatter)
237
+ # Invoke a specific Instant constructor to avoid this warning in JRuby
238
+ # > ambiguous Java methods found, using org.joda.time.Instant(long)
239
+ org.joda.time.Instant.java_class.constructor(Java::long).new_instance(
240
+ t.tv_sec * 1000 + t.tv_usec / 1000
241
+ ).to_java.toDateTime.toString(formatter)
237
242
  else
238
- # Use an event field.
239
- value = nil
240
- obj = self
241
-
242
- # If the top-level value exists, use that and don't try
243
- # to "look" into data structures.
244
- if self[key]
245
- value = self[key]
246
- else
247
- # "." is what ES uses to access structured data, so adopt that
248
- # idea here, too. "foo.bar" will access key "bar" under hash "foo".
249
- key.split('.').each do |segment|
250
- if obj
251
- value = obj[segment] rescue nil
252
- obj = obj[segment] rescue nil
253
- else
254
- value = nil
255
- break
256
- end
257
- end # key.split.each
258
- end # if self[key]
259
-
243
+ value = self[key]
260
244
  case value
261
- when nil
262
- tok # leave the %{foo} if this field does not exist in this event.
263
- when Array
264
- value.join(",") # Join by ',' if value is an array
265
- when Hash
266
- value.to_json # Convert hashes to json
267
- else
268
- value # otherwise return the value
269
- end
270
- end
271
- end
245
+ when nil
246
+ tok # leave the %{foo} if this field does not exist in this event.
247
+ when Array
248
+ value.join(",") # Join by ',' if value is an array
249
+ when Hash
250
+ value.to_json # Convert hashes to json
251
+ else
252
+ value # otherwise return the value
253
+ end # case value
254
+ end # 'key' checking
255
+ end # format.gsub...
272
256
  end # def sprintf
273
257
 
274
- public
275
- def ==(other)
276
- #puts "#{self.class.name}#==(#{other.inspect})"
277
- if !other.is_a?(self.class)
278
- return false
279
- end
258
+ # Shims to remove after event v1 is the default.
259
+ def tags=(value); self["tags"] = value; end
260
+ def tags; return self["tags"]; end
261
+ def message=(value); self["message"] = value; end
262
+ def source=(value); self["source"] = value; end
263
+ def type=(value); self["type"] = value; end
264
+ def type; return self["type"]; end
265
+ def fields; return self.to_hash; end
280
266
 
281
- return other.to_hash == self.to_hash
282
- end # def ==
267
+ def tag(value)
268
+ # Generalize this method for more usability
269
+ self["tags"] ||= []
270
+ self["tags"] << value unless self["tags"].include?(value)
271
+ end
283
272
  end # class LogStash::Event
@@ -1,4 +1,4 @@
1
- $: << File.join(File.dirname(__FILE__), "..", "..", "vendor", "bundle")
1
+ #$: << File.join(File.dirname(__FILE__), "..", "..", "vendor", "bundle")
2
2
 
3
3
  module LogStash
4
4
  module Inputs; end
@@ -9,6 +9,7 @@ module LogStash
9
9
  module File; end
10
10
  module Web; end
11
11
  module Util; end
12
+ module PluginMixins; end
12
13
 
13
14
  SHUTDOWN = :shutdown
14
15
  end # module LogStash
@@ -0,0 +1,105 @@
1
+ require "logstash/namespace"
2
+
3
+ module LogStash::Util
4
+ UNAME = case RbConfig::CONFIG["host_os"]
5
+ when /^linux/; "linux"
6
+ else; RbConfig::CONFIG["host_os"]
7
+ end
8
+
9
+ PR_SET_NAME = 15
10
+ def self.set_thread_name(name)
11
+ if RUBY_ENGINE == "jruby"
12
+ # Keep java and ruby thread names in sync.
13
+ Java::java.lang.Thread.currentThread.setName(name)
14
+ end
15
+ Thread.current[:name] = name
16
+
17
+ if UNAME == "linux"
18
+ require "logstash/util/prctl"
19
+ # prctl PR_SET_NAME allows up to 16 bytes for a process name
20
+ # since MRI 1.9, JRuby, and Rubinius use system threads for this.
21
+ LibC.prctl(PR_SET_NAME, name[0..16], 0, 0, 0)
22
+ end
23
+ end # def set_thread_name
24
+
25
+ # Merge hash 'src' into 'dst' nondestructively
26
+ #
27
+ # Duplicate keys will become array values
28
+ #
29
+ # [ src["foo"], dst["foo"] ]
30
+ def self.hash_merge(dst, src)
31
+ src.each do |name, svalue|
32
+ if dst.include?(name)
33
+ dvalue = dst[name]
34
+ if dvalue.is_a?(Hash) && svalue.is_a?(Hash)
35
+ dvalue = hash_merge(dvalue, svalue)
36
+ elsif svalue.is_a?(Array)
37
+ if dvalue.is_a?(Array)
38
+ # merge arrays without duplicates.
39
+ dvalue |= svalue
40
+ else
41
+ dvalue = [dvalue] | svalue
42
+ end
43
+ else
44
+ if dvalue.is_a?(Array)
45
+ dvalue << svalue unless dvalue.include?(svalue)
46
+ else
47
+ dvalue = [dvalue, svalue] unless dvalue == svalue
48
+ end
49
+ end
50
+
51
+ dst[name] = dvalue
52
+ else
53
+ # dst doesn't have this key, just set it.
54
+ dst[name] = svalue
55
+ end
56
+ end
57
+
58
+ return dst
59
+ end # def self.hash_merge
60
+
61
+ # Merge hash 'src' into 'dst' nondestructively
62
+ #
63
+ # Duplicate keys will become array values
64
+ # Arrays merged will simply be appended.
65
+ #
66
+ # [ src["foo"], dst["foo"] ]
67
+ def self.hash_merge_with_dups(dst, src)
68
+ src.each do |name, svalue|
69
+ if dst.include?(name)
70
+ dvalue = dst[name]
71
+ if dvalue.is_a?(Hash) && svalue.is_a?(Hash)
72
+ dvalue = hash_merge(dvalue, svalue)
73
+ elsif svalue.is_a?(Array)
74
+ if dvalue.is_a?(Array)
75
+ # merge arrays without duplicates.
76
+ dvalue += svalue
77
+ else
78
+ dvalue = [dvalue] + svalue
79
+ end
80
+ else
81
+ if dvalue.is_a?(Array)
82
+ dvalue << svalue unless dvalue.include?(svalue)
83
+ else
84
+ dvalue = [dvalue, svalue] unless dvalue == svalue
85
+ end
86
+ end
87
+
88
+ dst[name] = dvalue
89
+ else
90
+ # dst doesn't have this key, just set it.
91
+ dst[name] = svalue
92
+ end
93
+ end
94
+
95
+ return dst
96
+ end # def self.hash_merge
97
+
98
+ def self.hash_merge_many(*hashes)
99
+ dst = {}
100
+ hashes.each do |hash|
101
+ hash_merge_with_dups(dst, hash)
102
+ end
103
+ return dst
104
+ end # def hash_merge_many
105
+ end # module LogStash::Util
@@ -0,0 +1,49 @@
1
+ require "logstash/namespace"
2
+ require "logstash/util"
3
+
4
+ module LogStash::Util::FieldReference
5
+ def compile(str)
6
+ if str[0,1] != '['
7
+ return <<-"CODE"
8
+ lambda do |e, &block|
9
+ return block.call(e, #{str.inspect}) unless block.nil?
10
+ return e[#{str.inspect}]
11
+ end
12
+ CODE
13
+ end
14
+
15
+ code = "lambda do |e, &block|\n"
16
+ selectors = str.scan(/(?<=\[).+?(?=\])/)
17
+ selectors.each_with_index do |tok, i|
18
+ last = (i == selectors.count() - 1)
19
+ code << " # [#{tok}]#{ last ? " (last selector)" : "" }\n"
20
+
21
+ if last
22
+ code << <<-"CODE"
23
+ return block.call(e, #{tok.inspect}) unless block.nil?
24
+ CODE
25
+ end
26
+
27
+ code << <<-"CODE"
28
+ if e.is_a?(Array)
29
+ e = e[#{tok.to_i}]
30
+ else
31
+ e = e[#{tok.inspect}]
32
+ end
33
+ return e if e.nil?
34
+ CODE
35
+
36
+ end
37
+ code << "return e\nend"
38
+ #puts code
39
+ return code
40
+ end # def compile
41
+
42
+ def exec(str, obj, &block)
43
+ @__fieldeval_cache ||= {}
44
+ @__fieldeval_cache[str] ||= eval(compile(str))
45
+ return @__fieldeval_cache[str].call(obj, &block)
46
+ end
47
+
48
+ extend self
49
+ end # module LogStash::Util::FieldReference
@@ -0,0 +1,139 @@
1
+ require "logstash/event"
2
+ require "insist"
3
+
4
+ describe LogStash::Event do
5
+ subject do
6
+ LogStash::Event.new(
7
+ "@timestamp" => Time.iso8601("2013-01-01T00:00:00.000Z"),
8
+ "type" => "sprintf",
9
+ "message" => "hello world",
10
+ "tags" => [ "tag1" ],
11
+ "source" => "/home/foo",
12
+ "a" => "b",
13
+ "c" => {
14
+ "d" => "f",
15
+ "e" => {"f" => "g"}
16
+ },
17
+ "f" => { "g" => { "h" => "i" } },
18
+ "j" => {
19
+ "k1" => "v",
20
+ "k2" => [ "w", "x" ],
21
+ "k3" => {"4" => "m"},
22
+ 5 => 6,
23
+ "5" => 7
24
+ }
25
+ )
26
+ end
27
+
28
+ context "#sprintf" do
29
+ it "should report a unix timestamp for %{+%s}" do
30
+ insist { subject.sprintf("%{+%s}") } == "1356998400"
31
+ end
32
+
33
+ it "should report a time with %{+format} syntax", :if => RUBY_ENGINE == "jruby" do
34
+ insist { subject.sprintf("%{+YYYY}") } == "2013"
35
+ insist { subject.sprintf("%{+MM}") } == "01"
36
+ insist { subject.sprintf("%{+HH}") } == "00"
37
+ end
38
+
39
+ it "should report fields with %{field} syntax" do
40
+ insist { subject.sprintf("%{type}") } == "sprintf"
41
+ insist { subject.sprintf("%{message}") } == subject["message"]
42
+ end
43
+
44
+ it "should print deep fields" do
45
+ insist { subject.sprintf("%{[j][k1]}") } == "v"
46
+ insist { subject.sprintf("%{[j][k2][0]}") } == "w"
47
+ end
48
+
49
+ it "should be able to take a non-string for the format" do
50
+ insist { subject.sprintf(2) } == "2"
51
+ end
52
+ end
53
+
54
+ context "#[]" do
55
+ it "should fetch data" do
56
+ insist { subject["type"] } == "sprintf"
57
+ end
58
+ it "should fetch fields" do
59
+ insist { subject["a"] } == "b"
60
+ insist { subject['c']['d'] } == "f"
61
+ end
62
+ it "should fetch deep fields" do
63
+ insist { subject["[j][k1]"] } == "v"
64
+ insist { subject["[c][d]"] } == "f"
65
+ insist { subject['[f][g][h]'] } == "i"
66
+ insist { subject['[j][k3][4]'] } == "m"
67
+ insist { subject['[j][5]'] } == 7
68
+
69
+ end
70
+
71
+ it "should be fast?", :if => ENV["SPEEDTEST"] do
72
+ 2.times do
73
+ start = Time.now
74
+ 100000.times { subject["[j][k1]"] }
75
+ puts "Duration: #{Time.now - start}"
76
+ end
77
+ end
78
+ end
79
+
80
+ context "#append" do
81
+ it "should append strings to an array" do
82
+ subject.append(LogStash::Event.new("message" => "another thing"))
83
+ insist { subject["message"] } == [ "hello world", "another thing" ]
84
+ end
85
+
86
+ it "should concatenate tags" do
87
+ subject.append(LogStash::Event.new("tags" => [ "tag2" ]))
88
+ insist { subject["tags"] } == [ "tag1", "tag2" ]
89
+ end
90
+
91
+ context "when event field is nil" do
92
+ it "should add single value as string" do
93
+ subject.append(LogStash::Event.new({"field1" => "append1"}))
94
+ insist { subject[ "field1" ] } == "append1"
95
+ end
96
+ it "should add multi values as array" do
97
+ subject.append(LogStash::Event.new({"field1" => [ "append1","append2" ]}))
98
+ insist { subject[ "field1" ] } == [ "append1","append2" ]
99
+ end
100
+ end
101
+
102
+ context "when event field is a string" do
103
+ before { subject[ "field1" ] = "original1" }
104
+
105
+ it "should append string to values, if different from current" do
106
+ subject.append(LogStash::Event.new({"field1" => "append1"}))
107
+ insist { subject[ "field1" ] } == [ "original1", "append1" ]
108
+ end
109
+ it "should not change value, if appended value is equal current" do
110
+ subject.append(LogStash::Event.new({"field1" => "original1"}))
111
+ insist { subject[ "field1" ] } == "original1"
112
+ end
113
+ it "should concatenate values in an array" do
114
+ subject.append(LogStash::Event.new({"field1" => [ "append1" ]}))
115
+ insist { subject[ "field1" ] } == [ "original1", "append1" ]
116
+ end
117
+ it "should join array, removing duplicates" do
118
+ subject.append(LogStash::Event.new({"field1" => [ "append1","original1" ]}))
119
+ insist { subject[ "field1" ] } == [ "original1", "append1" ]
120
+ end
121
+ end
122
+ context "when event field is an array" do
123
+ before { subject[ "field1" ] = [ "original1", "original2" ] }
124
+
125
+ it "should append string values to array, if not present in array" do
126
+ subject.append(LogStash::Event.new({"field1" => "append1"}))
127
+ insist { subject[ "field1" ] } == [ "original1", "original2", "append1" ]
128
+ end
129
+ it "should not append string values, if the array already contains it" do
130
+ subject.append(LogStash::Event.new({"field1" => "original1"}))
131
+ insist { subject[ "field1" ] } == [ "original1", "original2" ]
132
+ end
133
+ it "should join array, removing duplicates" do
134
+ subject.append(LogStash::Event.new({"field1" => [ "append1","original1" ]}))
135
+ insist { subject[ "field1" ] } == [ "original1", "original2", "append1" ]
136
+ end
137
+ end
138
+ end
139
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-event
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.5
4
+ version: 1.2.02
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,72 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-12 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2013-09-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: guard
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: guard-rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
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
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: insist
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - '='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.0.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - '='
76
+ - !ruby/object:Gem::Version
77
+ version: 1.0.0
14
78
  description: Library that contains the classes required to create LogStash events
15
79
  email:
16
80
  - jls@semicomplete.com
@@ -21,8 +85,9 @@ files:
21
85
  - lib/logstash-event.rb
22
86
  - lib/logstash/event.rb
23
87
  - lib/logstash/namespace.rb
24
- - lib/logstash/time.rb
25
- - lib/logstash/version.rb
88
+ - lib/logstash/util/fieldreference.rb
89
+ - lib/logstash/util.rb
90
+ - spec/event.rb
26
91
  - LICENSE
27
92
  homepage: https://github.com/logstash/logstash
28
93
  licenses:
@@ -45,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
45
110
  version: '0'
46
111
  requirements: []
47
112
  rubyforge_project:
48
- rubygems_version: 1.8.24
113
+ rubygems_version: 1.8.25
49
114
  signing_key:
50
115
  specification_version: 3
51
116
  summary: Library that contains the classes required to create LogStash events
@@ -1,32 +0,0 @@
1
- require "logstash/namespace"
2
-
3
- # Provide our own Time wrapper for ISO8601 support
4
- # Example:
5
- # >> LogStash::Time.now.to_iso8601
6
- # => "2010-10-17 00:25:24.619014-0700"
7
- #
8
- # >> LogStash::Time.now.utc.to_iso8601
9
- # => "2010-10-17 07:25:26.788704Z"
10
- module LogStash::Time
11
- if RUBY_ENGINE == "jruby"
12
- require "java"
13
- DateTime = org.joda.time.DateTime
14
- DateTimeZone = org.joda.time.DateTimeZone
15
- def self.now
16
- # org.joda.time.DateTime#to_s returns the time in ISO8601 form :)
17
- # Could call DateTime.new(DateTimeZone::UTC) but JRuby calls the
18
- # DateTime#new(Object) constructor instead of the
19
- # DateTime#new(DateTimeZone) constructor. I was unable to get java_send to invoke this constructor,
20
- # so instead I have to do DateTime#new#withZone(UTC)
21
- return DateTime.new.withZone(DateTimeZone::UTC).to_s
22
- end # def initialize
23
- else
24
- # Otherwise, use ruby stdlib Time, which is much slower than Joda.
25
- ISO8601_STRFTIME = "%04d-%02d-%02dT%02d:%02d:%02d.%06d%+03d:00".freeze
26
- def self.now
27
- now = Time.new.utc
28
- return sprintf(ISO8601_STRFTIME, now.year, now.month, now.day, now.hour,
29
- now.min, now.sec, now.tv_usec, now.utc_offset / 3600)
30
- end
31
- end
32
- end # module LogStash::Time
@@ -1,5 +0,0 @@
1
- # The version of logstash.
2
- LOGSTASH_VERSION = "1.1.5"
3
-
4
- # Note to authors: this should not include dashes because 'gem' barfs if
5
- # you include a dash in the version string.