一、前言
看了下马哥的内部资料,关于es 日志采集的部署方案,感觉可以直接 logback 输出到 logstash,再到es
二、安装logstash
没有虚拟机,依然安装的是windows版本,解压后config下新建logstash.conf,添加默认输出内容
input {
beats {
port => 5044
}
}
output {
stdout { codec => rubydebug }
}
然后bin下新建start.bat,添加以下内容,双击启动
./logstash.bat -f ../config/logstash.conf
三、springboot 集成 logback
添加pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!--logStash-->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.3</version>
</dependency>
更改build
<!-- 资源文件配置 -->
<resources>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*.yml</include>
<include>*.xml</include>
</includes>
</resource>
</resources>
resources下新建logback文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<appender name="stash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>127.0.0.1:9999</destination>
<includeCallerData>true</includeCallerData>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeCallerData>true</includeCallerData>
</encoder>
</appender>
<logger name="com.example.logbacklogstash" level="debug" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="stash" />
<appender-ref ref="FILE"/>
</logger>
<logger name="root" level="warn" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="stash" />
<appender-ref ref="FILE"/>
</logger>
</configuration>
更改spring 配置文件
spring:
elasticsearch:
rest:
uris: http://127.0.0.1:9200
logging:
config: classpath:logback-spring.xml
添加环绕通知切面
package com.example.logbacklogstash.aspect;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@Aspect
@Component
public class SysAccessLog {
@Pointcut("execution (* com.example.logbacklogstash..*Controller.*(..))")
public void SysAccessLog() {
}
//环绕通知,环绕增强,相当于MethodInterceptor
@Around(value = "SysAccessLog()")
public Object doArround(ProceedingJoinPoint pjp) throws Exception {
long stime = System.currentTimeMillis();
try {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest req = attributes.getRequest();
// 记录下请求内容
log.info("[HTTP <<<] ");
log.info("[HTTP_URL] : {}" , req.getRequestURL().toString());
log.info("[HTTP_METHOD] : {}" , req.getMethod());
log.info("[HTTP_ACTION] : {}", pjp.getSignature().getDeclaringTypeName()+ "." + pjp.getSignature().getName());
Object[] args = getMethodArg(pjp.getArgs());
log.info("[HTTP_PARAMS] : {}", JSON.toJSONString(args[0]));
log.info("[REMOTE_IP] : {}" , req.getRemoteAddr());
//运行方法
Object o = pjp.proceed();
log.info("[HTTP_RESPONSE]: {}",JSON.toJSONString(o));
return o;
} catch (Throwable throwable) {
throw new Exception(throwable.getMessage(), throwable);
} finally {
long etime = System.currentTimeMillis();
log.info("[HTTP_TIME] : {}", (etime - stime)+"ms");
}
}
private Object[] getMethodArg(Object[] args) {
Object[] arguments = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
arguments[i] = args[i];
}
return arguments;
}
}
添加通用结果
package com.example.logbacklogstash.base;
import lombok.Data;
/**
* 通用结果
* @author wenx
* @date 2020-09-30
*/
@Data
public class Result {
private String code;
private String msg;
// private Object data;
public Result() {
}
public Result(String code, String msg) {
this.code = code;
this.msg = msg;
}
public Result error(){
return this.render(ResultEnum.error.getCode(),"error");
}
public Result error(String msg){
return this.render(ResultEnum.error.getCode(),msg);
}
public Result fail(String msg){
return this.render(ResultEnum.fail.getCode(),msg);
}
public Result fail(){
return this.render(ResultEnum.fail.getCode(),"fail");
}
public Result fail(Object data){
return this.render(ResultEnum.fail.getCode(),"fail",data);
}
public Result success(){
return this.render(ResultEnum.success.getCode(),"success");
}
public Result success(Object data){
return this.render(ResultEnum.success.getCode(),"success",data);
}
public Result success(String msg){
return this.render(ResultEnum.success.getCode(),msg);
}
public Result render(String code, String msg){
this.code = code;
this.msg = msg;
return this;
}
public Result render(String ret, String msg, Object data){
this.code = ret;
this.msg = msg;
// this.data = data;
return this;
}
}
通用结果枚举
package com.example.logbacklogstash.base;
import lombok.Getter;
/**
* 返回结果enum
* @author wenx
* @date 2020-09-30
*/
@Getter
public enum ResultEnum {
//结果
success("1"),fail("0"),error("2");
private String code;
ResultEnum(String code) {
this.code = code;
}
}
添加一个测试controller
package com.example.logbacklogstash.controller;
import com.example.logbacklogstash.base.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 测试controller 拟定接口模板
* @author wenx
* @date 2020-07-16
*/
@Slf4j
@RestController
public class TestController {
@GetMapping("/api/test")
public Result testForm(@RequestParam(value = "param",required = false,defaultValue = "")String param){
log.debug("param:{}",param);
return new Result().success();
}
}
最后需要修改刚才的logstash配置然后重启,这里有个误区,之前以为tcp里的port是logstash的端口,后来才发现是交互信息的channel,应该是和socket client一样。
# Sample Logstash configuration for creating a simple
# Beats -> Logstash -> Elasticsearch pipeline.
input {
tcp {
##host:port就是上面appender中的 destination,这里其实把logstash作为服务,开启9250端口接收logback发出的消息
host => "127.0.0.1"
port => "9999"
mode => "server"
tags => ["tags"]
codec => json_lines
}
beats {
port => 5044
}
}
filter{
date{
match => ["timestamp","dd-MMM-yyyy:HH:mm:ss Z"]
}
}
output {
stdout { codec => rubydebug }
elasticsearch {
hosts => "localhost:9200"
index => "logstash-%{+YYYY.MM.dd}"
}
file {
path => "/data/ELK/logstash/%{+YYYY.MM.dd}-out.txt"
codec => line
}
}
-end-
网友评论