美文网首页Nginx高端成长之路学习小殿玩转大数据
通过Hive及其Udf函数进行Nginx日志分析

通过Hive及其Udf函数进行Nginx日志分析

作者: 司小幽 | 来源:发表于2017-08-10 13:05 被阅读63次

    需求

    nginx日志格式:

    '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
    

    (暂且不将$remote_addr与$remote_user之间的-看做一个字段,所以一共有9个字段)

    举个栗子:

    180.173.250.74 - - [08/Jan/2015:12:38:08 +0800] "GET /avatar/xxx.png HTTP/1.1" 200 968 "https://www.iteblog.com/archives/994" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36" "180.173.250.01"
    (不同字段之间以空格分隔)

    具体需求

    将nginx的日志按照nginx.conf中定义的字段解析至hive中,偏向于用udf实现

    环境

    Hadoop2.6.3三节点(2个slave)
    Hive-1.2.1
    MySQL-5.1.73

    实现

    方案一(非Udf)

    思路

    用Hive分析nginx日志

    反思

    上述方法是通过hive自带的RegexSerDe(需要在hive中加入hive-contrib-.jar*,类似下文添加Udf jar包的方式),通常情况下我们会通过

    ROW FORMAT DELIMITED FIELDS TERMINATED BY  '某个分隔符'
    

    默认情况下,Hive的分隔符是'\u0001',同时似乎在默认情况下,如果指定多个分隔符,比如'###',hive在实际使用中并不支持。

    详情请见:
    hive分隔符支持多个字符吗?
    而这种方案使用的RegexSerDe,我的理解就是一个高逼格的,不是原先单一的,同时较原先复杂的,在某种意义上也可以理解为多分隔符的正则实现

    方案二(Udf)

    思路

    建三张表:
    nginx_origin:只有一个String类型的content字段,用它去关联Hdfs上的nginx原始日志,字段值即为那举个栗子中的内容
    建表语句

    CREATE EXTERNAL TABLE nginx_origin(content string) location 'Hdfs上的nginx日志所在目录的路径';
    

    nginx_temp:
    表结构和nginx_origin一样,但是字段里的值为经过Udf函数处理过后,结构化后的值。

    建表语句
    create table nginx_temp STORED AS TEXTFILE as select structed(*) from nginx_origin;

    Udf函数:

    public class StructedNginxLog extends UDF {
        //通过正则将nginx日志转换为String类型数组
        public String[] structedNginxLog(String logEntryLine){
            Pattern pattern = Pattern.compile(
                    "(\\d+\\.\\d+\\.\\d+\\.\\d+)\\s\\W\\s(.*)\\s(\\[[^\\[\\]]+\\])\\s(\".*\")\\s(\\d{3})\\s(\\d+)\\s(\".*\")\\s(\".*\")\\s(\".*\")");
            Matcher matcher = pattern.matcher(logEntryLine);
            if(matcher.groupCount()==9){
               
            String[] logArray = new String[9];
            while (matcher.find()) {
                for (int i = 1; i <= logArray.length; i++) {
                    logArray[i-1] = matcher.group(i);
                }
            }
            return logArray;
            }else{
                return null;
            }
        }
        
    //将结构化在数组中的各字段值重新拼接成temp表里的字段值
        public String evaluate(String logEntryLine){
            String[] logArray = structedNginxLog(logEntryLine);
            if(logArray==null){
                return null;
            }
            else{
            StringBuilder constructLogBuilder = new StringBuilder();
            for(int i = 0 ;i<logArray.length;i++){
              if(i<logArray.length-1){
                constructLogBuilder.append(logArray[i]+"\u0002");//需要注意分隔符
              }
              else{
                  constructLogBuilder.append(logArray[i]);
              }
                 
            }
            return constructLogBuilder.toString();
            }
        }
    }
    

    Hive添加jar包:
    ①临时生效:add jar {udf jar包路径}
    ②永久生效:

    修改hive-env.sh,修改最后一行的#export HIVE_AUX_JARS_PATH=exportHIVE_AUX_JARS_PATH=udf的jar文件目录路径来实现

    创建临时函数

    create temporary function structed as {继承自UDF的类名};
    

    nginx_push
    主要是在建该表时,指定好Udf结构化的那个分隔符,然后指向nginx_temp的hdfs目录即可
    建表语句

    create external table nginx_push
    (remote_addr STRING,remote_user STRING,
     time_local  STRING, request STRING, 
    status STRING, body_bytes_sent STRING,
     http_referer STRING, http_user_agent STRING,
     http_x_forwarded_for STRING)
     ROW FORMAT DELIMITED FIELDS TERMINATED BY '\u0002' STORED AS TEXTFILE 
    location 'nginx temp表的hdfs目录路径';
    

    反思

    起初是将字段之间的分隔符,通过'\u0001'定义,但是后来发现,Hive默认的分隔符就是这个,所以如果指定这个的话,temp表中的那个唯一的字段的值就仅仅是原来一长串中的开头——IP地址,后来通过将分隔符修改为\u0002来解决,也想过用'|',但是尽量越不常见越好。

    相关文章

      网友评论

        本文标题:通过Hive及其Udf函数进行Nginx日志分析

        本文链接:https://www.haomeiwen.com/subject/gwchrxtx.html