--- /dev/null
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: logstash-event
+Source: FIXME <http://example.com/>
+
+Files: *
+Copyright: <years> <put author's name and email here>
+ <years> <likewise for another author>
+License: GPL-2+ (FIXME)
+
+Files: debian/*
+Copyright: 2014 <>
+License: GPL-2+ (FIXME)
+Comment: the Debian packaging is licensed under the same terms as the original package.
+
+License: GPL-2+ (FIXME)
+ This program is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ .
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the GNU General Public License for more
+ details.
+ .
+ You should have received a copy of the GNU General Public
+ License along with this package; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/GPL-2'.
--- /dev/null
+require "json"
+require "logstash/time"
+require "logstash/namespace"
+require "uri"
+require "time"
+
+# General event type.
+# Basically a light wrapper on top of a hash.
+#
+# TODO(sissel): properly handle lazy properties like parsed time formats, urls,
+# etc, as necessary.
+class LogStash::Event
+ public
+ def initialize(data=nil)
+ @cancelled = false
+
+ @data = {
+ "@source" => "unknown",
+ "@tags" => [],
+ "@fields" => {},
+ }
+ @data.merge!(data) unless data.nil?
+ @data["@timestamp"] ||= LogStash::Time.now
+ end # def initialize
+
+ if RUBY_ENGINE == "jruby"
+ @@date_parser = Java::org.joda.time.format.ISODateTimeFormat.dateTimeParser.withOffsetParsed
+ else
+ # TODO(sissel): LOGSTASH-217
+ @@date_parser ||= nil
+ end
+
+ public
+ def self.from_json(json)
+ return LogStash::Event.new(JSON.parse(json))
+ end # def self.from_json
+
+ public
+ def cancel
+ @cancelled = true
+ end # def cancel
+
+ public
+ def cancelled?
+ return @cancelled
+ end # def cancelled?
+
+ # Create a deep-ish copy of this event.
+ public
+ def clone
+ newdata = @data.clone
+ newdata["@fields"] = {}
+ fields.each do |k,v|
+ newdata["@fields"][k] = v.clone
+ end
+ return LogStash::Event.new(newdata)
+ end # def clone
+
+ public
+ def to_s
+ return self.sprintf("%{@timestamp} %{@source}: %{@message}")
+ end # def to_s
+
+ public
+ def timestamp; @data["@timestamp"]; end # def timestamp
+ def timestamp=(val); @data["@timestamp"] = val; end # def timestamp=
+
+ public
+ def unix_timestamp
+ if RUBY_ENGINE != "jruby"
+ # This is really slow. See LOGSTASH-217
+ return Time.parse(timestamp).to_f
+ else
+ time = @@date_parser.parseDateTime(timestamp)
+ return time.getMillis.to_f / 1000
+ end
+ end
+
+ public
+ def source; @data["@source"]; end # def source
+ def source=(val)
+ uri = URI.parse(val) rescue nil
+ val = uri if uri
+ if val.is_a?(URI)
+ @data["@source"] = val.to_s
+ @data["@source_host"] = val.host
+ @data["@source_path"] = val.path
+ else
+ @data["@source"] = val
+ @data["@source_host"] = val
+ end
+ end # def source=
+
+ public
+ def source_host; @data["@source_host"]; end # def source_host
+ def source_host=(val); @data["@source_host"] = val; end # def source_host=
+
+ public
+ def source_path; @data["@source_path"]; end # def source_path
+ def source_path=(val); @data["@source_path"] = val; end # def source_path=
+
+ public
+ def message; @data["@message"]; end # def message
+ def message=(val); @data["@message"] = val; end # def message=
+
+ public
+ def type; @data["@type"]; end # def type
+ def type=(val); @data["@type"] = val; end # def type=
+
+ public
+ def tags; @data["@tags"]; end # def tags
+ def tags=(val); @data["@tags"] = val; end # def tags=
+
+ # field-related access
+ public
+ def [](key)
+ # If the key isn't in fields and it starts with an "@" sign, get it out of data instead of fields
+ if ! @data["@fields"].has_key?(key) and key.slice(0,1) == "@"
+ return @data[key]
+ # Exists in @fields (returns value) or doesn't start with "@" (return null)
+ else
+ return @data["@fields"][key]
+ end
+ end # def []
+
+ public
+ def []=(key, value)
+ if @data.has_key?(key)
+ @data[key] = value
+ else
+ @data["@fields"][key] = value
+ end
+ end # def []=
+
+ def fields; return @data["@fields"] end # def fields
+
+ public
+ def to_json(*args); return @data.to_json(*args) end # def to_json
+ def to_hash; return @data end # def to_hash
+
+ public
+ def overwrite(event)
+ @data = event.to_hash
+ end
+
+ public
+ def include?(key)
+ return (@data.include?(key) or @data["@fields"].include?(key))
+ end # def include?
+
+ # Append an event to this one.
+ public
+ def append(event)
+ if event.message
+ if self.message
+ self.message += "\n" + event.message
+ else
+ self.message = event.message
+ end
+ end
+ self.tags |= event.tags
+
+ # Append all fields
+ event.fields.each do |name, value|
+ if self.fields.include?(name)
+ if !self.fields[name].is_a?(Array)
+ self.fields[name] = [self.fields[name]]
+ end
+ if value.is_a?(Array)
+ self.fields[name] |= value
+ else
+ self.fields[name] << value unless self.fields[name].include?(value)
+ end
+ else
+ self.fields[name] = value
+ end
+ end # event.fields.each
+ end # def append
+
+ # Remove a field
+ public
+ def remove(field)
+ if @data.has_key?(field)
+ @data.delete(field)
+ else
+ @data["@fields"].delete(field)
+ end
+ end # def remove
+
+ # sprintf. This could use a better method name.
+ # The idea is to take an event and convert it to a string based on
+ # any format values, delimited by %{foo} where 'foo' is a field or
+ # metadata member.
+ #
+ # For example, if the event has @type == "foo" and @source == "bar"
+ # then this string:
+ # "type is %{@type} and source is %{@source}"
+ # will return
+ # "type is foo and source is bar"
+ #
+ # If a %{name} value is an array, then we will join by ','
+ # If a %{name} value does not exist, then no substitution occurs.
+ #
+ # TODO(sissel): It is not clear what the value of a field that
+ # is an array (or hash?) should be. Join by comma? Something else?
+ public
+ def sprintf(format)
+ if format.index("%").nil?
+ return format
+ end
+
+ return format.gsub(/%\{[^}]+\}/) do |tok|
+ # Take the inside of the %{ ... }
+ key = tok[2 ... -1]
+
+ if key == "+%s"
+ # Got %{+%s}, support for unix epoch time
+ if RUBY_ENGINE != "jruby"
+ # This is really slow. See LOGSTASH-217
+ Date.parse(self.timestamp).to_i
+ else
+ datetime = @@date_parser.parseDateTime(self.timestamp)
+ (datetime.getMillis / 1000).to_i
+ end
+ elsif key[0,1] == "+"
+ # We got a %{+TIMEFORMAT} so use joda to format it.
+ if RUBY_ENGINE != "jruby"
+ # This is really slow. See LOGSTASH-217
+ datetime = Date.parse(self.timestamp)
+ format = key[1 .. -1]
+ datetime.strftime(format)
+ else
+ datetime = @@date_parser.parseDateTime(self.timestamp)
+ format = key[1 .. -1]
+ datetime.toString(format) # return requested time format
+ end
+ else
+ # Use an event field.
+ value = nil
+ obj = self
+
+ # If the top-level value exists, use that and don't try
+ # to "look" into data structures.
+ if self[key]
+ value = self[key]
+ else
+ # "." is what ES uses to access structured data, so adopt that
+ # idea here, too. "foo.bar" will access key "bar" under hash "foo".
+ key.split('.').each do |segment|
+ if obj
+ value = obj[segment] rescue nil
+ obj = obj[segment] rescue nil
+ else
+ value = nil
+ break
+ end
+ end # key.split.each
+ end # if self[key]
+
+ case value
+ when nil
+ tok # leave the %{foo} if this field does not exist in this event.
+ when Array
+ value.join(",") # Join by ',' if value is an array
+ when Hash
+ value.to_json # Convert hashes to json
+ else
+ value # otherwise return the value
+ end
+ end
+ end
+ end # def sprintf
+
+ public
+ def ==(other)
+ #puts "#{self.class.name}#==(#{other.inspect})"
+ if !other.is_a?(self.class)
+ return false
+ end
+
+ return other.to_hash == self.to_hash
+ end # def ==
+end # class LogStash::Event
--- /dev/null
+require "logstash/namespace"
+
+# Provide our own Time wrapper for ISO8601 support
+# Example:
+# >> LogStash::Time.now.to_iso8601
+# => "2010-10-17 00:25:24.619014-0700"
+#
+# >> LogStash::Time.now.utc.to_iso8601
+# => "2010-10-17 07:25:26.788704Z"
+module LogStash::Time
+ if RUBY_ENGINE == "jruby"
+ require "java"
+ DateTime = org.joda.time.DateTime
+ DateTimeZone = org.joda.time.DateTimeZone
+ def self.now
+ # org.joda.time.DateTime#to_s returns the time in ISO8601 form :)
+ # Could call DateTime.new(DateTimeZone::UTC) but JRuby calls the
+ # DateTime#new(Object) constructor instead of the
+ # DateTime#new(DateTimeZone) constructor. I was unable to get java_send to invoke this constructor,
+ # so instead I have to do DateTime#new#withZone(UTC)
+ return DateTime.new.withZone(DateTimeZone::UTC).to_s
+ end # def initialize
+ else
+ # Otherwise, use ruby stdlib Time, which is much slower than Joda.
+ ISO8601_STRFTIME = "%04d-%02d-%02dT%02d:%02d:%02d.%06d%+03d:00".freeze
+ def self.now
+ now = Time.new.utc
+ return sprintf(ISO8601_STRFTIME, now.year, now.month, now.day, now.hour,
+ now.min, now.sec, now.tv_usec, now.utc_offset / 3600)
+ end
+ end
+end # module LogStash::Time