Scala解析Nginx日志为对象

/**
  * Nginx日志数据转换类
  */
object NginxLogParser{
  /**
    * 解析正则表达式
    * .r用于指明PARTTERN是一个正则表达式对象
    * 9个值:客户端访问IP、用户标识clientIdentd、用户userId、访问时间dateTime、请求方式mode、请求状态responseCode、返回文件的大小contentSize、跳转来源referrer、UA信息
    */
  val PATTERN =
    """(\S+) (\S+) (\S+) (\[.*\]) (\".*\") (\d{3}) (\d+) (\".*?\") (\".*?\")""".r

  def parseLog2Line(log: String): AccessLog = {
      def makeWifiLogs(): AccessLog = {
        new AccessLog("", "", "", "", "", 0, 0, "", "")
      }

      if (log.isEmpty) {
        val logs = PATTERN.findFirstMatchIn(log)
        if (logs.isEmpty) {
          throw new RuntimeException("Cannot parse log line: " + log)
        }
        val m = logs.get
        new AccessLog(m.group(1), m.group(2), m.group(3), m.group(4),m.group(5), m.group(6).toInt,
        m.group(7).toLong, m.group(8), m.group(9))
      }else{
      makeWifiLogs()
    }
  }

  def main(args: Array[String]) {
    val line = """111.128.69.30 - - [11/Sep/2018:00:01:00 +0800] "GET /api/getinfo.php?tid=1e35c7b357cd&rid=059e15c97800&gw=gaoke.com&did=2424826&sn=GAOKE_Q330&action=run HTTP/1.1" 200 315 "http://gaoke.com:8848/wx.html?tid=1e35c7b357cd&rid=059e15c97800&gw=gaoke.com&did=2424826&sn=GAOKE_Q330" "Mozilla/5.0 (Linux; Android 6.0; S9 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36" "-""""

    val log = NginxLogParser.parseLog2Line(line);
    println(log.ip)
    println(log.clientIdent)
    println(log.userId)
    println(log.timestamp)
    println(log.request)
    println(log.responseCode)
    println(log.contentSize)
    println(log.referrer)
  }
}
/**
  * 日志文件对象
  */
case class AccessLog(
  ip: String,            //设备用户的真实ip地址
  clientIdent: String,   //用户标识
  userId: String,        //用户
  timestamp: String,     //访问日期时间
  request: String,       //请求信息,get/post,mac值等
  responseCode: Int,     //请求状态 200成功,304静态加载
  contentSize: Long,     //返回文件的大小
  referrer: String,      //跳转来源
  ua: String             //UA信息
//  forward:String //跳转页面

) extends Serializable {
  override def toString: String = ip+","+clientIdent+","+userId+","+timestamp+","+request+","+responseCode+","+contentSize+","+referrer+","+ua
}

 

## 1、数据处理 #### (1)定义样例类 数据源准备完毕后,就可以编写 Flink 程序连接 Kafka,消费其中的日志数据了。为了在程序中便捷地处理日志信息,首先定义样例类,用于表示日志信息,代码如下: ```scala case class NginxLog( ip: String, // 客户端 IP 地址 timestamp: Long, // 日志时间戳 method: String, // 请求方法 url: String, // 请求的 URL status: Int, // 请求的 HTTP 状态码 userAgent: String, // 客户端的 User-Agent referer: String // 客户端的 Referer ) ``` #### (2)解析日志(程序入口对象) 为了后续能够分析日志中的各项指标,需要提取日志中的字段。接下来,定义解析日志的函数,代码如下: ```scala def parseLogLine(logLine: String): NginxLog = { val pattern = """^(\S+) - - \[(.+)\] "(\S+) (\S+) HTTP/1.1" (\d+) (\d+) "(.+)""(.+)"$""".r logLine match { case pattern(ip, timestamp, method, url, status, bytes, referer, userAgent) => val dateFormat = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.ENGLISH) val eventTime = dateFormat.parse(timestamp).getTime NginxLog(ip, eventTime, method, url, status.toInt, userAgent, referer) case _ => throw new RuntimeException(s"Cannot parse log line: $logLine") } } ``` 这段代码定义了一个 parseLogLine ()函数,它接收一个字符串类型的 Nginx 日志行,并将其解析为一个 NginxLog 类型的对象。具体实现如下: 首先,定义一个正则表达式模式 pattern,用于匹配 Nginx 日志行中的各个字段。模式包含多个用于匹配具体字段的分组,例如,(\S+)匹配一个或多个非空白字符,(.+)匹配一个或多个任意字符。 然后,使用 match 关键字对 logLine 进行模式匹配。如果 logLine 能够匹配 pattern,则将匹配结果中的各个字段提取出来,并使用 SimpleDateFormat 将时间戳字符串 timestamp 解析为时间戳数值 eventTime。最后,将提取出来的字段构造成一个 NginxLog 对象并返回。如果 logLine 无法匹配 pattern,则会抛出一个运行时异常,表示无法解析该行日志。 #### (3)生成水位线(程序入口对象-main方法) 这里连接 Kafka 消费数据,并在数据流中生成水位线,具体代码如下: ```scala val env = StreamExecutionEnvironment.getExecutionEnvironment env.setParallelism(1) val source: KafkaSource[String] = KafkaSource .builder() .setBootstrapServers("192.168.200.131:9092") .setTopics("nginx_logs") .setGroupId("test-group") .setStartingOffsets(OffsetsInitializer.earliest()) .setValueOnlyDeserializer(new SimpleStringSchema()) .build() // 使用 KafkaSource val kafkaStream: DataStream[String] = env.fromSource( source, WatermarkStrategy.noWatermarks(), "Kafka Source" ) val nginxLogs: DataStream[NginxLog] = kafkaStream .map(parseLogLine(_)) .assignTimestampsAndWatermarks( WatermarkStrategy //(log: NginxLog, _) => log.timestamp .forBoundedOutOfOrderness[NginxLog](Duration.ofSeconds(10)) .withTimestampAssigner(new SerializableTimestampAssigner[NginxLog] { override def extractTimestamp(element: NginxLog, recordTimestamp: Long): Long = element.timestamp }) ) ``` 上述代码使用 Flink Kafka Connector 中的 KafkaSource 从 Kafka Topic"nginx_logs"中读取数据。首先,使用 StreamExecutionEnvironment. getExecutionEnvironment 获取 Flink 执行环境。接着,创建 KafkaSource 实例,设置 Kafka Broker 地址、Topic 名称、消费者组 ID、反序列化器等属性。然后,使用 env. fromSource () 方法将 KafkaSource 转换为 DataStream,同时指定水印策略为 noWatermarks (),即不引入水印。随后,通过. map (parseLogLine (_)) 对 DataStream 中的符串格式的日志信息进行解析,转换为 NginxLog 样例类的对象。最后,使用 assignTimestamp sAndWatermarks ()方法指定水印策略,使用 forBoundedOutOfOrderness 指定延迟上界,即允许数据最多延迟 10s,并使用 extractTimestamp ()方法指定时间戳字段。
最新发布
06-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

訾零

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值