SpringBoot日志打印实践

news2024/10/5 9:15:59

背景

在项目当中,我们经常需要打印一些日志埋点信息,这些日志埋点信息,在后续软件的运维、稳定性建设中发挥了巨大的作用:

  • 问题追踪:通过埋点日志中的关键信息,帮助定位系统异常原因
  • 系统监控:通过日志,监控系统的运行情况,包括性能指标、访问频率、错误等
  • 数据分析:分析用户行为、系统性能和业务趋势等
  • 调试:通过查看日志,帮助开发人员了解程序在执行过程中的状态和行为

SpringBoot整合Logback实现日志打印

SpringBoot默认使用Slf4j作为日志门面,并集成Logback作为日志实现。要在springboot中实现日志打印,只需要引入下列依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

然后在配置文件中,配置对应的日志级别:

logging:
  level:
    root: INFO

对某些特定的包,需要指定日志级别,则配置如下:

logging:
    level:
        com.example.demo: DEBUG

最后,我们创建logback-spring.xml,来自定义日志的配置信息,包括日志输出文件、日志格式等

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOG_PATH" value="logs"/>
    <property name="LOG_FILE" value="${LOG_PATH}/spring-boot-logger.log"/>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>common.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/spring-boot-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

然后,我们在需要打印日志的类,加上Slf4j注解,然后使用log来打印日志信息即可,如下代码所示:

package com.yang.web.controller;

import com.yang.api.common.ResultT;
import com.yang.api.common.command.RegisterCommand;
import com.yang.api.common.dto.UserDTO;
import com.yang.api.common.facade.UserFacade;
import com.yang.web.request.RegisterRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/user")
@Slf4j
public class UserController {
    @Autowired
    private UserFacade userFacade;

    @GetMapping(value = "/{id}")
    public ResultT<UserDTO> queryById(@PathVariable("id") Integer id) {
        log.info("queryById===========");
        return userFacade.getById(id);
    }

    @PostMapping(value = "/register")
    public ResultT<String> register(@RequestBody RegisterRequest registerRequest) {
        RegisterCommand registerCommand = convert2RegisterCommand(registerRequest);
        return userFacade.register2(registerCommand);
    }

    private RegisterCommand convert2RegisterCommand(RegisterRequest registerRequest) {
        RegisterCommand registerCommand = new RegisterCommand();
        registerCommand.setLoginId(registerRequest.getLoginId());
        registerCommand.setEmail(registerRequest.getEmail());
        registerCommand.setPassword(registerRequest.getPassword());
        registerCommand.setExtendMaps(registerRequest.getExtendMaps());
        return registerCommand;
    }
}

然后访问queryById,打印结果如下:

日志打印工具类

在logback-spring.xml中,我们虽然能配置日志打印的格式,但是不够灵活,因此,我们可以添加一个日志打印工具类,通过该工具类,来自定义项目中的日志打印格式,以方便后续更好地通过日志排查、定位问题。

首先创建一个日志打印抽象类,定义日志打印的格式:

package com.yang.core.infrastructure.log;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

public abstract class AbstractLogPrinter {
    protected String bizCode;
    
    protected List<String> params = new ArrayList<>();

    protected String msg;

    protected Throwable e;

    public AbstractLogPrinter addBizCode(String bizCode) {
        this.bizCode = bizCode;
        return this;
    }

    public AbstractLogPrinter addMsg(String msg) {
        this.msg = msg;
        return this;
    }

    public AbstractLogPrinter addParam(String key, String value) {
        this.params.add(key);
        this.params.add(value);
        return this;
    }

    public AbstractLogPrinter addThrowable(Throwable e) {
        this.e = e;
        return this;
    }

    public abstract void printBizLog();

    public abstract void printErrorLog();

    public abstract String getSeparator();

    public String commonContent() {
        StringBuilder stringBuilder = new StringBuilder();
        String separator = getSeparator();
        stringBuilder.append("bizCode").append(":")
                .append(this.bizCode).append(separator);
        if (!CollectionUtils.isEmpty(params)) {
            for (int i = 0; i < params.size(); i += 2) {
                stringBuilder.append(params.get(i))
                        .append(":")
                        .append(params.get(i + 1))
                        .append(separator);
            }
        }
        if (StringUtils.isNotEmpty(msg)) {
            stringBuilder.append("msg").append(":")
                    .append(msg).append(separator);
        }
        return stringBuilder.toString();
    }
}

然后创建日志打印实现类,在实现类中,定制实现日志打印的级别、分隔符等内容

package com.yang.core.infrastructure.log;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class PlatformLogPrinter extends AbstractLogPrinter {

    public void printBizLog() {
        log.info(commonContent());
    }

    public void printErrorLog() {
        if (e != null) {
            log.error(commonContent(), e);
        } else {
            log.error(commonContent());
        }
    }

    @Override
    public String getSeparator() {
        return "<|>";
    }
}

同时,为了方便打印日志,创建一个日志打印创建者

package com.yang.core.infrastructure.log;

public class PlatformLogger {

    public static AbstractLogPrinter build() {
        return new PlatformLogPrinter();
    }
}

上述内容准备完毕后,我们在controller中,使用PlatformLogger来打印日志,修改后的代码如下:

package com.yang.web.controller;

import com.yang.api.common.ResultT;
import com.yang.api.common.command.RegisterCommand;
import com.yang.api.common.dto.UserDTO;
import com.yang.api.common.facade.UserFacade;
import com.yang.core.infrastructure.log.PlatformLogger;
import com.yang.web.request.RegisterRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/user")
public class UserController {
    @Autowired
    private UserFacade userFacade;

    @GetMapping(value = "/{id}")
    public ResultT<UserDTO> queryById(@PathVariable("id") Integer id) {
        PlatformLogger.build()
                .addBizCode("queryById")
                .addParam("id", id.toString())
                .addMsg("query by id")
                .printBizLog();
        return userFacade.getById(id);
    }
    
    @GetMapping(value = "/error/{id}")
    public ResultT testError(@PathVariable("id") Integer id) {
        try {
            int i = 1 / 0;
        } catch (Throwable t) {
            PlatformLogger.build()
                    .addBizCode("testError")
                    .addParam("id", id.toString())
                    .addMsg("test error print")
                    .addThrowable(t)
                    .printErrorLog();
        }
        return ResultT.fail();
    }

    @PostMapping(value = "/register")
    public ResultT<String> register(@RequestBody RegisterRequest registerRequest) {
        RegisterCommand registerCommand = convert2RegisterCommand(registerRequest);
        return userFacade.register2(registerCommand);
    }

    private RegisterCommand convert2RegisterCommand(RegisterRequest registerRequest) {
        RegisterCommand registerCommand = new RegisterCommand();
        registerCommand.setLoginId(registerRequest.getLoginId());
        registerCommand.setEmail(registerRequest.getEmail());
        registerCommand.setPassword(registerRequest.getPassword());
        registerCommand.setExtendMaps(registerRequest.getExtendMaps());
        return registerCommand;
    }
}

启动项目,分别访问queryById和testError,打印日志内容如下:

日志分文件打印

一般情况下,我们的项目会分为不同的模块,每一个模块承担不同的职责,比如bussiness模块,主要是负责业务逻辑代码的实现,业务逻辑编排等;web模块主要负责http请求的接收,参数的校验,入参转化为业务层入参等;而core模块主要负责基础能力实现,比如持久化数据库、领域服务实现等。

对于不同的模块,我们希望将日志输出到不同的文件当中,从而协助我们后续定位问题以及建设不同模块下的监控,包括基础服务监控、业务成功率监控等。

因此,我们在不同的模块下,分别实现不同的日志打印工具类:

package com.yang.web.log;

import com.yang.core.infrastructure.log.AbstractLogPrinter;

public class WebLogger {
    public static AbstractLogPrinter build() {
        return new WebLogPrinter();
    }
}


package com.yang.web.log;

import com.yang.core.infrastructure.log.AbstractLogPrinter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class WebLogPrinter extends AbstractLogPrinter {
    @Override
    public void printBizLog() {
        log.info(commonContent());
    }

    @Override
    public void printErrorLog() {
        if (this.e != null) {
            log.error(commonContent(), e);
        } else {
            log.error(commonContent());
        }
    }

    @Override
    public String getSeparator() {
        return "<|>";
    }
}


package com.yang.business.log;

public class BusinessLogger {
    public static BusinessLogPrinter build() {
        return new BusinessLogPrinter();
    }
}


package com.yang.business.log;

import com.yang.core.infrastructure.log.AbstractLogPrinter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BusinessLogPrinter extends AbstractLogPrinter {
    @Override
    public void printBizLog() {
        log.info(commonContent());
    }

    @Override
    public void printErrorLog() {
        if (this.e != null) {
            log.error(commonContent(), e);
        } else {
            log.error(commonContent());
        }
    }

    @Override
    public String getSeparator() {
        return "<|>";
    }
}

然后我们修改logback-spring.xml文件,将不同的日志打印工具类,输出到不同的日志文件中

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOG_PATH" value="logs"/>
    <property name="LOG_FILE" value="${LOG_PATH}/spring-boot-logger.log"/>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>common.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/spring-boot-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="PLATFORM_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>platform.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/platform-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="BUSINESS_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>business.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/business-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="WEB_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>web.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/web-logger.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>

    <!-- 工具类PlatformLogPrinter的logger -->
    <logger name="com.yang.core.infrastructure.log.PlatformLogPrinter" level="INFO" additivity="false">
        <appender-ref ref="PLATFORM_FILE" />
    </logger>

    <!-- 工具类BusinessLogPrinter的logger -->
    <logger name="com.yang.business.log.BusinessLogPrinter" level="INFO" additivity="false">
        <appender-ref ref="BUSINESS_FILE" />
    </logger>

    <!-- 工具类WebLogPrinter的logger -->
    <logger name="com.yang.web.log.WebLogPrinter" level="INFO" additivity="false">
        <appender-ref ref="WEB_FILE" />
    </logger>
</configuration>

最后,分别在web模块、business模块和core模块下,添加埋点日志

// WEB模块
@GetMapping(value = "/{id}")
    public ResultT<UserDTO> queryById(@PathVariable("id") Integer id) {
        WebLogger.build()
                .addBizCode("userController_queryById")
                .addParam("id", id.toString())
                .addMsg("query by id")
                .printBizLog();
        return userFacade.getById(id);
    }

// Business模块
@Override
    public ResultT<UserDTO> getById(Integer id) {
        UserQueryDomainRequest userQueryDomainRequest = new UserQueryDomainRequest.UserQueryDomainRequestBuilder()
                .queryMessage(id.toString())
                .userQueryType(UserQueryType.ID)
                .build();
        UserQueryDomainResponse userQueryDomainResponse = userDomainService.query(userQueryDomainRequest);
        List<UserAccount> userAccountList = userQueryDomainResponse.getUserAccountList();
        UserDTO userDTO = null;
        if (!CollectionUtils.isEmpty(userAccountList)) {
            UserAccount userAccount = userAccountList.get(0);
            userDTO = userDTOConvertor.convert2DTO(userAccount);
        }
        BusinessLogger.build()
                .addBizCode("userFacade_getById")
                .addParam("id", id.toString())
                .addParam("userDTO", JSONObject.toJSONString(userDTO))
                .addMsg("get by id")
                .printBizLog();
        return ResultT.success(userDTO);
    }


// core模块
public UserQueryDomainResponse query(UserQueryDomainRequest userQueryDomainRequest) {
        UserQueryType userQueryType = userQueryDomainRequest.getUserQueryType();
        UserDO userDO = null;
        switch (userQueryType) {
            case ID:
                userDO = queryById(Integer.valueOf(userQueryDomainRequest.getQueryMessage()));
                break;
            case EMAIL:
                userDO = queryByEmail(userQueryDomainRequest.getQueryMessage());
                break;
            case LOGIN_ID:
                userDO = queryByLoginId(userQueryDomainRequest.getQueryMessage());
                break;
        }
        if (userDO == null) {
            return new UserQueryDomainResponse();
        }
        UserAccount userAccount = new UserAccount();
        userAccount.setId(userDO.getId());
        userAccount.setLoginId(userDO.getLoginId());
        userAccount.setEmail(userDO.getEmail());
        userAccount.setFeatureMap(FeatureUtils.convert2FeatureMap(userDO.getFeatures()));
        userAccount.setCreateTime(userDO.getCreateTime());
        userAccount.setUpdateTime(userDO.getUpdateTime());
        UserQueryDomainResponse userQueryDomainResponse = new UserQueryDomainResponse();
        List<UserAccount> userAccounts = new ArrayList<>();
        userAccounts.add(userAccount);
        userQueryDomainResponse.setUserAccountList(userAccounts);

        PlatformLogger.build()
                .addBizCode("userDomainService_query")
                .addParam("queryMsg", userQueryDomainRequest.getQueryMessage())
                .addParam("queryType", userQueryDomainRequest.getUserQueryType().name())
                .printBizLog();
        return userQueryDomainResponse;
    }

启动项目,访问queryById接口,可以看到在web.log,business.log和platform.log下分别打印了不同的日志信息

参考文档

【Spring Boot】深入解密Spring Boot日志:最佳实践与策略解析-腾讯云开发者社区-腾讯云 (tencent.com)

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

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

相关文章

《Linux从小白到高手》理论篇:Linux用户和组相关的命令

List item 本篇介绍Linux用户和组相关的命令&#xff0c;看完本文&#xff0c;有关Linux用户和组相关的常用命令你就掌握了99%了。Linux用户和组相关的命令可以分为以下六类&#xff1a; 一.用户和用户组相关查询操作命令&#xff1a; Id id命令用于显示用户的身份标识。常见…

解表之紫苏

** 声明&#xff1a;本文介绍的中药仅供学习使用&#xff0c;请勿擅自使用&#xff0c;否则后果自负&#xff01;&#xff01;&#xff01;因水平有限&#xff0c;如有不当之处&#xff0c;请批评指正&#xff01;&#xff01;&#xff01;&#xff01;图片来源网络&#xff0…

No.1 | 从小白到入门:我的渗透测试笔记

嘿&#xff0c;小伙伴们&#xff01;好久不见啊&#xff0c;是不是都以为我失踪了&#xff1f;&#x1f602; 其实呢&#xff0c;最近一直在埋头苦学&#xff0c;感觉自己就像是在技术的海洋里游泳&#xff0c;每天都在吸收新知识。现在终于有时间冒个泡&#xff0c;跟大家分享…

Linux进程控制(2)(进程程序替换1)

目录 续--上一章 1.WIFEXITED && WEXITSTATUS 2.非阻塞等待 进程程序替换 1.先观代码 && 现象 2.原理解释 3.将代码改成多进程版 4.使用所有的替换方法&#xff0c;并且认识函数参数的含义 续--上一章 1.WIFEXITED && WEXITSTATUS 若需要知道退…

ODX相关基础知识普及

一、ODX概述 1.1 背景与意义 介绍汽车电子化的发展 随着科技的飞速发展&#xff0c;汽车电子化已成为现代汽车工业的重要趋势。从早期的机械控制到现代的电子控制&#xff0c;汽车电子化经历了巨大的变革。早期&#xff0c;汽车的主要控制系统都是基于机械原理&#xff0c;通…

Cisco ACI Simulator 6.0(7e)M 发布下载,新增功能简介

Cisco ACI Simulator 6.0(7e)M - ACI 模拟器 Application Centric Infrastructure (ACI) Simulator Software 请访问原文链接&#xff1a;https://sysin.org/blog/cisco-acisim-6/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.…

[C/C++开发]链接动态库在不同操作系统上的行为

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 想必很多人已经了解了动态库与静态库,在实际开发中也经常使用. 但是,有必要了解在windows和Linux上开发c程序生成和链接动态库的不同行为,因为经常混淆或者自以为找到了动态库,这里简单学习并澄清一下.其中许多内容来…

【Python】pyenv:管理多版本 Python 环境的利器

pyenv 是一个强大的 Python 版本管理工具&#xff0c;它允许开发者在同一台计算机上轻松安装和管理多个 Python 版本。对于需要在不同项目中使用不同 Python 版本的开发者来说&#xff0c;pyenv 是一个非常有用的工具&#xff0c;因为它可以帮助用户在全局和项目级别控制 Pytho…

PCL 给点云添加高斯噪声并保存

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 添加高斯噪声实现 2.1.2 可视化函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&…

角色动画——RootMotion全解

1. Unity(2022)的应用 由Animtor组件控制 在Animation Clip下可进行详细设置 ​ 官方文档的介绍(Animation选项卡 - Unity 手册) 上述动画类型在Rag选项卡中设置: Rig 选项卡上的设置定义了 Unity 如何将变形体映射到导入模型中的网格&#xff0c;以便能够将其动画化。 对于人…

污水排放口细粒度检测数据集,污-水排放口的类型包括10类目标,10000余张图像,yolo格式目标检测,9GB数据量。

污水排放口细粒度检测数据集&#xff0c;污-水排放口的类型包括10类目标&#xff08;1 合流下水道&#xff0c;2 雨水&#xff0c;3 工业废水&#xff0c;4 农业排水&#xff0c;5 牲畜养殖&#xff0c;6 水产养殖&#xff0c;7 地表径流&#xff0c;8 废水处理厂&…

yub‘s Algorithmic Adventures_Day5

Day5 反转链表 link&#xff1a;206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 思路分析 与数组不同&#xff0c;链表没必要定义新的链表进行存储【对内存空间的浪费】 直接改变next指针即可. 注意头节点指向的下一个节点为null 双指针法 class Solution {publi…

杂谈c语言——6.浮点数的存储

1.浮点数在内存中的存储 常⻅的浮点数&#xff1a;3.14159、1E10等&#xff0c;浮点数家族包括&#xff1a; float、double、long double 类型。 浮点数表⽰的范围&#xff1a; float.h 中定义 1.1 练习 #include<stdio.h>int main() {int n 9;float* pFloat (floa…

Golang | Leetcode Golang题解之第456题132模式

题目&#xff1a; 题解&#xff1a; func find132pattern(nums []int) bool {candidateI, candidateJ : []int{-nums[0]}, []int{-nums[0]}for _, v : range nums[1:] {idxI : sort.SearchInts(candidateI, 1-v)idxJ : sort.SearchInts(candidateJ, -v)if idxI < idxJ {ret…

智能视界·大模型驱动视频矩阵管理系统

开头先配两张ER图 一张不带字段&#xff0c;一张带字段&#xff0c;剩下的内容按需拿取 1.产品介绍 产品名称&#xff1a; 智能视界大模型驱动视频矩阵管理系统 主要功能&#xff1a; 智能视频分析与识别 功能介绍&#xff1a;该系统集成先进的人工智能大模型&#xff0c;能…

热轧钢带缺陷数据集,Xsteel表面缺陷数据集(X-SDD),其中包含七种典型的热轧带钢缺陷类型,共有1360个缺陷图像。

热轧钢带缺陷数据集&#xff0c;称为Xsteel表面缺陷数据集&#xff08;X-SDD&#xff09;&#xff0c;其中包含七种典型的热轧带钢缺陷类型&#xff0c;共有1360个缺陷图像。与常用的NEU表面缺陷数据库&#xff08;NEU-CLS&#xff09;的六种缺陷类型相比&#xff0c;X-SDD包含…

Ray_Tracing_The_Next_Week

1动态模糊 动态模糊在摄影中就是快门的速度慢&#xff0c;捕捉光的时间长&#xff0c;物体运动时进行捕捉成像&#xff0c;拍出来的结果是这个运动过程每一帧的平均值 我们的思路是&#xff1a; 每一条光线都拥有自己存在的一个时间点。随着时间变化随机生成光线,一般来说我…

全新芒果YOLOv10改进135:最新注意力机制EMA:即插即用,具有跨空间学习的高效多尺度注意力模块,ICCASSP 2023

💡本篇内容:芒果YOLOv10改进135:最新注意力机制EMA:即插即用,具有跨空间学习的高效多尺度注意力模块,ICCASSP 2023 **具有跨空间学习的高效多尺度注意力模块EMA | 即插即用 该模块通常包括多个并行的注意力子模块,每个子模块关注于输入数据的不同尺度或分辨率。这些子模块…

Study-Oracle-10-ORALCE19C-RAC集群维护

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。 一、RAC的逻辑架构与进程 1、RAC 与单实例进程的对比 2、RAC相关进程功能 3、在主机查看RAC进程 其他的不列举了 4、RAC集群启停命令 检查集群状态 ORACLE 19C …

2-112基于matlab的协同干扰功率分配模型

基于matlab的协同干扰功率分配模型&#xff0c;带操作界面的功率分配GUI&#xff0c;可以实现对已有功率的分配优化&#xff0c;可以手动输入参数值。4个干扰山区分二批总干扰功率&#xff0c;每个扇区包括威胁总系数、综合压制概率、目标函数增量等。程序已调通&#xff0c;可…