1

现有的需求是:从现有系统日志中采集部分数据,写入到MySQL中方便查询。

使用的版本为:Logstash-7.10.0,MySQL-5.7.22,jdk1.8

主要说明三个部分:

  • 数据来源:微服务生成的所有日志文件,为了方便跨服务器收集多份日志,引入另外一个工具 Filebeat(不用也可以)。
  • 数据筛选:特定格式的日志内容或带有某些标识的日志内容,规定日志内容以及日志格式,方便logstash filter做过滤等操作。
  • 写入数据:将logstash中处理的数据写入MySQL中,假设我们的表结构如下:
CREATE TABLE `logstash_log_temp` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `serve` char(16) NOT NULL COMMENT '服务名',
  `level` char(8) NOT NULL COMMENT '日志等级',
  `pid` int(8) NOT NULL COMMENT '进程号',
  `ip` int(16) NOT NULL COMMENT 'ip地址',
  `content` varchar(512) NOT NULL COMMENT '内容',
  `logdate` datetime NOT NULL COMMENT '时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

第一步:安装

官网下载后将Logstash-7.10.0.tar.gz放到需要的服务器上,解压:
tar -zxvf Logstash-7.10.0.tar.gz

进入到logstash-7.10.0/config,复制一个配置文件,并改名:
cp logstash-simple.conf logstash-mysql.conf

第二步:添加配置

第一个配置是jvm.optios,是用来添加jvm参数,这里不做过多说明。
第二个是logstash.yml文件,基本上不需要改动。
第三个logstash-mysql的配置是收集数据和写入数据的重点。
这个配置分为三部分

1. 数据来源:

logstash自己可以读取日志文件,这里我们通过Filebeat来收集日志数据。

input {
    
}

下面贴出部分logstash自己读取日志的配置

input {
  file {
    # 多个日志可以写成数组形式 path => ['a.log','b.log']
    path => '/data/dev/logs/monitor.log'
    # 从日志最后开始读取数据
    start_position => 'end'
    # 设置新的行分隔符,默认为“ \ n”。
    #delimiter => "\n"
    # 对多久之前的文件修改做忽略,单位秒
    ignore_older => 0
    # 设置最多打开文件量,此值没有默认值,但是存在一个内部限制4095
    #max_open_files => 4095
  }
}
2.数据处理

数据处理是从日志数据中找到我们需要的数据,并作一些方便我们处理的操作,主要是在filter模块中做处理,其中还有一些特殊的处理模式

1.其中 filter grok match 的匹配要严格对应日志格式,可以根据logstash现有提供的匹配正则来匹配日志格式,这里提供一个在线测试地址,效果如下:
image.png

下面是服务中logback的部分配置以及打印出的日志:

<property name="CONSOLE_LOG_PATTERN"
    value="%clr(MONITOR) ${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr([%ip]) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

注:其中加入了MONITOR%ip,其中MONITOR表示当前服务编码,此处可替换为自己服务的编码或名称(%ip自定义日志字段可参考文章)。

MONITOR 2021-03-01 12:51:00.000  INFO 20147 [172.16.26.62] --- [   scheduling-1] c.jason.monitor.schedule.ScheduledTask   : &MONITOR&={"modularName":"日志监控列表-info","operationType":"R","operationLogin":"1","operationUserName":"admin","operationRoleName":"管理员","tenantId":"tenant_system","operationResult":"1","logInfo":"OK"}

通过match后的数据可根据需求删掉一些不需要的字段:

    ### 以下去掉多个字段,
    remove_field => "beat.version"
    remove_field => "beat.name"
    ### 也可以使用数组形式,两种形式可同时存在,以下这些字段仅为减少logstash日志量
    remove_field => ["cloud","input","log","agent","host","ecs"]

:若存在多种日志格式,则配置多个match来匹配对应的日志;需要多少就写多少,当上一个match不匹配后会依次用下面的match做处理,如果都不符合,则这条数据会被丢弃。

match => ["message", "\[%{GREEDYDATA:serve}\]\[%{TIMESTAMP_ISO8601:logdate}\]\[%{LOGLEVEL:level}%{SPACE}\] %{GREEDYDATA:content}"]
match => ["message", "%{TIMESTAMP_ISO8601:logdate} %{SPACE}%{LOGLEVEL} %{POSINT} --- \[%{GREEDYDATA:thread}\] %{GREEDYDATA:path} : %{GREEDYDATA:content}"]

2.根据我们自己的需求,对数据做一些处理,我们排除掉DEBUG以及没有&MONITOR&=特殊标识的日志后,将服务名大写,将日志中的特殊字段做去除等操作后,就会得到日志内容中的json字符串,然后我们再将这部分数据做json转化,方便后续处理。最后再将我们日志中的时间做格式化。

filter {
  grok {
    match => {"message" => "%{WORD:serve} %{TIMESTAMP_ISO8601:logdate} %{LOGLEVEL:level} %{POSINT} \[%{IP:ip}\] --- \[%{GREEDYDATA}\] %{GREEDYDATA} : %{GREEDYDATA:content}"}

    ### 去掉 TIMESTAMP_ISO8601, GREEDYDATA 解析出来的多余字段
    remove_field => ["YEAR","MONTHNUM","MONTHDAY","HOUR","MINUTE","SECOND","SPACE","IPV4","IPV6","GREEDYDATA"]
  }

  ### DEBUG 级别的日志过滤掉
  if [level] == "DEBUG" {
    drop {}
  }

  ### content 中不包含这个字段的日志过滤掉
  if "&MONITOR&=" not in [content] {
    drop {}
  }

  mutate {
    ### 将 serve 中的值转化为大写
    uppercase => ["serve"]
    ### 将 pid 转化为 String 类型
    convert => {
         "pid" => "string"
    }
    ### 去掉 content 前后空格
    strip => ["content"]
    ### 将content中的&MONITOR&=替换为“”
    gsub => ["content", "&MONITOR&=", ""]
  }

  ### 将上一步中的 content 字段做 json 解析
  json {
    source => "content"
    ### 解析成json后将 IP 字段去掉
    remove_field => ["IP"]
  }

  date {
    ### 将时间格式化
    match => ["logdate", "yyyy-MM-dd HH:mm:ss.SSS Z"]
    timezone => "Asia/Shanghai"
    target => "logdate"
  }
}
3.写入MySQL

需要配置MySQL的信息,此处配置了一个库,如果有多个库,则配置多个jdbc模块。

output {
  jdbc {
    ### mysql driver path
    driver_jar_path => "/data/dev/server/lib/mysql-connector-java-5.1.46.jar"
    driver_class => "com.mysql.jdbc.Driver"
    ### connection
    connection_string => "jdbc:mysql://localhost:3306/monitor?useUnicode=true&characterEncoding=utf8&useSSL=false"
    username => "root"
    password => "clover"
    ### sql
    statement => ["INSERT INTO logstash_log_temp(serve, level, pid, ip, content, logdate) VALUES(?, ?, ? ,INET_ATON(?), ? ,?)", "[serve]", "[level]", "[pid]", "[ip]", "[content]", "[logdate]"]
  }
}

**注:**写入到数据库需要logstash-out-jdbc插件;但这个插件不在默认的插件列表中,所以我们需要手动的安装一下。

确保有网络的前提下,执行命令:
./logstash-plugin install logstash-output-jdbc

如果服务器没有网络环境,则可以在任何一台有网络,有java环境的电脑上创建一个插件,这里以windows为例:

  • 首先下载一个windows版本的Logstash-7.10.0
  • bin目录下执行命令 logstash-plugin install logstash-output-jdbc
  • 执行 logstash-plugin list,列表中有logstash-out-jdbc则安装成功
  • 将插件剥离打包命令 logtash-plugin prepare-offline-pack --overwrite --output logstash-output-jdbc.zip logstash-output-jdbc
  • 将生成的zip放到服务器上,并在服务器bin目录下执行./logstash-plugin install file:///data/dev/software/logstash-output-jdbc.zip
  • 执行logstash-plugin list检查安装情况。

第三步:启动

配置完所有步骤后,启动logstash
./logstash -f ../config/logstash-mysql.conf
检查数据库中数据

    • -

贴出完整logstash-mysql.conf内容如下:

input {
  beats {
    port => 5044
  }
}

filter {
  grok {
    match => {"message" => "%{WORD:serve} %{TIMESTAMP_ISO8601:logdate} %{LOGLEVEL:level} %{POSINT} \[%{IP:ip}\] --- \[%{GREEDYDATA}\] %{GREEDYDATA} : %{GREEDYDATA:content}"}

    remove_field => ["cloud","input","log","agent","host","ecs"]
    ### 去掉 TIMESTAMP_ISO8601, GREEDYDATA 解析出来的多余字段
    remove_field => ["YEAR","MONTHNUM","MONTHDAY","HOUR","MINUTE","SECOND","SPACE","IPV4","IPV6","GREEDYDATA"]
  }

  ### DEBUG 级别的日志过滤掉
  if [level] == "DEBUG" {
    drop {}
  }

  ### content 中不包含这个字段的日志过滤掉
  if "&MONITOR&=" not in [content] {
    drop {}
  }

  mutate {
    ### 将 serve 中的值转化为大写
    uppercase => ["serve"]
    ### 将 pid 转化为 String 类型
    #convert => {
    #     "pid" => "string"
    #}
    ### 将content中的&MONITOR&=替换为空
    gsub => ["content", "&MONITOR&=", ""]
    ### 去掉某个字段的前后空格,比如content
    #strip => ["content"]
  }

  ### 将上一步中的 content 字段做 json 解析
  json {
    source => "content"
  }

  date {
    ### 将时间格式化
    match => ["logdate", "yyyy-MM-dd HH:mm:ss.SSS Z"]
    timezone => "Asia/Shanghai"
    target => "logdate"
  }
}

output {
  ### 多个库则声明多个jdbc
  jdbc {
    ### mysql driver path
    driver_jar_path => "/data/dev/server/lib/mysql-connector-java-5.1.46.jar"
    driver_class => "com.mysql.jdbc.Driver"
    ### connection
    connection_string => "jdbc:mysql://localhsot:3306/monitor?useUnicode=true&characterEncoding=utf8&useSSL=false"
    username => "root"
    password => "clover"
    ### sql ip地址用INET_NTOA(`host_ip`)来获取
    statement => ["INSERT INTO logstash_log_temp(serve, level, pid, ip, content, logdate) VALUES(?, ?, ? ,INET_ATON(?), ? ,?)", "[serve]", "[level]", "[pid]", "[ip]", "[content]", "[logdate]"]
    ]
  }

  ### logstash数据转化成json格式写入日志中
  stdout {
    codec => json_lines
  }
}

虚惊一百场
19 声望7 粉丝

1 + 1 = 2