文章目录
- 一、Rest接口日志
- 二、Nginx日志
- 三、采集日志
- 四、夜莺查看Nginx日志
- 五、夜莺查看Rest接口日志
一、Rest接口日志
- 记录日志字典定义
接口URL | 接口名称,类别,入参全记录,出参全记录,入参字段1:中文名1/入参字段2:中文名2,出参字段1:中文名1 |
---|---|
/test/api/login | 账户登录,登录,false,false,accountName:账户名,accessToken:Token/expiresTime:有效期/person.name:姓名 |
/test/api/role/findById | 查询角色,应用,true,true,role.roleId:角色ID,role.roleName:角色名 |
- RestLogAspect.java
package cn.test.manage.config.aspect;import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;/*** @description Rest接口日志记录*/
@Aspect
@Component
public class RestLogAspect {private static Log logger = LogFactory.getLog(RestLogAspect.class);@Resourceprivate RedisTemplate redisTemplate;@Value("${server.servlet.application-display-name}")private String system;private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'+08:00'");private static DecimalFormat df = new DecimalFormat ("#.##");@Pointcut("execution(public * cn.test.manage.web.rest.*.*.*(..))")private void pointcut() {}@Around(value = "pointcut()")public Object log(ProceedingJoinPoint joinPoint) throws Throwable {Date date = new Date();Object result = joinPoint.proceed();try {// 第一个参数固定为请求参数,第二个参数固定为HttpServletRequestHttpServletRequest request = (HttpServletRequest) joinPoint.getArgs()[1];// 从字典获取需要记录日志的接口Object dict = redisTemplate.opsForHash().get("DICT_", system + "_logs" + "." + request.getRequestURI());if (dict == null || StringUtils.isEmpty(dict.toString())) {return result;}JSONObject json = new JSONObject();json.put("logtype", "customize"); // 固定标识, 用于日志采集json.put("client", request.getRemoteAddr()); // 客户端IPjson.put("host", request.getRemoteHost()); // 服务端IPjson.put("port", request.getServerPort()); // 服务端端口json.put("starttime", sdft.format(date)); // 接口开始时间json.put("url", request.getRequestURI()); // 接口URLjson.put("system", system); // 系统名称setArgInfo(json, joinPoint.getArgs()[0], result, dict.toString());// 获取tokenString token = request.getParameter("token");if (StringUtils.isEmpty(token) || "null".equals(token)) {token = request.getHeader("Authorization");if (token != null && token.startsWith("Bearer ")) {token = token.substring(7);}}json.put("token", token); // tokenString accountId = (String) redisTemplate.opsForValue().get("token-" + token);if (accountId != null) {Map<String, String> map = (Map<String, String>) redisTemplate.opsForValue().get(accountId);if (map != null) {json.put("accountid", map.get("accountId")); // 账户IDjson.put("accountname", map.get("accountName")); // 账户名json.put("username", map.get("name")); // 用户名}}// 接口代码执行时间,会小于响应时间json.put("resptime", df.format((double)(new Date().getTime()-date.getTime())/1000));logger.info("\n"+JSONObject.toJSONString(json));} catch (Exception e) {logger.error(e);}return result;}/*** 设置参数信息*/private void setArgInfo(JSONObject json, Object request, Object response, String argstr) {String requeststr = JSONObject.toJSONString(request);String responsestr = JSONObject.toJSONString(response);JSONObject requestObject = JSONObject.parseObject(requeststr);JSONObject responseObject = JSONObject.parseObject(responsestr);String[] args = argstr.split(",");json.put("apidesc", args[0]); // 接口描述json.put("apitype", args[1]); // 接口类型if (responseObject.getString("operateSuccess") == null || "false".equals(responseObject.getString("operateSuccess"))) {json.put("level", "ERROR"); // 日志级别} else {json.put("level", "INFO");}json.put("msg", responseObject.getString("operateSuccess") + " " + responseObject.getString("msg"));if ("true".equals(args[2])) {json.put("request", requeststr); // 请求完整内容}if ("true".equals(args[3])) {json.put("response", responsestr); // 响应完整内容}json.put("req", getInfo(json, requestObject, args[4])); // 请求概要内容json.put("resp", getInfo(json, responseObject, args[5])); // 响应概要内容}/*** 获取参数概要信息,集合只取第一个元素*/private String getInfo(JSONObject json, JSONObject object, String argstr) {String str = "";for (String obj : argstr.split("/")) {Object jsonObject = object;String fields = obj.split(":")[0];String desc = obj.split(":")[1];for (String field : fields.split("\\.")) {if (jsonObject instanceof JSONObject) {jsonObject = ((JSONObject) jsonObject).get(field);} else if (jsonObject instanceof JSONArray) {while (jsonObject instanceof JSONArray) {jsonObject = ((JSONArray) jsonObject).get(0);}jsonObject = ((JSONObject) jsonObject).get(field);} else {logger.info(jsonObject);break;}}str += desc + ": " + jsonObject + ", ";}str = str.endsWith(", ") ? str.substring(0,str.length()-2) : str;return str;}
}
二、Nginx日志
- nginx.conf 中日志配置
# 日志配置open_log_file_cache max=1000 inactive=20s valid=1m min_uses=3;# 客户端地址 时间 协议 响应状态 响应字节数 响应时间 客户端log_format main '{"logtype":"customize",' # 固定标识, 用于日志采集'"starttime":"$time_iso8601",' # 日志写入时间'"url":"$uri",' # 请求的URL'"protocol":"$server_protocol",' # 请求使用的协议'"upgrade":"$http_upgrade",' # 是否升级 WebSocket'"status":"$status",' # 响应状态'"host": "$http_host",' # 服务端地址(客户端请求的)'"client": "$remote_addr",' # 客户端地址'"reqsize": $request_length,' # 请求内容大小(byte)'"respsize": $bytes_sent,' # 响应内容大小 byte)'"resptime": $request_time,' # 响应时间(s)'"connnum": $connection_requests,' # 当前通过一个连接获得的请求数量'"agent": "$http_user_agent"}'; # 用户终端代理access_log /var/log/nginx/access.log main buffer=32k flush=5s;error_log /var/log/nginx/error.log warn;
三、采集日志
- filebeat.yml
filebeat.inputs:
- type: filestreampaths:- /home/nginx/logs/access.logtags: ["nginx-access"]processors:- decode_json_fields:fields: ["message"]target: "nginx"max_depth: 1- type: filestreampaths:- /home/nginx/logs/error.logtags: ["nginx-error"]- type: filestreampaths:- /home/docker/logs/*tags: ["crontab-log"]- type: filestreampaths:- /home/logs/test/all.logtags: ["test"]processors:- decode_json_fields:fields: ["message"]target: "test"max_depth: 1output.elasticsearch:hosts: ["192.168.1.12:9200"]preset: balancedprotocol: "http"username: "elastic"password: "123456"indices:- index: filebeat-6.13-nginx-%{+yyyy.MM}when.contains: {tags: nginx, nginx.logtype: customize}- index: filebeat-6.13-ser-%{+yyyy.MM}when.contains: {tags: test, test.logtype: customize}- index: filebeat-6.13-%{+yyyy.MM}setup.template.settings:index.number_of_shards: 1index.codec: best_compressionprocessors:- drop_fields:fields: ["log","host","input","agent","ecs"]
- filebeat服务重启
sudo systemctl restart filebeat
sudo systemctl status filebeat
四、夜莺查看Nginx日志
- 日志分析 > 索引模式 > 创建索引模式
- 日志分析 > 索引模式 > 编辑 字段别名
- 日志分析 > 即时查询
五、夜莺查看Rest接口日志
- 日志分析 > 索引模式 > 创建索引模式
- 日志分析 > 索引模式 > 编辑 字段别名
- 日志分析 > 即时查询