Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package ch.qos.logback.classic.turbo;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.util.GroupContext;
import ch.qos.logback.core.spi.FilterReply;
import org.slf4j.MDC;
import org.slf4j.Marker;

/**
* 自定义 TurboFilter:在日志事件创建前,将 GroupContext 中的 group 注入 MDC。
* SiftingAppender 会根据 MDC 中的 LOG_GROUP 值路由到不同的日志文件。
* @author YoranYe
*/
public class GroupRoutingFilter extends TurboFilter {

private String mdcKey = GroupContext.getMdcKey(); // 默认 "LOG_GROUP"
private String defaultGroup = "default";

@Override
public FilterReply decide(Marker marker, Logger logger, Level level,
String format, Object[] params, Throwable t) {
if (!isStarted()) {
return FilterReply.NEUTRAL;
}

// 优先从 GroupContext(ThreadLocal)获取 group
String group = GroupContext.getGroup();

// 如果 GroupContext 中没有,则检查 MDC 中是否已有(兼容手动 MDC.put 的场景)
if (group == null || group.isEmpty()) {
group = MDC.get(mdcKey);
}

// 如果都没有,使用默认值
if (group == null || group.isEmpty()) {
group = defaultGroup;
}

// 注入 MDC,供 SiftingAppender 的 Discriminator 使用
MDC.put(mdcKey, group);

return FilterReply.NEUTRAL;
}

@Override
public void start() {
super.start();
}

@Override
public void stop() {
super.stop();
}

// ---- getter/setter(支持在 logback.xml 中配置) ----

public String getMdcKey() {
return mdcKey;
}

public void setMdcKey(String mdcKey) {
this.mdcKey = mdcKey;
}

public String getDefaultGroup() {
return defaultGroup;
}

public void setDefaultGroup(String defaultGroup) {
this.defaultGroup = defaultGroup;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ch.qos.logback.classic.util;

/**
* 基于 ThreadLocal 的日志分组上下文。
* 用于在调用日志前设置 group,TurboFilter 会自动读取并注入 MDC。
* @author YoranYe
*/
public class GroupContext {

private static final String MDC_KEY = "LOG_GROUP";
private static final ThreadLocal<String> GROUP_HOLDER = new ThreadLocal<String>();

/**
* 设置当前线程的日志分组
*/
public static void setGroup(String group) {
GROUP_HOLDER.set(group);
}

/**
* 获取当前线程的日志分组
*/
public static String getGroup() {
return GROUP_HOLDER.get();
}

/**
* 清除当前线程的日志分组(必须在 finally 中调用,防止内存泄漏)
*/
public static void clear() {
GROUP_HOLDER.remove();
}

/**
* 获取 MDC 中使用的 key
*/
public static String getMdcKey() {
return MDC_KEY;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package ch.qos.logback.classic.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* 带分组功能的日志工厂。
* 模拟 LoggerFactory.getLogger(Class, String group) 的效果。
*
* 使用方式:
* <pre>
* Logger logger = GroupLoggerFactory.getLogger(OrderService.class, "order");
* try {
* logger.info("处理订单...");
* } finally {
* GroupLoggerFactory.clearGroup();
* }
* </pre>
*
* @author YoranYe
*/
public class GroupLoggerFactory {

/**
* 获取带分组的 Logger。
* 日志将输出到 logs/{group}/ 目录下。
*
* @param clazz 当前类
* @param group 日志分组名称(如 "order", "user", "payment")
* @return SLF4J Logger 实例
*/
public static Logger getLogger(Class<?> clazz, String group) {
// 将 group 设置到 ThreadLocal 中,TurboFilter 会自动读取并注入 MDC
GroupContext.setGroup(group);
return LoggerFactory.getLogger(clazz);
}

/**
* 获取带分组的 Logger(使用 logger name 而非 Class)。
*/
public static Logger getLogger(String name, String group) {
GroupContext.setGroup(group);
return LoggerFactory.getLogger(name);
}

/**
* 手动设置当前线程的日志分组。
* 适用于 AOP、拦截器等场景。
*/
public static void setGroup(String group) {
GroupContext.setGroup(group);
}

/**
* 清除当前线程的日志分组。
* 必须在 finally 块中调用,防止线程复用时 MDC 污染。
*/
public static void clearGroup() {
GroupContext.clear();
org.slf4j.MDC.remove(GroupContext.getMdcKey());
}
}