[ 
https://issues.apache.org/jira/browse/FLUME-2787?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14940488#comment-14940488
 ] 

Tianji Li commented on FLUME-2787:
----------------------------------

We experienced similar issues in our work and figured out a way to fix it. 

In essence, ElasticSearch is not really schema less, because the underlying 
Lucene requires strict schemas. The schema-less of ElasticSearch is achieved by 
it guessing what data format should be used when data is coming in. But the 
guessing work sometimes fails, and fails silently, leaving people pulling lots 
of hair off to understand.

To resolve this guessing work therefore is to explicitly tell ElasticSearch 
what your schema is, before sending any data in.

There are two ways to achieve this.

First, using the index templates provided by ElasticSearch 
(https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html).
 The idea of these templates is that one can set mappings (mapping means schema 
in the ElasticSearch terms) for many indices before using them. This is not a 
good option when using Flume and ElasticSearch at the same time. The reason is 
Flume automately rotates the indices on a daily basis (which is a good thing 
when indices are huge). To use the templates, one has to keep the rotation in 
mind al the time, and change the templates at good timing.

Second, we prefer therefore to let Flume set the mappings/schemas when needed. 
Again due to Flume's index rotation, setting mappings should be done in 
EventSerializers. I implemented this as in the attached patch. 

Now, let me give the results.

If not using agent.sinks.elastic-sink.serializer = 
org.apache.flume.sink.elasticsearch.ElasticSearchDynamicSerializer, as 
mentioned by [~goi.cto] above, depending on version of ElasticSearch used, the 
actual mapping can vary. For example, using ElasticSearch 1.7.1, the latest 
version, the field names I got are not 'body' and 'timestamp', but are shown 
below (Please check the details in the noserializer file in the patch.):
{
   "noserializer-2015-10-01": {
      "mappings": {
         "logs": {
            "properties": {
               "@fields": {
                  "properties": {
                     "timestamp": {
                        "type": "string"
                     }
                  }
               },
               "@message": {
                  "type": "string"
               },
               "@timestamp": {
                  "type": "date",
                  "format": "dateOptionalTime"
               }
            }
         }
      }
   }
}

If using agent.sinks.elastic-sink.serializer = 
org.apache.flume.sink.elasticsearch.ElasticSearchDynamicSerializer, again as 
[~goi.cto] said, the 'body' field is a string, rather than a 'date_time'. 
Please check the details in the dynamic.conf file in the patch.
{
   "dynamic-2015-10-01": {
      "mappings": {
         "logs": {
            "properties": {
               "body": {
                  "type": "string"
               },
               "timestamp": {
                  "type": "string"
               }
            }
         }
      }
   }
}

If using the manualSerializer I implemented, the mapping will be below. See the 
manual.conf in the patch for how to use it.
{
   "manual-2015-10-01": {
      "mappings": {
         "logs": {
            "_timestamp": {
               "enabled": true,
               "store": true,
               "path": "timestamp",
               "format": "date_time",
               "default": "1970-01-01T00:00:00.000Z"
            },
            "_index": {
               "enabled": true
            },
            "properties": {
               "body": {
                  "type": "string",
                  "index": "not_analyzed",
                  "doc_values": true
               },
               "timestamp": {
                  "type": "date",
                  "format": "dateOptionalTime"
               }
            }
         }
      }
   }
} 

In our work, and in many other use cases, manual mapping is needed on string 
field such as people full names. Say we index FirstName and LastName in a same 
field, and do a terms aggregation which supports to get distinct full names. If 
we use the org.apache.flume.sink.elasticsearch.ElasticSearchDynamicSerializer, 
the field will be 'analyzed' by default which means ElasticSearch will index 
firstName and lastName separately. For example, if we index:
  New York
  New York
  New York
  New Mexico
  New Jersey
in a field that is analyzed, then this term aggregation
{
    "size" : 0,
    "aggs" : {
        "bodyTerms" : {
            "terms" : {
                "field" : "body"
            }
        }
    }
}

will give us:
{
   "took": 1,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 5,
      "max_score": 0,
      "hits": []
   },
   "aggregations": {
      "bodyTerms": {
         "doc_count_error_upper_bound": 0,
         "sum_other_doc_count": 0,
         "buckets": [
            {
               "key": "new",
               "doc_count": 5
            },
            {
               "key": "york",
               "doc_count": 3
            },
            {
               "key": "jersey",
               "doc_count": 1
            },
            {
               "key": "mexico",
               "doc_count": 1
            }
         ]
      }
   }
}


To resolve this, we have to change the field to be 'not_analyzed' which will 
give us (using the 
org.apache.flume.sink.elasticsearch.ElasticSearchManualSerializer in the patch):
{
   "took": 2,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 5,
      "max_score": 0,
      "hits": []
   },
   "aggregations": {
      "bodyTerms": {
         "doc_count_error_upper_bound": 0,
         "sum_other_doc_count": 0,
         "buckets": [
            {
               "key": "New York",
               "doc_count": 3
            },
            {
               "key": "New Jersey",
               "doc_count": 1
            },
            {
               "key": "New Mexico",
               "doc_count": 1
            }
         ]
      }
   }
}



> org.apache.flume.sink.elasticsearch.ElasticSearchDynamicSerializer does not 
> serialize @timestamp correctly
> ----------------------------------------------------------------------------------------------------------
>
>                 Key: FLUME-2787
>                 URL: https://issues.apache.org/jira/browse/FLUME-2787
>             Project: Flume
>          Issue Type: Bug
>          Components: Sinks+Sources
>            Reporter: Eran W
>            Priority: Minor
>         Attachments: FLUME-2787.patch
>
>
> When using 
> agent.sinks.elastic-sink.serializer = 
> org.apache.flume.sink.elasticsearch.ElasticSearchDynamicSerializer
> the event timestamp is stored as string and not dateOptionalTime if the 
> agent.sinks.elastic-sink.serializer is not set the code works as expected.
> REPRO:
> 1) use the following config
> agent.channels.channel-elastic.type = memory
> agent.channels.channel-elastic.capacity = 1000
> agent.channels.channel-elastic.transactionCapacity = 100
> # Define a source on agent and connect to channel.
> agent.sources.tail-source.type = exec
> agent.sources.tail-source.command = tail -4000 /home/cto/hs_err_pid11679.log
> agent.sources.tail-source.channels = channel-elastic
> #####INTERCEPTORS
> agent.sources.tail-source.interceptors = timestampInterceptor
> agent.sources.tail-source.interceptors.timestampInterceptor.type = 
> org.apache.flume.interceptor.TimestampInterceptor$Builder
> agent.sources.tail-source.interceptors.timestampInterceptor.preserveExisting 
> = true
> agent.sinks.elastic-sink.channel = channel-elastic
> agent.sinks.elastic-sink.type = 
> org.apache.flume.sink.elasticsearch.ElasticSearchSink
> agent.sinks.elastic-sink.hostNames = 127.0.0.1:9300
> agent.sinks.elastic-sink.indexName = flume_index
> agent.sinks.elastic-sink.indexType = logs_type
> agent.sinks.elastic-sink.clusterName = elasticsearch
> agent.sinks.elastic-sink.batchSize = 10
> agent.sinks.elastic-sink.ttl = 5d
> agent.sinks.elastic-sink.serializer = 
> org.apache.flume.sink.elasticsearch.ElasticSearchDynamicSerializer
> # Finally, activate.
> agent.channels = channel-elastic
> agent.sources = tail-source
> agent.sinks =  elastic-sink
> 2) run it
> 3) look at the elastic index which was created:
> {
> "state": "open",
> "settings": {
> "index": {
> "creation_date": "1441728286466",
> "number_of_shards": "5",
> "number_of_replicas": "1",
> "version": {
> "created": "1070199"
> },
> "uuid": "9u-OCPxoQHWwURHyxh15lA"
> }
> },
> "mappings": {
> "logs_type": {
> "properties": {
> "body": {
> "type": "string"
> },
> "timestamp": {
> "type": "string"
> }
> }
> }
> },
> "aliases": [ ]
> }



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to