Spring 핵심원리 고급편 - 필드 동기화

목차

필드 동기화

public interface LogTrace {
TraceStatus begin(String message);
void end(TraceStatus status);
void exception(TraceStatus status, Exception e);
}

필드 동기화를 위한 로그 분석기 만들기

  • TraceId 객체가 없을 경우 새로 생성한다.
  • TraceId 객체가 있을 경우 Level 을 증가 시킨다.
private void syncTraceId(){
if(traceIdHolder == null){
traceIdHolder = new TraceId();
}else{
traceIdHolder = traceIdHolder.createNextId();
}
}
  • TraceId 내 Level 이 0일 경우 해당 객체를 null 로 초기화 한다.
  • TraceId 내 Level 이 0이 아닐 경우 Level을 1 감소시킨다.
private void releaseTraceId() {
if(traceIdHolder.isFirstLevel()){
traceIdHolder = null; // destroy
}else{
traceIdHolder = traceIdHolder.createPreviousId();
}
}

TraceId 객체는 파라미터로 전달되는 것이 아니라 traceIdHolder 필드에 저장된다.

@Slf4j
public class FieldLogTrace implements LogTrace {

private static final String START_PREFIX = "-->";
private static final String COMPLETE_PREFIX = "<--";
private static final String EX_PREFIX = "<X-";

private TraceId traceIdHolder; // traceId 동기화, 동시성 이슈

@Override
public TraceStatus begin(String message) {

syncTraceId();
TraceId traceId = traceIdHolder;
Long startTimeMs = System.currentTimeMillis();

log.info("[{}] {}{}", traceId.getId(), addSpace(START_PREFIX,
traceId.getLevel()), message);

return new TraceStatus(traceId, startTimeMs, message);
}

private void syncTraceId(){
if(traceIdHolder == null){
traceIdHolder = new TraceId();
}else{
traceIdHolder = traceIdHolder.createNextId();
}
}

@Override
public void end(TraceStatus status) {
complete(status, null);
}

@Override
public void exception(TraceStatus status, Exception e) {
complete(status, e);
}

private void complete(TraceStatus status, Exception e) {

Long stopTimeMs = System.currentTimeMillis();
long resultTimeMs = stopTimeMs - status.getStartTimeMs();
TraceId traceId = status.getTraceId();

if (e == null) {
log.info("[{}] {}{} time={}ms", traceId.getId(),
addSpace(COMPLETE_PREFIX, traceId.getLevel()), status.getMessage(),
resultTimeMs);
} else {
log.info("[{}] {}{} time={}ms ex={}", traceId.getId(),
addSpace(EX_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs,
e.toString());
}

releaseTraceId();
}

private void releaseTraceId() {
if(traceIdHolder.isFirstLevel()){
traceIdHolder = null; // destroy
}else{
traceIdHolder = traceIdHolder.createPreviousId();
}
}

private static String addSpace(String prefix, int level) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < level; i++) {
sb.append((i == level - 1) ? "|" + prefix : "| ");
}
return sb.toString();
}
}
class FieldLogTraceTest {

FieldLogTrace trace = new FieldLogTrace();

@Test
void begin_end_level2(){
TraceStatus status1 = trace.begin("hello1");
TraceStatus status2 = trace.begin("hello2");

trace.end(status2);
trace.end(status1);
}

@Test
void begin_end_level2_exception(){
TraceStatus status1 = trace.begin("hello1");
TraceStatus status2 = trace.begin("hello2");

trace.exception(status2, new IllegalStateException());
trace.exception(status1, new IllegalStateException());
}

}
Share