Avro中带有Morphlines的子记录

时间:2015-03-25 14:06:50

标签: json flume avro

我尝试使用kite-sdk morphline模块将JSON转换为Avro。在玩完之后,我可以使用简单的模式(没有复杂的数据类型)将JSON转换为Avro。

然后我更进了一步并修改了Avro架构,如下所示(subrec.avsc)。如您所见,模式由子记录组成。

一旦我尝试使用morphlines.conf和subrec.avsc将JSON转换为Avro,它就失败了。

以某种方式,toAvro函数不会转换JSON路径"/record_type[]/alert/action"

morphlines.conf

morphlines : [
   {
   id : morphline1
   importCommands : ["org.kitesdk.**"]

   commands : [
      # Read the JSON blob
      { readJson: {} }

      { logError { format : "record: {}", args : ["@{}"] } }

      # Extract JSON
      { extractJsonPaths { flatten: false, paths: {
              "/record_type[]/alert/action" : /alert/action,
              "/record_type[]/alert/signature_id" : /alert/signature_id,
              "/record_type[]/alert/signature" : /alert/signature,
              "/record_type[]/alert/category" : /alert/category,
              "/record_type[]/alert/severity" : /alert/severity
      } } }

      { logError { format : "EXTRACTED THIS : {}", args : ["@{}"] } }

      { extractJsonPaths { flatten: false, paths: {
              timestamp : /timestamp,
              event_type : /event_type,
              source_ip : /src_ip,
              source_port : /src_port,
              destination_ip : /dest_ip,
              destination_port : /dest_port,
              protocol : /proto,
      } } }

      # Create Avro according to schema
      { logError { format : "WE GO TO AVRO"} }

      { toAvro { schemaFile : /etc/flume/conf/conf.empty/subrec.avsc } }

      # Create Avro container
      { logError { format : "WE GO TO BINARY"} }
      { writeAvroToByteArray { format: containerlessBinary } }

      { logError { format : "DONE!!!"} }
   ]
   }
]

subrec.avsc

{
  "type" : "record",
  "name" : "Event",
  "fields" : [ {
    "name" : "timestamp",
    "type" : "string"
  }, {
    "name" : "event_type",
    "type" : "string"
  }, {
    "name" : "source_ip",
    "type" : "string"
  }, {
    "name" : "source_port",
    "type" : "int"
  }, {
    "name" : "destination_ip",
    "type" : "string"
  }, {
    "name" : "destination_port",
    "type" : "int"
  }, {
    "name" : "protocol",
    "type" : "string"
  }, {
    "name": "record_type",
    "type" : ["null", {
      "name" : "alert",
      "type" : "record",
      "fields" : [ {
            "name" : "action",
            "type" : "string"
        }, {
            "name" : "signature_id",
            "type" : "int"
        }, {
            "name" : "signature",
            "type" : "string"
        }, {
            "name" : "category",
            "type" : "string"
        }, {
            "name" : "severity",
            "type" : "int"
        }
      ] } ]
  } ]
}

{ logError { format : "EXTRACTED THIS : {}", args : ["@{}"] } }上的输出我输出以下内容:

[{
    /record_type[]/alert / action = [allowed], 
    /record_type[]/alert / category = [],
    /record_type[]/alert / severity = [3],
    /record_type[]/alert / signature = [GeoIP from NL,
    Netherlands],
    /record_type[]/alert / signature_id = [88006],
    _attachment_body = [{
            "timestamp": "2015-03-23T07:42:01.303046",
            "event_type": "alert",
            "src_ip": "1.1.1.1",
            "src_port": 18192,
            "dest_ip": "46.231.41.166",
            "dest_port": 62004,
            "proto": "TCP",
            "alert": {
                "action": "allowed",
                "gid": "1",
                "signature_id": "88006",
                "rev": "1",
                "signature" : "GeoIP from NL, Netherlands ",
                "category" : ""
                "severity" : "3"
                }
            }], 
    _attachment_mimetype=[json/java + memory],
    basename = [simple_eve.json]
}]

1 个答案:

答案 0 :(得分:1)

更新2017-06-22

您必须使用addValues或setValues填充结构中的数据才能使其正常工作

{
    addValues {
        micDefaultHeader : [
            {
                eventTimestampString : "2017-06-22 18:18:36"
            }
        ]
    }
}

在调整morphline toAvro的源代码之后,无论你在映射结构中放置什么,看起来该记录是第一个要评估的对象。

解决方案非常简单,但不幸的是,花了一点时间,eclipse,在调试模式下运行水槽代理,克隆源代码和大量咖啡。

在这里。

我的架构:

{
  "type" : "record",
  "name" : "co_lowbalance_event",
  "namespace" : "co.tigo.billing.cboss.lowBalance",
  "fields" : [ {
    "name" : "dummyValue",
    "type" : "string",
    "default" : "dummy"
  }, {
    "name" : "micDefaultHeader",
    "type" : {
      "type" : "record",
      "name" : "mic_default_header_v_1_0",
      "namespace" : "com.millicom.schemas.root.struct",
      "doc" : "standard millicom header definition",
      "fields" : [ {
        "name" : "eventTimestampString",
        "type" : "string",
        "default" : "12345678910"
      } ]
    }
  } ]
}

morphlines文件:

morphlines : [
        {
                id : convertJsonToAvro
                importCommands : ["org.kitesdk.**"]
                commands : [
                        {
                                readJson {
                                        outputClass : java.util.Map
                                }
                        }

                        {
                                addValues {
                                   micDefaultHeader : [{}]
                                }
                        }


                        {
                                logDebug { format : "my record: {}", args : ["@{}"] } 
                        }


                        {
                                toAvro {
                                        schemaFile : /home/asarubbi/Development/test/co_lowbalance_event.avsc
                                        mappings : {
                                                "micDefaultHeader" : micDefaultHeader
                                                "micDefaultHeader/eventTimestampString" : eventTimestampString
                                        }

                                }
                        }


                        {
                                writeAvroToByteArray {
                                        format : containerlessJSON
                                        codec : null
                                }
                        }
                ]
        }
]

魔法在于:

{
   addValues {
      micDefaultHeader : [{}]
   }
}

并在映射中:

mappings : {
    "micDefaultHeader" : micDefaultHeader
    "micDefaultHeader/eventTimestampString" : eventTimestampString
}

说明:

在代码中,评估的第一个字段名称是RECORD类型的micDefaultHeader。因为没有办法为RECORD指定一个默认值(逻辑上正确),toAvro代码会对此进行评估,不会在映射中配置任何值,因此它失败时会检测到(错误地)该记录为空时它应该是'吨。

然而,看一下代码,您可能会发现它需要一个Map对象,不包含任何值以取悦解析器并继续下一个元素。

所以我们使用addValues添加一个地图对象,并用空地图[{}]填充它。请注意,这必须与导致您为空值的记录的名称匹配。在我的情况下“micDefaultHeader”

如果您有更好的解决方案,请随时发表评论,因为这看起来像是“脏修复”