196 lines
7.4 KiB
Java
196 lines
7.4 KiB
Java
package research.loghunter.service;
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import org.springframework.data.domain.Page;
|
|
import org.springframework.data.domain.PageRequest;
|
|
import org.springframework.data.domain.Pageable;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import research.loghunter.dto.ErrorLogDto;
|
|
import research.loghunter.dto.FileTreeDto;
|
|
import research.loghunter.entity.ErrorLog;
|
|
import research.loghunter.entity.Server;
|
|
import research.loghunter.repository.ErrorLogRepository;
|
|
import research.loghunter.repository.ScannedFileRepository;
|
|
import research.loghunter.repository.ServerRepository;
|
|
|
|
import java.time.LocalDateTime;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
@Transactional(readOnly = true)
|
|
public class ErrorLogService {
|
|
|
|
private final ErrorLogRepository errorLogRepository;
|
|
private final ScannedFileRepository scannedFileRepository;
|
|
private final ServerRepository serverRepository;
|
|
|
|
public Page<ErrorLogDto> search(
|
|
Long serverId,
|
|
Long patternId,
|
|
String severity,
|
|
String filePath,
|
|
LocalDateTime startDate,
|
|
LocalDateTime endDate,
|
|
String keyword,
|
|
int page,
|
|
int size
|
|
) {
|
|
Pageable pageable = PageRequest.of(page, size);
|
|
Page<ErrorLog> errorLogs = errorLogRepository.searchErrors(
|
|
serverId, patternId, severity, filePath, startDate, endDate, keyword, pageable);
|
|
|
|
return errorLogs.map(this::toDto);
|
|
}
|
|
|
|
public Page<ErrorLogDto> findByServerId(Long serverId, int page, int size) {
|
|
Pageable pageable = PageRequest.of(page, size);
|
|
Page<ErrorLog> errorLogs = errorLogRepository.findByServerIdOrderByOccurredAtDesc(serverId, pageable);
|
|
return errorLogs.map(this::toDto);
|
|
}
|
|
|
|
public ErrorLogDto findById(Long id) {
|
|
return errorLogRepository.findById(id)
|
|
.map(this::toDto)
|
|
.orElseThrow(() -> new RuntimeException("ErrorLog not found: " + id));
|
|
}
|
|
|
|
/**
|
|
* 트리 구조 데이터 조회
|
|
*/
|
|
public List<FileTreeDto.ServerNode> getFileTree() {
|
|
// 파일별 에러 통계 조회
|
|
List<Object[]> stats = errorLogRepository.getFileErrorStats();
|
|
|
|
// 서버별로 그룹핑
|
|
Map<Long, List<Object[]>> serverGroups = new LinkedHashMap<>();
|
|
for (Object[] stat : stats) {
|
|
Long serverId = ((Number) stat[0]).longValue();
|
|
serverGroups.computeIfAbsent(serverId, k -> new ArrayList<>()).add(stat);
|
|
}
|
|
|
|
List<FileTreeDto.ServerNode> result = new ArrayList<>();
|
|
|
|
for (Map.Entry<Long, List<Object[]>> entry : serverGroups.entrySet()) {
|
|
Long serverId = entry.getKey();
|
|
List<Object[]> serverStats = entry.getValue();
|
|
|
|
String serverName = (String) serverStats.get(0)[1];
|
|
|
|
// 경로별로 그룹핑
|
|
Map<String, List<FileTreeDto.FileNode>> pathGroups = new LinkedHashMap<>();
|
|
int serverTotalErrors = 0;
|
|
|
|
for (Object[] stat : serverStats) {
|
|
String filePath = (String) stat[2];
|
|
int errorCount = ((Number) stat[3]).intValue();
|
|
int criticalCount = ((Number) stat[4]).intValue();
|
|
int errorLevelCount = ((Number) stat[5]).intValue();
|
|
int warnCount = ((Number) stat[6]).intValue();
|
|
|
|
// 경로와 파일명 분리
|
|
int lastSlash = filePath.lastIndexOf('/');
|
|
String path = lastSlash > 0 ? filePath.substring(0, lastSlash) : "/";
|
|
String fileName = lastSlash > 0 ? filePath.substring(lastSlash + 1) : filePath;
|
|
|
|
FileTreeDto.FileNode fileNode = FileTreeDto.FileNode.builder()
|
|
.filePath(filePath)
|
|
.fileName(fileName)
|
|
.errorCount(errorCount)
|
|
.criticalCount(criticalCount)
|
|
.errorLevelCount(errorLevelCount)
|
|
.warnCount(warnCount)
|
|
.build();
|
|
|
|
pathGroups.computeIfAbsent(path, k -> new ArrayList<>()).add(fileNode);
|
|
serverTotalErrors += errorCount;
|
|
}
|
|
|
|
// PathNode 생성
|
|
List<FileTreeDto.PathNode> pathNodes = new ArrayList<>();
|
|
for (Map.Entry<String, List<FileTreeDto.FileNode>> pathEntry : pathGroups.entrySet()) {
|
|
List<FileTreeDto.FileNode> files = pathEntry.getValue();
|
|
int pathTotalErrors = files.stream().mapToInt(FileTreeDto.FileNode::getErrorCount).sum();
|
|
|
|
pathNodes.add(FileTreeDto.PathNode.builder()
|
|
.path(pathEntry.getKey())
|
|
.totalErrorCount(pathTotalErrors)
|
|
.files(files)
|
|
.build());
|
|
}
|
|
|
|
result.add(FileTreeDto.ServerNode.builder()
|
|
.serverId(serverId)
|
|
.serverName(serverName)
|
|
.totalErrorCount(serverTotalErrors)
|
|
.paths(pathNodes)
|
|
.build());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 서버별 파일 목록 조회
|
|
*/
|
|
public List<String> getFilesByServer(Long serverId) {
|
|
if (serverId == null) {
|
|
return errorLogRepository.findDistinctFilePaths();
|
|
}
|
|
return errorLogRepository.findDistinctFilePathsByServerId(serverId);
|
|
}
|
|
|
|
/**
|
|
* 선택한 ID들의 에러 삭제
|
|
*/
|
|
@Transactional
|
|
public int deleteByIds(List<Long> ids) {
|
|
if (ids == null || ids.isEmpty()) {
|
|
return 0;
|
|
}
|
|
return errorLogRepository.deleteByIdIn(ids);
|
|
}
|
|
|
|
/**
|
|
* 파일 삭제 (에러로그 + 스캔기록)
|
|
*/
|
|
@Transactional
|
|
public Map<String, Object> deleteFileAndErrors(Long serverId, String filePath) {
|
|
int deletedErrors = errorLogRepository.deleteByServerIdAndFilePath(serverId, filePath);
|
|
int deletedFiles = scannedFileRepository.deleteByServerIdAndFilePath(serverId, filePath);
|
|
|
|
return Map.of(
|
|
"success", true,
|
|
"deletedErrors", deletedErrors,
|
|
"deletedScannedFiles", deletedFiles
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 서버+파일 기준 에러 삭제
|
|
*/
|
|
@Transactional
|
|
public int deleteByServerAndFile(Long serverId, String filePath) {
|
|
return errorLogRepository.deleteByServerIdAndFilePath(serverId, filePath);
|
|
}
|
|
|
|
private ErrorLogDto toDto(ErrorLog errorLog) {
|
|
return ErrorLogDto.builder()
|
|
.id(errorLog.getId())
|
|
.serverId(errorLog.getServer().getId())
|
|
.serverName(errorLog.getServer().getName())
|
|
.patternId(errorLog.getPattern().getId())
|
|
.patternName(errorLog.getPattern().getName())
|
|
.filePath(errorLog.getFilePath())
|
|
.lineNumber(errorLog.getLineNumber())
|
|
.summary(errorLog.getSummary())
|
|
.context(errorLog.getContext())
|
|
.severity(errorLog.getSeverity())
|
|
.occurredAt(errorLog.getOccurredAt())
|
|
.createdAt(errorLog.getCreatedAt())
|
|
.build();
|
|
}
|
|
}
|