log 처리 수정

This commit is contained in:
2025-03-26 15:26:51 +09:00
parent e0b537bdb5
commit c5b0cd9b9d
4 changed files with 488 additions and 107 deletions

View File

@@ -112,14 +112,14 @@ public class ChangeDetector {
return changes;
}
Object beforeAttrib = DocAttributeHandler.getAttribValue(beforeDoc);
Object afterAttrib = DocAttributeHandler.getAttribValue(afterDoc);
if (beforeAttrib == null || afterAttrib == null) {
return changes;
}
try {
Object beforeAttrib = DocAttributeHandler.getAttribValue(beforeDoc);
Object afterAttrib = DocAttributeHandler.getAttribValue(afterDoc);
if (beforeAttrib == null || afterAttrib == null) {
return changes;
}
Map<String, Object> oldAttribMap = convertToMap(beforeAttrib);
Map<String, Object> newAttribMap = convertToMap(afterAttrib);
@@ -301,6 +301,90 @@ public class ChangeDetector {
return changes;
}
public static <T> List<FieldChange> detectCollectionChanges(Collection<T> items, String tableName) {
List<FieldChange> changes = new ArrayList<>();
if (items.isEmpty()) {
return changes;
}
// 첫 번째 항목의 클래스를 기준으로 처리
Object firstItem = items.iterator().next();
// 연관 ID 필드 추출
String idFieldName = findIdFieldName(firstItem.getClass());
// 모든 항목을 처리하되 주요 식별자는 한 번만 기록
boolean idRecorded = false;
for (T item : items) {
Map<String, Object> itemMap = objectMapper.convertValue(item, Map.class);
// ID 기록 (한번만)
if (!idRecorded && itemMap.containsKey(idFieldName)) {
changes.add(new FieldChange(idFieldName, itemMap.get(idFieldName), null));
idRecorded = true;
}
// 각 항목의 고유한 식별자와 내용 기록
for (Map.Entry<String, Object> entry : itemMap.entrySet()) {
String fieldName = entry.getKey();
if (isExcludedField(fieldName) || fieldName.equals(idFieldName)) continue;
// 고유 식별자로 필드 구분
String uniqueId = getUniqueIdentifier(item, itemMap, tableName);
String uniqueFieldName = fieldName + "-" + uniqueId;
changes.add(new FieldChange(uniqueFieldName, entry.getValue(), null));
}
}
return changes;
}
// 고유 식별자 생성 메서드
private static String getUniqueIdentifier(Object item, Map<String, Object> itemMap, String tableName) {
// 테이블별 특별 처리
if ("item".equalsIgnoreCase(tableName)) {
return String.valueOf(itemMap.get("type")) + "-" +
String.valueOf(itemMap.get("reward_group_id"));
} else if ("message".equalsIgnoreCase(tableName)) {
return String.valueOf(itemMap.get("language"));
}
// 기본 로직
if (itemMap.containsKey("lang") || itemMap.containsKey("language")) {
return String.valueOf(itemMap.getOrDefault("lang",
itemMap.getOrDefault("language", "unknown")));
}
// 기타 식별자 검사
for (String key : Arrays.asList("type", "code", "uuid", "seq", "order")) {
if (itemMap.containsKey(key)) {
return String.valueOf(itemMap.get(key));
}
}
// 최후 수단: 해시코드
return String.valueOf(System.identityHashCode(item));
}
private static String findIdFieldName(Class<?> clazz) {
// 일반적인 ID 필드명
String[] candidates = {"id", "ID", clazz.getSimpleName().toLowerCase() + "Id"};
for (String candidate : candidates) {
try {
clazz.getDeclaredField(candidate);
return candidate;
} catch (NoSuchFieldException e) {
// 다음 후보 시도
}
}
return "id"; // 기본값
}
private static boolean isExcludedField(String fieldName) {
return EXCLUDED_FIELDS.contains(fieldName) ||
EXCLUDED_FIELDS.contains(toSnakeCase(fieldName)) ||
@@ -346,11 +430,19 @@ public class ChangeDetector {
}
private static Map<String, Object> convertToMap(Object attrib) throws JsonProcessingException {
if (attrib instanceof String) {
return objectMapper.readValue((String) attrib,
new TypeReference<LinkedHashMap<String, Object>>() {});
if (attrib == null) {
return new HashMap<>();
}
if (attrib instanceof String) {
// JSON 문자열인 경우
return objectMapper.readValue((String) attrib, Map.class);
} else if (attrib instanceof Map) {
// 이미 Map인 경우
return (Map<String, Object>) attrib;
} else {
// 다른 객체 타입인 경우
return objectMapper.convertValue(attrib, Map.class);
}
return objectMapper.convertValue(attrib,
new TypeReference<LinkedHashMap<String, Object>>() {});
}
}

View File

@@ -3,6 +3,7 @@ package com.caliverse.admin.history.service;
import com.caliverse.admin.domain.adminlog.FieldChange;
import com.caliverse.admin.domain.entity.HISTORYTYPE;
import com.caliverse.admin.dynamodb.domain.doc.DynamoDBDocBase;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.global.component.transaction.TransactionIdManager;
import com.caliverse.admin.history.ChangeDetector;
import com.caliverse.admin.history.domain.DynamodbHistoryLogInfo;
@@ -24,79 +25,79 @@ public class DynamodbHistoryLogService {
private final TransactionIdManager transactionIdManager;
private final DynamodbHistoryLogRepository dynamodbHistoryLogRepository;
public DynamodbHistoryLogInfo insertHistoryLog(HISTORYTYPE historyType,
public void insertHistoryLog(HISTORYTYPE historyType,
String message,
DynamoDBDocBase metadata,
String userId,
String userIP
DynamoDBDocBase metadata
){
List<FieldChange> changes = ChangeDetector.detectInsertChanges(metadata);
DynamodbHistoryLogInfo historyLog = new DynamodbHistoryLogInfo(
EDBOperationType.INSERT,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
userId,
userIP,
metadata
);
if(!changes.isEmpty()) {
DynamodbHistoryLogInfo historyLog = new DynamodbHistoryLogInfo(
EDBOperationType.INSERT,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
CommonUtils.getAdmin() == null ? "" : CommonUtils.getAdmin().getEmail(),
CommonUtils.getClientIp() == null ? "" : CommonUtils.getClientIp(),
metadata
);
return dynamodbHistoryLogRepository.save(historyLog);
dynamodbHistoryLogRepository.save(historyLog);
}
}
public DynamodbHistoryLogInfo updateHistoryLog(HISTORYTYPE historyType,
public void updateHistoryLog(HISTORYTYPE historyType,
String message,
DynamoDBDocBase beforeMetadata,
DynamoDBDocBase afterMetadata,
String userId,
String userIP
DynamoDBDocBase afterMetadata
){
List<FieldChange> changes = ChangeDetector.detectChanges(
beforeMetadata,
afterMetadata
);
DynamodbHistoryLogInfo historyLog = new DynamodbHistoryLogInfo(
EDBOperationType.UPDATE,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
userId,
userIP,
afterMetadata
);
if(!changes.isEmpty()) {
DynamodbHistoryLogInfo historyLog = new DynamodbHistoryLogInfo(
EDBOperationType.UPDATE,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
CommonUtils.getAdmin() == null ? "" : CommonUtils.getAdmin().getEmail(),
CommonUtils.getClientIp() == null ? "" : CommonUtils.getClientIp(),
afterMetadata
);
return dynamodbHistoryLogRepository.save(historyLog);
dynamodbHistoryLogRepository.save(historyLog);
}
}
public DynamodbHistoryLogInfo deleteHistoryLog(HISTORYTYPE historyType,
public void deleteHistoryLog(HISTORYTYPE historyType,
String message,
DynamoDBDocBase metadata,
String userId,
String userIP
DynamoDBDocBase metadata
){
List<FieldChange> changes = ChangeDetector.detectDeleteChanges(metadata);
DynamodbHistoryLogInfo historyLog = new DynamodbHistoryLogInfo(
EDBOperationType.DELETE,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
userId,
userIP,
metadata
);
if(!changes.isEmpty()) {
DynamodbHistoryLogInfo historyLog = new DynamodbHistoryLogInfo(
EDBOperationType.DELETE,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
CommonUtils.getAdmin() == null ? "" : CommonUtils.getAdmin().getEmail(),
CommonUtils.getClientIp() == null ? "" : CommonUtils.getClientIp(),
metadata
);
return dynamodbHistoryLogRepository.save(historyLog);
dynamodbHistoryLogRepository.save(historyLog);
}
}
public List<DynamodbHistoryLogInfo> getAllHistoryLogs() {

View File

@@ -2,23 +2,29 @@ package com.caliverse.admin.history.service;
import com.caliverse.admin.domain.adminlog.FieldChange;
import com.caliverse.admin.domain.entity.HISTORYTYPE;
import com.caliverse.admin.global.common.code.CommonCode;
import com.caliverse.admin.global.common.code.ErrorCode;
import com.caliverse.admin.global.common.exception.RestApiException;
import com.caliverse.admin.global.component.transaction.TransactionIdManager;
import com.caliverse.admin.history.ChangeDetector;
import com.caliverse.admin.history.domain.MysqlHistoryLogInfo;
import com.caliverse.admin.history.entity.EDBOperationType;
import com.caliverse.admin.history.repository.MysqlHistoryLogRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.*;
@Service
@RequiredArgsConstructor
@Slf4j
public class MysqlHistoryLogService {
private final TransactionIdManager transactionIdManager;
private final MysqlHistoryLogRepository mysqlHistoryLogRepository;
private final ObjectMapper objectMapper;
public <T> void insertHistoryLog(HISTORYTYPE historyType,
String tableName,
@@ -27,22 +33,28 @@ public class MysqlHistoryLogService {
String userId,
String userIP
){
try {
List<FieldChange> changes = ChangeDetector.detectInsertChanges(data);
List<FieldChange> changes = ChangeDetector.detectInsertChanges(data);
if(!changes.isEmpty()) {
MysqlHistoryLogInfo historyLog = new MysqlHistoryLogInfo(
EDBOperationType.INSERT,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
userId,
userIP,
data
);
MysqlHistoryLogInfo historyLog = new MysqlHistoryLogInfo(
EDBOperationType.INSERT,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
userId,
userIP,
data
);
mysqlHistoryLogRepository.save(historyLog);
mysqlHistoryLogRepository.save(historyLog);
}
}catch(Exception e){
log.error("Insert historyLog Save Error", e);
throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_HISTORY_SAVE.getMessage());
}
}
public <T> void updateHistoryLog(HISTORYTYPE historyType,
@@ -53,24 +65,30 @@ public class MysqlHistoryLogService {
String userId,
String userIP
){
List<FieldChange> changes = ChangeDetector.detectChanges(
beforeData,
afterData
);
try {
List<FieldChange> changes = ChangeDetector.detectChanges(
beforeData,
afterData
);
if(!changes.isEmpty()) {
MysqlHistoryLogInfo historyLog = new MysqlHistoryLogInfo(
EDBOperationType.UPDATE,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
userId,
userIP,
afterData
);
MysqlHistoryLogInfo historyLog = new MysqlHistoryLogInfo(
EDBOperationType.UPDATE,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
userId,
userIP,
afterData
);
mysqlHistoryLogRepository.save(historyLog);
mysqlHistoryLogRepository.save(historyLog);
}
}catch(Exception e){
log.error("Update historyLog Save Error", e);
throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_HISTORY_SAVE.getMessage());
}
}
public <T> void deleteHistoryLog(HISTORYTYPE historyType,
@@ -81,21 +99,33 @@ public class MysqlHistoryLogService {
String userIP
){
List<FieldChange> changes = ChangeDetector.detectDeleteChanges(data);
// List<FieldChange> changes = ChangeDetector.detectDeleteChanges(data);
try {
List<FieldChange> changes;
if (data instanceof Collection && !((Collection<?>) data).isEmpty()) {
changes = ChangeDetector.detectCollectionChanges((Collection<?>) data, tableName);
} else {
changes = ChangeDetector.detectDeleteChanges(data);
}
if(!changes.isEmpty()){
MysqlHistoryLogInfo historyLog = new MysqlHistoryLogInfo(
EDBOperationType.DELETE,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
userId,
userIP,
data
);
MysqlHistoryLogInfo historyLog = new MysqlHistoryLogInfo(
EDBOperationType.DELETE,
historyType,
tableName,
message,
transactionIdManager.getCurrentTransactionId(),
changes,
userId,
userIP,
data
);
mysqlHistoryLogRepository.save(historyLog);
mysqlHistoryLogRepository.save(historyLog);
}
}catch(Exception e){
log.error("Delete historyLog Save Error", e);
throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_HISTORY_SAVE.getMessage());
}
}
public List<MysqlHistoryLogInfo> getAllHistoryLogs() {

View File

@@ -0,0 +1,258 @@
package com.caliverse.admin.history.service;
import com.caliverse.admin.global.common.constants.AdminConstants;
import com.caliverse.admin.logs.logservice.businesslogservice.IBusinessLogService;
import lombok.Getter;
import org.bson.Document;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import java.util.ArrayList;
import java.util.List;
@Getter
public abstract class historyLogServiceBase implements IBusinessLogService {
private MongoTemplate mongoTemplate;
public historyLogServiceBase(MongoTemplate mongoTemplate){
this.mongoTemplate = mongoTemplate;
}
//protected abstract Criteria makeCriteria(String startTime, String endTime);
protected AggregationOperation getDefaultProjectOperationRegex(){
AggregationOperation projectOperation1 = context ->
new Document("$project",
new Document(AdminConstants.MONGO_DB_KEY_LANGUAGE_TYPE,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"LanguageType\":\"([^\"]+)\"")
)
)
.append(AdminConstants.MONGO_DB_KEY_MESSAGE, "$message")
.append(AdminConstants.MONGO_DB_KEY_LOGTIME, "$logTime")
.append(AdminConstants.MONGO_DB_KEY_USER_GUID,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"UserGuid\":\"([^\"]+)\"")
)
)
.append(AdminConstants.MONGO_DB_KEY_USER_NICKNAME,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"UserNickname\":\"([^\"]+)\"")
)
)
.append(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"AccountId\":\"([^\"]+)\"")
)
)
.append(AdminConstants.MONGO_DB_KEY_LOGIN_TIME,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"LoginTime\":\"([^\"]+)\"")
)
)
.append(AdminConstants.MONGO_DB_KEY_LOGOUT_TIME,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"LogoutTime\":\"([^\"]+)\"")
)
)
.append(AdminConstants.MONGO_DB_KEY_TRAN_ID,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"TranId\":\"([^\"]+)\"")
)
)
.append(AdminConstants.MONGO_DB_KEY_ACTION,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"Action\":\"([^\"]+)\"")
)
)
.append(AdminConstants.MONGO_DB_KEY_DOMAIN,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"Domain\":\"([^\"]+)\"")
)
)
.append(AdminConstants.MONGO_DB_KEY_SERVER_TYPE,
new Document("$regexFind",
new Document("input", "$message")
.append("regex", "\"ServerType\":\"([^\"]+)\"")
)
)
);
return projectOperation1;
}
protected AggregationOperation getDefaultProjectOperationCapture(){
AggregationOperation projectOperation2 = context ->
new Document("$project",
new Document(AdminConstants.MONGO_DB_KEY_LANGUAGE_TYPE,
new Document("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$languageType.captures", 0))
),
"Ko"
)
)
)
.append(AdminConstants.MONGO_DB_KEY_USER_GUID,
new Document()
.append("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$userGuid.captures", 0))
),
"None"
)
)
)
.append(AdminConstants.MONGO_DB_KEY_USER_NICKNAME,
new Document()
.append("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$userNickname.captures", 0))
),
"None"
)
)
)
.append(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID,
new Document()
.append("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$accountId.captures", 0))
),
"None"
)
)
)
.append(AdminConstants.MONGO_DB_KEY_LOGIN_TIME,
new Document()
.append("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$loginTime.captures", 0))
),
""
)
)
)
.append(AdminConstants.MONGO_DB_KEY_LOGOUT_TIME,
new Document()
.append("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$logoutTime.captures", 0))
),
""
)
)
)
.append(AdminConstants.MONGO_DB_KEY_TRAN_ID,
new Document()
.append("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$tranId.captures", 0))
),
""
)
)
)
.append(AdminConstants.MONGO_DB_KEY_ACTION,
new Document()
.append("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$action.captures", 0))
),
""
)
)
)
.append(AdminConstants.MONGO_DB_KEY_DOMAIN,
new Document()
.append("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$domain.captures", 0))
),
""
)
)
)
.append(AdminConstants.MONGO_DB_KEY_SERVER_TYPE,
new Document()
.append("$ifNull", List.of(
new Document("$toString",
new Document("$arrayElemAt", List.of("$serverType.captures", 0))
),
""
)
)
)
.append(AdminConstants.MONGO_DB_KEY_LOGTIME, "$logTime")
.append(AdminConstants.MONGO_DB_KEY_LOGMONTH,
new Document("$substr" , List.of("$logTime", 0, 7)) //0000-00
)
.append(AdminConstants.MONGO_DB_KEY_LOGDAY,
new Document("$substr" , List.of("$logTime", 0, 10)) //0000-00-00
)
.append(AdminConstants.MONGO_DB_KEY_LOGHOUR,
new Document("$substr" , List.of("$logTime", 0, 13))
)
.append(AdminConstants.MONGO_DB_KEY_LOGMINUTE,
new Document("$substr" , List.of("$logTime", 0, 16))
)
.append("message", "$message")
);
return projectOperation2;
}
private AggregationOperation getDefaultProjectOperationName(){
ProjectionOperation projectOperation = Aggregation.project()
.and(AdminConstants.MONGO_DB_KEY_LOGTIME).as(AdminConstants.MONGO_DB_KEY_LOGTIME)
.and(AdminConstants.MONGO_DB_KEY_LOGMONTH).as(AdminConstants.MONGO_DB_KEY_LOGMONTH)
.and(AdminConstants.MONGO_DB_KEY_LOGDAY).as(AdminConstants.MONGO_DB_KEY_LOGDAY)
.and(AdminConstants.MONGO_DB_KEY_LOGHOUR).as(AdminConstants.MONGO_DB_KEY_LOGHOUR)
.and(AdminConstants.MONGO_DB_KEY_LOGMINUTE).as(AdminConstants.MONGO_DB_KEY_LOGMINUTE)
.and(AdminConstants.MONGO_DB_KEY_MESSAGE).as(AdminConstants.MONGO_DB_KEY_MESSAGE)
.and(AdminConstants.MONGO_DB_KEY_LANGUAGE_TYPE).as(AdminConstants.MONGO_DB_KEY_LANGUAGE_TYPE)
.and(AdminConstants.MONGO_DB_KEY_USER_GUID).as(AdminConstants.MONGO_DB_KEY_USER_GUID)
.and(AdminConstants.MONGO_DB_KEY_USER_NICKNAME).as(AdminConstants.MONGO_DB_KEY_USER_NICKNAME)
.and(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID).as(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID)
.and(AdminConstants.MONGO_DB_KEY_LOGIN_TIME).as(AdminConstants.MONGO_DB_KEY_LOGIN_TIME)
.and(AdminConstants.MONGO_DB_KEY_LOGOUT_TIME).as(AdminConstants.MONGO_DB_KEY_LOGOUT_TIME)
.and(AdminConstants.MONGO_DB_KEY_TRAN_ID).as(AdminConstants.MONGO_DB_KEY_TRAN_ID)
.and(AdminConstants.MONGO_DB_KEY_ACTION).as(AdminConstants.MONGO_DB_KEY_ACTION)
.and(AdminConstants.MONGO_DB_KEY_DOMAIN).as(AdminConstants.MONGO_DB_KEY_DOMAIN)
.and(AdminConstants.MONGO_DB_KEY_SERVER_TYPE).as(AdminConstants.MONGO_DB_KEY_SERVER_TYPE)
.and("message").as("message")
;
return projectOperation;
}
protected List<AggregationOperation> setDefaultOperation(Criteria criteria){
List<AggregationOperation> operations = new ArrayList<>();
operations.add(Aggregation.match(criteria));
operations.add(getDefaultProjectOperationRegex());
operations.add(getDefaultProjectOperationCapture());
operations.add(getDefaultProjectOperationName());
return operations;
}
}