package org.skywalking.apm.plugin.mongodb.v3;
import com.mongodb.ReadPreference;
import com.mongodb.bulk.DeleteRequest;
import com.mongodb.bulk.InsertRequest;
import com.mongodb.bulk.UpdateRequest;
import com.mongodb.bulk.WriteRequest;
import com.mongodb.operation.*;
import org.bson.BsonDocument;
import org.skywalking.apm.agent.core.conf.Config;
import org.skywalking.apm.agent.core.context.ContextManager;
import org.skywalking.apm.agent.core.plugin.interceptor.EnhancedClassInstanceContext;
import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodInvokeContext;
import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.skywalking.apm.trace.Span;
import org.skywalking.apm.trace.tag.Tags;
import java.util.List;
/**
* {@link MongoDBMethodInterceptor} intercept method of {@link com.mongodb.Mongo#execute(ReadOperation, ReadPreference)}
* or {@link com.mongodb.Mongo#execute(WriteOperation)}. record the mongoDB host, operation name and the key of the
* operation.
*
* @author baiyang
*/
public class MongoDBMethodInterceptor implements InstanceMethodsAroundInterceptor {
/**
* The key name that MongoDB host in {@link EnhancedClassInstanceContext#context}.
*/
static final String MONGODB_HOST = "MONGODB_HOST";
/**
* The key name that MongoDB port in {@link EnhancedClassInstanceContext#context}.
*/
static final String MONGODB_PORT = "MONGODB_PORT";
private static final String MONGODB_COMPONENT = "MongoDB";
private static final String METHOD = "MongoDB/";
private static final int FILTER_LENGTH_LIMIT = 256;
private static final String EMPTY = "";
@Override
public void beforeMethod(final EnhancedClassInstanceContext context,
final InstanceMethodInvokeContext interceptorContext, final MethodInterceptResult result) {
Object[] arguments = interceptorContext.allArguments();
String methodName = arguments[0].getClass().getSimpleName();
Span span = ContextManager.createSpan(METHOD + methodName);
Tags.COMPONENT.set(span, MONGODB_COMPONENT);
Tags.DB_TYPE.set(span, MONGODB_COMPONENT);
Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
Tags.SPAN_LAYER.asDB(span);
if (Config.Plugin.MongoDB.TRACE_PARAM) {
Tags.DB_STATEMENT.set(span, methodName + " " + this.getTraceParam(arguments[0]));
}
}
@Override
public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext,
Object ret) {
Span span = ContextManager.activeSpan();
Tags.PEER_HOST.set(span, context.get(MONGODB_HOST, String.class));
Tags.PEER_PORT.set(span, (Integer) context.get(MONGODB_PORT));
ContextManager.stopSpan();
return ret;
}
@Override
public void handleMethodException(Throwable t, EnhancedClassInstanceContext context,
InstanceMethodInvokeContext interceptorContext) {
ContextManager.activeSpan().log(t);
}
/**
* Convert ReadOperation interface or WriteOperation interface to the implementation class. Get the method name and
* filter info.
*/
@SuppressWarnings("rawtypes")
private String getTraceParam(Object obj) {
if (obj instanceof CountOperation) {
BsonDocument filter = ((CountOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof DistinctOperation) {
BsonDocument filter = ((DistinctOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof FindOperation) {
BsonDocument filter = ((FindOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof GroupOperation) {
BsonDocument filter = ((GroupOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof ListCollectionsOperation) {
BsonDocument filter = ((ListCollectionsOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof MapReduceWithInlineResultsOperation) {
BsonDocument filter = ((ListCollectionsOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof DeleteOperation) {
List<DeleteRequest> writeRequestList = ((DeleteOperation) obj).getDeleteRequests();
return getFilter(writeRequestList);
} else if (obj instanceof InsertOperation) {
List<InsertRequest> writeRequestList = ((InsertOperation) obj).getInsertRequests();
return getFilter(writeRequestList);
} else if (obj instanceof UpdateOperation) {
List<UpdateRequest> writeRequestList = ((UpdateOperation) obj).getUpdateRequests();
return getFilter(writeRequestList);
} else if (obj instanceof CreateCollectionOperation) {
String filter = ((CreateCollectionOperation) obj).getCollectionName();
return limitFilter(filter);
} else if (obj instanceof CreateIndexesOperation) {
List<String> filter = ((CreateIndexesOperation) obj).getIndexNames();
return limitFilter(filter.toString());
} else if (obj instanceof CreateViewOperation) {
String filter = ((CreateViewOperation) obj).getViewName();
return limitFilter(filter);
} else if (obj instanceof FindAndDeleteOperation) {
BsonDocument filter = ((FindAndDeleteOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof FindAndReplaceOperation) {
BsonDocument filter = ((FindAndReplaceOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof FindAndUpdateOperation) {
BsonDocument filter = ((FindAndUpdateOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof MapReduceToCollectionOperation) {
BsonDocument filter = ((MapReduceToCollectionOperation) obj).getFilter();
return limitFilter(filter.toString());
} else if (obj instanceof MixedBulkWriteOperation) {
List<? extends WriteRequest> writeRequestList = ((MixedBulkWriteOperation) obj).getWriteRequests();
return getFilter(writeRequestList);
} else {
return EMPTY;
}
}
private String getFilter(List<? extends WriteRequest> writeRequestList) {
StringBuilder params = new StringBuilder();
for (WriteRequest request : writeRequestList) {
if (request instanceof InsertRequest) {
params.append(((InsertRequest) request).getDocument().toString()).append(",");
} else if (request instanceof DeleteRequest) {
params.append(((DeleteRequest) request).getFilter()).append(",");
} else if (request instanceof UpdateRequest) {
params.append(((UpdateRequest) request).getFilter()).append(",");
}
if (params.length() > FILTER_LENGTH_LIMIT) {
params.append("...");
break;
}
}
return params.toString();
}
private String limitFilter(String filter) {
final StringBuilder params = new StringBuilder();
if (filter.length() > FILTER_LENGTH_LIMIT) {
return params.append(filter.substring(0, FILTER_LENGTH_LIMIT)).append("...").toString();
} else {
return filter;
}
}
}