发邮件1、创建邮箱

发邮件

1、创建邮箱

需要你有1个邮箱,可以正常发送邮件。

尽可能有另1个邮箱,可以正常接收。

2、打开已有/创建邮箱,申请授权码

2.1 登录网页版邮箱

3、安装邮件客户端

在工作中,一般都会通过邮箱进行沟通。

在工作的电脑上,安装一个邮箱客户端,方便发送和接收邮件。

手机上,尽量也安装一个邮箱客户端,方便后续就接收入职通知或者面试通知等。

后续使用了日志技术,代码在发送异常的时候,也会通过邮件提示。

4、配置SpringMail

4.1 配置项目

在qingqing项目中的admin-api中配置

复制代码
    <dependencies>
        <dependency>
            <groupId>com.javasm</groupId>
            <artifactId>common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
    </dependencies>

4.2 配置项目

复制代码
server:
  tomcat:
    max-swallow-size: 10MB
    max-http-form-post-size: 10MB
  port: 8088
spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB
  application:
    name: admin_api
  datasource:
    #配置数据库连接池种类
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/qingqing?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: root
    druid:
      initial-size: 5
      max-active: 100
      min-idle: 10
  data:
    redis:
      host: localhost
      port: 6379
      password: javasm
      database: 2
  task:
    execution:
      pool:
        core-size: 10
        max-size: 20
        queue-capacity: 1000
    scheduling:
      pool:
        size: 10
  mail:
    protocol: smtp
    host: smtp.163.com
    port: 465
    username: 17335592100@163.com
    password: CLQ5XnDsUs3nxbRS
    properties:
      mail:
        smtp:
          auth: true
          ssl:
            enable: true
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

5、纯Java发邮件

复制代码
package com.javasm.qingqing;
​
import jakarta.mail.Message;
import jakarta.mail.Session;
import jakarta.mail.Transport;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
​
import java.util.Properties;
​
@SpringBootTest
public class JavaMailTest {
​
    //这个案例是纯Java实现的,比较繁琐,看一看就可以了
    //通过这个案例,来了解,发送邮件底层都需要哪些角色
    @Test
    void f1() throws Exception {
        //定义账号信息
        //发件人
        String account = "17335592100@163.com";
        //收件人
        String to = "chengdujavasm@126.com";
        //发件人密码
        String password = "CLQ5XnDsUs3nxbRS";
        //SMTP信息
        Properties properties = new Properties();
        //连接协议
        properties.put("mail.ransport.protocol","smtp");
        //邮箱域名
        properties.put("mail.smtp.host","smtp.163.com");
        //端口号  默认是25
        properties.put("mail.smtp.port",465);
        //服务端认证
        properties.put("mail.smtp.auth","true");
        //安全连接
        properties.put("mail.smtp.ssl.enable","true");
        //初始化,会话对象
        Session session = Session.getDefaultInstance(properties);
        //发消息对象
        Message msg = new MimeMessage(session);
        //配置 发送人信息
        msg.setFrom(new InternetAddress(account));
        //配置 收件人信息
        msg.setRecipient(Message.RecipientType.TO,new InternetAddress(to));
        //配置  抄送人信息
        msg.setRecipient(Message.RecipientType.CC,new InternetAddress(account));
        //邮件标题
        msg.setSubject("今日天气");
        //邮件内容
        msg.setText("晴天,0~11°");
        //邮差对象   这封信,是由邮差交给收件人
        Transport transport = session.getTransport();
        //配置 邮箱的用户名和密码
        transport.connect(account,password);
        //邮差 发送邮件
        transport.sendMessage(msg,msg.getAllRecipients());
    }
}
​

6、SpringMail发送邮件

6.1 纯文本

复制代码
package com.javasm.qingqing;
​
import jakarta.annotation.Resource;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
​
@SpringBootTest
public class SpringMailTest {
    //SpringMail的客户端对象
    @Resource
    JavaMailSender javaMailSender;
    //获取配置文件中,关于邮件的配置
    @Resource
    MailProperties mailProperties;
    //收件人信息
    private final static String to = "chengdujavasm@126.com";
​
    //发送纯文本
    @Test
    void f1() throws Exception {
        //邮件客户端对象中,获取消息对象
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        //消息辅助对象
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage);
        //发件人
        String from = mailProperties.getUsername();
        helper.setFrom(from);
        //收件人
        helper.setTo(to);
        //标题
        helper.setSubject("今日新闻-----红军城又失守了");
        //内容
        helper.setText("忽如一夜冻风来");
        //发送
        javaMailSender.send(mimeMessage);
​
    }
​
}

6.2 HTML

复制代码
​
    @Resource
    GameService gameService;
    
    @Test
    void f2() throws Exception {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage);
        helper.setFrom(mailProperties.getUsername());
        helper.setTo(to);
        helper.setSubject("测试HTML的案例");
        //拼接HTML代码
        List<Game> list = gameService.list();
        StringBuffer html = new StringBuffer();
        html.append("<h1>游戏列表</h1>");
        //循环table
        html.append("<table style='border:1px solid #000;text-align:center;'>");
        html.append("<tr><td>ID</td><td>游戏名</td><td>图标</td></tr>");
        list.forEach(game ->{
            html.append("<tr>");
            html.append("<td>");
            html.append(game.getId());
            html.append("</td>");
            html.append("<td>");
            html.append(game.getName());
            html.append("</td>");
            html.append("<td>");
            html.append("<img src='");
            html.append(game.getIcon());
            html.append("' style='width:50px;height:50px'>");
            html.append("</td>");
            html.append("</tr>");
        });
        html.append("</table>");
        html.append("<h2 style='color:red;'>游戏列表结束</h2>");
        //一张图片
        html.append("<img src='http://cd.ray-live.cn/imgs/headpic/pic_0.jpg'>");
        //设置邮件内容
        helper.setText(html.toString(),true);
        javaMailSender.send(mimeMessage);
    }

6.3 本地图片

复制代码
    @Test
    void f3() throws Exception {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        //multipart true 表示支持上传文件
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        helper.setFrom(mailProperties.getUsername());
        helper.setTo(to);
        helper.setSubject("本地图片上传测试");
        //内容
        StringBuilder html = new StringBuilder();
        html.append("<img src='cid:ei'>");
        helper.setText(html.toString(),true);
        //本地找一张图片
        File file = new File("D://save/pic_140.jpg");
        //文件上传
        helper.addInline("ei",file);
        //发送
        javaMailSender.send(mimeMessage);
​
    }

6.4 附件

复制代码
    @Test
    void f4() throws Exception {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        helper.setFrom(mailProperties.getUsername());
        helper.setTo(to);
        helper.setSubject("测试附件");
        String html = "<img src='cid:ei'>";
        helper.setText(html,true);
        File img = new File("D://save/pic_140.jpg");
        helper.addInline("ei",img);
        helper.addAttachment("abc.png",img);
        //附件
        File fu1 = new File("D://save/t1.docx");
        helper.addAttachment("test.docx",fu1);
        //发送
        javaMailSender.send(mimeMessage);
    }

7 、封装工具类

复制代码
package com.javasm.qingqing.common.utils;
​
import jakarta.annotation.Resource;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
​
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Set;
​
​
@Component
public class MailUtil {
​
    @Resource
    JavaMailSender javaMailSender;
    @Resource
    MailProperties mailProperties;
​
    public void sendMail(String email , String title, String count, File... files){
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
​
        try {
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
            //配置发件人
            helper.setFrom(mailProperties.getUsername());
            //配置收件人
            helper.setTo(email);
            //标题
            helper.setSubject(title);
            //内容
            helper.setText(count,true);
            //判断附件
            if (files != null){
                for (File file : files){
                    helper.addAttachment(file.getName(),file);
                }
            }
            //发送
            javaMailSender.send(mimeMessage);
​
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
​
    private static final Set<String> IMAGE_EXTENSIONS = Set.of(
            "jpg","jpeg","png","gif","bmp","wbp"
    );
​
    //用来判断,file是不是图片类型
    public boolean isImage(File file){
        //文件对象null || 文件不存在||文件不是文件是文件夹
        if (file == null || !file.exists() || !file.isFile()){
            return false;
        }
        //扩展名
        String fileName = file.getName();
        //后缀
        int indexOf = fileName.lastIndexOf(".");
        if (indexOf == -1) return false;
        //获取扩展名
        String extension = fileName.substring(indexOf + 1).toLowerCase();
        if (!IMAGE_EXTENSIONS.contains(extension)){
            return false;
        }
        //校验实际的内容是不是
        try {
            BufferedImage image = ImageIO.read(file);
            return image != null;
        } catch (IOException e) {
            return false;
        }
    }
​
}
​

8、重置密码

前端页面,点击重置按钮 → 请求API接口 → 服务端发送邮件,到用户邮箱 → 用户点击链接,跳转到前端页面 → 获取参数,传入后端接口 → 判断参数是否正确,返回对应的json → 前端确认页面,可以根据返回的不同json提示不同的信息 → 跳转到用户修改密码的表单页面

8.1 引入Thymeleaf

是SpringBoot默认提供的页面策略。

html的模板,不需要手动拼接HTML

复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

8.2 创建页面

在resources/templates 文件夹中,新建HTML

这是一个默认的存储thymeleaf页面的路径,不能随意更改,必须叫templates

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>密码找回</title>
</head>
<body>
    <div>
        <h1 style="color: gold">密码找回</h1>
    </div>
    <div>
        <h2 style="color: aqua">下方是找回密码的超链接,请点击</h2>
        <h3>亲爱的用户:<span th:text="${username}"></span></h3>
    </div>
    <div>
        <a th:href="@{http://127.0.0.1:8088/login/checkemail(t=${token})}">点击我找回密码,此链接10分钟内有效</a>
    </div>
</body>
</html>

8.3 发送邮件接口

复制代码
@RestController
@RequestMapping("/login")
public class LoginController {
    @Resource
    LoginService loginService;
​
    @GetMapping("/password/back")
    public R backPassword(String email){
        loginService.sendBackPasswordEmail(email);
        return R.ok();
    }
}
  • Service
复制代码
package com.javasm.qingqing.login.service.impl;
​
import com.javasm.qingqing.adminuser.entity.AdminUser;
import com.javasm.qingqing.adminuser.service.AdminUserService;
import com.javasm.qingqing.common.utils.JWTUtil;
import com.javasm.qingqing.common.utils.MailUtil;
import com.javasm.qingqing.login.service.LoginService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
​
@Service
public class LoginServiceImpl implements LoginService {
    @Resource
    MailUtil mailUtil;
    @Resource
    TemplateEngine templateEngine;
​
    @Resource
    AdminUserService adminUserService;
​
    @Override
    public void sendBackPasswordEmail(String email) {
        //根据email地址,查询用户信息
        AdminUser adminUser = adminUserService.getByEmail(email);
        if (adminUser != null){
            //发送邮件
            //确认 发送的html是什么
            //从thymeleaf中获取html代码
            //import org.thymeleaf.context.Context;
            Context context = new Context();
            context.setVariable("username",adminUser.getNickname());
            //获取token字符串
            String token = JWTUtil.generateEmail(email);
            context.setVariable("token",token);
            //获取静态html   backpassword.html 页面 传入 值 context对象
            String html = templateEngine.process("backpassword",context);
            mailUtil.sendMail(email,"密码找回",html);
        }
    }
}
​

8.4 修改JWTUtil

复制代码
public class JWTUtil {
    //生成的服务器秘钥
    private static final String keyStr = "Oon2+pDRDSxQ8uVt4zuDc1ogr9p0JjVJlNd2aVMqPWE=";
​
    private static final Integer expUidTime = 1000 * 60 * 60 * 24 * 15;
    private static final Integer expEmailTime = 1000 * 60 * 10;
​
    public static final String Server_Token = "javasm_token";
​
​
    public static String generateEmail(String email) {
        Map<String,Object> map = new HashMap<>();
        map.put("email",email);
        return generateToken(map,expEmailTime);
    }
    //加密的方法,把uid加密成Token字符串
    public static String generateUid(String uid){
        Map<String,Object> map = new HashMap<>();
        map.put("uid",uid);
        return generateToken(map,expUidTime);
    }
​
    private static String generateToken(Map<String,Object> map,Integer expTime){
        //因为加密的字符串,要给一个过期时间
        //记录什么时候生成的Token
        Date time = new Date();
        //设置过期时间,这个字符串多久过期失效
        Date lastTime = new Date(time.getTime() + expTime);
        //生成一个Key
        SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(keyStr));
        return Jwts.builder()
                .setClaims(map) //加密的数据
                .setIssuedAt(time)  //标准签名,加密的时间
                .setExpiration(lastTime)    //过期时间
                .signWith(key)  //指定签名Key,Header中的秘钥
                .compact();
    }
​
    public static String parseEmail(String token){
        return parse("email",token);
    }
​
    public static String parseUid(String token){
        return parse("uid",token);
    }
    //解密
    private static String parse(String mapKey ,String token){
        //解密 获取和加密一样的key
        SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(keyStr));
​
        String string = null;
        try {
            Jws<Claims> jws = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            Claims body = jws.getBody();
            //获取加密前的字符串
            string = body.get(mapKey, String.class);
        } catch (ExpiredJwtException e) {
            throw new JavasmException(ExceptionEnum.Token_Expired_Error);
        } catch (UnsupportedJwtException e) {
            throw new JavasmException(ExceptionEnum.Token_Unsupported_Error);
        } catch (SignatureException e) {
            throw new JavasmException(ExceptionEnum.Token_Signature_Error);
        } catch (Exception e) {
            throw new JavasmException(ExceptionEnum.SYSTEM_ERROR);
        }
        return string;
    }
​
    //生成服务端知道的秘钥,只有服务端能知道,不能泄露
    //不能随意更改,如果秘钥更改了,之前生成的所有Token都会失效
    //这个方法不会频繁执行,只会在生产秘钥的时候执行1次
    private static void  generateKey(){
        //指定算法
        SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        //生成加密的字符串
        String encode = Encoders.BASE64.encode(key.getEncoded());
​
        System.out.println(encode);
    }
​
​
​
}

8.5 校验Token的接口

复制代码
    @GetMapping("/checkemail")
    public R checkEmail(@RequestParam("t") String token){
        AdminUser adminUser = loginService.checkEmail(token);
        return R.ok(adminUser);
    }
复制代码
    @Override
    public AdminUser checkEmail(String token) {
        String email = JWTUtil.parseEmail(token);
        AdminUser adminUser = adminUserService.getByEmail(email);
        return adminUser;
    }

Log4J2

1、介绍

日志技术

用来记录程序运行的轨迹

SpringBoot自带了一个日志框架,logback

log4j2和 logback 都使用了统一的日志门面

日志门面是一个类似接口的框架,所有的日志框架,都实现了日志门面。

在使用日志的时候,如果从locback切换到log4j2是不会影响编码的。

  • 为什么需要使用日志技术?

现在我们的代码,产生的日志和异常信息,都是输出在idea的控制台上。

以后,代码发布到线上之后,程序是要在Linux下运行的。

Linux不能在一个idea中运行程序,也就没有控制台,也无法打断点去debug

在linux下调试Bug,只能通过输出日志去调试,在线上排查bug,必须通过观察日志去排查。

Log4j2能够实现,把不同级别的日志,分配到不同的文件夹和文件中,方便排查。

目前国内最流行的日志框架。

  • 三个组件

    • Logger

    • appender

    • Layout

2、日志级别

ALL < Trace < Debug < Info < Warn < Error < Fatal < OFF

常用:Debug,Info,Warn,Error

  • Debug

    • 调试阶段,使用的日志级别

    • 之前都是使用System.out输出日志,使用完了之后,必须删除,不能留下痕迹

    • 改变使用System.out的习惯

    • 调试阶段的日志,不需要删除,可以一直保留

    • 只需要在正式运行的时候,把不想看的debug级别日志屏蔽掉就可以了

  • Info

    • 程序正常运行过程中,输出的日志

    • 一般这个级别是不会被屏蔽的

    • 有一些位置,值的正常输出,或者提醒,包括线上调试追踪bug

    • 一般的默认级别的日志

  • Warn

    • 没有严格的要求在什么位置使用

    • 程序运行过程中,一些值存在危险/有可能 产生异常

    • 作为危险的提示

  • Error

    • 出现在异常中

    • 异常处理之后,显示的日志级别

    • Error的出现,代表程序抛出了Exception,需要让程序员/运维 知道/注意 的内容

    • 一般Error级别的日志,都需要通过邮件通知管理员/项目组成员

3、测试

复制代码
@RestController
@RequestMapping("/admin/user")
public class AdminUserController {
    /**
     * import org.apache.logging.log4j.LogManager;
     * import org.apache.logging.log4j.Logger;
     */
    Logger logger = LogManager.getLogger(AdminUserController.class);
​
    @GetMapping("/f1")
    public R f1(){
        logger.debug("-------------------我是Debug");
        logger.info("-------------------我是Info");
        logger.warn("-------------------我是警告");
        logger.error("-------------------我是Error!!!!");
        return R.ok();
    }
}
  • Slf4j
复制代码
​
import com.javasm.qingqing.common.exception.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
@RequestMapping("/admin/user2")
@Slf4j
public class AdminUserController2 {
​
    @GetMapping("/f1")
    public R f1(){
        log.debug("-------------------我是Debug");
        log.info("-------------------我是Info");
        log.warn("-------------------我是警告");
        log.error("-------------------我是Error!!!!");
        return R.ok();
    }
}
​

4、引入依赖

因为当前框架已经有了Logback日志,需要在web的依赖中,把它移除掉

需要把web的依赖放到第一位,并且检查项目中,不能有多个Web的依赖

把公共模块common中的web依赖,拿到admin-api的pom.xml中

复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
  • 加入新的依赖
复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>
  • 添加log4j2.xml
复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 记录的一些变量  没有生效-->
    <appenders>
        <!-- 控制台输出的日志格式 -->
        <console name="Console" target="SYSTEM_OUT">
            <PatternLayout
                    pattern="%d{HH:mm:ss} %-5level %class{36} %L %M ----- %msg%n" />
        </console>
        <!-- fileName:输出路径 filePattern:命名规则 -->
        <!-- name 配置的名字,唯一 -->
        <!-- 这个配置的作用,是指定 DEBUG级别的日志,输出到对应文件中-->
        <RollingFile name="RollingFileDebug"
                     fileName="D:/logs/debug.log"
                     filePattern="D:/logs/$${date:yyyy-MM-dd}/debug-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                <!--Filters决定日志事件能否被输出。过滤条件有三个值:ACCEPT(接受), DENY(拒绝) or NEUTRAL(中立).-->
                <!--如果接受/拒绝,日志到这里就会记录,然后结束,如果是中立,则会继续记录.-->
                <!--
                    level:将被过滤的级别。
                    onMatch:默认值是NEUTRAL
                    onMismatch:默认是DENY
                -->
                <!-- 过滤的日志级别是debug-->
                <ThresholdFilter level="DEBUG"/>
                <!-- 如果当前记录的日志级别>info则返回 onMatch 的值,小于info则onMismatch 的值-->
                <ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL" />
            </Filters>
            <!-- 输出格式 -->
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %class{36} %L %M - %msg%n" />
​
            <Policies>
                <!-- 单个日志文件的大小限制 -->
                <SizeBasedTriggeringPolicy size="100 MB" />
            </Policies>
            <!-- 最多保留20个日志文件 -->
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
​
        <RollingFile name="RollingFileInfo"
                     fileName="D:/logs/info.log"
                     filePattern="D:/logs/$${date:yyyy-MM-dd}/info-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                <ThresholdFilter level="INFO" />
                <ThresholdFilter level="WARN" onMatch="DENY"
                                 onMismatch="NEUTRAL" />
            </Filters>
            <!-- 输出格式 -->
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" />
            <Policies>
                <!-- SizeBasedTriggeringPolicy单个文件的大小限制 -->
                <SizeBasedTriggeringPolicy size="100 MB" />
            </Policies>
            <!-- DefaultRolloverStrategy同一个文件下的最大文件数 -->
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
​
        <RollingFile name="RollingFileWarn"
                     fileName="D:/logs/warn.log"
                     filePattern="D:/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                <ThresholdFilter level="WARN" />
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL" />
            </Filters>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" />
            <Policies>
                <!--<TimeBasedTriggeringPolicy modulate="true" interval="1"/> -->
                <SizeBasedTriggeringPolicy size="100 MB" />
            </Policies>
            <!--最多保留20个日志文件 -->
            <DefaultRolloverStrategy max="20" min="0" />
        </RollingFile>
​
        <RollingFile name="RollingFileError"
                     fileName="D:/logs/error.log"
                     filePattern="D:/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                <ThresholdFilter level="ERROR" />
                <ThresholdFilter level="FATAL" onMatch="DENY" onMismatch="NEUTRAL" />
            </Filters>
            <PatternLayout attern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" />
            <Policies>
                <!--<TimeBasedTriggeringPolicy modulate="true" interval="1"/> -->
                <SizeBasedTriggeringPolicy size="100 MB" />
            </Policies>
            <!--最多保留20个日志文件 -->
            <DefaultRolloverStrategy max="20" min="0" />
        </RollingFile>
        <!--  name 标签名字
            subject 邮件标题
            to:发送给谁,多个人用,分隔  aaa@163.com,bbb@qq.com
            from:发送邮件的账户  (需要在邮箱后台,设置中,开启SMTP服务)
            smtpProtocol  smtp 发送的协议
            smtpUsername  发送账号
            smtpPassword  发送密码
            smtpHost  服务器地址:
                                POP3服务器: pop.163.com
                                SMTP服务器: smtp.163.com
                                IMAP服务器: imap.163.com
            smtpPort 发送端口, 163的是465  加上会报错 Got bad greeting from SMTP host smtp.163.com, port 465 , response [EOF]
​
        -->
        <!--<SMTP name="Mail"
              subject="过往云烟的异常信息"
              to="chengdujavasm@126.com,cxds5536@163.com"
              from="19008239181@163.com"
              smtpPassword="KXNZHOZDMLTVWHOZ"
              smtpUsername="19008239181@163.com"
              smtpProtocol="smtp"
              smtpHost="smtp.163.com"
              bufferSize="10"
              ignoreExceptions="false"
              smtpDebug="true"
        >
            <ThresholdFilter level="ERROR" onMatch="ACCEPT"/>
            <PatternLayout>
                <pattern>%d %p [%C] [%t] %m%n</pattern>
            </PatternLayout>
        </SMTP>-->
    </appenders>
​
    <loggers>
        <!-- 设置当前的默认日志级别-->
        <!--是SpringBoot项目的默认日志级别-->
        <root level="INFO">
            <!--配置生效的位置,如果不配置则不输出-->
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileDebug"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
        <!--额外配置的logger-->
        <!--记录druid-sql的记录-->
        <logger name="druid.sql.Statement" level="debug" additivity="false">
            <appender-ref ref="druidSqlRollingFile"/>
        </logger>
        <!-- log4j2自带的过滤标签,额外指定 特定包的日志级别 -->
        <logger name="org.springframework.web" level="error"></logger>
        <logger name="org.springframework.core" level="error"></logger>
        <logger name="org.springframework.beans" level="error"></logger>
        <logger name="org.springframework.boot" level="error"></logger>
        <!--单独设置自己的包 为 debug级别-->
        <!--单独配置 自己的包-->
        <logger name="com.javasm" level="debug" additivity="true">
            <!--输入日志到邮件-->
            <!--<appender-ref ref="Mail" />-->
        </logger>
        <!--异步日志配置-->
        <!--<AsyncLogger name="org.springframework" level="error" includeLocation="true">
        <AppenderRef ref="RollingFileError"></AppenderRef>
        </AsyncLogger>
        <AsyncLogger name="org.mybatis" level="error" includeLocation="true">
        <AppenderRef ref="RollingFileError"></AppenderRef>
        </AsyncLogger>
        <AsyncLogger name="com.alibaba.druid" level="error"  includeLocation="true">
        <AppenderRef ref="RollingFileError"></AppenderRef>
        </AsyncLogger>
​
        <AsyncRoot level="debug" includeLocation="true">
        <appender-ref ref="Console"/>
        <appender-ref ref="RollingFileInfo"/>
        <appender-ref ref="RollingFileWarn"/>
        <appender-ref ref="RollingFileError"/>
        </AsyncRoot>-->
    </loggers>
</configuration>
  • 符号
复制代码
PatternLayout格式化符号说明:
%p 或 %level:输出日志信息的优先级,即DEBUG,INFO,WARN,ERROR,FATAL。
%d:输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,如:%d{yyyy/MM/dd HH:mm:ss,SSS}。
%r:输出自应用程序启动到输出该log信息耗费的毫秒数。
%t:输出产生该日志事件的线程名。
%class:输出日志信息所属的类目,通常就是所在类的全名。
%M:输出产生日志信息的方法名。
%F:输出日志消息产生时所在的文件名称。
%L:输出代码中的行号。
%m 或%msg或%message::输出代码中指定的具体日志信息。
%n:输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”。
%x:输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%%:输出一个“%”字符。
另外,还可以在%与格式字符之间加上修饰符来控制其最小长度、最大长度、和文本的对齐方式。如:
1)%-20:“-”号表示左对齐,不满足20个字符则以空格代替。
2)%.30:指定输出category的名称,最大的长度是30,如果category的名称长度大于30的话,就会将左边多出的字符截掉,但小于30的话也不会补空格。
相关推荐
麦麦鸡腿堡2 小时前
Java_类的加载
java·开发语言
JIngJaneIL2 小时前
基于java + vue校园快递物流管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js
超级大只老咪3 小时前
数组的正向存储VS反向存储(Java)
java·开发语言·python
毕设源码-赖学姐3 小时前
【开题答辩全过程】以 基于JSP的物流信息网的设计与实现为例,包含答辩的问题和答案
java·开发语言
Honmaple3 小时前
Spring AI 2.x 发布:全面拥抱 Java 21,Redis 史诗级增强
java·人工智能·spring
代码or搬砖3 小时前
Java集合-Set讲解
java·开发语言
渣娃-小晴晴3 小时前
java集合在并发环境下应用时的注意事项
java·后端
北极糊的狐3 小时前
若依系统报错net::ERR_CONNECTION_TIMED_OUT的原因
java·windows·sql·mybatis