我试图找到在Java的反应式/事件驱动异步编程中使用MDC的方法,但是我没有找到。
有人能够解释一下如何在回调事件/方法中传播MDC变量吗?
在这种情况下,如何跟踪请求直到响应被提供,就像传统的同步编程一样?
MDC.put("transId", transId);
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%date] %level [%mdc{transId}] %m%n</pattern>
</encoder>
</appender>
注释和AOP可以缓解在调用中传递transId的某些乏味。
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");
}
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中解释的那样,在PostgreSQL中使用SELECT CAST(txid_current() AS text)
来获取底层数据库事务标识符。