java - How to log data after the DB is committed for sure - Stack Overflow

admin2025-04-16  4

I used Java Spring to create code that basically stores a specific value in a DB.

And I want to leave a success log if the value saving is successful. Initially, I left a log at the bottom like the code below.

    @Transactional
    public void save(Long id, String target) {

        repository.save(id, target);

        log.info("save success! -> id : {}, target : {}", id, target);
    }

However, at the moment of logging, the values were not yet reflected in the DB, so if something went wrong with the logic later, the log would be logged but the values would not actually be saved. So I realized that I was logging in the wrong place.

here is a question: As a workaround, I was planning to use a TransactionalEventListener to take logs after the DB commit (i.e. after the value is actually saved) and looked for information.

However, I've had a hard finding examples of using listeners for this purpose, so I'm wondering if using TransactionalEventListener for this purpose is the right way to go, and if not, what other ways are there to collect reliable logs like this?

Thanks for your help

I used Java Spring to create code that basically stores a specific value in a DB.

And I want to leave a success log if the value saving is successful. Initially, I left a log at the bottom like the code below.

    @Transactional
    public void save(Long id, String target) {

        repository.save(id, target);

        log.info("save success! -> id : {}, target : {}", id, target);
    }

However, at the moment of logging, the values were not yet reflected in the DB, so if something went wrong with the logic later, the log would be logged but the values would not actually be saved. So I realized that I was logging in the wrong place.

here is a question: As a workaround, I was planning to use a TransactionalEventListener to take logs after the DB commit (i.e. after the value is actually saved) and looked for information.

However, I've had a hard finding examples of using listeners for this purpose, so I'm wondering if using TransactionalEventListener for this purpose is the right way to go, and if not, what other ways are there to collect reliable logs like this?

Thanks for your help

Share asked Feb 2 at 3:04 s9e5ons9e5on 211 silver badge5 bronze badges 0
Add a comment  | 

2 Answers 2

Reset to default 1

Because of the @Transactional annotation, the ACID properties are maintained for this method. This means that the ultimate database commit happens after the method runs successfully without any error. However, as the logger is part of your method, it prints the log before committing the data into the database.

You can handle this in multiple ways. One of them is using @Aspect another one, you can use @TransactionalEventListener.

Here is an example for TransactionalEventListener:

Listener

@Component
@Slf4j
public class TransactionLogEventListener {
  @EventListener
  @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
  public void handleTransactionEvent(TransactionLogEvent event) {
    log.info("save success! -> id : {}, target : {}", event.getId(), event.getTarget());
  }
}

Event

@Getter
public class TransactionLogEvent extends ApplicationEvent {

  private final Long id;
  private final String target;

  public TransactionEmailEvent(Object source, Long id, String target) {
    super(source);
    this.id = id;
    this.target = target;
  }
}

// .. Component
// ... other codes
@Autowired
private ApplicationEventPublisher applicationEventPublisher;

@Transactional
public void save(Long id, String target) {

  repository.save(id, target);

  applicationEventPublisher.publishEvent(new TransactionLogEvent(this, id, target));
}

TransactionalEventListener is a good option and tuhin47 has nicely explained how to use it.

But since your case is quite simple — just logging, I’d like to suggest another approach that I find simpler.

Instead of TransactionalEventListener you could register after commit logic using TransactionSynchronizationManager.registerSynchronization

Define something like this:

public class TransactionUtil {
    public static void runAfterCommit(Runnable action) {
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                action.run();
            }
        });
    }
}

And then use it:

@Transactional
public void save(Long id, String target) {
    repository.save(id, target);
    runAfterCommit(() -> log.info("save success! -> id : {}, target : {}", id, target))
}

I like this approach because

  • after-commit logic is written in the transaction method.
    So it is easy to understand what happens after transaction is committed.
    I believe it’s harder to find which listener is associated with a published event, but of course, IDEA helps with that
  • it is suitable if you want to minimize or avoid usage of Spring annotations

Disadvantages of the TransactionSynchronizationManager approach that I have encountered

  • not everyone likes static methods and util classes
  • harder to mock comparing to TransactionalEventListener but still manageable
转载请注明原文地址:http://anycun.com/QandA/1744810410a87935.html