Rest接口/Nginx日志记录和采集

news2024/11/15 9:48:27

文章目录

  • 一、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);

    @Resource
    private 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 {
            // 第一个参数固定为请求参数,第二个参数固定为HttpServletRequest
            HttpServletRequest 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());                // 客户端IP
            json.put("host", request.getRemoteHost());                  // 服务端IP
            json.put("port", request.getServerPort());                  // 服务端端口
            json.put("starttime", sdft.format(date));                   // 接口开始时间
            json.put("url", request.getRequestURI());                   // 接口URL
            json.put("system", system);                                 // 系统名称
            setArgInfo(json, joinPoint.getArgs()[0], result, dict.toString());
            // 获取token
            String 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);                                   // token
            String 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"));        // 账户ID
                    json.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: filestream
  paths:
    - /home/nginx/logs/access.log
  tags: ["nginx-access"]
  processors:
    - decode_json_fields:
        fields: ["message"]
        target: "nginx"
        max_depth: 1

- type: filestream
  paths:
    - /home/nginx/logs/error.log
  tags: ["nginx-error"]
  
- type: filestream
  paths:
    - /home/docker/logs/*
  tags: ["crontab-log"]

- type: filestream
  paths:
    - /home/logs/test/all.log
  tags: ["test"]
  processors:
    - decode_json_fields:
        fields: ["message"]
        target: "test"
        max_depth: 1

output.elasticsearch:
  hosts: ["192.168.1.12:9200"]
  preset: balanced
  protocol: "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: 1
  index.codec: best_compression

processors:
  - drop_fields:
      fields: ["log","host","input","agent","ecs"]
  • filebeat服务重启
sudo systemctl restart filebeat
sudo systemctl status filebeat

四、夜莺查看Nginx日志

  • 日志分析 > 索引模式 > 创建索引模式

在这里插入图片描述

  • 日志分析 > 索引模式 > 编辑 字段别名

在这里插入图片描述

  • 日志分析 > 即时查询

在这里插入图片描述

五、夜莺查看Rest接口日志

  • 日志分析 > 索引模式 > 创建索引模式

在这里插入图片描述- 日志分析 > 索引模式 > 编辑 字段别名

在这里插入图片描述

  • 日志分析 > 即时查询

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1612673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】开始使用优先队列

送给大家一句话: 这世上本来就没有童话&#xff0c;微小的获得都需要付出莫大的努力。 – 简蔓 《巧克力色微凉青春》 开始使用优先队列 1 前言2 优先队列2.1 什么是优先队列2.2 使用手册2.3 仿函数 3 优先队列的实现3.1 基本框架3.2 插入操作3.3 删除操作3.4 其他函数 4 总结T…

kubernetes的网络通信实现原理

网络原理 Kubernetes网络原理详解&#xff1a;一、Kubernetes 网络实现1.容器到容器&#xff08;同一Pod内&#xff09;通信流程&#xff1a;2. pod之间的通信&#xff08;以Calico为例&#xff09;&#xff1a; 二、CNI 网络模型三、网络策略四、开源的容器网络方案五、 常见网…

Linux管道共享内存

前言 进程虽然是独立运行的个体&#xff0c;但它们之间有时候需要协作才能完成一项工作&#xff0c;比如有两个进程需要同步数据&#xff0c;进程 A 把数据准备好后&#xff0c;想把数据发往进程 B&#xff0c;进程 B 必须被提前通知有数据即将到来&#xff0c;或者进程 A 想发…

Spark集群的搭建

1.1搭建Spark集群 Spark集群环境可分为单机版环境、单机伪分布式环境和完全分布式环境。本节任务是学习如何搭建不同模式的Spark集群&#xff0c;并查看Spark的服务监控。读者可从官网下载Spark安装包&#xff0c;本文使用的是spark-2.0.0-bin-hadoop2.7.gz。 1.1.1搭建单机版…

《乱弹篇(30)厌战的杜诗》

时下地球村有一伙成天叫嚣着“打打杀杀”、鼓吹快快发动战争的狂人&#xff0c;他们视老百姓的生命如草芥&#xff0c;毫不珍惜。没有遭受过战火焚烧的人&#xff0c;也跟着成天吠叫“快开战吧”。然而中国唐朝大诗人却是个“厌战派”&#xff0c;他对战争的厌恶集中表现在诗《…

实在RPA设计器试用导引

一、产品概述 实在RPA设计器是一款将人工智能(AI)与机器人流程自动化(RPA)深度融合的可视化自动流程编辑器。它通过AI推荐与桌面嵌入式交互&#xff0c;极大简化了RPA的使用难度&#xff0c;让普通业务人员也能轻松使用。实在RPA设计器具备以下核心优势&#xff1a; 兼容性&a…

我与C++的爱恋:类和对象(四)

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;我与C的爱恋 ​ 朋友们大家好&#xff01;本篇是类和对象的最后一个部分。 一、static成员 声明为static的类成员称为类的静态成员&#xff0c;用static修饰的成员变量&#xff0c;称之…

系统架构最佳实践 -- 相关JAVA架构

1. java 类加载器架构 2. JVM 架构 3. Java 技术体系 4. 线程运行架构 5. Java 体系&#xff08;编译与运行&#xff09;结构 6. JMS 技术架构 7. JMX 技术架构 8. Spring 架构 9. Hibernate 架构 10. ibatis 架构 11. Struts2 架构 12. Struts1 架构 13. JBPM 14. EJB 技术架构…

Java面试八股之marshalling和demarshalling

marshalling和demarshalling Marshalling&#xff08;序列化&#xff09;是将内存中的对象状态转化为适合传输或存储的格式&#xff08;如字节流、JSON、XML&#xff09;&#xff0c;以便进行网络通信、持久化存储或跨平台/语言交互操作。Demarshalling&#xff08;反序列化&a…

渗透测试入门教程,从零基础入门到精通(非常详细)

目录 什么是渗透测试 渗透测试的重要性 渗透测试的前置技能 开始入门学习路线 什么是渗透测试 渗透测试&#xff0c;通常被视为模拟黑客的一种安全评估行为&#xff0c;其目的在于全面挖掘目标网站或主机的潜在安全漏洞。与真实的黑客攻击不同&#xff0c;渗透测试旨在发现…

9.MMD 基础内容总结及制作成品流程

前期准备 1. 导入场景和模型 在左上角菜单栏&#xff0c;显示里将编辑模型时保持相机和光照勾选上&#xff0c;有助于后期调色 将抗锯齿和各向异性过滤勾掉&#xff0c;可以节省资源&#xff0c;避免bug 在分辨率设定窗口&#xff0c;可以调整分辨率 3840x2160 4k分辨率 1…

05 MySQL--字段约束、事务、视图

1. CONSTRAINT 约束 创建表时&#xff0c;可以给表的字段添加约束&#xff0c;可以保证数据的完整性、有效性。比如大家上网注册用户时常见的&#xff1a;用户名不能为空。对不起&#xff0c;用户名已存在。等提示信息。 约束包括&#xff1a; 非空约束&#xff1a;not null检…

kafka实验部署

一、前期准备 二、kafka实验 在zookeeper后继续进行操作 2.1 为ndoe1、node2、node3作出部署 2.1.1 解压kafka压缩包&#xff08;node1举例&#xff09; 2.1.2 操作 将解压后的kafka移动到kafka&#xff0c;进入到kafka下的config中&#xff0c;复制文件 2.1.2.1 编辑server.pr…

LWIP开发之静态IP为什么接收和发送不了数据

使用的硬件开发板是探索者F4 V3版本 这里用的LWIP的lwIP例程7 lwIP_NETCONN_UDP实验 问了开发板的官方和其他人都说不清楚&#xff1b;搞了两天&#xff0c;浪费了两天时间&#xff1b; 最奇葩的问题还在于只能单片机发送&#xff0c;上位机能接收。而上位机发送单片机不能接…

虚拟机扩容方法

概述 我的虚拟机开始的内存是40G,接下来要扩成60GB 扩容步骤 步骤1 步骤2 步骤3 修改扩容后的磁盘大小&#xff0c;修改后的值只可以比原来的大&#xff0c;修改完成后点击扩展&#xff0c;等待扩展完成 步骤4 虽然外面扩展成功&#xff0c;但是新增的磁盘空间虚拟机内部还…

自媒体个人品牌IP策划打造孵化运营方案

【干货资料持续更新&#xff0c;以防走丢】 自媒体个人品牌IP策划打造孵化运营方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 ppt可编辑&#xff08;完整资料包含以下内容&#xff09;目录个人IP孵化方案概要&#xff1a; 1. 目标定位与市场分析 - 女性…

QMT和Ptrade有什么区别?该如何选择?

QMT&#xff08;Quantitative Model Trading&#xff09;和Ptrade&#xff08;Professional Trading&#xff09;是两种不同的交易策略和方法&#xff0c;它们在金融市场中被广泛应用。了解它们的区别有助于投资者根据自己的需求和目标做出选择&#xff1a; QMT&#xff08;量…

AI+PS快捷键大全!

hello&#xff0c;我是小索奇&#xff0c; 你会用Photoshop&#xff08;PS&#xff09;或者&#xff08;Illustrator&#xff09;AI吗&#xff1f;相信很多人都会接触到吧&#xff0c;但有一部分人很少用快捷键&#xff0c;仅凭借鼠标点击来实现功能&#xff0c;殊不知快捷键能…

在线拍卖系统,基于SpringBoot+Vue+MySql开发的在线拍卖系统设计和实现

目录 一. 系统介绍 二. 功能模块 2.1. 管理员功能模块 2.2. 用户功能模块 2.3. 前台首页功能模块 2.4. 部分代码实现 一. 系统介绍 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系…

感知机学习算法中的Novikoff定理证明中的隐含背景知识

一、引言 《统计学习方法》&#xff08;李航著&#xff09;第二章感知机学习时&#xff0c;其中的Novikoff定理是关于感知机算法收敛性的一个重要定理。这个定理保证了对于线性可分的数据集&#xff0c;感知机学习算法最终能够收敛到一个解&#xff0c;即存在一个权重向量 w 和…