TracePlatformTransactionManager.java
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.tx;
import javax.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanAndScope;
import org.springframework.cloud.sleuth.ThreadLocalSpan;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
/**
* A trace representation of a {@link PlatformTransactionManager}.
*
* @author Marcin Grzejszczak
* @since 3.1.0
*/
public class TracePlatformTransactionManager implements PlatformTransactionManager {
private static final Log log = LogFactory.getLog(TracePlatformTransactionManager.class);
protected final PlatformTransactionManager delegate;
private final BeanFactory beanFactory;
private Tracer tracer;
volatile ThreadLocalSpan threadLocalSpan;
public TracePlatformTransactionManager(PlatformTransactionManager delegate, BeanFactory beanFactory) {
this.delegate = delegate;
this.beanFactory = beanFactory;
}
@PostConstruct
void initialize() {
if (this.threadLocalSpan == null) {
this.threadLocalSpan = new ThreadLocalSpan(tracer());
}
}
@Override
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
initialize();
SpanAndScope spanAndScope = this.threadLocalSpan.get();
Span currentSpan = spanAndScope != null ? spanAndScope.getSpan() : tracer().currentSpan();
Span span = fallbackSpan();
try {
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
TransactionStatus status = this.delegate.getTransaction(definition);
taggedSpan(currentSpan, span, def, status);
return status;
}
catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(
"Exception occurred while trying to get a transaction, will mark the span with error and report it");
}
span.error(e);
span.end();
throw e;
}
}
Span fallbackSpan() {
return SleuthTxSpan.TX_SPAN.wrap(tracer().nextSpan()).name(SleuthTxSpan.TX_SPAN.getName()).start();
}
private Span taggedSpan(Span currentSpan, Span span, TransactionDefinition def, TransactionStatus status) {
if (status.isNewTransaction() || currentSpan == null) {
if (log.isDebugEnabled()) {
log.debug("Creating new span cause a new transaction is started");
}
TracePlatformTransactionManagerTags.tag(span, def, this.delegate.getClass());
}
else {
span = currentSpan;
}
this.threadLocalSpan.set(span);
return span;
}
@Override
public void commit(TransactionStatus status) throws TransactionException {
SpanAndScope spanAndScope = this.threadLocalSpan.get();
if (spanAndScope == null) {
if (log.isDebugEnabled()) {
log.debug("No span and scope found - this shouldn't happen, sth is wrong");
}
this.delegate.commit(status);
return;
}
Exception ex = null;
Span span = spanAndScope.getSpan();
try {
if (log.isDebugEnabled()) {
log.debug("Wrapping commit");
}
this.delegate.commit(status);
}
catch (Exception e) {
ex = e;
span.error(e);
throw e;
}
finally {
SleuthTxSpan.TX_SPAN.wrap(span).event(SleuthTxSpan.Events.COMMIT);
spanAndScope.close();
if (ex == null) {
if (log.isDebugEnabled()) {
log.debug("No exception was found - will clear thread local span");
}
this.threadLocalSpan.remove();
}
}
}
@Override
public void rollback(TransactionStatus status) throws TransactionException {
SpanAndScope spanAndScope = this.threadLocalSpan.get();
if (spanAndScope == null) {
if (log.isDebugEnabled()) {
log.debug("No span and scope found - this shouldn't happen, sth is wrong");
}
this.delegate.rollback(status);
return;
}
Span span = spanAndScope.getSpan();
try {
if (log.isDebugEnabled()) {
log.debug("Wrapping rollback");
}
this.delegate.rollback(status);
}
catch (Exception e) {
span.error(e);
throw e;
}
finally {
SleuthTxSpan.TX_SPAN.wrap(span).event(SleuthTxSpan.Events.ROLLBACK);
spanAndScope.close();
this.threadLocalSpan.remove();
}
}
private Tracer tracer() {
if (this.tracer == null) {
this.tracer = this.beanFactory.getBean(Tracer.class);
}
return this.tracer;
}
}