@@ -20,11 +20,19 @@ package cassandra
20
20
package driver
21
21
22
22
import com .datastax .driver .core ._
23
+ import com .datastax .oss .driver .api .core .cql
24
+ import com .datastax .oss .driver .api .core .cql .PrepareRequest
25
+ import com .datastax .oss .driver .internal .core .cql .{CqlPrepareHandler , CqlRequestHandler }
23
26
import kamon .instrumentation .cassandra .driver .DriverInstrumentation .ClusterManagerBridge
24
27
import kamon .instrumentation .cassandra .metrics .HasPoolMetrics
25
- import kamon .instrumentation .context .HasContext .MixinWithInitializer
28
+ import kamon .instrumentation .context .HasContext
29
+ import kamon .trace .Span
26
30
import kanela .agent .api .instrumentation .InstrumentationBuilder
27
31
import kanela .agent .api .instrumentation .bridge .FieldBridge
32
+ import kanela .agent .libs .net .bytebuddy .asm .Advice
33
+
34
+ import java .util .concurrent .CompletionStage
35
+ import java .util .function .BiConsumer
28
36
29
37
class DriverInstrumentation extends InstrumentationBuilder {
30
38
@@ -63,10 +71,10 @@ class DriverInstrumentation extends InstrumentationBuilder {
63
71
.advise(method(" onException" ), OnExceptionAdvice )
64
72
.advise(method(" onTimeout" ), OnTimeoutAdvice )
65
73
.advise(method(" onSet" ), OnSetAdvice )
66
- .mixin(classOf [MixinWithInitializer ])
74
+ .mixin(classOf [HasContext . MixinWithInitializer ])
67
75
68
76
onSubTypesOf(" com.datastax.driver.core.Message$Response" )
69
- .mixin(classOf [MixinWithInitializer ])
77
+ .mixin(classOf [HasContext . MixinWithInitializer ])
70
78
71
79
onType(" com.datastax.driver.core.ArrayBackedResultSet" )
72
80
.advise(method(" fromMessage" ), OnResultSetConstruction )
@@ -76,7 +84,7 @@ class DriverInstrumentation extends InstrumentationBuilder {
76
84
* we need to carry parent-span id through result sets
77
85
*/
78
86
onType(" com.datastax.driver.core.ArrayBackedResultSet$MultiPage" )
79
- .mixin(classOf [MixinWithInitializer ])
87
+ .mixin(classOf [HasContext . MixinWithInitializer ])
80
88
onType(" com.datastax.driver.core.ArrayBackedResultSet$MultiPage" )
81
89
.advise(method(" queryNextPage" ), OnFetchMore )
82
90
@@ -88,6 +96,17 @@ class DriverInstrumentation extends InstrumentationBuilder {
88
96
.mixin(classOf [HasPoolMetrics .Mixin ])
89
97
.advise(method(" setLocationInfo" ), HostLocationAdvice )
90
98
99
+
100
+ /**
101
+ * Cassandra Driver 4.10 support
102
+ */
103
+ onTypes(
104
+ " com.datastax.oss.driver.internal.core.cql.CqlPrepareHandler" ,
105
+ " com.datastax.oss.driver.internal.core.cql.CqlRequestHandler" )
106
+ .mixin(classOf [HasContext .Mixin ])
107
+ .advise(isConstructor(), OnRequestHandlerConstructorAdvice )
108
+ .advise(method(" onThrottleReady" ), OnThrottleReadyAdvice )
109
+
91
110
}
92
111
93
112
object DriverInstrumentation {
@@ -96,3 +115,54 @@ object DriverInstrumentation {
96
115
def getClusterName : String
97
116
}
98
117
}
118
+
119
+ object OnRequestHandlerConstructorAdvice {
120
+
121
+ @ Advice .OnMethodExit ()
122
+ def exit (@ Advice .This requestHandler : HasContext , @ Advice .Argument (0 ) req : Any ): Unit = {
123
+ val (operationName, statement) = req match {
124
+ case pr : PrepareRequest => (QueryOperations .QueryPrepareOperationName , pr.getQuery())
125
+ case ss : cql.SimpleStatement => (QueryOperations .QueryOperationName , ss.getQuery())
126
+ case bs : cql.BoundStatement => (QueryOperations .QueryOperationName , bs.getPreparedStatement.getQuery())
127
+ case bs : cql.BatchStatement => (QueryOperations .BatchOperationName , " " )
128
+ }
129
+
130
+ // Make that every case added to the "onTypes" clause for the Cassandra 4.x support
131
+ // is also handled in this match.
132
+ val resultStage : CompletionStage [_] = requestHandler match {
133
+ case cph : CqlPrepareHandler => cph.handle()
134
+ case crh : CqlRequestHandler => crh.handle()
135
+ }
136
+
137
+ val clientSpan = Kamon .clientSpanBuilder(operationName, " cassandra.driver" )
138
+ .tag(" db.type" , " cassandra" )
139
+ .tag(" db.statement" , statement)
140
+ .start()
141
+
142
+ /**
143
+ * We are registering a callback on the result CompletionStage because the setFinalResult and
144
+ * setFinalError methods might be called more than once on the same request handler.
145
+ */
146
+ resultStage.whenComplete(new BiConsumer [Any , Throwable ] {
147
+ override def accept (result : Any , error : Throwable ): Unit = {
148
+ if (error != null ) {
149
+ clientSpan
150
+ .fail(error)
151
+ .finish()
152
+ }
153
+ else {
154
+ clientSpan.finish()
155
+ }
156
+ }
157
+ })
158
+ }
159
+ }
160
+
161
+ object OnThrottleReadyAdvice {
162
+
163
+ @ Advice .OnMethodEnter ()
164
+ def enter (@ Advice .This requestHandler : HasContext ): Unit = {
165
+ val querySpan = requestHandler.context.get(Span .Key )
166
+ querySpan.mark(" cassandra.throttle.ready" )
167
+ }
168
+ }
0 commit comments