Fast Issue Tracing in High‑Concurrency Java Systems Using a Custom CommonContext
This article explains how to use a custom CommonContext with conditional logging and lambda‑based delayed evaluation to quickly trace issues in high‑concurrency Java back‑end systems without degrading performance, and demonstrates the performance improvements through before‑and‑after measurements.
In high‑concurrency Java back‑end services, logging is usually limited to parameter validation failures or caught exceptions to avoid performance impact, making rapid problem diagnosis difficult when requests do not meet expectations.
The author proposes a generic CommonContext that records a request’s lifecycle, enables a log switch based on a special UUID, and stores both log entries and auxiliary data, allowing developers to pinpoint the exact logic that affected a request.
Below is the original implementation of CommonContext :
@Slf4j
@Data
public class CommonContext {
// log buffer
private StringBuffer logSb = new StringBuffer();
// log switch
private boolean isDebug;
// generic parameters
private boolean compare = false;
// intermediate results
private Set
targetSet = new HashSet<>();
public void clearContext() {
targetSet = Collections.emptySet();
compare = false;
}
public void debug(String message) {
if (!isDebug || StringUtils.isEmpty(message)) {
return;
}
logSb.append(message).append("\t\n");
}
public void debug(String format, Object... argArray) {
if (!isDebug) {
return;
}
String[] msgArray = new String[argArray.length];
for (int i = 0; i < argArray.length; i++) {
msgArray[i] = JSON.toJSONString(argArray[i]);
}
FormattingTuple ft = MessageFormatter.arrayFormat(format, msgArray);
logSb.append(ft.getMessage()).append("\t\n");
}
public void debugEnd() {
if (!isDebug) {
return;
}
String msg = logSb.toString();
log.info(msg);
}
}A typical usage pattern shows how the context is created, the debug flag is set according to a special UUID, and logs are added before and after the core business call:
@Override
public Response method(Request request) {
if (checkParam(request)) {
log.error("request param error:{}", JSON.toJSONString(request));
return Response.failed(ResponseCode.PARAM_INVALID);
}
CallerInfo info = Profiler.registerInfo(Ump.getUmpKey(xxxx), false, true);
ParamVO paramVO = request.getParam();
try {
CommonContext context = new CommonContext();
context.setDebug(Constants.SPECIAL_UUID.equals(request.getUuid()));
Long userId = paramVO.getUserId();
context.setCompare(paramVO.getCompare());
context.debug("输入参数:{}", paramVO);
List result = userAppService.match(context, paramVO);
context.debug("输出结果:{}", result);
context.clearContext();
Response response = Response.success(result);
context.debugEnd(response);
return response;
} catch (Exception e) {
log.error("method error", e);
Profiler.functionError(info);
return Response.failed(ResponseCode.ERROR);
} finally {
Profiler.registerInfoEnd(info);
}
}When logging a large activityList directly, the CPU usage spiked, tp99 latency rose to 35 seconds, and the service became unstable. Commenting out the logging reduced tp99 to ~15 ms, confirming that the eager evaluation of log arguments caused the slowdown.
To avoid this, the article suggests delayed logging using lambda expressions, similar to Log4j’s supplier‑based API, so that the expensive computation is performed only when the log switch is enabled.
The upgraded CommonContext introduces a Supplier ‑based debug method and a separate debugDetail method for fine‑grained control:
package org.example;
import com.alibaba.fastjson.JSON;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
@Slf4j
@Data
public class CommonContext {
private StringBuffer logSb = new StringBuffer();
private boolean isDebug;
private boolean isDebugDetail;
private boolean compare = false;
private Set
targetSet = new HashSet<>();
public void clearContext() {
targetSet = Collections.emptySet();
compare = false;
}
public void setDebugDetail(boolean debugDetail) {
if (debugDetail) {
isDebug = true;
}
isDebugDetail = debugDetail;
}
public void debug(String message) {
if (!isDebug || StringUtils.isEmpty(message)) {
return;
}
logSb.append(message).append("\t\n");
}
public void debug(String format, Object... argArray) {
if (!isDebug) {
return;
}
String[] msgArray = new String[argArray.length];
for (int i = 0; i < argArray.length; i++) {
msgArray[i] = JSON.toJSONString(argArray[i]);
}
FormattingTuple ft = MessageFormatter.arrayFormat(format, msgArray);
logSb.append(ft.getMessage()).append("\t\n");
}
public void debug(String message, Supplier
... paramSuppliers) {
if (!isDebug) {
return;
}
commonDebug(message, paramSuppliers);
}
public void debugDetail(String message, Supplier
... paramSuppliers) {
if (!isDebugDetail) {
return;
}
commonDebug(message, paramSuppliers);
}
private void commonDebug(String message, Supplier
... paramSuppliers) {
String[] msgArray = new String[paramSuppliers.length];
for (int i = 0; i < paramSuppliers.length; i++) {
msgArray[i] = JSON.toJSONString(paramSuppliers[i].get());
}
FormattingTuple ft = MessageFormatter.arrayFormat(message, msgArray);
logSb.append(ft.getMessage()).append("\t\n");
}
public void debugEnd() {
if (!isDebug) {
return;
}
String msg = logSb.toString();
log.info(msg);
}
}Usage after the upgrade shows how to enable detailed logging only when the corresponding flag is true, passing the expensive computation as a lambda supplier:
CommonContext context = new CommonContext();
context.setDebug(Constants.SPECIAL_UUID.equals(request.getUuid()));
context.setDebugDetail(Constants.SPECIAL_UUID2.equals(request.getUuid()));
context.debugDetail("activityList:{}", () -> activityList.stream()
.map(ActivityInfo::toString)
.collect(Collectors.joining("######")));Performance measurements indicate that the tp99 latency dropped from 35 seconds to a few milliseconds after converting all heavy‑weight log statements to lazy‑evaluated lambdas, confirming the effectiveness of the approach.
In summary, by introducing a generic CommonContext with conditional and lazy logging, developers can obtain detailed trace information in high‑concurrency back‑end services without sacrificing throughput or latency.
JD Retail Technology
Official platform of JD Retail Technology, delivering insightful R&D news and a deep look into the lives and work of technologists.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.