22
33import com .datadoghq .trace .DDTags ;
44import com .mongodb .MongoClientOptions ;
5+ import com .mongodb .event .CommandFailedEvent ;
6+ import com .mongodb .event .CommandListener ;
57import com .mongodb .event .CommandStartedEvent ;
8+ import com .mongodb .event .CommandSucceededEvent ;
69import io .opentracing .Span ;
7- import io .opentracing .contrib .mongo .TracingCommandListener ;
8- import io .opentracing .contrib .mongo .TracingCommandListenerFactory ;
10+ import io .opentracing .Tracer ;
911import io .opentracing .tag .Tags ;
12+ import java .net .Inet4Address ;
13+ import java .net .InetAddress ;
14+ import java .nio .ByteBuffer ;
1015import java .util .Arrays ;
16+ import java .util .Collections ;
1117import java .util .List ;
1218import java .util .Map ;
19+ import java .util .concurrent .ConcurrentHashMap ;
1320import lombok .extern .slf4j .Slf4j ;
1421import org .bson .BsonArray ;
1522import org .bson .BsonDocument ;
2128@ Slf4j
2229public class MongoHelper extends DDAgentTracingHelper <MongoClientOptions .Builder > {
2330
24- private static final List <String > WHILDCARD_FIELDS =
25- Arrays .asList ("ordered" , "insert" , "count" , "find" );
26- private static final BsonValue HIDDEN_CAR = new BsonString ("?" );
27-
2831 public MongoHelper (final Rule rule ) {
2932 super (rule );
3033 }
@@ -42,62 +45,137 @@ public MongoHelper(final Rule rule) {
4245 protected MongoClientOptions .Builder doPatch (final MongoClientOptions .Builder builder )
4346 throws Exception {
4447
45- final TracingCommandListener listener = TracingCommandListenerFactory . create (tracer );
48+ final DDTracingCommandListener listener = new DDTracingCommandListener (tracer );
4649 builder .addCommandListener (listener );
4750
4851 setState (builder , 1 );
4952
5053 return builder ;
5154 }
5255
53- public void decorate (final Span span , final CommandStartedEvent event ) {
54- try {
55- // normalize the Mongo command so that parameters are removed from the string
56- final BsonDocument normalized = norm (event .getCommand ());
57- final String mongoCmd = normalized .toString ();
56+ public static class DDTracingCommandListener implements CommandListener {
57+ /**
58+ * The values of these mongo fields will not be scrubbed out. This allows the non-sensitive
59+ * collection names to be captured.
60+ */
61+ private static final List <String > UNSCRUBBED_FIELDS =
62+ Arrays .asList ("ordered" , "insert" , "count" , "find" , "create" );
63+
64+ private static final BsonValue HIDDEN_CHAR = new BsonString ("?" );
65+ private static final String MONGO_OPERATION = "mongo.query" ;
66+
67+ static final String COMPONENT_NAME = "java-mongo" ;
68+ private final Tracer tracer ;
69+ /** Cache for (request id, span) pairs */
70+ private final Map <Integer , Span > cache = new ConcurrentHashMap <>();
71+
72+ public DDTracingCommandListener (Tracer tracer ) {
73+ this .tracer = tracer ;
74+ }
5875
59- // add specific resource name and replace the `db.statement` OpenTracing
60- // tag with the quantized version of the Mongo command
76+ @ Override
77+ public void commandStarted (CommandStartedEvent event ) {
78+ Span span = buildSpan (event );
79+ cache .put (event .getRequestId (), span );
80+ }
81+
82+ @ Override
83+ public void commandSucceeded (CommandSucceededEvent event ) {
84+ Span span = cache .remove (event .getRequestId ());
85+ if (span != null ) {
86+ span .finish ();
87+ }
88+ }
89+
90+ @ Override
91+ public void commandFailed (CommandFailedEvent event ) {
92+ Span span = cache .remove (event .getRequestId ());
93+ if (span != null ) {
94+ onError (span , event .getThrowable ());
95+ span .finish ();
96+ }
97+ }
98+
99+ private Span buildSpan (CommandStartedEvent event ) {
100+ Tracer .SpanBuilder spanBuilder =
101+ tracer .buildSpan (MONGO_OPERATION ).withTag (Tags .SPAN_KIND .getKey (), Tags .SPAN_KIND_CLIENT );
102+
103+ Span span = spanBuilder .startManual ();
104+ try {
105+ decorate (span , event );
106+ } catch (final Throwable e ) {
107+ log .warn ("Couldn't decorate the mongo query: " + e .getMessage (), e );
108+ }
109+
110+ return span ;
111+ }
112+
113+ private static void onError (Span span , Throwable throwable ) {
114+ Tags .ERROR .set (span , Boolean .TRUE );
115+ span .log (Collections .singletonMap ("error.object" , throwable ));
116+ }
117+
118+ public static void decorate (Span span , CommandStartedEvent event ) {
119+ // scrub the Mongo command so that parameters are removed from the string
120+ final BsonDocument scrubbed = scrub (event .getCommand ());
121+ final String mongoCmd = scrubbed .toString ();
122+
123+ Tags .COMPONENT .set (span , COMPONENT_NAME );
124+ Tags .DB_STATEMENT .set (span , mongoCmd );
125+ Tags .DB_INSTANCE .set (span , event .getDatabaseName ());
126+ // add specific resource name
61127 span .setTag (DDTags .RESOURCE_NAME , mongoCmd );
62- span .setTag (Tags .DB_STATEMENT .getKey (), mongoCmd );
63128 span .setTag (DDTags .SPAN_TYPE , "mongodb" );
64- } catch (final Throwable e ) {
65- log .warn ("Couldn't decorate the mongo query: " + e .getMessage (), e );
66- }
67- }
129+ span .setTag (DDTags .SERVICE_NAME , "mongo" );
130+
131+ Tags .PEER_HOSTNAME .set (span , event .getConnectionDescription ().getServerAddress ().getHost ());
132+
133+ InetAddress inetAddress =
134+ event .getConnectionDescription ().getServerAddress ().getSocketAddress ().getAddress ();
68135
69- private BsonDocument norm (final BsonDocument origin ) {
70- final BsonDocument normalized = new BsonDocument ();
71- for (final Map .Entry <String , BsonValue > entry : origin .entrySet ()) {
72- if (WHILDCARD_FIELDS .contains (entry .getKey ())) {
73- normalized .put (entry .getKey (), entry .getValue ());
136+ if (inetAddress instanceof Inet4Address ) {
137+ byte [] address = inetAddress .getAddress ();
138+ Tags .PEER_HOST_IPV4 .set (span , ByteBuffer .wrap (address ).getInt ());
74139 } else {
75- final BsonValue child = norm (entry .getValue ());
76- normalized .put (entry .getKey (), child );
140+ Tags .PEER_HOST_IPV6 .set (span , inetAddress .getHostAddress ());
77141 }
142+
143+ Tags .PEER_PORT .set (span , event .getConnectionDescription ().getServerAddress ().getPort ());
144+ Tags .DB_TYPE .set (span , "mongo" );
78145 }
79- return normalized ;
80- }
81146
82- private BsonValue norm (final BsonArray origin ) {
83- final BsonArray normalized = new BsonArray ();
84- for (final BsonValue value : origin ) {
85- final BsonValue child = norm (value );
86- normalized .add (child );
147+ private static BsonDocument scrub (final BsonDocument origin ) {
148+ final BsonDocument scrub = new BsonDocument ();
149+ for (final Map .Entry <String , BsonValue > entry : origin .entrySet ()) {
150+ if (UNSCRUBBED_FIELDS .contains (entry .getKey ()) && entry .getValue ().isString ()) {
151+ scrub .put (entry .getKey (), entry .getValue ());
152+ } else {
153+ final BsonValue child = scrub (entry .getValue ());
154+ scrub .put (entry .getKey (), child );
155+ }
156+ }
157+ return scrub ;
87158 }
88- return normalized ;
89- }
90159
91- private BsonValue norm (final BsonValue origin ) {
160+ private static BsonValue scrub (final BsonArray origin ) {
161+ final BsonArray scrub = new BsonArray ();
162+ for (final BsonValue value : origin ) {
163+ final BsonValue child = scrub (value );
164+ scrub .add (child );
165+ }
166+ return scrub ;
167+ }
92168
93- final BsonValue normalized ;
94- if (origin .isDocument ()) {
95- normalized = norm (origin .asDocument ());
96- } else if (origin .isArray ()) {
97- normalized = norm (origin .asArray ());
98- } else {
99- normalized = HIDDEN_CAR ;
169+ private static BsonValue scrub (final BsonValue origin ) {
170+ final BsonValue scrubbed ;
171+ if (origin .isDocument ()) {
172+ scrubbed = scrub (origin .asDocument ());
173+ } else if (origin .isArray ()) {
174+ scrubbed = scrub (origin .asArray ());
175+ } else {
176+ scrubbed = HIDDEN_CHAR ;
177+ }
178+ return scrubbed ;
100179 }
101- return normalized ;
102180 }
103181}
0 commit comments