我试图找到一种在Java中基于反应/事件的异步编程中使用MDC的方法,但是我找不到。
有人可以解释如何在回调事件/方法中传播MDC变量吗?
在这种情况下,如何像在传统的同步编程中那样跟踪请求直到响应得到响应?
答案 0 :(得分:1)
可以通过编程方式完成
MDC.put("transId", transId);
transId变量包含您要跟踪的交易ID。
此登录配置之后:
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%date] %level [%mdc{transId}] %m%n</pattern>
</encoder>
</appender>
请注意,MDC使用threadLocal存储上下文,因此,如果您跳过服务器或线程(例如,使用工作模式),则必须在新线程中重置MDC上下文。我想这可能是您真正要问的问题,但是没有捷径可以在线程更改中保持MDC上下文。这意味着您必须在调用和回叫中都将transId作为参数发送。
注释和AOP可以减轻在调用中传输transId的麻烦。
答案 1 :(得分:0)
正如我在this article中所述,您可以使用MDC日志记录将各种应用程序级变量打印到日志中。
因此,考虑到我们将当前数据库事务ID放在MDC日志中:
MDC.put("txId", String.format(" TxId: [%s]", transactionId(entityManager)));
我们可以使用以下日志附加程序模式将txId
日志变量打印到日志中:
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>TRACE</level>
</filter>
<encoder>
<Pattern>%-5p [%t]:%X{txId} %c{1} - %m%n</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
%X{txId}
模式用于引用txId
日志变量。
因此,在执行以下测试用例时:
try {
doInJPA(entityManager -> {
MDC.put(
"txId",
String.format(
" TxId: [%s]",
transactionId(entityManager)
)
);
Post post = entityManager.createQuery(
"select p " +
"from Post p " +
"where p.id = :id", Post.class)
.setParameter("id", 1L)
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
.getSingleResult();
try {
executeSync(() -> {
try {
doInJPA(_entityManager -> {
MDC.put(
"txId",
String.format(
" TxId: [%s]",
transactionId(_entityManager)
)
);
Post _post = (Post) _entityManager.createQuery(
"select p " +
"from Post p " +
"where p.id = :id", Post.class)
.setParameter("id", 1L)
.unwrap(org.hibernate.query.Query.class)
.setLockOptions(
new LockOptions()
.setLockMode(LockMode.PESSIMISTIC_WRITE)
.setTimeOut(LockOptions.NO_WAIT)
)
.getSingleResult();
});
} finally {
MDC.remove("txId");
}
});
} catch (Exception expected) {
assertTrue(
ExceptionUtil
.rootCause(expected)
.getMessage()
.contains(
"could not obtain lock on row in relation"
)
);
}
});
} finally {
MDC.remove("txId");
}
请注意,我们正在从MDC日志变量存储中删除txId,以便在退出当前数据访问方法后,不会将其附加到日志中。
Hibernate将生成以下日志条目:
DEBUG [Alice]: n.t.d.l.SLF4JQueryLoggingListener -
Time:1,
Success:True,
Type:Prepared,
Batch:False,
QuerySize:1,
BatchSize:0,
Query:["
SELECT CAST(txid_current() AS text)
"],
Params:[()]
DEBUG [Alice]: TxId: [796989] n.t.d.l.SLF4JQueryLoggingListener -
Name:DATA_SOURCE_PROXY,
Time:3,
Success:True,
Type:Prepared,
Batch:False,
QuerySize:1,
BatchSize:0,
Query:["
SELECT p.id AS id1_0_,
p.title AS title2_0_,
p.version AS version3_0_
FROM post p
WHERE p.id = ?
FOR UPDATE OF p "],
Params:[(
1
)]
DEBUG [Bob]: n.t.d.l.SLF4JQueryLoggingListener -
Time:1,
Success:True,
Type:Prepared,
Batch:False,
QuerySize:1,
BatchSize:0,
Query:["
SELECT CAST(txid_current() AS text)
"],
Params:[()]
DEBUG [Bob]: TxId: [796990] n.t.d.l.SLF4JQueryLoggingListener -
Time:0,
Success:False,
Type:Prepared,
Batch:False,
QuerySize:1,
BatchSize:0,
Query:["
SELECT p.id AS id1_0_,
p.title AS title2_0_,
p.version AS version3_0_
FROM post p
WHERE p.id = ?
FOR UPDATE OF p NOWAIT "],
Params:[(
1
)]
WARN [Bob]: TxId: [796990] o.h.e.j.s.SqlExceptionHelper -
SQL Error: 0, SQLState: 55P03
ERROR [Bob]: TxId: [796990] o.h.e.j.s.SqlExceptionHelper -
ERROR: could not obtain lock on row in relation "post"
为设置TxId
MDC日志变量后执行的每个SQL语句添加txId
条目。
正如我在this article中所述,SELECT CAST(txid_current() AS text)
在PostgreSQL中用于获取基础数据库事务标识符。