发邮件
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的话也不会补空格。