@Component(value="blTransactionLifecycleMonitor") public class TransactionLifecycleMonitor extends Object implements BroadleafApplicationListener<TransactionLifecycleEvent>, org.springframework.context.ApplicationContextAware, org.springframework.context.SmartLifecycle, SqlStatementLoggable
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:
loggingReportingLagThreshold
. This could indicate the thread has moved on and the transaction was
not correctly finalized.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 the stuckThreshold
has not elapsed.loggingThreshold
and no change in thread stack for stuckThreshold
. Long running or stuck transactions
can account for long resource lock times.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.Modifier and Type | Field and Description |
---|---|
protected boolean |
abbreviateStatements |
protected int |
abbreviateStatementsLength |
protected int |
countMax |
protected boolean |
decompressStatementForLog |
protected boolean |
enabled |
protected Map<Integer,TransactionInfo> |
infos |
protected boolean |
isStarted |
protected long |
loggingPollingResolution |
protected long |
loggingReportingLagThreshold |
protected long |
loggingThreshold |
protected int |
maxQueryListSize |
protected List<TransactionInfoCustomModifier> |
modifiers |
protected long |
stuckThreshold |
protected Timer |
timer |
protected List<LifecycleAwareJpaTransactionManager> |
transactionManagers |
protected boolean |
useCompression |
Constructor and Description |
---|
TransactionLifecycleMonitor() |
Modifier and Type | Method and Description |
---|---|
protected StackTraceElement[] |
compileThreadInformation(long currentTime,
TransactionInfo info,
Thread thread) |
String |
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 |
finalizeTransaction(TransactionLifecycleEvent event) |
int |
getCountMax() |
protected TransactionInfo |
getCurrentTransactionInfo() |
protected javax.persistence.EntityManager |
getEntityManagerFromTransactionObject(Object transactionObject) |
static TransactionLifecycleMonitor |
getInstance() |
long |
getLoggingPollingResolution() |
long |
getLoggingReportingLagThreshold() |
long |
getLoggingThreshold() |
int |
getPhase() |
long |
getStuckThreshold() |
protected void |
groomInProgressTransactionInfos() |
void |
init() |
boolean |
isAsynchronous()
Indicates if this application listener should be run in a background thread if a TaskExecutor is configured.
|
protected boolean |
isAtLeastOneTransactionManagerEnabled() |
boolean |
isAutoStartup() |
boolean |
isRunning() |
boolean |
isUseCompression() |
void |
log(String statement) |
void |
onApplicationEvent(TransactionLifecycleEvent event) |
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 |
stop(Runnable callback) |
@Autowired(required=false) protected List<LifecycleAwareJpaTransactionManager> transactionManagers
@Autowired(required=false) protected List<TransactionInfoCustomModifier> modifiers
@Value(value="${log.transaction.lifecycle.logging.threshold.millis:600000}") protected long loggingThreshold
@Value(value="${log.transaction.lifecycle.stuck.threshold.millis:300000}") protected long stuckThreshold
@Value(value="${log.transaction.lifecycle.logging.polling.resolution.millis:30000}") protected long loggingPollingResolution
@Value(value="${log.transaction.lifecycle.reporting.lag.threshold.millis:300000}") protected long loggingReportingLagThreshold
@Value(value="${log.transaction.lifecycle.info.count.max:5000}") protected int countMax
@Value(value="${log.transaction.lifecycle.use.compression:true}") protected boolean useCompression
@Value(value="${log.transaction.lifecycle.abbreviate.statements:true}") protected boolean abbreviateStatements
@Value(value="${log.transaction.lifecycle.abbreviate.statements.length:200}") protected int abbreviateStatementsLength
@Value(value="${log.transaction.lifecycle.decompress.statement:false}") protected boolean decompressStatementForLog
@Value(value="${log.transaction.lifecycle.query.list.max.size:100}") protected int maxQueryListSize
protected Map<Integer,TransactionInfo> infos
protected boolean isStarted
protected boolean enabled
protected Timer timer
public static TransactionLifecycleMonitor getInstance()
@PostConstruct public void init()
public void setApplicationContext(org.springframework.context.ApplicationContext applicationContext) throws org.springframework.beans.BeansException
setApplicationContext
in interface org.springframework.context.ApplicationContextAware
org.springframework.beans.BeansException
public boolean isAutoStartup()
isAutoStartup
in interface org.springframework.context.SmartLifecycle
public void stop(Runnable callback)
stop
in interface org.springframework.context.SmartLifecycle
public void start()
start
in interface org.springframework.context.Lifecycle
public void stop()
stop
in interface org.springframework.context.Lifecycle
public boolean isRunning()
isRunning
in interface org.springframework.context.Lifecycle
public int getPhase()
getPhase
in interface org.springframework.context.Phased
getPhase
in interface org.springframework.context.SmartLifecycle
public boolean isAsynchronous()
BroadleafApplicationListener
isAsynchronous
in interface BroadleafApplicationListener<TransactionLifecycleEvent>
BroadleafApplicationEventMulticaster
public void onApplicationEvent(TransactionLifecycleEvent event)
onApplicationEvent
in interface org.springframework.context.ApplicationListener<TransactionLifecycleEvent>
public void log(String statement)
log
in interface SqlStatementLoggable
public String decompressLogLine(String compressedFromLog) throws IOException
decompressLogLine
in interface SqlStatementLoggable
IOException
public long getLoggingThreshold()
public void setLoggingThreshold(long loggingThreshold)
public long getStuckThreshold()
public void setStuckThreshold(long stuckThreshold)
public long getLoggingPollingResolution()
public void setLoggingPollingResolution(long loggingPollingResolution)
public long getLoggingReportingLagThreshold()
public void setLoggingReportingLagThreshold(long loggingReportingLagThreshold)
public int getCountMax()
public void setCountMax(int countMax)
public boolean isUseCompression()
public void setUseCompression(boolean useCompression)
protected void groomInProgressTransactionInfos()
protected boolean detectLeakage(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info)
protected boolean detectExpiry(List<Integer> infosToRemove, Integer key, long currentTime, TransactionInfo info, StackTraceElement[] elements)
protected StackTraceElement[] compileThreadInformation(long currentTime, TransactionInfo info, Thread thread)
protected void finalizeTransaction(TransactionLifecycleEvent event)
protected javax.persistence.EntityManager getEntityManagerFromTransactionObject(Object transactionObject)
protected TransactionInfo getCurrentTransactionInfo()
protected boolean isAtLeastOneTransactionManagerEnabled()
Copyright © 2022. All rights reserved.