学习正则表达式之nginx日志分析

        转眼间进入csdn已经四年了,白嫖了四年的资源,不过依旧是一个小白,哈哈哈.....废话不多说正题开始。

        在学习正则表达式之前,咱们先来了解一下,正则表达式是做什么的,度娘问一下。

正则表达式regex 或者 regexp[1] (有时称为有理表达式)[2][3]是定义搜索模式的字符序列。通常这种模式通过字符串搜索算法用于字符串上的“查找”或“查找并替换”操作,或者用于输入验证。它是在理论计算机科学和形式语言理论中发展起来的一种技术。

        总结一下:用来字符串搜索的算法,用于字符串上的“查找”或“查找并替换”操作。

        前些天,小编的一个朋友问了一个问题:如何解析nginx日志文件,并存入数据库中,随后我立即查看了一下nginx日志,发现这根本没有啥相通之处,不能用平常的字符串截取的方法进行截取,随后赶紧去找度娘查了一下,然后左看右看,发现正则表达式可以进行一个拆分,但是由于很久没有用正则表达式了,全给搞忘了,大佬们写的表达式拆开还可以认识字符,合起来就是认不得,于是小编就开始研究起来了。在长达好几个小时的研究下,终于简单认识了正则表达式,以下简单介绍一下。本节所有代码全是java代码。

        单个字符查找:String regx = "a";

        字符串查找:String regx = "akff";        

特殊字符查找
换行符\n
换页符\f
回车符\r
空白符\s
制表符\t

具体用法,小编在这就不细细书写了

匹配区间进行模糊查找

写法1写法2含义
[0-9]\d匹配0到9的数字
\D(大写)匹配非0到9的数字
[A-Za-z0-9]\w匹配常用所有的字符
\W(大写)匹配非常用所有的字符
\s匹配空白字符
\S(大写)匹配所有非空白字符

简单的代码测试,各位同学慢慢看

public class RegxTest2 {
    public static void main(String[] args) {
        String regx_d = "\\d";
        String regx_D = "\\D";
        String regx_w = "\\w";
        String regx_W = "\\W";
        String regx_s = "\\s";
        String regx_S = "\\S";

        String str = "asdfas()((&*9afsd7()*)fas80..  asdf";
        Pattern pattern = Pattern.compile(regx_d);
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
        System.out.println("----------这是分割线-----------");
        pattern = Pattern.compile(regx_D);
        matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
        System.out.println("----------这是分割线-----------");
        pattern = Pattern.compile(regx_w);
        matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
        System.out.println("----------这是分割线-----------");
        pattern = Pattern.compile(regx_W);
        matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
        System.out.println("----------这是分割线-----------");
        pattern = Pattern.compile(regx_s);
        matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
        System.out.println("----------这是分割线-----------");
        pattern = Pattern.compile(regx_S);
        matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
        System.out.println("----------这是分割线-----------");
    }
}

以上都是匹配单个字符的通用匹配符,接下来是多字符匹配区间写法

写法含义
?匹配 0个或1个
*匹配大于等于0个字符
+匹配大于等于1个

还有一种特殊的写法,利用大括号{  },完全的写法{min,max}

写法

含义

{x}

特定的匹配x次
{min,}至少min次
{min,max}介于min到max次之间
{0,max}至多max次

 以上的匹配都是书写于需要匹配的字符或字符串后

 例如:查找字符串中所有连续的数字

String regx_d = "\\d*";
String str = "asfasdfasd324432asd233";
Pattern pattern = Pattern.compile(regx_d);
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
    if (!"".equals(matcher.group())) {
        System.out.println(matcher.group());
    }
}

 例如:查找字符串中所有连续的常用字符

String regx_d = "\\w*";

String str = "asfasdfasd32443))2asd23**3";
Pattern pattern = Pattern.compile(regx_d);
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
    if (!"".equals(matcher.group())) {
        System.out.println(matcher.group());
    }
}

或者利用{}表达式

String regx_d = "\\w{1,}";

其余部分小编不再演示,最后最重要的分组部分

如果需要匹配多个字符串类型,则需要用()将需要匹配的部分放入到括号里面

例如:

String regx_d = "(\\d*.\\d*)\\s([a-z]*.[a-z]*)";

这个里面分了两组,第一组是查询111.11111这种数字类型的数据,然后第二组是查询sds.sda这种小写字母类型的数据,输出的时候使用以下方式进行输出,如果是group(0)输出查到的完整的第一个数据(122.2312 www.ccc)

System.out.println(matcher.group(1));//输出查找到的第一个匹配模式中第一个括号内的数据

System.out.println(matcher.group(2));//输出查找到的第一个匹配模式中第二个括号内的数据

String regx_d = "(\\d*.\\d*)\\s([a-z]*.[a-z]*)";
String str = "122.2312 www.ccc 11223.12312 fafas.gdsdg";
Pattern pattern = Pattern.compile(regx_d);
Matcher matcher = pattern.matcher(str);
matcher.find();
System.out.println(matcher.group(0));//输出查到的完整的第一个数据(122.2312 www.ccc)
System.out.println(matcher.group(1));//输出查到的完整的第一个数据的第一个匹配括号的内容(122.2312)
System.out.println(matcher.group(2));输出查到的完整的第一个数据的第二个匹配括号的内容(www.ccc)

为什么不进行全部查找呢,这个小编就没有细细的去分析了,如果需要输出全部结果,则需循环查找,因为matcher.find()只要找到第一个匹配成功之后就会停止,如需继续匹配则需循环匹配

while (matcher.find()) 

在学完以上的匹配规则之后,接下来就是我们的重头戏,nginx日志匹配,首先我们来观察一下一般的nginx日志

127.0.0.1 - - [26/Mar/2022:22:32:13 +0800] "GET /eduservice/teacher/info?token=undefined HTTP/1.1" 200 101 "http://localhost:9528/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.46"

是不是看完之后,开始吐槽了,小编看完之后也和你是一样的心情

我们开始简单的分析,首先是ip:127.0.0.1   众所周知ip一般都是固定的模式,我们就可以得出匹配规则,这里面的“.”表示匹配一个普通的字符

String nginxRegex1 = "(\\d+.\\d+.\\d+.\\d+)"

接下来是访问时间: [26/Mar/2022:22:32:13 +0800] ,这里面是不是也是固定的格式,格式化的日期和一个时区,是不是也是固定的搭配,根据这搭配进行编写规则即可,小编使用的更简单的一种匹配规则,利用中括号,管他里面是啥,只要匹配中括号即可,这里面的“.”表示所有的字符都可以匹配

String nginxRegex5 = "(\\[.*\\])";

接下来的是请求方式,资源参数以及请求协议:"GET /test/test/test?token=safas HTTP/1.1",基本上也是一些固定的格式,在这里小编偷偷懒,没有具体的去区别,就把这块的内容用引号区别,但是,后面的请求根路径以及客户端信息浏览器信息也是用双引号引起来的,这个时候,我就直接用的引号来截取数据,然后在进行一个分别赋值,这里的“.”也表示通配符,“?”表示一次匹配一个,也可用大括号

String nginxRegex4 = "(\".*?\")";

接下来是响应状态码以及请求时间(毫秒)200 101,因为状态码市固定的三位数,响应时间不固定,但至少都是大于0的,所以使用“+”而不用“*”

String nginxRegex3 = "(\\d{3}\\s\\d+)";

最后是代码,因为小编为了更好的能够区别每一块的内容,就进行了分别查找,就没有统一查找,因为只是简单的实现,并没有存入数据库,如有存入数据库需要,则用一个list进行保存,然后批量插入数据库

接下来就是全部代码

public class RegxTest {
    public static void main(String[] args) {
        String nginxLog ;
        try {
            File file = new File("日志文件路径");
            FileInputStream fis = new FileInputStream(file);
            //Construct BufferedReader from InputStreamReader
            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
            String line = null;
            while ((line = br.readLine()) != null) {
                nginxLog = line;
                //匹配查询ip地址
                String nginxRegex1 = "(\\d+.\\d+.\\d+.\\d+)";
                //匹配查询状态码以及响应时间
                String nginxRegex3 = "(\\d{3}\\s\\d+)";
                //匹配所有的双引号信息
                String nginxRegex4 = "(\".*?\")";
                //匹配以[号开头的]号结尾的信息
                String nginxRegex5 = "(\\[.*\\])";
                //为了区别,分开编译
                Pattern pattern1 = Pattern.compile(nginxRegex1);
                Pattern pattern3 = Pattern.compile(nginxRegex3);
                Pattern pattern4 = Pattern.compile(nginxRegex4);
                Pattern pattern5 = Pattern.compile(nginxRegex5);

                Matcher matcher1 = pattern1.matcher(nginxLog);
                Matcher matcher3 = pattern3.matcher(nginxLog);
                Matcher matcher4 = pattern4.matcher(nginxLog);
                Matcher matcher5 = pattern5.matcher(nginxLog);

                try {
                    matcher1.find();
                    matcher3.find();
                    matcher5.find();
                    NginxAccessLog nginxLog1 = new NginxAccessLog();
                    nginxLog1.setIp(matcher1.group(0));
                    nginxLog1.setTimeZone(matcher5.group(0));
                    nginxLog1.setStateResponseTime(matcher3.group(0));
                    int flag = 0;
                    while (matcher4.find()) {
                        flag++;
                        if (flag == 1) {
                            nginxLog1.setMethodPath(matcher4.group(0));
                        } else if (flag == 2) {
                            nginxLog1.setUrl(matcher4.group(0));
                        } else {
                            nginxLog1.setUserAgent(matcher4.group(0));
                        }
                    }
                    System.out.println(nginxLog1);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

以下是存储日志实体类

@Data
public class NginxAccessLog {
    //    日志访问ip
    private String ip;
    //  访问时间以及时区
    private String timeZone;
    //    请求的方式,资源路径及参数
    private String methodPath;
    //    状态码,及响应时间
    private String stateResponseTime;
    //    请求根url
    private String url;
}

这是小编第一次写文章,如有写的不好的地方请给予指正,私信给小编喔,小编会尽快给予答复,以后小编也会不定期的更新各种小知识,如果你遇到不会的,可以私信小编,一起讨论学习,如有版权等问题请私信小编。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
filebeat中的正则表达式可以用于对日志文件进行过滤和解析。filebeat支持多种正则表达式语法,例如PCRE、Golang和Oniguruma等。在filebeat中,常见的正则表达式应用包括: 1. 在输入文件路径中使用正则表达式,以匹配多个文件; 2. 在filebeat配置文件中使用正则表达式,以从日志中提取特定字段; 3. 在filebeat处理器中使用正则表达式,以将字段重命名或删除。 下面是一个使用正则表达式Nginx日志中提取字段的示例: ``` # filebeat.yml filebeat.inputs: - type: log paths: - /var/log/nginx/access.log processors: - dissect: tokenizer: "%{[@metadata][nginx][access]}" field: "message" target_prefix: "nginx.access" - grok: match: message: '%{IPORHOST:[nginx][access][remote_ip]} - %{DATA:[nginx][access][user_name]} \[%{HTTPDATE:[nginx][access][time]}\] "%{WORD:[nginx][access][http_method]} %{DATA:[nginx][access][http_version]}" %{NUMBER:[nginx][access][http_response_code]} %{NUMBER:[nginx][access][body_sent][bytes]} "%{DATA:[nginx][access][referrer]}" "%{DATA:[nginx][access][agent]}"' - date: match_formats: ["dd/MMM/YYYY:H:m:s Z"] target_field: "@timestamp" source_field: "[nginx][access][time]" fields_under_root: true fields: type: nginx output.elasticsearch: hosts: ["localhost:9200"] ``` 以上配置文件中使用了grok处理器,其中的正则表达式可以从Nginx日志中提取remote_ip、user_name、time、http_method、http_version、http_response_code、body_sent、referrer和agent等字段。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值