加了日志系统

This commit is contained in:
zlzw 2024-12-05 10:36:15 +08:00
parent 1803ef845c
commit 6ee610f4cc
11 changed files with 227 additions and 33 deletions

3
.gitignore vendored
View File

@ -35,4 +35,5 @@ build/
### dir ###
/live/
/databases/
/cache/
/cache/
/logs/

15
pom.xml
View File

@ -144,6 +144,21 @@
<artifactId>selenium-java</artifactId>
<version>4.26.0</version>
</dependency>
<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>
</dependencies>
<build>

View File

@ -1,29 +1,32 @@
package com.yutou;
import com.yutou.common.okhttp.HttpLoggingInterceptor;
import com.yutou.common.utils.ConfigTools;
import com.yutou.common.utils.Log;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication()
public class BilibiliApplication {
public static String version = "0.8.4";
public static String version = "0.9";
public static void main(String[] args) {
HttpLoggingInterceptor.setLog(false);
Log.i("启动版本", version);
HttpLoggingInterceptor.setLog(ConfigTools.load(ConfigTools.CONFIG, "HttpLog", Boolean.class, false));
SpringApplication.run(BilibiliApplication.class, args);
}
//TODO 优化 1 手动中断录制后,没有时间戳问题需要解决,看是提示转码还是有什么其他解决方案 | 改成了通过q来退出
//TODO 优化 2 创建nfo文件看看要不要改成录制后生成,或者触发打包指令再生成
//TODO 优化 3 录制完成前应该也允许查看礼物信息和SC以及弹幕,未停止录制改成从开始时间到当前时间的弹幕和礼物信息,已停止录制则询问是否转码
//TODO 优化 4 视频页面礼物的图标,有数据计算错误以及单项猛增的话导致其他项目无法查看的问题(可参考泛式死亡笔记录播) | 换成饼状图
//TODO 修复 1 开播时有概率连续触发创建nfo和记录视频到数据宽度问题.
//TODO 测试 1 需要测试网络中断下,弹幕重连机制
//TODO 测试 2 在导出jar包后再测试完整的录制功能
//TODO 测试 3 需要确认项目的内存占用情况
//TODO 开发 1 完成用户列表的所有功能,包括移出用户和筛选ck和登陆扫码二维码的优化
//TODO 优化 2 创建nfo文件看看要不要改成录制后生成,或者触发打包指令再生成|改了
//TODO 优化 3 录制完成前应该也允许查看礼物信息和SC以及弹幕,未停止录制改成从开始时间到当前时间的弹幕和礼物信息,已停止录制则询问是否转码|改了
//TODO 优化 4 视频页面礼物的图标,有数据计算错误以及单项猛增的话导致其他项目无法查看的问题(可参考泛式死亡笔记录播) | 换成饼状图 | 改了
//TODO 修复 1 开播时有概率连续触发创建nfo和记录视频到数据宽度问题. | 改了
//TODO 测试 1 需要测试网络中断下,弹幕重连机制|好像没问题
//TODO 测试 2 在导出jar包后再测试完整的录制功能|没问题
//TODO 测试 3 需要确认项目的内存占用情况|好像还行
//TODO 开发 1 完成用户列表的所有功能,包括移出用户和筛选ck和登陆扫码二维码的优化|改了
//TODO 开发 2 数据中心
//TODO 开发 3 视频页面打包导出视频和弹幕
//TODO 开发 4 弹幕随开播时间录制
//TODO 开发 5 追加关键词检测
//TODO 开发 4 弹幕随开播时间录制|有了
//TODO 开发 5 追加关键词检测|加了
}

View File

@ -15,7 +15,6 @@ import com.yutou.biliapi.bean.websocket.WebSocketBody;
import com.yutou.biliapi.bean.websocket.WebSocketHeader;
import com.yutou.biliapi.bean.websocket.live.WSData;
import com.yutou.biliapi.databases.BiliBiliLoginDatabase;
import com.yutou.biliapi.databases.BiliLiveDatabase;
import com.yutou.biliapi.utils.BytesUtils;
import com.yutou.bilibili.services.LiveDatabasesService;
import com.yutou.bilibili.services.LiveService;
@ -45,7 +44,6 @@ import java.util.concurrent.TimeUnit;
@Service
public class WebSocketServer {
ThreadPoolExecutor executor;
private static WebSocketServer instance;
Map<LiveRoomConfig, DanmuTask> roomMap;
private final List<String> userStopList = new ArrayList<>();//手动停止列表
@Resource
@ -164,15 +162,17 @@ public class WebSocketServer {
private final LiveRoomConfig roomConfig;
private final HeartbeatTask heartbeatTask;
private final boolean isUser;
private final String logTag;
public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId, boolean isUser) {
super(serverUri);
Log.i("WebSocketClientTh.WebSocketClientTh : " + serverUri);
logTag = "WebSocket-" + roomId.getRoomId();
Log.getDynamicLogger(logTag).info("WebSocketClientTh.WebSocketClientTh = {}", serverUri);
this.isUser = isUser;
this.roomConfig = roomId;
Brotli4jLoader.ensureAvailability();
heartbeatTask = new HeartbeatTask();
addHeader("User-Agent", ConfigTools.getUserAgent());
heartbeatTask = new HeartbeatTask();
connect();
}
@ -182,7 +182,7 @@ public class WebSocketServer {
heartbeatTask.setSocket(this);
heartbeatTask.sendInitAuthData();
new Timer().schedule(heartbeatTask, 1000, 30000);
Log.i("WebSocketClientTh.onOpen", roomConfig.getRoomId());
Log.getDynamicLogger(logTag).info("WebSocketClientTh.onOpen,{}", roomConfig.getRoomId());
}
@Override
@ -199,7 +199,7 @@ public class WebSocketServer {
@Override
public void onClose(int i, String s, boolean b) {
Log.e("WebSocketClientTh.onClose", "i = " + i + ", s = " + s + ", b = " + b, roomConfig.getRoomId(), heartbeatTask.socket.isOpen());
Log.getDynamicLogger(logTag).error("WebSocketClientTh.onClose. {},{},{}", "i = " + i + ", s = " + s + ", b = " + b, roomConfig.getRoomId(), heartbeatTask.socket.isOpen());
roomMap.remove(roomConfig);
heartbeatTask.cancel();
if (i == 1006) {
@ -211,7 +211,7 @@ public class WebSocketServer {
@Override
public void onError(Exception e) {
Log.i("WebSocketClientTh.onError", roomConfig.getRoomId());
Log.getDynamicLogger(logTag).info("WebSocketClientTh.onError,{}", roomConfig.getRoomId());
Log.e(e);
roomMap.remove(roomConfig);
heartbeatTask.cancel();
@ -239,7 +239,7 @@ public class WebSocketServer {
}
private void danmu(byte[] bytes) {
//Log.i("未压缩:" + new String(bytes));
Log.getDynamicLogger(logTag).info("未压缩:{}", new String(bytes));
}
private void unzipDanmu(byte[] bytes, boolean useHeader) {
@ -248,16 +248,14 @@ public class WebSocketServer {
DirectDecompress directDecompress = Decoder.decompress(bytes);
if (directDecompress.getResultStatus() == DecoderJNI.Status.DONE) {
WebSocketBody body = new WebSocketBody(directDecompress.getDecompressedData());
// Log.i("协议:" + useHeader + " 命令数:" + body.getBodyList().size());
Log.getDynamicLogger(logTag).info("协议:{},命令数:{}", useHeader, +body.getBodyList().size());
for (JSONObject json : body.getBodyList()) {
WSData parse = WSData.parse(json);
liveDatabasesService.getLiveDatabase(roomConfig.getRoomId()).addSource(parse);
// Log.i("解压:" + parse);
Log.getDynamicLogger(logTag).info("解压:{}", parse);
}
// Log.i();
// Log.i();
} else {
Log.e(new RuntimeException("解压失败"));
Log.getDynamicLogger(logTag).error(new RuntimeException("解压失败"));
}
} catch (Exception e) {
Log.e(e);
@ -295,10 +293,10 @@ public class WebSocketServer {
json.put("uid", 0);
}
LoginCookieDatabaseBean cookie = BiliBiliLoginDatabase.getInstance().getCookie(roomConfig.getLoginUid());
Log.d("cookie:", cookie, "RoomId:" + roomConfig);
try {
json.put("roomid", new BigInteger(roomConfig.getRoomId()));
json.put("protover", 3);
// json.put("buvid3", BiliUserUtils.getBuvid(roomConfig.isLogin() ? BiliBiliLoginDatabase.getInstance().getCookie(roomConfig.getRoomId()) : null));
json.put("platform", "web");
json.put("type", 2);
json.put("key", roomConfig.getLiveInfo().getToken());

View File

@ -1,13 +1,17 @@
package com.yutou.bilibili.Tools;
import com.yutou.biliapi.bean.live.LiveRoomInfo;
import com.yutou.bilibili.services.SystemService;
import com.yutou.common.utils.DynamicLogFile;
import com.yutou.common.utils.Log;
import jakarta.annotation.Resource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 服务启动后执行
@ -16,11 +20,11 @@ import java.util.logging.Logger;
public class ApplicationInit implements ApplicationRunner {
@Resource
SystemService systemConfigService;
Logger logger = LogManager.getLogger(ApplicationInit.class);
@Override
public void run(ApplicationArguments args) throws Exception {
Logger logger = Logger.getLogger("ApplicationInit");
logger.log(Level.INFO, "服务启动后执行");
logger.info("服务启动后执行");
systemConfigService.start();
}

View File

@ -81,7 +81,9 @@ public class SystemService {
}
public void stop() {
scheduled.cancel(true);
if(scheduled!=null) {
scheduled.cancel(true);
}
videoService.stopAll();
}

View File

@ -8,13 +8,14 @@ import okio.Buffer;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public class HttpLoggingInterceptor implements Interceptor {
private static final String TAG = "HttpLogging";
private static final Charset UTF8 = Charset.forName("UTF-8");
private static final Charset UTF8 = StandardCharsets.UTF_8;
private volatile Level printLevel = Level.BODY;
private java.util.logging.Level colorLevel;
@ -51,7 +52,7 @@ public class HttpLoggingInterceptor implements Interceptor {
private void log(String message) {
//logger.log(colorLevel, message);
if (prLog) {
Log.i(TAG, message);
Log.getDynamicLogger(TAG).info(message);
}
//Log.e(TAG,message);
}

View File

@ -0,0 +1,123 @@
package com.yutou.common.utils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.yutou.bilibili.Tools.DateFormatUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.appender.rolling.*;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class DynamicLogFile {
// 创建一个缓存用于存储Logger对象最大容量为1000过期时间为10分钟
static Cache<String, Logger> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterAccess(10, TimeUnit.MINUTES)
.removalListener(it -> {
if (it.wasEvicted()) {
if (it.getKey() != null) {
String loggerName = (String) it.getKey();
remove(loggerName, true);
}
}
})
.build();
// 根据loggerName获取Logger对象如果缓存中不存在则创建一个新的Logger对象并放入缓存
public static Logger getLogger(String loggerName) {
try {
return cache.get(loggerName, () -> {
configureLogger(loggerName);
return LogManager.getLogger(loggerName);
});
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
// 配置Logger对象
private static void configureLogger(String loggerName) {
LoggerContext context = (LoggerContext) LogManager.getContext(false);
Configuration config = context.getConfiguration();
// 创建日志格式
Layout<String> layout = PatternLayout.newBuilder()
.withPattern("%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%thread] (%F:%L) : %m%n")
.build();
// 创建时间触发策略
TimeBasedTriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder()
.build();
// 创建文件大小触发策略
SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy("100 MB");
// 创建组合触发策略
CompositeTriggeringPolicy triggeringPolicy = CompositeTriggeringPolicy.createPolicy(timePolicy, sizePolicy);
// 创建滚动文件Appender
RollingFileAppender appender = RollingFileAppender.newBuilder()
.setName(loggerName)
.withFileName("logs" + "/" + DateFormatUtils.getInstance().format(new Date(), "yyyy-MM-dd") + "/" + loggerName + ".log")
.withFilePattern("logs" + "/" + "%d{yyyy-MM-dd}" + "/" + loggerName + "-%i.log.gz")
.setLayout(layout)
.setImmediateFlush(true)
.withAppend(true)
.setIgnoreExceptions(false)
.withPolicy(triggeringPolicy)
.build();
appender.start();
config.addAppender(appender);
// 获取Logger对象
org.apache.logging.log4j.core.Logger coreLogger = context.getLogger(loggerName);
if (coreLogger == null) {
throw new IllegalStateException("Logger with name " + loggerName + " does not exist.");
}
// 将Appender添加到Logger对象中
coreLogger.addAppender(appender);
coreLogger.setLevel(Level.ALL);
coreLogger.setAdditive(false);
// 更新Logger对象
context.updateLoggers();
}
// 移除Logger对象
public static void remove(String loggerName) {
remove(loggerName, false);
}
// 私有方法移除Logger对象isAuto参数用于判断是否是自动移除
private static void remove(String loggerName, boolean isAuto) {
if (!isAuto) {
cache.invalidate(loggerName);
}
LoggerContext context = (LoggerContext) LogManager.getContext(false);
Configuration config = context.getConfiguration();
org.apache.logging.log4j.core.Logger coreLogger = context.getLogger(loggerName);
Appender appender = config.getAppender(loggerName);
if (appender != null) {
appender.stop();
coreLogger.removeAppender(appender);
}
config.getAppenders().remove(loggerName);
context.updateLoggers();
}
}

View File

@ -2,6 +2,7 @@ package com.yutou.common.utils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.StackLocatorUtil;
@ -11,6 +12,14 @@ public class Log {
System.out.println();
}
public static Logger getDynamicLogger(String loggerName) {
return DynamicLogFile.getLogger(loggerName);
}
public static void removeDynamicLogger(String loggerName) {
DynamicLogFile.remove(loggerName);
}
public static void i(Object... log) {
if (!((boolean) ConfigTools.load(ConfigTools.CONFIG, "logcat"))) {
return;

View File

@ -1,4 +1,5 @@
server.port=8880
logging.config=classpath:log4j2.xml
logging.file.path=./logs
logging.level.com.log.controller = trace
# 启用Hibernate SQL日志

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!-- 控制台输出 -->
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p [%thread] (%F:%L) : %m%n%throwable"/>
</Console>
<!-- 动态路由日志文件 -->
<Routing name="RoutingAppender">
<Routes pattern="$${date:yyyy-MM-dd}">
<Route>
<RollingFile name="Rolling-${date:yyyy-MM-dd}" fileName="logs/${date:yyyy-MM-dd}/system.log"
filePattern="logs/%d{yyyy-MM-dd}/system-%i.log.gz">
<PatternLayout>
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%thread] (%F:%L) : %m%n%throwable</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="1024 MB"/>
</Policies>
</RollingFile>
</Route>
</Routes>
</Routing>
</Appenders>
<Loggers>
<!-- 根日志记录器 -->
<Root level="info">
<AppenderRef ref="ConsoleAppender"/>
<AppenderRef ref="RoutingAppender"/>
</Root>
</Loggers>
</Configuration>