Class TransactionLifecycleMonitor
java.lang.Object
org.broadleafcommerce.common.persistence.transaction.TransactionLifecycleMonitor
- All Implemented Interfaces:
EventListener
,BroadleafApplicationListener<TransactionLifecycleEvent>
,SqlStatementLoggable
,org.springframework.beans.factory.Aware
,org.springframework.context.ApplicationContextAware
,org.springframework.context.ApplicationListener<TransactionLifecycleEvent>
,org.springframework.context.Lifecycle
,org.springframework.context.Phased
,org.springframework.context.SmartLifecycle
@Component("blTransactionLifecycleMonitor")
public class TransactionLifecycleMonitor
extends Object
implements BroadleafApplicationListener<TransactionLifecycleEvent>, org.springframework.context.ApplicationContextAware, org.springframework.context.SmartLifecycle, SqlStatementLoggable
Responsible for most of the work related to monitoring and logging transaction state. This class is notified via a
LifecycleAwareJpaTransactionManager
when a key TransactionLifecycleEvent
occurs. Based on this information,
this monitor attempts to detect transaction fault states and report them as part of SUPPORT level logging (SupportLogger
).
Currently, the monitor attempts to recognize the following fault states:
- TRANSACTIONMONITOR(1) - Leaked Resource: The transaction thread is not considered stuck, but no queries have been issued against the
tracked EntityManager in
loggingReportingLagThreshold
. This could indicate the thread has moved on and the transaction was not correctly finalized. - TRANSACTIONMONITOR(2) - Long Running Transaction: The transaction thread is not considered stuck, but the transaction info has been alive
for
loggingThreshold
. This is not necessarily a fault, but may warrant review. Long running or stuck transactions can account for long resource lock times. Note, this case can later become a stuck thread if thestuckThreshold
has not elapsed. - TRANSACTIONMONITOR(3) - Exception During Finalization: The transaction is attempting to finalize normally, but has experienced an exception during that finalization attempt. This could indicate a problem communicating with the backing database and may result in orphaned resources in the data tier.
- TRANSACTIONMONITOR(4) - Stuck Thread: The transaction thread is considered stuck with a transaction info alive
for
loggingThreshold
and no change in thread stack forstuckThreshold
. Long running or stuck transactions can account for long resource lock times. - TRANSACTIONMONITOR(5) - Alive At Shutdown: The transaction info is considered active at the time of container shutdown. This is not necessarily a fault, but may warrant review. Items at shutdown may simply be waiting for final closure. However, subsequent system kill calls (if applicable) could prematurely exit these connections and they may be interesting for review.
LifecycleAwareJpaTransactionManager
via LifecycleAwareJpaTransactionManager.isEnabled()
.
If no enabled transaction manager is found, the monitor is fully disabled.
The loggingThreshold
variable can be controlled via the 'log.transaction.lifecycle.logging.threshold.millis' property.
The default value is 600000.
The stuckThreshold
variable can be controlled via the 'log.transaction.lifecycle.stuck.threshold.millis' property.
The default values is 300000.
The loggingPollingResolution
variable can be controlled via the 'log.transaction.lifecycle.logging.polling.resolution.millis' property.
The default value is 30000.
The loggingReportingLagThreshold
variable can be controlled via the 'log.transaction.lifecycle.reporting.lag.threshold.millis' property.
The default value is 300000.
The countMax
variable can be controlled via the 'log.transaction.lifecycle.info.count.max' property. The default value is 5000.
The useCompression
variable can be controlled via the 'log.transaction.lifecycle.use.compression' property.
The default value is true.
The abbreviateStatements
variable can be controlled via the 'log.transaction.lifecycle.abbreviate.statements' property.
The default value is true.
The abbreviateStatementsLength
variable can be controlled via the 'log.transaction.lifecycle.abbreviate.statements.length' property.
The default value is 200.
The decompressStatementForLog
variable can be controlled via the 'log.transaction.lifecycle.decompress.statement' property.
The default value is false.
The maxQueryListSize
variable can be controlled via the 'log.transaction.lifecycle.query.list.max.size' property.
The default value is 100.
This monitor is intended for temporary usage as part of transaction related debugging efforts. From both a heap utilization
and performance standpoint, this monitor is suitable for production with default settings. Performance impacts should be minor and will generally
be related to creation of the intial call stack, compression of that call stack and subsequent compression of sql statements.
Compression can be turned off via the 'log.transaction.lifecycle.use.compression' for a minor performance benefit at the cost
of additional heap usage. Heap usage can be capped via the 'log.transaction.lifecycle.info.count.max' property, but if the max
happens to be reached, new transactions will not be monitored. This is more a safety net feature than anything and it's not
anticipated that the default max count will be reached during normal usage. Set 'log.transaction.lifecycle.info.count.max' to
-1 to uncap growth.
To avoid overly large disk usage in logs (in case many log statements are emitted with many queries per), the system will truncate
statements to a default length of 200 characters and leave those statements compressed in the logs (if they were configured to be
compressed). See the abbreviateStatements
and abbreviateStatementsLength
variable to change this behavior.
To view the decompressed version, you'll need to pass the compressed string from the log line to the
decompressLogLine(String)
method to see the decompressed version. This can be easily done by writing a simple
main program that instantiates TransactionLifecycleMonitor and calls this method. To emit the uncompressed version of the
queries instead to the logs, change the decompressStatementForLog
property value. Finally, by default, the system
will only remember and emit the last 100 queries in the transaction. This value can be tweaked via the maxQueryListSize
variable. Set this value to -1 to uncap the expansion of the query list.- Author:
- Jeff Fischer
-
Field Summary
FieldsModifier and TypeFieldDescriptionprotected boolean
protected int
protected int
protected boolean
protected boolean
protected Map<Integer,
TransactionInfo> protected boolean
protected long
protected long
protected long
protected int
protected List<TransactionInfoCustomModifier>
protected long
protected Timer
protected List<LifecycleAwareJpaTransactionManager>
protected boolean
Fields inherited from interface org.springframework.context.SmartLifecycle
DEFAULT_PHASE
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprotected StackTraceElement[]
compileThreadInformation
(long currentTime, TransactionInfo info, Thread thread) decompressLogLine
(String compressedFromLog) protected boolean
detectExpiry
(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info, StackTraceElement[] elements) protected boolean
detectLeakage
(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info) protected void
int
protected TransactionInfo
protected jakarta.persistence.EntityManager
getEntityManagerFromTransactionObject
(Object transactionObject) static TransactionLifecycleMonitor
long
long
long
int
getPhase()
long
protected void
void
init()
boolean
Indicates if this application listener should be run in a background thread if a TaskExecutor is configured.protected boolean
boolean
boolean
boolean
void
void
void
setApplicationContext
(org.springframework.context.ApplicationContext applicationContext) void
setCountMax
(int countMax) void
setLoggingPollingResolution
(long loggingPollingResolution) void
setLoggingReportingLagThreshold
(long loggingReportingLagThreshold) void
setLoggingThreshold
(long loggingThreshold) void
setStuckThreshold
(long stuckThreshold) void
setUseCompression
(boolean useCompression) void
start()
void
stop()
void
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
Methods inherited from interface org.springframework.context.ApplicationListener
supportsAsyncExecution
-
Field Details
-
transactionManagers
-
modifiers
-
loggingThreshold
@Value("${log.transaction.lifecycle.logging.threshold.millis:600000}") protected long loggingThreshold -
stuckThreshold
@Value("${log.transaction.lifecycle.stuck.threshold.millis:300000}") protected long stuckThreshold -
loggingPollingResolution
@Value("${log.transaction.lifecycle.logging.polling.resolution.millis:30000}") protected long loggingPollingResolution -
loggingReportingLagThreshold
@Value("${log.transaction.lifecycle.reporting.lag.threshold.millis:300000}") protected long loggingReportingLagThreshold -
countMax
@Value("${log.transaction.lifecycle.info.count.max:5000}") protected int countMax -
useCompression
@Value("${log.transaction.lifecycle.use.compression:true}") protected boolean useCompression -
abbreviateStatements
@Value("${log.transaction.lifecycle.abbreviate.statements:true}") protected boolean abbreviateStatements -
abbreviateStatementsLength
@Value("${log.transaction.lifecycle.abbreviate.statements.length:200}") protected int abbreviateStatementsLength -
decompressStatementForLog
@Value("${log.transaction.lifecycle.decompress.statement:false}") protected boolean decompressStatementForLog -
maxQueryListSize
@Value("${log.transaction.lifecycle.query.list.max.size:100}") protected int maxQueryListSize -
infos
-
isStarted
protected boolean isStarted -
enabled
protected boolean enabled -
timer
-
-
Constructor Details
-
TransactionLifecycleMonitor
public TransactionLifecycleMonitor()
-
-
Method Details
-
getInstance
-
init
@PostConstruct public void init() -
setApplicationContext
public void setApplicationContext(org.springframework.context.ApplicationContext applicationContext) throws org.springframework.beans.BeansException - Specified by:
setApplicationContext
in interfaceorg.springframework.context.ApplicationContextAware
- Throws:
org.springframework.beans.BeansException
-
isAutoStartup
public boolean isAutoStartup()- Specified by:
isAutoStartup
in interfaceorg.springframework.context.SmartLifecycle
-
stop
- Specified by:
stop
in interfaceorg.springframework.context.SmartLifecycle
-
start
public void start()- Specified by:
start
in interfaceorg.springframework.context.Lifecycle
-
stop
public void stop()- Specified by:
stop
in interfaceorg.springframework.context.Lifecycle
-
isRunning
public boolean isRunning()- Specified by:
isRunning
in interfaceorg.springframework.context.Lifecycle
-
getPhase
public int getPhase()- Specified by:
getPhase
in interfaceorg.springframework.context.Phased
- Specified by:
getPhase
in interfaceorg.springframework.context.SmartLifecycle
-
isAsynchronous
public boolean isAsynchronous()Description copied from interface:BroadleafApplicationListener
Indicates if this application listener should be run in a background thread if a TaskExecutor is configured. If no TaskExecutor is configure with a bean name of "blApplicationEventMulticastTaskExecutor" then this will be run synchronously regardless. Generally, this should return false as the default implementation has no reliability guarantees associated with Asynch processing. However, for convenience this allows asynch processing for situations that don't require guaranteed processing. For example, publishing statistics or log events in a background thread would be candidates for background processing. Handling important database updates would not.- Specified by:
isAsynchronous
in interfaceBroadleafApplicationListener<TransactionLifecycleEvent>
- Returns:
- See Also:
-
onApplicationEvent
- Specified by:
onApplicationEvent
in interfaceorg.springframework.context.ApplicationListener<TransactionLifecycleEvent>
-
log
- Specified by:
log
in interfaceSqlStatementLoggable
-
decompressLogLine
- Specified by:
decompressLogLine
in interfaceSqlStatementLoggable
- Throws:
IOException
-
getLoggingThreshold
public long getLoggingThreshold() -
setLoggingThreshold
public void setLoggingThreshold(long loggingThreshold) -
getStuckThreshold
public long getStuckThreshold() -
setStuckThreshold
public void setStuckThreshold(long stuckThreshold) -
getLoggingPollingResolution
public long getLoggingPollingResolution() -
setLoggingPollingResolution
public void setLoggingPollingResolution(long loggingPollingResolution) -
getLoggingReportingLagThreshold
public long getLoggingReportingLagThreshold() -
setLoggingReportingLagThreshold
public void setLoggingReportingLagThreshold(long loggingReportingLagThreshold) -
getCountMax
public int getCountMax() -
setCountMax
public void setCountMax(int countMax) -
isUseCompression
public boolean isUseCompression() -
setUseCompression
public void setUseCompression(boolean useCompression) -
groomInProgressTransactionInfos
protected void groomInProgressTransactionInfos() -
detectLeakage
protected boolean detectLeakage(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info) -
detectExpiry
protected boolean detectExpiry(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info, StackTraceElement[] elements) -
compileThreadInformation
protected StackTraceElement[] compileThreadInformation(long currentTime, TransactionInfo info, Thread thread) -
finalizeTransaction
-
getEntityManagerFromTransactionObject
protected jakarta.persistence.EntityManager getEntityManagerFromTransactionObject(Object transactionObject) -
getCurrentTransactionInfo
-
isAtLeastOneTransactionManagerEnabled
protected boolean isAtLeastOneTransactionManagerEnabled()
-