代码提交
This commit is contained in:
36
pom.xml
36
pom.xml
@@ -17,41 +17,33 @@
|
||||
<java.version>21</java.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>metona-maven</id>
|
||||
<url>https://gitee.com/thzxx/maven/raw/master</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>1.9.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.rocksdb</groupId>
|
||||
<artifactId>rocksdbjni</artifactId>
|
||||
<version>8.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
<version>5.8.21</version>
|
||||
<version>5.8.25</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.metona</groupId>
|
||||
<artifactId>metona-cache-spring-boot-starter</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
package cn.somkit.fmt.action;
|
||||
|
||||
import cn.somkit.fmt.annotation.ApiOperate;
|
||||
import cn.somkit.fmt.config.RocksDBConfig;
|
||||
import cn.somkit.fmt.utils.MD5Utils;
|
||||
import cn.somkit.fmt.utils.OsInfoUtil;
|
||||
import cn.somkit.fmt.utils.RocksDBUtils;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -50,48 +45,15 @@ public class DownloadAction {
|
||||
File folder = new File(path);
|
||||
File[] listOfFiles = folder.listFiles();
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
List<String> keys = RocksDBUtils.getAllKey(RocksDBConfig.RocksDB_Column_Family);
|
||||
|
||||
if(listOfFiles != null){
|
||||
for (File file : listOfFiles) {
|
||||
if(!file.isFile()){
|
||||
continue;
|
||||
}
|
||||
if(!keys.contains(file.getName())){
|
||||
try (FileInputStream is = new FileInputStream(file)) {
|
||||
String hash = MD5Utils.md5HashCode(is);
|
||||
RocksDBUtils.put(RocksDBConfig.RocksDB_Column_Family, file.getName(), hash);
|
||||
}catch (Exception e){e.printStackTrace();}
|
||||
}
|
||||
BigDecimal filesize = BigDecimal.valueOf(file.length())
|
||||
.divide(BigDecimal.valueOf(1024 * 1024), 2, RoundingMode.HALF_UP);
|
||||
Long time = file.lastModified();
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String filetime = format.format(time);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("filename", file.getName());
|
||||
map.put("filepath", file.getAbsolutePath());
|
||||
map.put("filesize", filesize);
|
||||
map.put("filetime", filetime);
|
||||
Map<String, Object> map = getObjectMap(file);
|
||||
list.add(map);
|
||||
}
|
||||
List<String> filenames = list.stream().map(map -> String.valueOf(map.get("filename"))).toList();
|
||||
keys.forEach(key -> {
|
||||
try {
|
||||
if(!filenames.contains(key)){
|
||||
RocksDBUtils.delete(RocksDBConfig.RocksDB_Column_Family, key);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
keys.forEach(key -> {
|
||||
try {
|
||||
RocksDBUtils.delete(RocksDBConfig.RocksDB_Column_Family, key);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
if(StringUtils.hasText(keyboard) && !CollectionUtils.isEmpty(list)){
|
||||
list = list.stream().filter(map -> String.valueOf(map.get("filename"))
|
||||
@@ -104,7 +66,20 @@ public class DownloadAction {
|
||||
return mv;
|
||||
}
|
||||
|
||||
@ApiOperate(description = "StreamingResponseBody方式下载文件")
|
||||
private static Map<String, Object> getObjectMap(File file) {
|
||||
BigDecimal filesize = BigDecimal.valueOf(file.length())
|
||||
.divide(BigDecimal.valueOf(1024 * 1024), 2, RoundingMode.HALF_UP);
|
||||
Long time = file.lastModified();
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String filetime = format.format(time);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("filename", file.getName());
|
||||
map.put("filepath", file.getAbsolutePath());
|
||||
map.put("filesize", filesize);
|
||||
map.put("filetime", filetime);
|
||||
return map;
|
||||
}
|
||||
|
||||
@GetMapping(value = "/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public DeferredResult<ResponseEntity<StreamingResponseBody>> downloadFileByPath(String path, Boolean temp) throws Exception {
|
||||
final File file = new File(path);
|
||||
@@ -147,7 +122,6 @@ public class DownloadAction {
|
||||
|
||||
@PostMapping("/packZip")
|
||||
@ResponseBody
|
||||
@ApiOperate(description = "批量文件打包下载")
|
||||
public Map<String, Object> packZip(String filenames) throws Exception{
|
||||
try {
|
||||
String path = OsInfoUtil.isWindows() ? windows_path :
|
||||
@@ -191,17 +165,14 @@ public class DownloadAction {
|
||||
|
||||
@PostMapping("/delete")
|
||||
@ResponseBody
|
||||
@ApiOperate(description = "单文件删除")
|
||||
public String delete(String path) throws Exception{
|
||||
File file = new File(path);
|
||||
RocksDBUtils.delete(RocksDBConfig.RocksDB_Column_Family, file.getName());
|
||||
FileSystemUtils.deleteRecursively(file);
|
||||
return "删除成功";
|
||||
}
|
||||
|
||||
@PostMapping("/batchDel")
|
||||
@ResponseBody
|
||||
@ApiOperate(description = "批量文件删除")
|
||||
public String batchDel(String filenames) throws Exception{
|
||||
String path = OsInfoUtil.isWindows() ? windows_path :
|
||||
OsInfoUtil.isLinux() ? linux_path : null;
|
||||
@@ -213,7 +184,6 @@ public class DownloadAction {
|
||||
if(!Arrays.asList(filenames.split(",")).contains(file.getName())){
|
||||
continue;
|
||||
}
|
||||
RocksDBUtils.delete(RocksDBConfig.RocksDB_Column_Family, file.getName());
|
||||
FileSystemUtils.deleteRecursively(file);
|
||||
}
|
||||
return "删除成功";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package cn.somkit.fmt.action;
|
||||
|
||||
import cn.somkit.fmt.annotation.ApiOperate;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package cn.somkit.fmt.action;
|
||||
|
||||
import cn.somkit.fmt.annotation.ApiOperate;
|
||||
import cn.somkit.fmt.entity.LoggerMessage;
|
||||
import cn.somkit.fmt.utils.LoggerQueue;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/logging")
|
||||
@@ -13,4 +16,10 @@ public class LoggingAction {
|
||||
public String index() throws Exception{
|
||||
return "logging";
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@PostMapping("/get")
|
||||
public LoggerMessage getLoggerMessage() {
|
||||
return LoggerQueue.getInstance().poll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
package cn.somkit.fmt.action;
|
||||
|
||||
import cn.somkit.fmt.annotation.ApiOperate;
|
||||
import cn.somkit.fmt.config.RocksDBConfig;
|
||||
import cn.somkit.fmt.utils.MD5Utils;
|
||||
import cn.somkit.fmt.utils.OsInfoUtil;
|
||||
import cn.somkit.fmt.utils.RocksDBUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -36,7 +31,6 @@ public class UploadAction {
|
||||
|
||||
@PostMapping("/execute")
|
||||
@ResponseBody
|
||||
@ApiOperate(description = "文件上传")
|
||||
public Map<String, Object> execute(HttpServletRequest request) throws Exception{
|
||||
//多个文件上传 就只是简单的多文件上传保存在本地的磁盘
|
||||
if (request instanceof MultipartHttpServletRequest mrequest) {
|
||||
@@ -67,28 +61,9 @@ public class UploadAction {
|
||||
assert path != null;
|
||||
String filePath = "";
|
||||
if (file != null && file.getSize() > 0) { // 有文件上传
|
||||
String fileName = verify(file, null);// 创建文件名称
|
||||
if(StringUtils.hasText(fileName)){
|
||||
String hash = MD5Utils.md5HashCode(file.getInputStream());
|
||||
RocksDBUtils.put(RocksDBConfig.RocksDB_Column_Family, fileName, hash);
|
||||
filePath = path + File.separator + fileName;
|
||||
File saveFile = new File(filePath) ;
|
||||
file.transferTo(saveFile); // 文件保存
|
||||
}
|
||||
filePath = path + File.separator + file.getOriginalFilename();
|
||||
File saveFile = new File(filePath) ;
|
||||
file.transferTo(saveFile); // 文件保存
|
||||
}
|
||||
}
|
||||
|
||||
private String verify(MultipartFile file, String filename) throws Exception {
|
||||
String key = StringUtils.hasText(filename) ? filename : Objects.requireNonNull(file.getOriginalFilename());
|
||||
String hash = RocksDBUtils.get(RocksDBConfig.RocksDB_Column_Family, key);
|
||||
if(!StringUtils.hasText(hash)){
|
||||
return StringUtils.hasText(filename) ? filename : file.getOriginalFilename();
|
||||
}
|
||||
String newHash = MD5Utils.md5HashCode(file.getInputStream());
|
||||
if(!hash.equals(newHash)){
|
||||
String newFilename = "(1)" + file.getOriginalFilename();
|
||||
return verify(file, newFilename);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package cn.somkit.fmt.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@Documented
|
||||
public @interface ApiOperate {
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
String description() default "";
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package cn.somkit.fmt.annotation.aspect;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.somkit.fmt.annotation.ApiOperate;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
public class ApiOperateAspect {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
/** 换行符 */
|
||||
private static final String LINE_SEPARATOR = System.lineSeparator();
|
||||
|
||||
/** 以自定义 @ApiOperate 注解为切点 */
|
||||
@Pointcut("@annotation(apiOperate)")
|
||||
public void ApiOperate(ApiOperate apiOperate) {}
|
||||
|
||||
/**
|
||||
* 环绕注入
|
||||
* @param joinPoint
|
||||
* @param apiOperate
|
||||
* @return
|
||||
* @throws Throwable
|
||||
*/
|
||||
@Around(value = "ApiOperate(apiOperate)", argNames = "joinPoint,apiOperate")
|
||||
public Object doAround(ProceedingJoinPoint joinPoint, ApiOperate apiOperate) throws Throwable {
|
||||
long startTime = System.currentTimeMillis();
|
||||
Object result = joinPoint.proceed();
|
||||
|
||||
// 开始打印请求日志
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
assert attributes != null;
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
|
||||
long timeConsuming = System.currentTimeMillis() - startTime;
|
||||
|
||||
// 打印请求相关参数
|
||||
logger.info("========================================== Start ==========================================");
|
||||
// 打印请求 url
|
||||
logger.info("URL : {}", request.getRequestURL().toString());
|
||||
// 打印描述信息
|
||||
logger.info("Description : {}", apiOperate.description());
|
||||
// 打印 Http method
|
||||
logger.info("HTTP Method : {}", request.getMethod());
|
||||
// 打印调用 controller 的全路径以及执行方法
|
||||
logger.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
|
||||
// 打印请求的 IP
|
||||
logger.info("IP : {}", request.getRemoteAddr());
|
||||
// 打印请求Headers
|
||||
logger.info("Header Args : {}", headers(request));
|
||||
// 打印请求入参
|
||||
logger.info("Request Args : {}", Arrays.toString(joinPoint.getArgs()));
|
||||
// 打印出参
|
||||
logger.info("Response Args : {}", JSONUtil.toJsonStr(result));
|
||||
// 执行耗时
|
||||
logger.info("Time-Consuming : {} ms", timeConsuming);
|
||||
// 接口结束后换行,方便分割查看
|
||||
logger.info("=========================================== End ===========================================" + LINE_SEPARATOR);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有header参数
|
||||
* @param request
|
||||
* @return Map<String, String> headers
|
||||
*/
|
||||
private Map<String, String> headers(HttpServletRequest request){
|
||||
Map<String, String> headerMap = new HashMap<>();
|
||||
Enumeration<String> enumeration = request.getHeaderNames();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String name = enumeration.nextElement();
|
||||
String value = request.getHeader(name);
|
||||
headerMap.put(name, value);
|
||||
}
|
||||
return headerMap;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package cn.somkit.fmt.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Component
|
||||
public class RocksDBConfig {
|
||||
|
||||
public static String RocksDB_Path_Windows = "D://RocksDB";
|
||||
|
||||
public static String RocksDB_Path_Linux = "/usr/local/rocksdb";
|
||||
|
||||
public static String RocksDB_Column_Family = "default";
|
||||
|
||||
@Value("${somkit.db.rocks.path.windows}")
|
||||
public void setRocksDB_Path_Windows(String rocksDB_Path_Windows) {
|
||||
if(StringUtils.hasText(rocksDB_Path_Windows)){
|
||||
RocksDB_Path_Windows = rocksDB_Path_Windows;
|
||||
}
|
||||
}
|
||||
|
||||
@Value("${somkit.db.rocks.path.linux}")
|
||||
public void setRocksDB_Path_Linux(String rocksDB_Path_Linux) {
|
||||
if(StringUtils.hasText(rocksDB_Path_Linux)){
|
||||
RocksDB_Path_Linux = rocksDB_Path_Linux;
|
||||
}
|
||||
}
|
||||
|
||||
@Value("${somkit.db.rocks.column-family}")
|
||||
public void setRocksDB_Column_Family(String rocksDB_Column_Family){
|
||||
if(StringUtils.hasText(rocksDB_Column_Family)){
|
||||
RocksDB_Column_Family = rocksDB_Column_Family;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package cn.somkit.fmt.config;
|
||||
|
||||
import cn.somkit.fmt.entity.LoggerMessage;
|
||||
import cn.somkit.fmt.utils.ApplicationContextProvider;
|
||||
import cn.somkit.fmt.utils.LoggerQueue;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketLogConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
/* 不再通过构造函数注入,而是第一次使用时再拿 */
|
||||
private volatile SimpMessagingTemplate template;
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/ws-logs").setAllowedOriginPatterns("*").withSockJS();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry config) {
|
||||
config.enableSimpleBroker("/topic");
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void startPush() {
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
LoggerMessage log = LoggerQueue.getInstance().poll();
|
||||
if (log != null) {
|
||||
if (template == null) {
|
||||
// 延迟获取
|
||||
template = ApplicationContextProvider.getBean(SimpMessagingTemplate.class);
|
||||
}
|
||||
template.convertAndSend("/topic/logs", log);
|
||||
}
|
||||
}
|
||||
}, "log-push").start();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.somkit.fmt.filter;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.filter.Filter;
|
||||
import ch.qos.logback.core.spi.FilterReply;
|
||||
@@ -11,15 +12,35 @@ import java.time.ZoneId;
|
||||
|
||||
public class LogStashFilter extends Filter<ILoggingEvent> {
|
||||
|
||||
Level level;
|
||||
|
||||
public LogStashFilter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterReply decide(ILoggingEvent e) {
|
||||
LoggerMessage msg = new LoggerMessage(
|
||||
e.getLevel().toString(),
|
||||
e.getLoggerName(),
|
||||
e.getFormattedMessage(),
|
||||
Instant.ofEpochMilli(e.getTimeStamp()).atZone(ZoneId.systemDefault()).toLocalDateTime()
|
||||
);
|
||||
LoggerQueue.getInstance().push(msg); // 单例阻塞队列
|
||||
return FilterReply.ACCEPT;
|
||||
if (!this.isStarted()) {
|
||||
return FilterReply.NEUTRAL;
|
||||
} else {
|
||||
LoggerMessage msg = new LoggerMessage(
|
||||
e.getLevel().toString(),
|
||||
e.getLoggerName(),
|
||||
e.getFormattedMessage(),
|
||||
Instant.ofEpochMilli(e.getTimeStamp()).atZone(ZoneId.systemDefault()).toLocalDateTime()
|
||||
);
|
||||
LoggerQueue.getInstance().push(msg); // 单例阻塞队列
|
||||
return e.getLevel().isGreaterOrEqual(this.level) ? FilterReply.NEUTRAL : FilterReply.DENY;
|
||||
}
|
||||
}
|
||||
|
||||
public void setLevel(String level) {
|
||||
this.level = Level.toLevel(level);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (this.level != null) {
|
||||
super.start();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
package cn.somkit.fmt.utils;
|
||||
|
||||
import cn.somkit.fmt.config.RocksDBConfig;
|
||||
import org.rocksdb.*;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class RocksDBUtils {
|
||||
|
||||
private static RocksDB rocksDB;
|
||||
public static ConcurrentMap<String, ColumnFamilyHandle> columnFamilyHandleMap = new ConcurrentHashMap<>();
|
||||
public static int GET_KEYS_BATCH_SIZE = 100000;
|
||||
|
||||
static {
|
||||
try {
|
||||
String rocksDBPath = null; //RocksDB文件目录
|
||||
if (OsInfoUtil.isWindows()) {
|
||||
rocksDBPath = RocksDBConfig.RocksDB_Path_Windows; // 指定windows系统下RocksDB文件目录
|
||||
} else if(OsInfoUtil.isLinux()){
|
||||
rocksDBPath = RocksDBConfig.RocksDB_Path_Linux; // 指定linux系统下RocksDB文件目录
|
||||
}
|
||||
RocksDB.loadLibrary();
|
||||
Options options = new Options();
|
||||
options.setCreateIfMissing(true); //如果数据库不存在则创建
|
||||
List<byte[]> cfArr = RocksDB.listColumnFamilies(options, rocksDBPath); // 初始化所有已存在列族
|
||||
List<ColumnFamilyDescriptor> columnFamilyDescriptors = new ArrayList<>(); //ColumnFamilyDescriptor集合
|
||||
if (!ObjectUtils.isEmpty(cfArr)) {
|
||||
for (byte[] cf : cfArr) {
|
||||
columnFamilyDescriptors.add(new ColumnFamilyDescriptor(cf, new ColumnFamilyOptions()));
|
||||
}
|
||||
} else {
|
||||
columnFamilyDescriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY, new ColumnFamilyOptions()));
|
||||
}
|
||||
DBOptions dbOptions = new DBOptions();
|
||||
dbOptions.setCreateIfMissing(true);
|
||||
List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>(); //ColumnFamilyHandle集合
|
||||
rocksDB = RocksDB.open(dbOptions, rocksDBPath, columnFamilyDescriptors, columnFamilyHandles);
|
||||
for (int i = 0; i < columnFamilyDescriptors.size(); i++) {
|
||||
ColumnFamilyHandle columnFamilyHandle = columnFamilyHandles.get(i);
|
||||
String cfName = new String(columnFamilyDescriptors.get(i).getName(), StandardCharsets.UTF_8);
|
||||
columnFamilyHandleMap.put(cfName, columnFamilyHandle);
|
||||
}
|
||||
System.out.println("RocksDB init success!! path:" + rocksDBPath);
|
||||
} catch (Exception e) {
|
||||
System.out.println("RocksDB init failure!! error:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private RocksDBUtils(){}
|
||||
|
||||
public static ColumnFamilyHandle cfAddIfNotExist(String cfName) throws RocksDBException {
|
||||
ColumnFamilyHandle columnFamilyHandle;
|
||||
if (!columnFamilyHandleMap.containsKey(cfName)) {
|
||||
columnFamilyHandle = rocksDB.createColumnFamily(new ColumnFamilyDescriptor(cfName.getBytes(), new ColumnFamilyOptions()));
|
||||
columnFamilyHandleMap.put(cfName, columnFamilyHandle);
|
||||
System.out.println("cfAddIfNotExist success!! cfName:" + cfName);
|
||||
} else {
|
||||
columnFamilyHandle = columnFamilyHandleMap.get(cfName);
|
||||
}
|
||||
return columnFamilyHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列族,删除(如果存在)
|
||||
*/
|
||||
public static void cfDeleteIfExist(String cfName) throws RocksDBException {
|
||||
if (columnFamilyHandleMap.containsKey(cfName)) {
|
||||
rocksDB.dropColumnFamily(columnFamilyHandleMap.get(cfName));
|
||||
columnFamilyHandleMap.remove(cfName);
|
||||
System.out.println("cfDeleteIfExist success!! cfName:" + cfName);
|
||||
} else {
|
||||
System.out.println("cfDeleteIfExist containsKey!! cfName:" + cfName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增
|
||||
*/
|
||||
public static void put(String cfName, String key, String value) throws RocksDBException {
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName); //获取列族Handle
|
||||
rocksDB.put(columnFamilyHandle, key.getBytes(), value.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 增(批量)
|
||||
*/
|
||||
public static void batchPut(String cfName, Map<String, String> map) throws RocksDBException {
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName); //获取列族Handle
|
||||
WriteOptions writeOptions = new WriteOptions();
|
||||
WriteBatch writeBatch = new WriteBatch();
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
writeBatch.put(columnFamilyHandle, entry.getKey().getBytes(), entry.getValue().getBytes());
|
||||
}
|
||||
rocksDB.write(writeOptions, writeBatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删
|
||||
*/
|
||||
public static void delete(String cfName, String key) throws RocksDBException {
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName); //获取列族Handle
|
||||
rocksDB.delete(columnFamilyHandle, key.getBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查
|
||||
*/
|
||||
public static String get(String cfName, String key) throws RocksDBException {
|
||||
String value = null;
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName); //获取列族Handle
|
||||
byte[] bytes = rocksDB.get(columnFamilyHandle, key.getBytes());
|
||||
if (!ObjectUtils.isEmpty(bytes)) {
|
||||
value = new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查(多个键值对)
|
||||
*/
|
||||
public static Map<String, String> multiGetAsMap(String cfName, List<String> keys) throws RocksDBException {
|
||||
Map<String, String> map = new HashMap<>(keys.size());
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName); //获取列族Handle
|
||||
List<ColumnFamilyHandle> columnFamilyHandles;
|
||||
List<byte[]> keyBytes = keys.stream().map(String::getBytes).collect(Collectors.toList());
|
||||
columnFamilyHandles = IntStream.range(0, keys.size()).mapToObj(i -> columnFamilyHandle).collect(Collectors.toList());
|
||||
List<byte[]> bytes = rocksDB.multiGetAsList(columnFamilyHandles, keyBytes);
|
||||
for (int i = 0; i < bytes.size(); i++) {
|
||||
byte[] valueBytes = bytes.get(i);
|
||||
String value = "";
|
||||
if (!ObjectUtils.isEmpty(valueBytes)) {
|
||||
value = new String(valueBytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
map.put(keys.get(i), value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查(多个值)
|
||||
*/
|
||||
public static List<String> multiGetValueAsList(String cfName, List<String> keys) throws RocksDBException {
|
||||
List<String> values = new ArrayList<>(keys.size());
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName); //获取列族Handle
|
||||
List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>();
|
||||
List<byte[]> keyBytes = keys.stream().map(String::getBytes).collect(Collectors.toList());
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
columnFamilyHandles.add(columnFamilyHandle);
|
||||
}
|
||||
List<byte[]> bytes = rocksDB.multiGetAsList(columnFamilyHandles, keyBytes);
|
||||
for (byte[] valueBytes : bytes) {
|
||||
String value = "";
|
||||
if (!ObjectUtils.isEmpty(valueBytes)) {
|
||||
value = new String(valueBytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
values.add(value);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查(所有键)
|
||||
*/
|
||||
public static List<String> getAllKey(String cfName) throws RocksDBException {
|
||||
List<String> list = new ArrayList<>();
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName); //获取列族Handle
|
||||
try (RocksIterator rocksIterator = rocksDB.newIterator(columnFamilyHandle)) {
|
||||
for (rocksIterator.seekToFirst(); rocksIterator.isValid(); rocksIterator.next()) {
|
||||
list.add(new String(rocksIterator.key(), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分片查(键)
|
||||
*/
|
||||
public static List<String> getKeysFrom(String cfName, String lastKey) throws RocksDBException {
|
||||
List<String> list = new ArrayList<>(GET_KEYS_BATCH_SIZE);
|
||||
// 获取列族Handle
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName);
|
||||
try (RocksIterator rocksIterator = rocksDB.newIterator(columnFamilyHandle)) {
|
||||
if (lastKey != null) {
|
||||
rocksIterator.seek(lastKey.getBytes(StandardCharsets.UTF_8));
|
||||
rocksIterator.next();
|
||||
} else {
|
||||
rocksIterator.seekToFirst();
|
||||
}
|
||||
// 一批次最多 GET_KEYS_BATCH_SIZE 个 key
|
||||
while (rocksIterator.isValid() && list.size() < GET_KEYS_BATCH_SIZE) {
|
||||
list.add(new String(rocksIterator.key(), StandardCharsets.UTF_8));
|
||||
rocksIterator.next();
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查(所有键值)
|
||||
*/
|
||||
public static Map<String, String> getAll(String cfName) throws RocksDBException {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName); //获取列族Handle
|
||||
try (RocksIterator rocksIterator = rocksDB.newIterator(columnFamilyHandle)) {
|
||||
for (rocksIterator.seekToFirst(); rocksIterator.isValid(); rocksIterator.next()) {
|
||||
map.put(new String(rocksIterator.key(), StandardCharsets.UTF_8), new String(rocksIterator.value(), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查总条数
|
||||
*/
|
||||
public static int getCount(String cfName) throws RocksDBException {
|
||||
int count = 0;
|
||||
ColumnFamilyHandle columnFamilyHandle = cfAddIfNotExist(cfName); //获取列族Handle
|
||||
try (RocksIterator rocksIterator = rocksDB.newIterator(columnFamilyHandle)) {
|
||||
for (rocksIterator.seekToFirst(); rocksIterator.isValid(); rocksIterator.next()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -15,18 +15,52 @@ spring:
|
||||
multipart:
|
||||
max-file-size: 1024MB
|
||||
max-request-size: 10240MB
|
||||
threads:
|
||||
virtual:
|
||||
enabled: true
|
||||
|
||||
somkit:
|
||||
upload:
|
||||
path:
|
||||
windows: D://data/install/upload
|
||||
linux: /mnt/files
|
||||
db:
|
||||
rocks:
|
||||
path:
|
||||
windows: D://RocksDB//fmt
|
||||
linux: /usr/local/rocksdb/fmt
|
||||
column-family: default
|
||||
|
||||
logging:
|
||||
config: classpath:logback-spring.xml
|
||||
config: classpath:logback-spring.xml
|
||||
|
||||
metona:
|
||||
cache:
|
||||
# 缓存类型,支持以下类型:
|
||||
# - CONCURRENT_HASH_MAP: 基于 ConcurrentHashMap 的线程安全缓存
|
||||
# - WEAK_HASH_MAP: 基于 WeakHashMap 的弱引用缓存
|
||||
# - LINKED_HASH_MAP: 基于 LinkedHashMap 的 LRU 缓存
|
||||
type: LINKED_HASH_MAP
|
||||
|
||||
# 缓存的初始容量,默认值为 16
|
||||
initial-capacity: 128
|
||||
|
||||
# 缓存的最大容量,当缓存条目数超过该值时,会根据策略移除旧条目
|
||||
# 仅对 LINKED_HASH_MAP 类型有效
|
||||
maximum-size: 2000
|
||||
|
||||
# 写入后过期时间(单位由 time-unit 指定)
|
||||
# 默认值为 -1,表示永不过期
|
||||
expire-after-write: -1
|
||||
|
||||
# 访问后过期时间(单位由 time-unit 指定)
|
||||
# 默认值为 -1,表示永不过期
|
||||
expire-after-access: -1
|
||||
|
||||
# 时间单位,支持以下值:
|
||||
# - NANOSECONDS: 纳秒
|
||||
# - MICROSECONDS: 微秒
|
||||
# - MILLISECONDS: 毫秒(默认)
|
||||
# - SECONDS: 秒
|
||||
# - MINUTES: 分钟
|
||||
# - HOURS: 小时
|
||||
# - DAYS: 天
|
||||
time-unit: MILLISECONDS
|
||||
|
||||
# 是否记录缓存统计信息(如命中率、加载次数等)
|
||||
# 默认值为 false
|
||||
record-stats: true
|
||||
@@ -9,7 +9,7 @@
|
||||
<level>DEBUG</level>
|
||||
</filter>
|
||||
<filter class="cn.somkit.fmt.filter.LogStashFilter">
|
||||
|
||||
<level>DEBUG</level>
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
|
||||
|
||||
@@ -40,6 +40,12 @@ class LogMonitorAdaptive {
|
||||
enableFontSize: true, // 是否提供“字体大小 +/-”按钮
|
||||
enableWordWrap: true, // 是否提供“换行/不换行”切换按钮
|
||||
|
||||
/* --- 暂停/继续 的回调函数 --- */
|
||||
onTogglePause: () => {},
|
||||
|
||||
/* --- 创建完成 的回调函数 --- */
|
||||
onCreated: () => {},
|
||||
|
||||
...opts
|
||||
};
|
||||
|
||||
@@ -59,6 +65,11 @@ class LogMonitorAdaptive {
|
||||
this.initDOM();
|
||||
this.bindResize();
|
||||
this.bindGlobalEvents();
|
||||
|
||||
//执行回调函数
|
||||
if(this.cfg.onCreated && typeof this.cfg.onCreated === 'function'){
|
||||
this.cfg.onCreated();
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------ 初始化 ------------------------ */
|
||||
@@ -332,6 +343,10 @@ class LogMonitorAdaptive {
|
||||
this.pauseBtn.style.backgroundColor = this.isPaused
|
||||
? (this.cfg.theme === 'dark' ? '#d32f2f' : '#ff5252')
|
||||
: (this.cfg.theme === 'dark' ? '#444' : '#e7e7e7');
|
||||
//执行回调函数
|
||||
if(this.cfg.onTogglePause && typeof this.cfg.onTogglePause === 'function'){
|
||||
this.cfg.onTogglePause(this.isPaused);
|
||||
}
|
||||
}
|
||||
|
||||
toggleTheme() {
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
<body>
|
||||
<!-- 日志容器 -->
|
||||
<div id="logContainer"></div>
|
||||
<script th:src="@{/common/js/basic.js}" type="text/javascript" charset="utf-8"></script>
|
||||
<script th:src="@{/common/js/LogMonitorAdaptive.js}" type="text/javascript" charset="utf-8"></script>
|
||||
<script th:src="@{/common/js/sockjs.js}" type="text/javascript" charset="utf-8"></script>
|
||||
<script th:src="@{/common/js/stomp.js}" type="text/javascript" charset="utf-8"></script>
|
||||
<script type="text/javascript" th:inline="javascript" charset="utf-8">
|
||||
window.onload = () => {
|
||||
const logger = new LogMonitorAdaptive('#logContainer', {
|
||||
@@ -35,16 +34,24 @@
|
||||
enableWordWrap: true,
|
||||
showTimestamp: true, // 是否显示时间戳
|
||||
showLevel: true, // 是否显示日志级别标签
|
||||
});
|
||||
|
||||
const socket = new SockJS('/fmt/ws-logs');
|
||||
const stomp = Stomp.over(socket);
|
||||
stomp.connect({}, () => {
|
||||
stomp.subscribe('/topic/logs', (payload) => {
|
||||
const log = JSON.parse(payload.body);
|
||||
console.log(log);
|
||||
logger.log(log.message, log.level);
|
||||
});
|
||||
//暂停/继续 回调函数
|
||||
onTogglePause: (isPaused) => {
|
||||
console.log(isPaused ? '暂停' : '继续');
|
||||
},
|
||||
onCreated: () => {
|
||||
console.log('日志容器已创建');
|
||||
let index = setInterval(() => {
|
||||
const options = {
|
||||
url: Fmt.ctx() + '/logging/get',
|
||||
data: {},
|
||||
method: 'post'
|
||||
};
|
||||
Fmt.axios(options).then((result) => {
|
||||
console.log(result);
|
||||
logger.log(result.message, result.level);
|
||||
}).catch((err) => clearInterval(index));
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<span class="ax-line"></span>
|
||||
</div>
|
||||
<div class="ax-item">
|
||||
<a th:href="@{/logging/index}" class="ax-text">在线日志</a>
|
||||
<a th:href="@{/logging/index}" target="_blank" class="ax-text">在线日志</a>
|
||||
<span class="ax-line"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package cn.somkit.fmt;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class FmtApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user