概览
日志服务是项目中必不可少的的一个,通过面向切面的编程,我们可以统一处理日志逻辑,现在市面上有很多优秀的日志框架,但是者不妨碍我们通过自己编写的日志框架了解其底层原理。
思路
- aop采集日志
- 把日志存入一个有序的、堵塞的队列中
- 单独开一个线程通过异步方式,把队列中的日志写入到文件中。
核心代码
- pom配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>log-manager</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>log-manager</name>
<description>log-manager</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入aop插件包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 日志写入处理类
package com.example.logmanager.utils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@Component
public class LogManager {
private static BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
private static final String filePath = "C:\\Users\\Administrator\\Desktop\\123.log";
public LogManager() {
new LogThread().start();
}
public static void addLog(String msg){
blockingQueue.add(msg);
}
class LogThread extends Thread {
@Override
public void run() {
while (true) {
String log = blockingQueue.poll();
if(StringUtils.hasLength(log)) {
// 写入本地
FileUtils.writeText(filePath, log, true);
}
}
}
}
}
- 切面逻辑
package com.example.logmanager.aop;
import com.example.logmanager.utils.LogManager;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
@Component
@Aspect
public class AopLog {
@Pointcut("execution(public * com.example.logmanager.controller..*(..))")
public void log(){};
@Before("log()")
public void before(JoinPoint joinPoint){
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = Objects.requireNonNull(requestAttributes).getRequest();
LogManager.addLog("[请求的url]:" + request.getRequestURL());
LogManager.addLog("[请求的Ip]:" + request.getRemoteAddr());
LogManager.addLog("[类名 Class]:" + joinPoint.getSignature().getDeclaringTypeName());
}
}
- 文件操作工具类
package com.example.logmanager.utils;
import java.io.*;
import java.nio.channels.FileChannel;
public class FileUtils {
/*判断文件是否存在*/
public static boolean isExists(String filePath) {
File file = new File(filePath);
return file.exists();
}
/*判断是否是文件夹*/
public static boolean isDir(String path) {
File file = new File(path);
if (file.exists()) {
return file.isDirectory();
} else {
return false;
}
}
/**
* 文件或者目录重命名
*
* @param oldFilePath 旧文件路径
* @param newName 新的文件名,可以是单个文件名和绝对路径
* @return
*/
public static boolean renameTo(String oldFilePath, String newName) {
try {
File oldFile = new File(oldFilePath);
//若文件存在
if (oldFile.exists()) {
//判断是全路径还是文件名
if (newName.indexOf("/") < 0 && newName.indexOf("\\") < 0) {
//单文件名,判断是windows还是Linux系统
String absolutePath = oldFile.getAbsolutePath();
if (newName.indexOf("/") > 0) {
//Linux系统
newName = absolutePath.substring(0, absolutePath.lastIndexOf("/") + 1) + newName;
} else {
newName = absolutePath.substring(0, absolutePath.lastIndexOf("\\") + 1) + newName;
}
}
File file = new File(newName);
//判断重命名后的文件是否存在
if (file.exists()) {
System.out.println("该文件已存在,不能重命名");
} else {
//不存在,重命名
return oldFile.renameTo(file);
}
} else {
System.out.println("原该文件不存在,不能重命名");
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/*文件拷贝操作*/
public static void copy(String sourceFile, String targetFile) {
File source = new File(sourceFile);
File target = new File(targetFile);
target.getParentFile().mkdirs();
FileInputStream fis = null;
FileOutputStream fos = null;
FileChannel in = null;
FileChannel out = null;
try {
fis = new FileInputStream(source);
fos = new FileOutputStream(target);
in = fis.getChannel();//得到对应的文件通道
out = fos.getChannel();//得到对应的文件通道
in.transferTo(0, in.size(), out);//连接两个通道,并且从in通道读取,然后写入out通道
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
if (fos != null) {
fos.close();
}
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*读取Text文件操作*/
public static String readText(String filePath) {
String lines = "";
try {
FileReader fileReader = new FileReader(filePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = null;
while ((line = bufferedReader.readLine()) != null) {
lines += line + "\n";
}
} catch (Exception e) {
e.printStackTrace();
}
return lines;
}
/*写入Text文件操作*/
public static void writeText(String filePath, String content, boolean isAppend) {
FileOutputStream outputStream = null;
OutputStreamWriter outputStreamWriter = null;
BufferedWriter bufferedWriter = null;
try {
outputStream = new FileOutputStream(filePath, isAppend);
outputStreamWriter = new OutputStreamWriter(outputStream);
bufferedWriter = new BufferedWriter(outputStreamWriter);
bufferedWriter.write(content);
bufferedWriter.newLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedWriter != null) {
bufferedWriter.close();
}
if (outputStreamWriter != null) {
outputStreamWriter.close();
}
if (outputStream != null) {
outputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 测试controller
package com.example.logmanager.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping
public String test(){
return "succ";
}
}