@@ -46,54 +46,64 @@ class LogStash::Filters::Json < LogStash::Filters::Base
46
46
# NOTE: if the `target` field already exists, it will be overwritten!
47
47
config :target , :validate => :string
48
48
49
- public
49
+ JSONPARSEFAILURE_TAG = "_jsonparsefailure"
50
+
50
51
def register
51
52
# Nothing to do here
52
- end # def register
53
+ end
53
54
54
- public
55
55
def filter ( event )
56
-
57
-
58
- @logger . debug ( "Running json filter" , :event => event )
59
-
60
- return unless event . include? ( @source )
61
-
62
- # TODO(colin) this field merging stuff below should be handled in Event.
56
+ @logger . debug? && @logger . debug ( "Running json filter" , :event => event )
63
57
64
58
source = event [ @source ]
59
+ return unless source
65
60
66
61
begin
67
62
parsed = LogStash ::Json . load ( source )
68
- # If your parsed JSON is an array, we can't merge, so you must specify a
69
- # destination to store the JSON, so you will get an exception about
70
- if parsed . kind_of? ( Array ) && @target . nil?
71
- raise ( 'Parsed JSON arrays must have a destination in the configuration' )
72
- elsif @target . nil?
73
- event . to_hash . merge! parsed
74
- else
75
- event [ @target ] = parsed
63
+ rescue => e
64
+ event . tag ( JSONPARSEFAILURE_TAG )
65
+ @logger . warn ( "Error parsing json" , :source => @source , :raw => source , :exception => e )
66
+ return
67
+ end
68
+
69
+ if @target
70
+ event [ @target ] = parsed
71
+ else
72
+ unless parsed . is_a? ( Hash )
73
+ event . tag ( JSONPARSEFAILURE_TAG )
74
+ @logger . warn ( "Parsed JSON object/hash requires a target configuration option" , :source => @source , :raw => source )
75
+ return
76
76
end
77
77
78
- # If no target, we target the root of the event object. This can allow
79
- # you to overwrite @timestamp and this will typically happen for json
80
- # LogStash Event deserialized here.
81
- if !@target && event . timestamp . is_a? ( String )
82
- event . timestamp = LogStash ::Timestamp . parse_iso8601 ( event . timestamp )
78
+ # TODO: (colin) the timestamp initialization should be DRY'ed but exposing the similar code
79
+ # in the Event#init_timestamp method. See https://github.com/elastic/logstash/issues/4293
80
+
81
+ # a) since the parsed hash will be set in the event root, first extract any @timestamp field to properly initialized it
82
+ parsed_timestamp = parsed . delete ( LogStash ::Event ::TIMESTAMP )
83
+ begin
84
+ timestamp = parsed_timestamp ? LogStash ::Timestamp . coerce ( parsed_timestamp ) : nil
85
+ rescue LogStash ::TimestampParserError => e
86
+ timestamp = nil
83
87
end
84
88
85
- filter_matched ( event )
86
- rescue => e
87
- tag = "_jsonparsefailure"
88
- event [ "tags" ] ||= [ ]
89
- event [ "tags" ] << tag unless event [ "tags" ] . include? ( tag )
90
- @logger . warn ( "Trouble parsing json" , :source => @source ,
91
- :raw => event [ @source ] , :exception => e )
92
- return
89
+ # b) then set all parsed fields in the event
90
+ parsed . each { |k , v | event [ k ] = v }
91
+
92
+ # c) finally re-inject proper @timestamp
93
+ if parsed_timestamp
94
+ if timestamp
95
+ event . timestamp = timestamp
96
+ else
97
+ event . timestamp = LogStash ::Timestamp . new
98
+ @logger . warn ( "Unrecognized #{ LogStash ::Event ::TIMESTAMP } value, setting current time to #{ LogStash ::Event ::TIMESTAMP } , original in #{ LogStash ::Event ::TIMESTAMP_FAILURE_FIELD } field" , :value => parsed_timestamp . inspect )
99
+ event . tag ( LogStash ::Event ::TIMESTAMP_FAILURE_TAG )
100
+ event [ LogStash ::Event ::TIMESTAMP_FAILURE_FIELD ] = parsed_timestamp . to_s
101
+ end
102
+ end
93
103
end
94
104
95
- @logger . debug ( "Event after json filter" , :event => event )
96
-
97
- end # def filter
105
+ filter_matched ( event )
98
106
99
- end # class LogStash::Filters::Json
107
+ @logger . debug? && @logger . debug ( "Event after json filter" , :event => event )
108
+ end
109
+ end
0 commit comments