由于springBoot3修改了 servlet相关包的路径 TLog也要适配
重写TLogServletFilter
package com.yomahub.tlog.web.filter;
import com.yomahub.tlog.constant.TLogConstants;
import com.yomahub.tlog.context.TLogContext;
import com.yomahub.tlog.web.common.TLogWebCommon;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 支持servlet
* @author Bryan.Zhang
* @since 1.3.5
*/
public class TLogServletFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse){
try{
TLogWebCommon.loadInstance().preHandle((HttpServletRequest)request);
//把traceId放入response的header,为了方便有些人有这样的需求,从前端拿整条链路的traceId
((HttpServletResponse)response).addHeader(TLogConstants.TLOG_TRACE_KEY, TLogContext.getTraceId());
chain.doFilter(request, response);
return;
}finally {
TLogWebCommon.loadInstance().afterCompletion();
}
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
重写TLogWebCommon
package com.yomahub.tlog.web.common;
import com.yomahub.tlog.constant.TLogConstants;
import com.yomahub.tlog.context.TLogContext;
import com.yomahub.tlog.core.rpc.TLogLabelBean;
import com.yomahub.tlog.core.rpc.TLogRPCHandler;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* TLog web这块的逻辑封装类
*
* @author Bryan.Zhang
* @since 1.1.5
*/
public class TLogWebCommon extends TLogRPCHandler {
private final static Logger log = LoggerFactory.getLogger(TLogWebCommon.class);
private static volatile TLogWebCommon tLogWebCommon;
public static TLogWebCommon loadInstance() {
if (tLogWebCommon == null) {
synchronized (TLogWebCommon.class) {
if (tLogWebCommon == null) {
tLogWebCommon = new TLogWebCommon();
}
}
}
return tLogWebCommon;
}
public void preHandle(HttpServletRequest request) {
String traceId = request.getHeader(TLogConstants.TLOG_TRACE_KEY);
String spanId = request.getHeader(TLogConstants.TLOG_SPANID_KEY);
String preIvkApp = request.getHeader(TLogConstants.PRE_IVK_APP_KEY);
String preIvkHost = request.getHeader(TLogConstants.PRE_IVK_APP_HOST);
String preIp = request.getHeader(TLogConstants.PRE_IP_KEY);
TLogLabelBean labelBean = new TLogLabelBean(preIvkApp, preIvkHost, preIp, traceId, spanId);
processProviderSide(labelBean);
}
public void afterCompletion() {
cleanThreadLocal();
}
}
自定义SLF4JServiceProvider
package org.slf4j;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.status.StatusUtil;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.helpers.BasicMarkerFactory;
import org.slf4j.helpers.Util;
import org.slf4j.spi.MDCAdapter;
import org.slf4j.spi.SLF4JServiceProvider;
/**
* @date 2024/8/29
*/
public class TLogLogbackSLF4JServiceProvider implements SLF4JServiceProvider {
final static String NULL_CS_URL = CoreConstants.CODES_URL + "#null_CS";
/**
* Declare the version of the SLF4J API this implementation is compiled against.
* The value of this field is modified with each major release.
*/
// to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "2.0.99"; // !final
private LoggerContext defaultLoggerContext;
private IMarkerFactory markerFactory;
private MDCAdapter mdcAdapter;
// private final ContextSelectorStaticBinder contextSelectorBinder =
// ContextSelectorStaticBinder.getSingleton();
// private static Object KEY = new Object();
// private volatile boolean initialized = false;
@Override
public void initialize() {
defaultLoggerContext = new LoggerContext();
defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
initializeLoggerContext();
defaultLoggerContext.start();
markerFactory = new BasicMarkerFactory();
mdcAdapter = TLogLogbackTTLMdcAdapter.getInstance();
// set the MDCAdapter for the defaultLoggerContext immediately
defaultLoggerContext.setMDCAdapter(mdcAdapter);
}
private void initializeLoggerContext() {
try {
try {
new ContextInitializer(defaultLoggerContext).autoConfig();
} catch (JoranException je) {
Util.report("Failed to auto configure default logger context", je);
}
// LOGBACK-292
if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
}
// contextSelectorBinder.init(defaultLoggerContext, KEY);
} catch (Exception t) { // see LOGBACK-1159
Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
}
}
@Override
public ILoggerFactory getLoggerFactory() {
return defaultLoggerContext;
// if (!initialized) {
// return defaultLoggerContext;
//
//
// if (contextSelectorBinder.getContextSelector() == null) {
// throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
// }
// return contextSelectorBinder.getContextSelector().getLoggerContext();
}
@Override
public IMarkerFactory getMarkerFactory() {
return markerFactory;
}
@Override
public MDCAdapter getMDCAdapter() {
return mdcAdapter;
}
@Override
public String getRequestedApiVersion() {
return REQUESTED_API_VERSION;
}
}
自定义MDCAdapter
package org.slf4j;
import com.alibaba.ttl.TransmittableThreadLocal;
import org.slf4j.helpers.ThreadLocalMapOfStacks;
import org.slf4j.spi.MDCAdapter;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
/**
* 针对于Logback日志的mdc adapter。
*
* @author Bryan.Zhang
* @since 1.3.7
*/
public class TLogLogbackTTLMdcAdapter implements MDCAdapter {
final ThreadLocal<Map<String, String>> copyOnInheritThreadLocal = new TransmittableThreadLocal<>();
private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks();
private static final int WRITE_OPERATION = 1;
private static final int READ_OPERATION = 2;
private static TLogLogbackTTLMdcAdapter mdcMDCAdapter;
// keeps track of the last operation performed
final ThreadLocal<Integer> lastOperation = new ThreadLocal<>();
static {
mdcMDCAdapter = new TLogLogbackTTLMdcAdapter();
MDC.mdcAdapter = mdcMDCAdapter;
}
public static MDCAdapter getInstance() {
return mdcMDCAdapter;
}
private Integer getAndSetLastOperation(int op) {
Integer lastOp = lastOperation.get();
lastOperation.set(op);
return lastOp;
}
private static boolean wasLastOpReadOrNull(Integer lastOp) {
return lastOp == null || lastOp == READ_OPERATION;
}
private Map<String, String> duplicateAndInsertNewMap(Map<String, String> oldMap) {
Map<String, String> newMap = Collections.synchronizedMap(new HashMap<String, String>());
if (oldMap != null) {
// we don't want the parent thread modifying oldMap while we are
// iterating over it
synchronized (oldMap) {
newMap.putAll(oldMap);
}
}
copyOnInheritThreadLocal.set(newMap);
return newMap;
}
@Override
public void put(String key, String val) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
Map<String, String> oldMap = copyOnInheritThreadLocal.get();
Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);
if (wasLastOpReadOrNull(lastOp) || oldMap == null) {
Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);
newMap.put(key, val);
} else {
oldMap.put(key, val);
}
}
@Override
public void remove(String key) {
if (key == null) {
return;
}
Map<String, String> oldMap = copyOnInheritThreadLocal.get();
if (oldMap == null) {
return;
}
Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);
if (wasLastOpReadOrNull(lastOp)) {
Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);
newMap.remove(key);
} else {
oldMap.remove(key);
}
}
@Override
public void clear() {
lastOperation.set(WRITE_OPERATION);
copyOnInheritThreadLocal.remove();
}
@Override
public String get(String key) {
Map<String, String> map = getPropertyMap();
if ((map != null) && (key != null)) {
return map.get(key);
} else {
return null;
}
}
public Map<String, String> getPropertyMap() {
lastOperation.set(READ_OPERATION);
return copyOnInheritThreadLocal.get();
}
@Override
public Map getCopyOfContextMap() {
lastOperation.set(READ_OPERATION);
Map<String, String> hashMap = copyOnInheritThreadLocal.get();
if (hashMap == null) {
return null;
} else {
return new HashMap<>(hashMap);
}
}
@Override
public void pushByKey(String key, String value) {
threadLocalMapOfDeques.pushByKey(key, value);
}
@Override
public String popByKey(String key) {
return threadLocalMapOfDeques.popByKey(key);
}
@Override
public Deque<String> getCopyOfDequeByKey(String key) {
return threadLocalMapOfDeques.getCopyOfDequeByKey(key);
}
@Override
public void clearDequeByKey(String key) {
threadLocalMapOfDeques.clearDequeByKey(key);
}
@SuppressWarnings("unchecked")
@Override
public void setContextMap(Map contextMap) {
lastOperation.set(WRITE_OPERATION);
Map<String, String> newMap = Collections.synchronizedMap(new HashMap<>());
newMap.putAll(contextMap);
// the newMap replaces the old one for serialisation's sake
copyOnInheritThreadLocal.set(newMap);
}
}
加载TLogLogbackSLF4JServiceProvider
spi配置,services下添加文件:org.slf4j.spi.SLF4JServiceProvider,文件内容如下:
org.slf4j.TLogLogbackSLF4JServiceProvider
配置LogConfig
package com.forge.starter.log.config;
import com.yomahub.tlog.web.filter.TLogServletFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @date 2024/8/29
*/
@Configuration
@ComponentScan(value = "com.yomahub.tlog")
public class LogConfig {
@Bean
public FilterRegistrationBean<TLogServletFilter> loggingFilter() {
FilterRegistrationBean<TLogServletFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new TLogServletFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
完整项目结构
按照上述修改就可以完成SpringBoot3的适配了,其它TLog配置不需要修改