Summarize
Goal
I have an application that is written in Java using the Spring framework. There is a service that is being used as the handler for grabbing and releasing locks in the database (InnoDB). My goal is to be able to log the grabbing and releasing of the locks to create a lock history. For each lock interaction, I would like to know not only the name of the lock involved, but also where this request is coming from in the code (if possible, class name, method name, and line number).
My expected database entry will look something like this:
id | lock_name | clazz | method | line | lock_date | unlock_date | unlock_type |
---|---|---|---|---|---|---|---|
0 | tb_member | MemberTools | createMember | 123 | 2021-12-23 10:16:00 | 2021-12-23 10:16:01 | COMMIT |
1 | tb_member | MemberTools | editMember | 234 | 2021-12-23 10:16:01 | 2021-12-23 10:16:02 | COMMIT |
I would like to know if there is an easy way to obtain this given that I am using the Spring framework.
Describe
So far, I have tried two things:
- Forcing the caller to pass a reference to itself or its current
StackTraceElement
(usingThread.currentThread().getStackTrace()[1]
). This is not only extremely repetitive, but it also is prone to human error, as a developer might not realize that they need to pass in some reference to themselves. - Inside of the lock service, use the
getStackTrace
method and walk through the elements to find the "correct" one. This is made very hard by Spring and the fact that before a call actually reaches the inside of a class with the@Service
annotation, the call stack is muddled by numbers of calls between proxies and generated classes and such. Unless there is a deterministic way to find the number of calls in between theService
and thecaller
, then this doesn't seem like a good way either.
I have referenced this stack overflow question while working, but these do not take into account the usage of the Spring framework.
Show
A reproducible example will look something like this. First, the structure:
root\
LockService.java
getLock()
MemberTools.java
createMember()
LockService.java:
@Service
public class LockService {
@Transactional
public Lock getLock(String key) {
Lock searchLock = new Lock();
searchLock.setKey(key);
lockMapper.getLock(searchLock);
LockHistory lockHistory = new LockHistory();
// Fill out lockHistory object...
lockMapper.markAsLocked(lockHistory);
attachTransactionCompletedListener(lockHistory);
}
private void attachTransactionCompletedListener(LockHistory lockHistory) {
/* Attach a listener onto the current spring transaction so that we
* can update the database entry when the transaction finishes and
* the lock is released.
*/
}
}
MemberTools.java:
public class MemberTools {
@Autowired
LockService lockService;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createMember() {
lockService.getLock("tb_member");
/* Do create member stuff...
* When this returns, the lock will be released
* (either from COMMIT, ROLLBACK, or UNKNOWN Spring error)
*/
}
}
By the time the getLock()
method is reached, the stack trace is muddled with many calls that Spring inserts (proxies, reflections, etc.). Putting a breakpoint in this function and examining Thread.currentThread().getStackTrace()
will show this.