fluent-plugin-es-mohit 1.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,647 @@
1
+ require 'helper'
2
+ require 'date'
3
+
4
+ class ElasticsearchOutputDynamic < Test::Unit::TestCase
5
+ attr_accessor :index_cmds, :index_command_counts
6
+
7
+ def setup
8
+ Fluent::Test.setup
9
+ require 'fluent/plugin/out_elasticsearch_dynamic'
10
+ @driver = nil
11
+ end
12
+
13
+ def driver(tag='test', conf='')
14
+ @driver ||= Fluent::Test::BufferedOutputTestDriver.new(Fluent::ElasticsearchOutputDynamic, tag).configure(conf)
15
+ end
16
+
17
+ def sample_record
18
+ {'age' => 26, 'request_id' => '42', 'parent_id' => 'parent', 'routing_id' => 'routing'}
19
+ end
20
+
21
+ def stub_elastic_ping(url="http://localhost:9200")
22
+ stub_request(:head, url).to_return(:status => 200, :body => "", :headers => {})
23
+ end
24
+
25
+ def stub_elastic(url="http://localhost:9200/_bulk")
26
+ stub_request(:post, url).with do |req|
27
+ @index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
28
+ end
29
+ end
30
+
31
+ def stub_elastic_unavailable(url="http://localhost:9200/_bulk")
32
+ stub_request(:post, url).to_return(:status => [503, "Service Unavailable"])
33
+ end
34
+
35
+ def stub_elastic_with_store_index_command_counts(url="http://localhost:9200/_bulk")
36
+ if @index_command_counts == nil
37
+ @index_command_counts = {}
38
+ @index_command_counts.default = 0
39
+ end
40
+
41
+ stub_request(:post, url).with do |req|
42
+ index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
43
+ @index_command_counts[url] += index_cmds.size
44
+ end
45
+ end
46
+
47
+ def test_configure
48
+ config = %{
49
+ host logs.google.com
50
+ port 777
51
+ scheme https
52
+ path /es/
53
+ user john
54
+ password doe
55
+ }
56
+ instance = driver('test', config).instance
57
+
58
+ assert_equal 'logs.google.com', instance.host
59
+ assert_equal 777, instance.port
60
+ assert_equal 'https', instance.scheme
61
+ assert_equal '/es/', instance.path
62
+ assert_equal 'john', instance.user
63
+ assert_equal 'doe', instance.password
64
+ end
65
+
66
+ def test_defaults
67
+ config = %{
68
+ host logs.google.com
69
+ scheme https
70
+ path /es/
71
+ user john
72
+ password doe
73
+ }
74
+ instance = driver('test', config).instance
75
+
76
+ assert_equal "9200", instance.port
77
+ assert_equal "false", instance.logstash_format
78
+ assert_equal "true", instance.utc_index
79
+ assert_equal false, instance.time_key_exclude_timestamp
80
+ assert_equal "true", instance.reload_connections
81
+ assert_equal "false", instance.reload_on_failure
82
+ assert_equal "60", instance.resurrect_after
83
+ assert_equal "true", instance.ssl_verify
84
+ end
85
+
86
+ def test_legacy_hosts_list
87
+ config = %{
88
+ hosts host1:50,host2:100,host3
89
+ scheme https
90
+ path /es/
91
+ port 123
92
+ }
93
+ instance = driver('test', config).instance
94
+
95
+ assert_equal 3, instance.get_connection_options(nil)[:hosts].length
96
+ host1, host2, host3 = instance.get_connection_options(nil)[:hosts]
97
+
98
+ assert_equal 'host1', host1[:host]
99
+ assert_equal 50, host1[:port]
100
+ assert_equal 'https', host1[:scheme]
101
+ assert_equal '/es/', host2[:path]
102
+ assert_equal 'host3', host3[:host]
103
+ assert_equal 123, host3[:port]
104
+ assert_equal 'https', host3[:scheme]
105
+ assert_equal '/es/', host3[:path]
106
+ end
107
+
108
+ def test_hosts_list
109
+ config = %{
110
+ hosts https://john:password@host1:443/elastic/,http://host2
111
+ path /default_path
112
+ user default_user
113
+ password default_password
114
+ }
115
+ instance = driver('test', config).instance
116
+
117
+ assert_equal 2, instance.get_connection_options(nil)[:hosts].length
118
+ host1, host2 = instance.get_connection_options(nil)[:hosts]
119
+
120
+ assert_equal 'host1', host1[:host]
121
+ assert_equal 443, host1[:port]
122
+ assert_equal 'https', host1[:scheme]
123
+ assert_equal 'john', host1[:user]
124
+ assert_equal 'password', host1[:password]
125
+ assert_equal '/elastic/', host1[:path]
126
+
127
+ assert_equal 'host2', host2[:host]
128
+ assert_equal 'http', host2[:scheme]
129
+ assert_equal 'default_user', host2[:user]
130
+ assert_equal 'default_password', host2[:password]
131
+ assert_equal '/default_path', host2[:path]
132
+ end
133
+
134
+ def test_single_host_params_and_defaults
135
+ config = %{
136
+ host logs.google.com
137
+ user john
138
+ password doe
139
+ }
140
+ instance = driver('test', config).instance
141
+
142
+ assert_equal 1, instance.get_connection_options(nil)[:hosts].length
143
+ host1 = instance.get_connection_options(nil)[:hosts][0]
144
+
145
+ assert_equal 'logs.google.com', host1[:host]
146
+ assert_equal 9200, host1[:port]
147
+ assert_equal 'http', host1[:scheme]
148
+ assert_equal 'john', host1[:user]
149
+ assert_equal 'doe', host1[:password]
150
+ assert_equal nil, host1[:path]
151
+ end
152
+
153
+ def test_writes_to_default_index
154
+ stub_elastic_ping
155
+ stub_elastic
156
+ driver.emit(sample_record)
157
+ driver.run
158
+ assert_equal('fluentd', index_cmds.first['index']['_index'])
159
+ end
160
+
161
+ def test_writes_to_default_type
162
+ stub_elastic_ping
163
+ stub_elastic
164
+ driver.emit(sample_record)
165
+ driver.run
166
+ assert_equal('fluentd', index_cmds.first['index']['_type'])
167
+ end
168
+
169
+ def test_writes_to_speficied_index
170
+ driver.configure("index_name myindex\n")
171
+ stub_elastic_ping
172
+ stub_elastic
173
+ driver.emit(sample_record)
174
+ driver.run
175
+ assert_equal('myindex', index_cmds.first['index']['_index'])
176
+ end
177
+
178
+ def test_writes_to_speficied_index_uppercase
179
+ driver.configure("index_name MyIndex\n")
180
+ stub_elastic_ping
181
+ stub_elastic
182
+ driver.emit(sample_record)
183
+ driver.run
184
+ assert_equal('myindex', index_cmds.first['index']['_index'])
185
+ end
186
+
187
+ def test_writes_to_speficied_type
188
+ driver.configure("type_name mytype\n")
189
+ stub_elastic_ping
190
+ stub_elastic
191
+ driver.emit(sample_record)
192
+ driver.run
193
+ assert_equal('mytype', index_cmds.first['index']['_type'])
194
+ end
195
+
196
+ def test_writes_to_speficied_host
197
+ driver.configure("host 192.168.33.50\n")
198
+ stub_elastic_ping("http://192.168.33.50:9200")
199
+ elastic_request = stub_elastic("http://192.168.33.50:9200/_bulk")
200
+ driver.emit(sample_record)
201
+ driver.run
202
+ assert_requested(elastic_request)
203
+ end
204
+
205
+ def test_writes_to_speficied_port
206
+ driver.configure("port 9201\n")
207
+ stub_elastic_ping("http://localhost:9201")
208
+ elastic_request = stub_elastic("http://localhost:9201/_bulk")
209
+ driver.emit(sample_record)
210
+ driver.run
211
+ assert_requested(elastic_request)
212
+ end
213
+
214
+ def test_writes_to_multi_hosts
215
+ hosts = [['192.168.33.50', 9201], ['192.168.33.51', 9201], ['192.168.33.52', 9201]]
216
+ hosts_string = hosts.map {|x| "#{x[0]}:#{x[1]}"}.compact.join(',')
217
+
218
+ driver.configure("hosts #{hosts_string}")
219
+
220
+ hosts.each do |host_info|
221
+ host, port = host_info
222
+ stub_elastic_ping("http://#{host}:#{port}")
223
+ stub_elastic_with_store_index_command_counts("http://#{host}:#{port}/_bulk")
224
+ end
225
+
226
+ 1000.times do
227
+ driver.emit(sample_record.merge('age'=>rand(100)))
228
+ end
229
+
230
+ driver.run
231
+
232
+ # @note: we cannot make multi chunks with options (flush_interval, buffer_chunk_limit)
233
+ # it's Fluentd test driver's constraint
234
+ # so @index_command_counts.size is always 1
235
+
236
+ assert(@index_command_counts.size > 0, "not working with hosts options")
237
+
238
+ total = 0
239
+ @index_command_counts.each do |url, count|
240
+ total += count
241
+ end
242
+ assert_equal(2000, total)
243
+ end
244
+
245
+ def test_makes_bulk_request
246
+ stub_elastic_ping
247
+ stub_elastic
248
+ driver.emit(sample_record)
249
+ driver.emit(sample_record.merge('age' => 27))
250
+ driver.run
251
+ assert_equal(4, index_cmds.count)
252
+ end
253
+
254
+ def test_all_records_are_preserved_in_bulk
255
+ stub_elastic_ping
256
+ stub_elastic
257
+ driver.emit(sample_record)
258
+ driver.emit(sample_record.merge('age' => 27))
259
+ driver.run
260
+ assert_equal(26, index_cmds[1]['age'])
261
+ assert_equal(27, index_cmds[3]['age'])
262
+ end
263
+
264
+ def test_writes_to_logstash_index
265
+ driver.configure("logstash_format true\n")
266
+ time = Time.parse Date.today.to_s
267
+ logstash_index = "logstash-#{time.getutc.strftime("%Y.%m.%d")}"
268
+ stub_elastic_ping
269
+ stub_elastic
270
+ driver.emit(sample_record, time.to_i)
271
+ driver.run
272
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
273
+ end
274
+
275
+ def test_writes_to_logstash_utc_index
276
+ driver.configure("logstash_format true
277
+ utc_index false")
278
+ time = Time.parse Date.today.to_s
279
+ utc_index = "logstash-#{time.strftime("%Y.%m.%d")}"
280
+ stub_elastic_ping
281
+ stub_elastic
282
+ driver.emit(sample_record, time.to_i)
283
+ driver.run
284
+ assert_equal(utc_index, index_cmds.first['index']['_index'])
285
+ end
286
+
287
+ def test_writes_to_logstash_index_with_specified_prefix
288
+ driver.configure("logstash_format true
289
+ logstash_prefix myprefix")
290
+ time = Time.parse Date.today.to_s
291
+ logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m.%d")}"
292
+ stub_elastic_ping
293
+ stub_elastic
294
+ driver.emit(sample_record, time.to_i)
295
+ driver.run
296
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
297
+ end
298
+
299
+ def test_writes_to_logstash_index_with_specified_prefix_uppercase
300
+ driver.configure("logstash_format true
301
+ logstash_prefix MyPrefix")
302
+ time = Time.parse Date.today.to_s
303
+ logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m.%d")}"
304
+ stub_elastic_ping
305
+ stub_elastic
306
+ driver.emit(sample_record, time.to_i)
307
+ driver.run
308
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
309
+ end
310
+
311
+ def test_writes_to_logstash_index_with_specified_dateformat
312
+ driver.configure("logstash_format true
313
+ logstash_dateformat %Y.%m")
314
+ time = Time.parse Date.today.to_s
315
+ logstash_index = "logstash-#{time.getutc.strftime("%Y.%m")}"
316
+ stub_elastic_ping
317
+ stub_elastic
318
+ driver.emit(sample_record, time.to_i)
319
+ driver.run
320
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
321
+ end
322
+
323
+ def test_writes_to_logstash_index_with_specified_prefix_and_dateformat
324
+ driver.configure("logstash_format true
325
+ logstash_prefix myprefix
326
+ logstash_dateformat %Y.%m")
327
+ time = Time.parse Date.today.to_s
328
+ logstash_index = "myprefix-#{time.getutc.strftime("%Y.%m")}"
329
+ stub_elastic_ping
330
+ stub_elastic
331
+ driver.emit(sample_record, time.to_i)
332
+ driver.run
333
+ assert_equal(logstash_index, index_cmds.first['index']['_index'])
334
+ end
335
+
336
+ def test_doesnt_add_logstash_timestamp_by_default
337
+ stub_elastic_ping
338
+ stub_elastic
339
+ driver.emit(sample_record)
340
+ driver.run
341
+ assert_nil(index_cmds[1]['@timestamp'])
342
+ end
343
+
344
+ def test_adds_logstash_timestamp_when_configured
345
+ driver.configure("logstash_format true\n")
346
+ stub_elastic_ping
347
+ stub_elastic
348
+ ts = DateTime.now.to_s
349
+ driver.emit(sample_record)
350
+ driver.run
351
+ assert(index_cmds[1].has_key? '@timestamp')
352
+ assert_equal(index_cmds[1]['@timestamp'], ts)
353
+ end
354
+
355
+ def test_uses_custom_timestamp_when_included_in_record
356
+ driver.configure("logstash_format true\n")
357
+ stub_elastic_ping
358
+ stub_elastic
359
+ ts = DateTime.new(2001,2,3).to_s
360
+ driver.emit(sample_record.merge!('@timestamp' => ts))
361
+ driver.run
362
+ assert(index_cmds[1].has_key? '@timestamp')
363
+ assert_equal(index_cmds[1]['@timestamp'], ts)
364
+ end
365
+
366
+ def test_uses_custom_time_key
367
+ driver.configure("logstash_format true
368
+ time_key vtm\n")
369
+ stub_elastic_ping
370
+ stub_elastic
371
+ ts = DateTime.new(2001,2,3).to_s
372
+ driver.emit(sample_record.merge!('vtm' => ts))
373
+ driver.run
374
+ assert(index_cmds[1].has_key? '@timestamp')
375
+ assert_equal(index_cmds[1]['@timestamp'], ts)
376
+ end
377
+
378
+ def test_uses_custom_time_key_exclude_timestamp
379
+ driver.configure("logstash_format true
380
+ time_key vtm
381
+ time_key_exclude_timestamp true\n")
382
+ stub_elastic_ping
383
+ stub_elastic
384
+ ts = DateTime.new(2001,2,3).to_s
385
+ driver.emit(sample_record.merge!('vtm' => ts))
386
+ driver.run
387
+ assert(!index_cmds[1].key?('@timestamp'), '@timestamp should be missing')
388
+ end
389
+
390
+ def test_doesnt_add_tag_key_by_default
391
+ stub_elastic_ping
392
+ stub_elastic
393
+ driver.emit(sample_record)
394
+ driver.run
395
+ assert_nil(index_cmds[1]['tag'])
396
+ end
397
+
398
+ def test_adds_tag_key_when_configured
399
+ driver('mytag').configure("include_tag_key true\n")
400
+ stub_elastic_ping
401
+ stub_elastic
402
+ driver.emit(sample_record)
403
+ driver.run
404
+ assert(index_cmds[1].has_key?('tag'))
405
+ assert_equal(index_cmds[1]['tag'], 'mytag')
406
+ end
407
+
408
+ def test_adds_id_key_when_configured
409
+ driver.configure("id_key request_id\n")
410
+ stub_elastic_ping
411
+ stub_elastic
412
+ driver.emit(sample_record)
413
+ driver.run
414
+ assert_equal(index_cmds[0]['index']['_id'], '42')
415
+ end
416
+
417
+ def test_doesnt_add_id_key_if_missing_when_configured
418
+ driver.configure("id_key another_request_id\n")
419
+ stub_elastic_ping
420
+ stub_elastic
421
+ driver.emit(sample_record)
422
+ driver.run
423
+ assert(!index_cmds[0]['index'].has_key?('_id'))
424
+ end
425
+
426
+ def test_adds_id_key_when_not_configured
427
+ stub_elastic_ping
428
+ stub_elastic
429
+ driver.emit(sample_record)
430
+ driver.run
431
+ assert(!index_cmds[0]['index'].has_key?('_id'))
432
+ end
433
+
434
+ def test_adds_parent_key_when_configured
435
+ driver.configure("parent_key parent_id\n")
436
+ stub_elastic_ping
437
+ stub_elastic
438
+ driver.emit(sample_record)
439
+ driver.run
440
+ assert_equal(index_cmds[0]['index']['_parent'], 'parent')
441
+ end
442
+
443
+ def test_doesnt_add_parent_key_if_missing_when_configured
444
+ driver.configure("parent_key another_parent_id\n")
445
+ stub_elastic_ping
446
+ stub_elastic
447
+ driver.emit(sample_record)
448
+ driver.run
449
+ assert(!index_cmds[0]['index'].has_key?('_parent'))
450
+ end
451
+
452
+ def test_adds_parent_key_when_not_configured
453
+ stub_elastic_ping
454
+ stub_elastic
455
+ driver.emit(sample_record)
456
+ driver.run
457
+ assert(!index_cmds[0]['index'].has_key?('_parent'))
458
+ end
459
+
460
+ def test_adds_routing_key_when_configured
461
+ driver.configure("routing_key routing_id\n")
462
+ stub_elastic_ping
463
+ stub_elastic
464
+ driver.emit(sample_record)
465
+ driver.run
466
+ assert_equal(index_cmds[0]['index']['_routing'], 'routing')
467
+ end
468
+
469
+ def test_doesnt_add_routing_key_if_missing_when_configured
470
+ driver.configure("routing_key another_routing_id\n")
471
+ stub_elastic_ping
472
+ stub_elastic
473
+ driver.emit(sample_record)
474
+ driver.run
475
+ assert(!index_cmds[0]['index'].has_key?('_routing'))
476
+ end
477
+
478
+ def test_adds_routing_key_when_not_configured
479
+ stub_elastic_ping
480
+ stub_elastic
481
+ driver.emit(sample_record)
482
+ driver.run
483
+ assert(!index_cmds[0]['index'].has_key?('_routing'))
484
+ end
485
+
486
+ def test_remove_one_key
487
+ driver.configure("remove_keys key1\n")
488
+ stub_elastic_ping
489
+ stub_elastic
490
+ driver.emit(sample_record.merge('key1' => 'v1', 'key2' => 'v2'))
491
+ driver.run
492
+ assert(!index_cmds[1].has_key?('key1'))
493
+ assert(index_cmds[1].has_key?('key2'))
494
+ end
495
+
496
+ def test_remove_multi_keys
497
+ driver.configure("remove_keys key1, key2\n")
498
+ stub_elastic_ping
499
+ stub_elastic
500
+ driver.emit(sample_record.merge('key1' => 'v1', 'key2' => 'v2'))
501
+ driver.run
502
+ assert(!index_cmds[1].has_key?('key1'))
503
+ assert(!index_cmds[1].has_key?('key2'))
504
+ end
505
+
506
+ def test_request_error
507
+ stub_elastic_ping
508
+ stub_elastic_unavailable
509
+ driver.emit(sample_record)
510
+ assert_raise(Elasticsearch::Transport::Transport::Errors::ServiceUnavailable) {
511
+ driver.run
512
+ }
513
+ end
514
+
515
+ def test_garbage_record_error
516
+ stub_elastic_ping
517
+ stub_elastic
518
+ driver.emit("some garbage string")
519
+ driver.run
520
+ end
521
+
522
+ def test_connection_failed_retry
523
+ connection_resets = 0
524
+
525
+ stub_elastic_ping(url="http://localhost:9200").with do |req|
526
+ connection_resets += 1
527
+ end
528
+
529
+ stub_request(:post, "http://localhost:9200/_bulk").with do |req|
530
+ raise Faraday::ConnectionFailed, "Test message"
531
+ end
532
+
533
+ driver.emit(sample_record)
534
+
535
+ assert_raise(Fluent::ElasticsearchOutput::ConnectionFailure) {
536
+ driver.run
537
+ }
538
+ assert_equal(connection_resets, 3)
539
+ end
540
+
541
+ def test_reconnect_on_error_enabled
542
+ connection_resets = 0
543
+
544
+ stub_elastic_ping(url="http://localhost:9200").with do |req|
545
+ connection_resets += 1
546
+ end
547
+
548
+ stub_request(:post, "http://localhost:9200/_bulk").with do |req|
549
+ raise ZeroDivisionError, "any not host_unreachable_exceptions exception"
550
+ end
551
+
552
+ driver.configure("reconnect_on_error true\n")
553
+ driver.emit(sample_record)
554
+
555
+ assert_raise(ZeroDivisionError) {
556
+ driver.run
557
+ }
558
+
559
+ assert_raise(ZeroDivisionError) {
560
+ driver.run
561
+ }
562
+ assert_equal(connection_resets, 2)
563
+ end
564
+
565
+ def test_reconnect_on_error_disabled
566
+ connection_resets = 0
567
+
568
+ stub_elastic_ping(url="http://localhost:9200").with do |req|
569
+ connection_resets += 1
570
+ end
571
+
572
+ stub_request(:post, "http://localhost:9200/_bulk").with do |req|
573
+ raise ZeroDivisionError, "any not host_unreachable_exceptions exception"
574
+ end
575
+
576
+ driver.configure("reconnect_on_error false\n")
577
+ driver.emit(sample_record)
578
+
579
+ assert_raise(ZeroDivisionError) {
580
+ driver.run
581
+ }
582
+
583
+ assert_raise(ZeroDivisionError) {
584
+ driver.run
585
+ }
586
+ assert_equal(connection_resets, 1)
587
+ end
588
+
589
+ def test_update_should_not_write_if_theres_no_id
590
+ driver.configure("write_operation update\n")
591
+ stub_elastic_ping
592
+ stub_elastic
593
+ driver.emit(sample_record)
594
+ driver.run
595
+ assert_nil(index_cmds)
596
+ end
597
+
598
+ def test_upsert_should_not_write_if_theres_no_id
599
+ driver.configure("write_operation upsert\n")
600
+ stub_elastic_ping
601
+ stub_elastic
602
+ driver.emit(sample_record)
603
+ driver.run
604
+ assert_nil(index_cmds)
605
+ end
606
+
607
+ def test_create_should_not_write_if_theres_no_id
608
+ driver.configure("write_operation create\n")
609
+ stub_elastic_ping
610
+ stub_elastic
611
+ driver.emit(sample_record)
612
+ driver.run
613
+ assert_nil(index_cmds)
614
+ end
615
+
616
+ def test_update_should_write_update_op_and_doc_as_upsert_is_false
617
+ driver.configure("write_operation update
618
+ id_key request_id")
619
+ stub_elastic_ping
620
+ stub_elastic
621
+ driver.emit(sample_record)
622
+ driver.run
623
+ assert(index_cmds[0].has_key?("update"))
624
+ assert(!index_cmds[1]["doc_as_upsert"])
625
+ end
626
+
627
+ def test_upsert_should_write_update_op_and_doc_as_upsert_is_true
628
+ driver.configure("write_operation upsert
629
+ id_key request_id")
630
+ stub_elastic_ping
631
+ stub_elastic
632
+ driver.emit(sample_record)
633
+ driver.run
634
+ assert(index_cmds[0].has_key?("update"))
635
+ assert(index_cmds[1]["doc_as_upsert"])
636
+ end
637
+
638
+ def test_create_should_write_create_op
639
+ driver.configure("write_operation create
640
+ id_key request_id")
641
+ stub_elastic_ping
642
+ stub_elastic
643
+ driver.emit(sample_record)
644
+ driver.run
645
+ assert(index_cmds[0].has_key?("create"))
646
+ end
647
+ end
@@ -0,0 +1,23 @@
1
+ {
2
+ "template": "te*",
3
+ "settings": {
4
+ "number_of_shards": 1
5
+ },
6
+ "mappings": {
7
+ "type1": {
8
+ "_source": {
9
+ "enabled": false
10
+ },
11
+ "properties": {
12
+ "host_name": {
13
+ "type": "string",
14
+ "index": "not_analyzed"
15
+ },
16
+ "created_at": {
17
+ "type": "date",
18
+ "format": "EEE MMM dd HH:mm:ss Z YYYY"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }