加了日志系统

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

1
.gitignore vendored
View File

@ -36,3 +36,4 @@ build/
/live/ /live/
/databases/ /databases/
/cache/ /cache/
/logs/

15
pom.xml
View File

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

View File

@ -1,29 +1,32 @@
package com.yutou; package com.yutou;
import com.yutou.common.okhttp.HttpLoggingInterceptor; 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.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication() @SpringBootApplication()
public class BilibiliApplication { public class BilibiliApplication {
public static String version = "0.8.4"; public static String version = "0.9";
public static void main(String[] args) { 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); SpringApplication.run(BilibiliApplication.class, args);
} }
//TODO 优化 1 手动中断录制后,没有时间戳问题需要解决,看是提示转码还是有什么其他解决方案 | 改成了通过q来退出 //TODO 优化 1 手动中断录制后,没有时间戳问题需要解决,看是提示转码还是有什么其他解决方案 | 改成了通过q来退出
//TODO 优化 2 创建nfo文件看看要不要改成录制后生成,或者触发打包指令再生成 //TODO 优化 2 创建nfo文件看看要不要改成录制后生成,或者触发打包指令再生成|改了
//TODO 优化 3 录制完成前应该也允许查看礼物信息和SC以及弹幕,未停止录制改成从开始时间到当前时间的弹幕和礼物信息,已停止录制则询问是否转码 //TODO 优化 3 录制完成前应该也允许查看礼物信息和SC以及弹幕,未停止录制改成从开始时间到当前时间的弹幕和礼物信息,已停止录制则询问是否转码|改了
//TODO 优化 4 视频页面礼物的图标,有数据计算错误以及单项猛增的话导致其他项目无法查看的问题(可参考泛式死亡笔记录播) | 换成饼状图 //TODO 优化 4 视频页面礼物的图标,有数据计算错误以及单项猛增的话导致其他项目无法查看的问题(可参考泛式死亡笔记录播) | 换成饼状图 | 改了
//TODO 修复 1 开播时有概率连续触发创建nfo和记录视频到数据宽度问题. //TODO 修复 1 开播时有概率连续触发创建nfo和记录视频到数据宽度问题. | 改了
//TODO 测试 1 需要测试网络中断下,弹幕重连机制 //TODO 测试 1 需要测试网络中断下,弹幕重连机制|好像没问题
//TODO 测试 2 在导出jar包后再测试完整的录制功能 //TODO 测试 2 在导出jar包后再测试完整的录制功能|没问题
//TODO 测试 3 需要确认项目的内存占用情况 //TODO 测试 3 需要确认项目的内存占用情况|好像还行
//TODO 开发 1 完成用户列表的所有功能,包括移出用户和筛选ck和登陆扫码二维码的优化 //TODO 开发 1 完成用户列表的所有功能,包括移出用户和筛选ck和登陆扫码二维码的优化|改了
//TODO 开发 2 数据中心 //TODO 开发 2 数据中心
//TODO 开发 3 视频页面打包导出视频和弹幕 //TODO 开发 3 视频页面打包导出视频和弹幕
//TODO 开发 4 弹幕随开播时间录制 //TODO 开发 4 弹幕随开播时间录制|有了
//TODO 开发 5 追加关键词检测 //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.WebSocketHeader;
import com.yutou.biliapi.bean.websocket.live.WSData; import com.yutou.biliapi.bean.websocket.live.WSData;
import com.yutou.biliapi.databases.BiliBiliLoginDatabase; import com.yutou.biliapi.databases.BiliBiliLoginDatabase;
import com.yutou.biliapi.databases.BiliLiveDatabase;
import com.yutou.biliapi.utils.BytesUtils; import com.yutou.biliapi.utils.BytesUtils;
import com.yutou.bilibili.services.LiveDatabasesService; import com.yutou.bilibili.services.LiveDatabasesService;
import com.yutou.bilibili.services.LiveService; import com.yutou.bilibili.services.LiveService;
@ -45,7 +44,6 @@ import java.util.concurrent.TimeUnit;
@Service @Service
public class WebSocketServer { public class WebSocketServer {
ThreadPoolExecutor executor; ThreadPoolExecutor executor;
private static WebSocketServer instance;
Map<LiveRoomConfig, DanmuTask> roomMap; Map<LiveRoomConfig, DanmuTask> roomMap;
private final List<String> userStopList = new ArrayList<>();//手动停止列表 private final List<String> userStopList = new ArrayList<>();//手动停止列表
@Resource @Resource
@ -164,15 +162,17 @@ public class WebSocketServer {
private final LiveRoomConfig roomConfig; private final LiveRoomConfig roomConfig;
private final HeartbeatTask heartbeatTask; private final HeartbeatTask heartbeatTask;
private final boolean isUser; private final boolean isUser;
private final String logTag;
public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId, boolean isUser) { public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId, boolean isUser) {
super(serverUri); super(serverUri);
Log.i("WebSocketClientTh.WebSocketClientTh : " + serverUri); logTag = "WebSocket-" + roomId.getRoomId();
Log.getDynamicLogger(logTag).info("WebSocketClientTh.WebSocketClientTh = {}", serverUri);
this.isUser = isUser; this.isUser = isUser;
this.roomConfig = roomId; this.roomConfig = roomId;
Brotli4jLoader.ensureAvailability(); Brotli4jLoader.ensureAvailability();
heartbeatTask = new HeartbeatTask();
addHeader("User-Agent", ConfigTools.getUserAgent()); addHeader("User-Agent", ConfigTools.getUserAgent());
heartbeatTask = new HeartbeatTask();
connect(); connect();
} }
@ -182,7 +182,7 @@ public class WebSocketServer {
heartbeatTask.setSocket(this); heartbeatTask.setSocket(this);
heartbeatTask.sendInitAuthData(); heartbeatTask.sendInitAuthData();
new Timer().schedule(heartbeatTask, 1000, 30000); new Timer().schedule(heartbeatTask, 1000, 30000);
Log.i("WebSocketClientTh.onOpen", roomConfig.getRoomId()); Log.getDynamicLogger(logTag).info("WebSocketClientTh.onOpen,{}", roomConfig.getRoomId());
} }
@Override @Override
@ -199,7 +199,7 @@ public class WebSocketServer {
@Override @Override
public void onClose(int i, String s, boolean b) { 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); roomMap.remove(roomConfig);
heartbeatTask.cancel(); heartbeatTask.cancel();
if (i == 1006) { if (i == 1006) {
@ -211,7 +211,7 @@ public class WebSocketServer {
@Override @Override
public void onError(Exception e) { public void onError(Exception e) {
Log.i("WebSocketClientTh.onError", roomConfig.getRoomId()); Log.getDynamicLogger(logTag).info("WebSocketClientTh.onError,{}", roomConfig.getRoomId());
Log.e(e); Log.e(e);
roomMap.remove(roomConfig); roomMap.remove(roomConfig);
heartbeatTask.cancel(); heartbeatTask.cancel();
@ -239,7 +239,7 @@ public class WebSocketServer {
} }
private void danmu(byte[] bytes) { private void danmu(byte[] bytes) {
//Log.i("未压缩:" + new String(bytes)); Log.getDynamicLogger(logTag).info("未压缩:{}", new String(bytes));
} }
private void unzipDanmu(byte[] bytes, boolean useHeader) { private void unzipDanmu(byte[] bytes, boolean useHeader) {
@ -248,16 +248,14 @@ public class WebSocketServer {
DirectDecompress directDecompress = Decoder.decompress(bytes); DirectDecompress directDecompress = Decoder.decompress(bytes);
if (directDecompress.getResultStatus() == DecoderJNI.Status.DONE) { if (directDecompress.getResultStatus() == DecoderJNI.Status.DONE) {
WebSocketBody body = new WebSocketBody(directDecompress.getDecompressedData()); 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()) { for (JSONObject json : body.getBodyList()) {
WSData parse = WSData.parse(json); WSData parse = WSData.parse(json);
liveDatabasesService.getLiveDatabase(roomConfig.getRoomId()).addSource(parse); liveDatabasesService.getLiveDatabase(roomConfig.getRoomId()).addSource(parse);
// Log.i("解压:" + parse); Log.getDynamicLogger(logTag).info("解压:{}", parse);
} }
// Log.i();
// Log.i();
} else { } else {
Log.e(new RuntimeException("解压失败")); Log.getDynamicLogger(logTag).error(new RuntimeException("解压失败"));
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(e); Log.e(e);
@ -295,10 +293,10 @@ public class WebSocketServer {
json.put("uid", 0); json.put("uid", 0);
} }
LoginCookieDatabaseBean cookie = BiliBiliLoginDatabase.getInstance().getCookie(roomConfig.getLoginUid()); LoginCookieDatabaseBean cookie = BiliBiliLoginDatabase.getInstance().getCookie(roomConfig.getLoginUid());
Log.d("cookie:", cookie, "RoomId:" + roomConfig);
try { try {
json.put("roomid", new BigInteger(roomConfig.getRoomId())); json.put("roomid", new BigInteger(roomConfig.getRoomId()));
json.put("protover", 3); json.put("protover", 3);
// json.put("buvid3", BiliUserUtils.getBuvid(roomConfig.isLogin() ? BiliBiliLoginDatabase.getInstance().getCookie(roomConfig.getRoomId()) : null));
json.put("platform", "web"); json.put("platform", "web");
json.put("type", 2); json.put("type", 2);
json.put("key", roomConfig.getLiveInfo().getToken()); json.put("key", roomConfig.getLiveInfo().getToken());

View File

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

View File

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

View File

@ -8,13 +8,14 @@ import okio.Buffer;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Logger; import java.util.logging.Logger;
public class HttpLoggingInterceptor implements Interceptor { public class HttpLoggingInterceptor implements Interceptor {
private static final String TAG = "HttpLogging"; 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 volatile Level printLevel = Level.BODY;
private java.util.logging.Level colorLevel; private java.util.logging.Level colorLevel;
@ -51,7 +52,7 @@ public class HttpLoggingInterceptor implements Interceptor {
private void log(String message) { private void log(String message) {
//logger.log(colorLevel, message); //logger.log(colorLevel, message);
if (prLog) { if (prLog) {
Log.i(TAG, message); Log.getDynamicLogger(TAG).info(message);
} }
//Log.e(TAG,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.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.StackLocatorUtil; import org.apache.logging.log4j.util.StackLocatorUtil;
@ -11,6 +12,14 @@ public class Log {
System.out.println(); 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) { public static void i(Object... log) {
if (!((boolean) ConfigTools.load(ConfigTools.CONFIG, "logcat"))) { if (!((boolean) ConfigTools.load(ConfigTools.CONFIG, "logcat"))) {
return; return;

View File

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