44import static io .sentry .SpanDataConvention .DB_SYSTEM_KEY ;
55
66import com .jakewharton .nopen .annotation .Open ;
7+ import com .p6spy .engine .common .ConnectionInformation ;
78import com .p6spy .engine .common .StatementInformation ;
89import com .p6spy .engine .event .SimpleJdbcEventListener ;
910import io .sentry .IScopes ;
1011import io .sentry .ISentryLifecycleToken ;
1112import io .sentry .ISpan ;
1213import io .sentry .ScopesAdapter ;
1314import io .sentry .SentryIntegrationPackageStorage ;
14- import io .sentry .Span ;
1515import io .sentry .SpanOptions ;
1616import io .sentry .SpanStatus ;
1717import io .sentry .util .AutoClosableReentrantLock ;
2020import org .jetbrains .annotations .NotNull ;
2121import org .jetbrains .annotations .Nullable ;
2222
23- /** P6Spy JDBC event listener that creates {@link Span}s around database queries. */
2423@ Open
2524public class SentryJdbcEventListener extends SimpleJdbcEventListener {
2625 private static final String TRACE_ORIGIN = "auto.db.jdbc" ;
2726 private final @ NotNull IScopes scopes ;
28- private static final @ NotNull ThreadLocal <ISpan > CURRENT_SPAN = new ThreadLocal <>();
27+ private static final @ NotNull ThreadLocal <ISpan > CURRENT_QUERY_SPAN = new ThreadLocal <>();
28+ private static final @ NotNull ThreadLocal <ISpan > CURRENT_TRANSACTION_SPAN = new ThreadLocal <>();
2929
3030 private volatile @ Nullable DatabaseUtils .DatabaseDetails cachedDatabaseDetails = null ;
3131 protected final @ NotNull AutoClosableReentrantLock databaseDetailsLock =
@@ -52,7 +52,7 @@ public void onBeforeAnyExecute(final @NotNull StatementInformation statementInfo
5252 final @ NotNull SpanOptions spanOptions = new SpanOptions ();
5353 spanOptions .setOrigin (TRACE_ORIGIN );
5454 final ISpan span = parent .startChild ("db.query" , statementInformation .getSql (), spanOptions );
55- CURRENT_SPAN .set (span );
55+ CURRENT_QUERY_SPAN .set (span );
5656 }
5757 }
5858
@@ -61,10 +61,79 @@ public void onAfterAnyExecute(
6161 final @ NotNull StatementInformation statementInformation ,
6262 long timeElapsedNanos ,
6363 final @ Nullable SQLException e ) {
64- final ISpan span = CURRENT_SPAN .get ();
64+ finishSpan (CURRENT_QUERY_SPAN , statementInformation .getConnectionInformation (), e );
65+ }
66+
67+ @ Override
68+ public void onBeforeSetAutoCommit (
69+ final @ NotNull ConnectionInformation connectionInformation ,
70+ boolean newAutoCommit ,
71+ boolean currentAutoCommit ) {
72+ final boolean isSwitchingToManualCommit = !newAutoCommit && currentAutoCommit ;
73+ if (isSwitchingToManualCommit ) {
74+ startSpan (CURRENT_TRANSACTION_SPAN , "db.sql.transaction.begin" , "BEGIN" );
75+ }
76+ }
77+
78+ @ Override
79+ public void onAfterSetAutoCommit (
80+ final @ NotNull ConnectionInformation connectionInformation ,
81+ final boolean newAutoCommit ,
82+ final boolean oldAutoCommit ,
83+ final @ Nullable SQLException e ) {
84+ final boolean isSwitchingToManualCommit = !newAutoCommit && oldAutoCommit ;
85+ if (isSwitchingToManualCommit ) {
86+ finishSpan (CURRENT_TRANSACTION_SPAN , connectionInformation , e );
87+ }
88+ }
89+
90+ @ Override
91+ public void onBeforeCommit (final @ NotNull ConnectionInformation connectionInformation ) {
92+ startSpan (CURRENT_TRANSACTION_SPAN , "db.sql.transaction.commit" , "COMMIT" );
93+ }
94+
95+ @ Override
96+ public void onAfterCommit (
97+ final @ NotNull ConnectionInformation connectionInformation ,
98+ final long timeElapsedNanos ,
99+ final @ Nullable SQLException e ) {
100+ finishSpan (CURRENT_TRANSACTION_SPAN , connectionInformation , e );
101+ }
102+
103+ @ Override
104+ public void onBeforeRollback (final @ NotNull ConnectionInformation connectionInformation ) {
105+ startSpan (CURRENT_TRANSACTION_SPAN , "db.sql.transaction.rollback" , "ROLLBACK" );
106+ }
107+
108+ @ Override
109+ public void onAfterRollback (
110+ final @ NotNull ConnectionInformation connectionInformation ,
111+ final long timeElapsedNanos ,
112+ final @ Nullable SQLException e ) {
113+ finishSpan (CURRENT_TRANSACTION_SPAN , connectionInformation , e );
114+ }
115+
116+ private void startSpan (
117+ final @ NotNull ThreadLocal <ISpan > spanHolder ,
118+ final @ NotNull String operation ,
119+ final @ Nullable String description ) {
120+ final @ Nullable ISpan parent = scopes .getSpan ();
121+ if (parent != null && !parent .isNoOp ()) {
122+ final @ NotNull SpanOptions spanOptions = new SpanOptions ();
123+ spanOptions .setOrigin (TRACE_ORIGIN );
124+ final @ NotNull ISpan span = parent .startChild (operation , description , spanOptions );
125+ spanHolder .set (span );
126+ }
127+ }
128+
129+ private void finishSpan (
130+ final @ NotNull ThreadLocal <ISpan > spanHolder ,
131+ final @ Nullable ConnectionInformation connectionInformation ,
132+ final @ Nullable SQLException e ) {
133+ final @ Nullable ISpan span = spanHolder .get ();
65134
66135 if (span != null ) {
67- applyDatabaseDetailsToSpan (statementInformation , span );
136+ applyDatabaseDetailsToSpan (connectionInformation , span );
68137
69138 if (e != null ) {
70139 span .setThrowable (e );
@@ -73,7 +142,7 @@ public void onAfterAnyExecute(
73142 span .setStatus (SpanStatus .OK );
74143 }
75144 span .finish ();
76- CURRENT_SPAN . set ( null );
145+ spanHolder . remove ( );
77146 }
78147 }
79148
@@ -82,9 +151,9 @@ private void addPackageAndIntegrationInfo() {
82151 }
83152
84153 private void applyDatabaseDetailsToSpan (
85- final @ NotNull StatementInformation statementInformation , final @ NotNull ISpan span ) {
154+ final @ Nullable ConnectionInformation connectionInformation , final @ NotNull ISpan span ) {
86155 final @ NotNull DatabaseUtils .DatabaseDetails databaseDetails =
87- getOrComputeDatabaseDetails (statementInformation );
156+ getOrComputeDatabaseDetails (connectionInformation );
88157
89158 if (databaseDetails .getDbSystem () != null ) {
90159 span .setData (DB_SYSTEM_KEY , databaseDetails .getDbSystem ());
@@ -96,11 +165,11 @@ private void applyDatabaseDetailsToSpan(
96165 }
97166
98167 private @ NotNull DatabaseUtils .DatabaseDetails getOrComputeDatabaseDetails (
99- final @ NotNull StatementInformation statementInformation ) {
168+ final @ Nullable ConnectionInformation connectionInformation ) {
100169 if (cachedDatabaseDetails == null ) {
101170 try (final @ NotNull ISentryLifecycleToken ignored = databaseDetailsLock .acquire ()) {
102171 if (cachedDatabaseDetails == null ) {
103- cachedDatabaseDetails = DatabaseUtils .readFrom (statementInformation );
172+ cachedDatabaseDetails = DatabaseUtils .readFrom (connectionInformation );
104173 }
105174 }
106175 }
0 commit comments