diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java index d5a990d4ac8..adfaef07392 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java @@ -103,6 +103,8 @@ private enum AgentFeature { USM(propertyNameToSystemPropertyName(UsmConfig.USM_ENABLED), false), TELEMETRY(propertyNameToSystemPropertyName(GeneralConfig.TELEMETRY_ENABLED), true), DEBUGGER(propertyNameToSystemPropertyName(DebuggerConfig.DEBUGGER_ENABLED), false), + EXCEPTION_DEBUGGING( + propertyNameToSystemPropertyName(DebuggerConfig.EXCEPTION_REPLAY_ENABLED), false), DATA_JOBS(propertyNameToSystemPropertyName(GeneralConfig.DATA_JOBS_ENABLED), false), AGENTLESS_LOG_SUBMISSION( propertyNameToSystemPropertyName(GeneralConfig.AGENTLESS_LOG_SUBMISSION_ENABLED), false); @@ -149,6 +151,7 @@ public boolean isEnabledByDefault() { private static boolean usmEnabled = false; private static boolean telemetryEnabled = true; private static boolean debuggerEnabled = false; + private static boolean exceptionDebuggingEnabled = false; private static boolean agentlessLogSubmissionEnabled = false; /** @@ -259,6 +262,7 @@ public static void start( cwsEnabled = isFeatureEnabled(AgentFeature.CWS); telemetryEnabled = isFeatureEnabled(AgentFeature.TELEMETRY); debuggerEnabled = isFeatureEnabled(AgentFeature.DEBUGGER); + exceptionDebuggingEnabled = isFeatureEnabled(AgentFeature.EXCEPTION_DEBUGGING); agentlessLogSubmissionEnabled = isFeatureEnabled(AgentFeature.AGENTLESS_LOG_SUBMISSION); if (profilingEnabled) { @@ -1069,7 +1073,7 @@ private static void shutdownProfilingAgent(final boolean sync) { } private static void maybeStartDebugger(Instrumentation inst, Class scoClass, Object sco) { - if (!debuggerEnabled) { + if (!debuggerEnabled && !exceptionDebuggingEnabled) { return; } if (!remoteConfigEnabled) { diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/CapturedContext.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/CapturedContext.java index f89d7f16adf..6e278662543 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/CapturedContext.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/CapturedContext.java @@ -35,6 +35,7 @@ public class CapturedContext implements ValueReferenceResolver { private String spanId; private long duration; private final Map statusByProbeId = new LinkedHashMap<>(); + private Map watches; public CapturedContext() {} @@ -267,6 +268,10 @@ public Map getStaticFields() { return staticFields; } + public Map getWatches() { + return watches; + } + public Limits getLimits() { return limits; } @@ -288,6 +293,11 @@ public String getSpanId() { * instance representation into the corresponding string value. */ public void freeze(TimeoutChecker timeoutChecker) { + if (watches != null) { + // freeze only watches + watches.values().forEach(capturedValue -> capturedValue.freeze(timeoutChecker)); + return; + } if (arguments != null) { arguments.values().forEach(capturedValue -> capturedValue.freeze(timeoutChecker)); } @@ -383,6 +393,13 @@ private void putInStaticFields(String name, CapturedValue value) { staticFields.put(name, value); } + public void addWatch(CapturedValue value) { + if (watches == null) { + watches = new HashMap<>(); + } + watches.put(value.name, value); + } + public static class Status { public static final Status EMPTY_STATUS = new Status(ProbeImplementation.UNKNOWN); public static final Status EMPTY_CAPTURING_STATUS = diff --git a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WellKnownClasses.java b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WellKnownClasses.java index b6e2b2da9c3..c9b9ef5f50c 100644 --- a/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WellKnownClasses.java +++ b/dd-java-agent/agent-debugger/debugger-bootstrap/src/main/java/datadog/trace/bootstrap/debugger/util/WellKnownClasses.java @@ -26,24 +26,24 @@ public class WellKnownClasses { static { TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Class", WellKnownClasses::classToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.String", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Boolean", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Integer", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Long", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Double", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Character", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Byte", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Float", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Short", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.math.BigDecimal", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.math.BigInteger", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.time.Duration", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.time.Instant", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.time.LocalTime", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.time.LocalDate", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.time.LocalDateTime", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.util.UUID", WellKnownClasses::genericToString); - TO_STRING_FINAL_SAFE_CLASSES.put("java.net.URI", WellKnownClasses::genericToString); + TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.String", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Boolean", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Integer", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Long", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Double", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Character", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Byte", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Float", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.lang.Short", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.math.BigDecimal", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.math.BigInteger", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.time.Duration", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.time.Instant", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.time.LocalTime", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.time.LocalDate", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.time.LocalDateTime", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.util.UUID", String::valueOf); + TO_STRING_FINAL_SAFE_CLASSES.put("java.net.URI", String::valueOf); } private static final Map> SAFE_TO_STRING_FUNCTIONS = @@ -51,12 +51,13 @@ public class WellKnownClasses { static { SAFE_TO_STRING_FUNCTIONS.putAll(TO_STRING_FINAL_SAFE_CLASSES); - SAFE_TO_STRING_FUNCTIONS.put( - "java.util.concurrent.atomic.AtomicBoolean", WellKnownClasses::genericToString); - SAFE_TO_STRING_FUNCTIONS.put( - "java.util.concurrent.atomic.AtomicInteger", WellKnownClasses::genericToString); - SAFE_TO_STRING_FUNCTIONS.put( - "java.util.concurrent.atomic.AtomicLong", WellKnownClasses::genericToString); + SAFE_TO_STRING_FUNCTIONS.put("java.util.concurrent.atomic.AtomicBoolean", String::valueOf); + SAFE_TO_STRING_FUNCTIONS.put("java.util.concurrent.atomic.AtomicInteger", String::valueOf); + SAFE_TO_STRING_FUNCTIONS.put("java.util.concurrent.atomic.AtomicLong", String::valueOf); + SAFE_TO_STRING_FUNCTIONS.put("java.io.File", String::valueOf); + // implementations of java.io.file.Path interfaces + SAFE_TO_STRING_FUNCTIONS.put("sun.nio.fs.UnixPath", String::valueOf); + SAFE_TO_STRING_FUNCTIONS.put("sun.nio.fs.WindowsPath", String::valueOf); } private static final Set EQUALS_SAFE_CLASSES = new HashSet<>(); @@ -81,6 +82,9 @@ public class WellKnownClasses { EQUALS_SAFE_CLASSES.add("java.time.LocalDateTime"); EQUALS_SAFE_CLASSES.add("java.util.UUID"); EQUALS_SAFE_CLASSES.add("java.net.URI"); + EQUALS_SAFE_CLASSES.add("java.io.File"); + EQUALS_SAFE_CLASSES.add("sun.nio.fs.UnixPath"); + EQUALS_SAFE_CLASSES.add("sun.nio.fs.WindowsPath"); } private static final Set STRING_PRIMITIVES = @@ -93,7 +97,10 @@ public class WellKnownClasses { "java.time.LocalTime", "java.time.LocalDate", "java.time.LocalDateTime", - "java.util.UUID")); + "java.util.UUID", + "java.io.File", + "sun.nio.fs.UnixPath", + "sun.nio.fs.WindowsPath")); private static final Map, Map>> SPECIAL_TYPE_ACCESS = new HashMap<>(); @@ -236,10 +243,6 @@ private static String classToString(Object o) { return ((Class) o).getTypeName(); } - private static String genericToString(Object o) { - return String.valueOf(o); - } - public static boolean isEqualsSafe(Class clazz) { return clazz.isPrimitive() || clazz.isEnum() diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java index b0dfced64b9..4fa2fbff802 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/agent/DebuggerAgent.java @@ -58,11 +58,6 @@ public class DebuggerAgent { public static synchronized void run( Instrumentation instrumentation, SharedCommunicationObjects sco) { Config config = Config.get(); - if (!config.isDebuggerEnabled()) { - LOGGER.info("Debugger agent disabled"); - return; - } - LOGGER.info("Starting Dynamic Instrumentation"); ClassesToRetransformFinder classesToRetransformFinder = new ClassesToRetransformFinder(); setupSourceFileTracking(instrumentation, classesToRetransformFinder); Redaction.addUserDefinedKeywords(config); @@ -95,6 +90,7 @@ public static synchronized void run( DebuggerContext.initTracer(new DebuggerTracer(debuggerSink.getProbeStatusSink())); DefaultExceptionDebugger defaultExceptionDebugger = null; if (config.isDebuggerExceptionEnabled()) { + LOGGER.info("Starting Exception Replay"); defaultExceptionDebugger = new DefaultExceptionDebugger( configurationUpdater, @@ -112,6 +108,36 @@ public static synchronized void run( setupInstrumentTheWorldTransformer( config, instrumentation, debuggerSink, statsdMetricForwarder); } + // Dynamic Instrumentation + if (config.isDebuggerEnabled()) { + startDynamicInstrumentation( + instrumentation, sco, config, configurationUpdater, debuggerSink, classNameFiltering); + } + try { + /* + Note: shutdown hooks are tricky because JVM holds reference for them forever preventing + GC for anything that is reachable from it. + */ + Runtime.getRuntime().addShutdownHook(new ShutdownHook(configurationPoller, debuggerSink)); + } catch (final IllegalStateException ex) { + // The JVM is already shutting down. + } + ExceptionProbeManager exceptionProbeManager = + defaultExceptionDebugger != null + ? defaultExceptionDebugger.getExceptionProbeManager() + : null; + TracerFlare.addReporter( + new DebuggerReporter(configurationUpdater, sink, exceptionProbeManager)); + } + + private static void startDynamicInstrumentation( + Instrumentation instrumentation, + SharedCommunicationObjects sco, + Config config, + ConfigurationUpdater configurationUpdater, + DebuggerSink debuggerSink, + ClassNameFiltering classNameFiltering) { + LOGGER.info("Starting Dynamic Instrumentation"); String probeFileLocation = config.getDebuggerProbeFileLocation(); if (probeFileLocation != null) { Path probeFilePath = Paths.get(probeFileLocation); @@ -133,24 +159,9 @@ public static synchronized void run( } } subscribeConfigurationPoller(config, configurationUpdater, symDBEnablement); - try { - /* - Note: shutdown hooks are tricky because JVM holds reference for them forever preventing - GC for anything that is reachable from it. - */ - Runtime.getRuntime().addShutdownHook(new ShutdownHook(configurationPoller, debuggerSink)); - } catch (final IllegalStateException ex) { - // The JVM is already shutting down. - } } else { LOGGER.debug("No configuration poller available from SharedCommunicationObjects"); } - ExceptionProbeManager exceptionProbeManager = - defaultExceptionDebugger != null - ? defaultExceptionDebugger.getExceptionProbeManager() - : null; - TracerFlare.addReporter( - new DebuggerReporter(configurationUpdater, sink, exceptionProbeManager)); } private static DebuggerSink createDebuggerSink(Config config, ProbeStatusSink probeStatusSink) { diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java index e2959a90e53..79feb7e4b1b 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/probe/LogProbe.java @@ -7,6 +7,7 @@ import com.datadog.debugger.agent.StringTemplateBuilder; import com.datadog.debugger.el.EvaluationException; import com.datadog.debugger.el.ProbeCondition; +import com.datadog.debugger.el.Value; import com.datadog.debugger.el.ValueScript; import com.datadog.debugger.instrumentation.CapturedContextInstrumentor; import com.datadog.debugger.instrumentation.DiagnosticMessage; @@ -14,10 +15,12 @@ import com.datadog.debugger.instrumentation.MethodInfo; import com.datadog.debugger.sink.DebuggerSink; import com.datadog.debugger.sink.Snapshot; +import com.datadog.debugger.util.MoshiHelper; import com.squareup.moshi.Json; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.JsonReader; import com.squareup.moshi.JsonWriter; +import com.squareup.moshi.Types; import datadog.trace.api.Config; import datadog.trace.bootstrap.debugger.CapturedContext; import datadog.trace.bootstrap.debugger.DebuggerContext; @@ -29,9 +32,12 @@ import datadog.trace.bootstrap.debugger.ProbeRateLimiter; import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import java.io.IOException; +import java.lang.reflect.ParameterizedType; import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; import org.slf4j.Logger; @@ -249,6 +255,7 @@ public String toString() { private final String template; private final List segments; private final boolean captureSnapshot; + private transient List watches; @Json(name = "when") private final ProbeCondition probeCondition; @@ -320,6 +327,35 @@ private LogProbe( this.sampling = sampling; } + private static List parseWatchesFromTags(Tag[] tags) { + if (tags == null || tags.length == 0) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + for (Tag tag : tags) { + if ("dd_watches_dsl".equals(tag.getKey())) { + String ddWatches = tag.getValue(); + // this for POC only, parsing is not robust! + String[] splitWatches = ddWatches.split(","); + for (String watchDef : splitWatches) { + // remove curly braces + String refPath = watchDef.substring(1, watchDef.length() - 1); + result.add(new ValueScript(ValueScript.parseRefPath(refPath), refPath)); + } + } else if ("dd_watches_json".equals(tag.getKey())) { + String json = tag.getValue(); + try { + ParameterizedType type = Types.newParameterizedType(List.class, ValueScript.class); + result.addAll( + MoshiHelper.createMoshiWatches().>adapter(type).fromJson(json)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + return result; + } + public LogProbe copy() { return new LogProbe( language, @@ -347,6 +383,10 @@ public boolean isCaptureSnapshot() { return captureSnapshot; } + public List getWatches() { + return watches; + } + public ProbeCondition getProbeCondition() { return probeCondition; } @@ -390,16 +430,53 @@ public void evaluate( // sample if probe has condition and condition is true or has error sample(logStatus, methodLocation); } - if (logStatus.isSampled() && logStatus.getCondition()) { - StringTemplateBuilder logMessageBuilder = new StringTemplateBuilder(segments, LIMITS); - String msg = logMessageBuilder.evaluate(context, logStatus); - if (msg != null && msg.length() > LOG_MSG_LIMIT) { - StringBuilder sb = new StringBuilder(LOG_MSG_LIMIT + 3); - sb.append(msg, 0, LOG_MSG_LIMIT); - sb.append("..."); - msg = sb.toString(); + processMsgTemplate(context, logStatus); + processWatches(context, logStatus); + } + + private void processMsgTemplate(CapturedContext context, LogStatus logStatus) { + if (!logStatus.isSampled() || !logStatus.getCondition()) { + return; + } + StringTemplateBuilder logMessageBuilder = new StringTemplateBuilder(segments, LIMITS); + String msg = logMessageBuilder.evaluate(context, logStatus); + if (msg != null && msg.length() > LOG_MSG_LIMIT) { + StringBuilder sb = new StringBuilder(LOG_MSG_LIMIT + 3); + sb.append(msg, 0, LOG_MSG_LIMIT); + sb.append("..."); + msg = sb.toString(); + } + logStatus.setMessage(msg); + } + + private void processWatches(CapturedContext context, LogStatus logStatus) { + if (watches == null) { + watches = parseWatchesFromTags(tags); + } + if (watches.isEmpty()) { + return; + } + if (!logStatus.isSampled()) { + return; + } + for (ValueScript watch : watches) { + try { + Value result = watch.execute(context); + if (result.isUndefined()) { + throw new EvaluationException("UNDEFINED", watch.getDsl()); + } + if (result.isNull()) { + context.addWatch( + CapturedContext.CapturedValue.of(watch.getDsl(), Object.class.getTypeName(), null)); + } else { + context.addWatch( + CapturedContext.CapturedValue.of( + watch.getDsl(), Object.class.getTypeName(), result.getValue())); + } + } catch (EvaluationException ex) { + logStatus.addError(new EvaluationError(ex.getExpr(), ex.getMessage())); + logStatus.setLogTemplateErrors(true); } - logStatus.setMessage(msg); } } @@ -620,7 +697,7 @@ public boolean shouldSend() { } public boolean shouldReportError() { - return hasConditionErrors || hasLogTemplateErrors; + return sampled && (hasConditionErrors || hasLogTemplateErrors); } public boolean getCondition() { @@ -759,6 +836,7 @@ public static class Builder extends ProbeDefinition.Builder { private String template; private List segments; private boolean captureSnapshot; + private List watches; private ProbeCondition probeCondition; private Capture capture; private Sampling sampling; diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiHelper.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiHelper.java index 31e132639a6..788534d1497 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiHelper.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiHelper.java @@ -50,4 +50,8 @@ public static JsonAdapter> createGenericAdapter() { public static Moshi createMoshiSymbol() { return new Moshi.Builder().build(); } + + public static Moshi createMoshiWatches() { + return new Moshi.Builder().add(ValueScript.class, new ValueScript.ValueScriptAdapter()).build(); + } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiSnapshotHelper.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiSnapshotHelper.java index c0d747b89a1..8f87380cfd1 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiSnapshotHelper.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/util/MoshiSnapshotHelper.java @@ -37,6 +37,7 @@ public class MoshiSnapshotHelper { public static final String CAUGHT_EXCEPTIONS = "caughtExceptions"; public static final String ARGUMENTS = "arguments"; public static final String LOCALS = "locals"; + public static final String WATCHES = "watches"; public static final String THROWABLE = "throwable"; public static final String STATIC_FIELDS = "staticFields"; public static final String THIS = "this"; @@ -152,6 +153,20 @@ public void toJson(JsonWriter jsonWriter, CapturedContext capturedContext) throw return; } jsonWriter.beginObject(); + if (capturedContext.getWatches() != null) { + // only watches are serialized into the snapshot + jsonWriter.name(WATCHES); + jsonWriter.beginObject(); + SerializationResult resultWatches = + toJsonCapturedValues( + jsonWriter, + capturedContext.getWatches(), + capturedContext.getLimits(), + timeoutChecker); + jsonWriter.endObject(); // / watches + jsonWriter.endObject(); + return; + } jsonWriter.name(ARGUMENTS); jsonWriter.beginObject(); SerializationResult resultArgs = diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java index 2a83cccab82..44b4913f421 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java @@ -2422,6 +2422,36 @@ public void allProbesSameMethod() throws IOException, URISyntaxException { } } + @Test + public void watches() throws IOException, URISyntaxException { + final String CLASS_NAME = "CapturedSnapshot08"; + LogProbe probe = + createProbeBuilder(PROBE_ID, CLASS_NAME, "doit", null, null) + .evaluateAt(MethodLocation.EXIT) + .tags( + "dd_watches_dsl:{typed.fld.fld.msg},{nullTyped.fld}", + "dd_watches_json:[{\"dsl\":\"typed.fld.fld.msg_json\",\"json\":{\"getmember\":[{\"getmember\":[{\"getmember\":[{\"ref\":\"typed\"},\"fld\"]},\"fld\"]},\"msg\"]}}]") + .build(); + TestSnapshotListener listener = installProbes(CLASS_NAME, probe); + Class testClass = compileAndLoadClass(CLASS_NAME); + int result = Reflect.onClass(testClass).call("main", "1").get(); + assertEquals(3, result); + Snapshot snapshot = assertOneSnapshot(listener); + assertEquals(3, snapshot.getCaptures().getReturn().getWatches().size()); + assertCaptureWatches( + snapshot.getCaptures().getReturn(), + "typed.fld.fld.msg", + String.class.getTypeName(), + "hello"); + assertCaptureWatches( + snapshot.getCaptures().getReturn(), "nullTyped.fld", Object.class.getTypeName(), null); + assertCaptureWatches( + snapshot.getCaptures().getReturn(), + "typed.fld.fld.msg_json", + String.class.getTypeName(), + "hello"); + } + private TestSnapshotListener setupInstrumentTheWorldTransformer(String excludeFileName) { Config config = mock(Config.class); when(config.isDebuggerEnabled()).thenReturn(true); @@ -2655,6 +2685,13 @@ private void assertCaptureReturnValue( } } + private void assertCaptureWatches( + CapturedContext context, String name, String typeName, String value) { + CapturedContext.CapturedValue watch = context.getWatches().get(name); + assertEquals(typeName, watch.getType()); + assertEquals(value, MoshiSnapshotTestHelper.getValue(watch)); + } + private void assertCaptureThrowable( CapturedContext context, String typeName, String message, String methodName, int lineNumber) { CapturedContext.CapturedThrowable throwable = context.getCapturedThrowable(); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java index c2a00678067..27faecab7b7 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/ConfigurationTest.java @@ -121,6 +121,11 @@ public void deserializeLogProbes() throws Exception { ArrayList logProbes = new ArrayList<>(config.getLogProbes()); assertEquals(1, logProbes.size()); LogProbe logProbe0 = logProbes.get(0); + assertEquals(2, logProbe0.getTags().length); + assertEquals("dd_watches_dsl", logProbe0.getTags()[0].getKey()); + assertEquals("{object.objField.intField}", logProbe0.getTags()[0].getValue()); + assertEquals("env", logProbe0.getTags()[1].getKey()); + assertEquals("staging", logProbe0.getTags()[1].getValue()); assertEquals(8, logProbe0.getSegments().size()); assertEquals("this is a log line customized! uuid=", logProbe0.getSegments().get(0).getStr()); assertEquals("uuid", logProbe0.getSegments().get(1).getExpr()); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java index 006601235ae..2f6f3acdec6 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/SnapshotSerializationTest.java @@ -20,6 +20,7 @@ import static com.datadog.debugger.util.MoshiSnapshotHelper.TRUNCATED; import static com.datadog.debugger.util.MoshiSnapshotHelper.TYPE; import static com.datadog.debugger.util.MoshiSnapshotHelper.VALUE; +import static com.datadog.debugger.util.MoshiSnapshotHelper.WATCHES; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -39,12 +40,14 @@ import datadog.trace.bootstrap.debugger.ProbeLocation; import datadog.trace.bootstrap.debugger.util.TimeoutChecker; import datadog.trace.test.util.Flaky; +import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.math.BigDecimal; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Path; import java.time.Duration; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; @@ -307,6 +310,8 @@ static class WellKnownClasses { OptionalLong maybeLong = OptionalLong.of(84); Exception ex = new IllegalArgumentException("invalid arg"); StackTraceElement element = new StackTraceElement("Foo", "bar", "foo.java", 42); + File file = new File("/tmp/foo"); + Path path = file.toPath(); } @Test @@ -383,6 +388,10 @@ public void wellKnownClasses() throws IOException { assertPrimitiveValue(elementFields, "methodName", String.class.getTypeName(), "bar"); assertPrimitiveValue(elementFields, "fileName", String.class.getTypeName(), "foo.java"); assertPrimitiveValue(elementFields, "lineNumber", Integer.class.getTypeName(), "42"); + // file + assertPrimitiveValue(objLocalFields, "file", File.class.getTypeName(), "/tmp/foo"); + // path + assertPrimitiveValue(objLocalFields, "path", "sun.nio.fs.UnixPath", "/tmp/foo"); } @Test @@ -928,6 +937,35 @@ public void enumValues() throws IOException { assertEquals("TWO", enumValueJson.get("value")); } + @Test + public void watches() throws IOException { + JsonAdapter adapter = createSnapshotAdapter(); + Snapshot snapshot = createSnapshot(); + CapturedContext context = new CapturedContext(); + Map map = new HashMap<>(); + map.put("foo1", "bar1"); + map.put("foo2", "bar2"); + map.put("foo3", "bar3"); + context.addWatch(CapturedContext.CapturedValue.of("watch1", Map.class.getTypeName(), map)); + context.addWatch( + CapturedContext.CapturedValue.of( + "watch2", List.class.getTypeName(), Arrays.asList("1", "2", "3"))); + context.addWatch(CapturedContext.CapturedValue.of("watch3", Integer.TYPE.getTypeName(), 42)); + snapshot.setExit(context); + String buffer = adapter.toJson(snapshot); + System.out.println(buffer); + Map json = MoshiHelper.createGenericAdapter().fromJson(buffer); + Map capturesJson = (Map) json.get(CAPTURES); + Map returnJson = (Map) capturesJson.get(RETURN); + Map watches = (Map) returnJson.get(WATCHES); + assertNull(returnJson.get(LOCALS)); + assertNull(returnJson.get(ARGUMENTS)); + assertEquals(3, watches.size()); + assertMapItems(watches, "watch1", "foo1", "bar1", "foo2", "bar2", "foo3", "bar3"); + assertArrayItem(watches, "watch2", "1", "2", "3"); + assertPrimitiveValue(watches, "watch3", Integer.TYPE.getTypeName(), "42"); + } + private Map doFieldCount(int maxFieldCount) throws IOException { JsonAdapter adapter = createSnapshotAdapter(); Snapshot snapshot = createSnapshotForFieldCount(maxFieldCount); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java index 481e538e8f9..3a501b4e90e 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/probe/LogProbeTest.java @@ -5,9 +5,17 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.datadog.debugger.sink.Snapshot; +import datadog.trace.bootstrap.debugger.CapturedContext; +import datadog.trace.bootstrap.debugger.MethodLocation; import datadog.trace.bootstrap.debugger.ProbeId; +import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; public class LogProbeTest { private static final String LANGUAGE = "java"; @@ -56,6 +64,84 @@ public void log() { assertEquals("}", logProbe.getSegments().get(5).getStr()); } + @ParameterizedTest + @ValueSource(strings = {"ENTRY", "EXIT"}) + public void fillSnapshot_shouldSend(String methodLocation) { + LogProbe logProbe = createLog(null).evaluateAt(MethodLocation.valueOf(methodLocation)).build(); + CapturedContext entryContext = new CapturedContext(); + CapturedContext exitContext = new CapturedContext(); + prepareContext(entryContext, logProbe, MethodLocation.ENTRY); + prepareContext(exitContext, logProbe, MethodLocation.EXIT); + Snapshot snapshot = new Snapshot(Thread.currentThread(), logProbe, 10); + assertTrue(logProbe.fillSnapshot(entryContext, exitContext, null, snapshot)); + } + + @ParameterizedTest + @MethodSource("statusValues") + public void fillSnapshot( + boolean sampled, + boolean condition, + boolean conditionErrors, + boolean logTemplateErrors, + boolean shouldCommit) { + LogProbe logProbe = createLog(null).evaluateAt(MethodLocation.EXIT).build(); + CapturedContext entryContext = new CapturedContext(); + CapturedContext exitContext = new CapturedContext(); + LogProbe.LogStatus entryStatus = prepareContext(entryContext, logProbe, MethodLocation.ENTRY); + fillStatus(entryStatus, sampled, condition, conditionErrors, logTemplateErrors); + LogProbe.LogStatus exitStatus = prepareContext(exitContext, logProbe, MethodLocation.EXIT); + fillStatus(exitStatus, sampled, condition, conditionErrors, logTemplateErrors); + Snapshot snapshot = new Snapshot(Thread.currentThread(), logProbe, 10); + assertEquals(shouldCommit, logProbe.fillSnapshot(entryContext, exitContext, null, snapshot)); + } + + private void fillStatus( + LogProbe.LogStatus entryStatus, + boolean sampled, + boolean condition, + boolean conditionErrors, + boolean logTemplateErrors) { + entryStatus.setSampled(sampled); + entryStatus.setCondition(condition); + entryStatus.setConditionErrors(conditionErrors); + entryStatus.setLogTemplateErrors(logTemplateErrors); + entryStatus.setLogTemplateErrors(logTemplateErrors); + } + + private LogProbe.LogStatus prepareContext( + CapturedContext context, LogProbe logProbe, MethodLocation methodLocation) { + context.evaluate(PROBE_ID.getEncodedId(), logProbe, "", 0, methodLocation); + return (LogProbe.LogStatus) context.getStatus(PROBE_ID.getEncodedId()); + } + + private static Stream statusValues() { + return Stream.of( + // sampled, condition, conditionErrors, logTemplateErrors, shouldCommit + Arguments.of(true, true, false, false, true), + Arguments.of(true, false, false, false, false), + Arguments.of(true, false, true, false, true), + Arguments.of(true, false, true, true, true), + Arguments.of(true, false, false, true, true), + Arguments.of(false, false, false, false, false), + Arguments.of(false, true, false, false, false), + Arguments.of(false, false, true, false, false), + Arguments.of(false, false, false, true, false), + Arguments.of(false, false, true, true, false), + Arguments.of(false, true, true, true, false)); + } + + @Test + public void fillSnapshot_shouldSend_exit() { + LogProbe logProbe = createLog(null).evaluateAt(MethodLocation.EXIT).build(); + CapturedContext entryContext = new CapturedContext(); + entryContext.evaluate(PROBE_ID.getEncodedId(), logProbe, "", 0, MethodLocation.ENTRY); + entryContext.getStatus(PROBE_ID.getEncodedId()); + CapturedContext exitContext = new CapturedContext(); + exitContext.evaluate(PROBE_ID.getEncodedId(), logProbe, "", 0, MethodLocation.EXIT); + Snapshot snapshot = new Snapshot(Thread.currentThread(), logProbe, 10); + assertTrue(logProbe.fillSnapshot(entryContext, exitContext, null, snapshot)); + } + private LogProbe.Builder createLog(String template) { return LogProbe.builder() .language(LANGUAGE) diff --git a/dd-java-agent/agent-debugger/src/test/resources/test_log_probe.json b/dd-java-agent/agent-debugger/src/test/resources/test_log_probe.json index fc840be8e9f..81f2bcfb9fb 100644 --- a/dd-java-agent/agent-debugger/src/test/resources/test_log_probe.json +++ b/dd-java-agent/agent-debugger/src/test/resources/test_log_probe.json @@ -14,6 +14,7 @@ "created": "2021-03-31T13:26:52.519150+00:00", "active": true, "language": "java", + "tags": ["dd_watches_dsl:{object.objField.intField}", "env:staging"], "where": { "typeName": "VetController", "methodName": "showVetList" diff --git a/dd-java-agent/agent-logging/src/main/java/datadog/trace/logging/ddlogger/DDLoggerFactory.java b/dd-java-agent/agent-logging/src/main/java/datadog/trace/logging/ddlogger/DDLoggerFactory.java index 6cc9aff09b1..47d03e7cf9e 100644 --- a/dd-java-agent/agent-logging/src/main/java/datadog/trace/logging/ddlogger/DDLoggerFactory.java +++ b/dd-java-agent/agent-logging/src/main/java/datadog/trace/logging/ddlogger/DDLoggerFactory.java @@ -91,20 +91,10 @@ public void reinitialize() { // DDLoggerFactory can be called at very early stage, before Config is loaded // So to get property/env we use this custom function private static boolean isLogCollectionEnabled() { - // FIXME: For the initial rollout, we default log collection to true for IAST, Dynamic - // Instrumentation, and CI Visibility - // FIXME: For progressive rollout, we include by default Java < 11 hosts as product independent - // FIXME: sample users. - // FIXME: This should be removed once we default to true. - final boolean defaultValue = - isFlagEnabled("dd.iast.enabled", "DD_IAST_ENABLED", false) - || isFlagEnabled("dd.appsec.enabled", "DD_APPSEC_ENABLED", false) - || isFlagEnabled("dd.civisibility.enabled", "DD_CIVISIBILITY_ENABLED", false) - || isFlagEnabled( - "dd.dynamic.instrumentation.enabled", "DD_DYNAMIC_INSTRUMENTATION_ENABLED", false) - || !Platform.isJavaVersionAtLeast(11); return isFlagEnabled( - "dd.telemetry.log-collection.enabled", "DD_TELEMETRY_LOG_COLLECTION_ENABLED", defaultValue); + "dd.instrumentation.telemetry.enabled", "DD_INSTRUMENTATION_TELEMETRY_ENABLED", true) + && isFlagEnabled( + "dd.telemetry.log-collection.enabled", "DD_TELEMETRY_LOG_COLLECTION_ENABLED", true); } private static boolean isFlagEnabled( diff --git a/dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilerConfig.java b/dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilerConfig.java index 4e161dada37..959dc24b616 100644 --- a/dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilerConfig.java +++ b/dd-java-agent/agent-profiling/profiling-ddprof/src/main/java/com/datadog/profiling/ddprof/DatadogProfilerConfig.java @@ -412,11 +412,10 @@ public static long getLong(ConfigProvider configProvider, String key) { } public static boolean useJvmtiWallclockSampler(ConfigProvider configProvider) { - return !isWallClockProfilerEnabled(configProvider) - && getBoolean( - configProvider, - PROFILING_DATADOG_PROFILER_WALL_JVMTI, - PROFILING_DATADOG_PROFILER_WALL_JVMTI_DEFAULT); + return getBoolean( + configProvider, + PROFILING_DATADOG_PROFILER_WALL_JVMTI, + PROFILING_DATADOG_PROFILER_WALL_JVMTI_DEFAULT); } private static String normalizeKey(String key) { diff --git a/dd-java-agent/appsec/build.gradle b/dd-java-agent/appsec/build.gradle index 2a5758c3a3a..3c6c62b37ef 100644 --- a/dd-java-agent/appsec/build.gradle +++ b/dd-java-agent/appsec/build.gradle @@ -15,7 +15,7 @@ dependencies { implementation project(':internal-api') implementation project(':communication') implementation project(':telemetry') - implementation group: 'io.sqreen', name: 'libsqreen', version: '11.0.0' + implementation group: 'io.sqreen', name: 'libsqreen', version: '11.0.1' implementation libs.moshi testImplementation libs.bytebuddy @@ -62,29 +62,6 @@ jmhJar { exclude 'org/slf4j/impl/**/*' } - -task runSampleApp(type: GradleBuild, dependsOn: ':dd-java-agent:shadowJar') { - description = "Run AppSec sample app with instrumentation" - group = 'application' - - def agentShadowJar = project(':dd-java-agent').tasks['shadowJar'] - - def jvmArgs = '-Ddd.appsec.enabled=true' - if (project.hasProperty('jvmArgs')) { - jvmArgs += " ${project.'jvmArgs'}" - } - - doFirst { - startParameter = startParameter.newInstance() - startParameter.projectProperties = [ - testExecutable: System.getenv('TEST_EXECUTABLE') ?: '', - jvmArgs : "-javaagent:${agentShadowJar.archiveFile.get().asFile} $jvmArgs", - debugJvm : project.hasProperty('debugJvm') ? project.debugJvm : 'false', - ] - } - tasks = ['weblog:weblog-spring-app:bootRun'] -} - ext { minimumBranchCoverage = 0.6 minimumInstructionCoverage = 0.8 diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java index dc4e3c28f70..c48a9576ddb 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/gateway/AppSecRequestContext.java @@ -11,6 +11,7 @@ import datadog.trace.api.Config; import datadog.trace.api.http.StoredBodySupplier; import datadog.trace.api.internal.TraceSegment; +import datadog.trace.api.telemetry.LogCollector; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import io.sqreen.powerwaf.Additive; @@ -118,7 +119,7 @@ public class AppSecRequestContext implements DataBundle, Closeable { // set after additive is set private volatile PowerwafMetrics wafMetrics; private volatile PowerwafMetrics raspMetrics; - private AtomicInteger raspMetricsCounter; + private final AtomicInteger raspMetricsCounter = new AtomicInteger(0); private volatile boolean blocked; private volatile int timeouts; @@ -182,7 +183,6 @@ public Additive getOrCreateAdditive(PowerwafContext ctx, boolean createMetrics, } if (isRasp && raspMetrics == null) { this.raspMetrics = ctx.createMetrics(); - this.raspMetricsCounter = new AtomicInteger(0); } } @@ -433,7 +433,9 @@ public void close() { public void close(boolean requiresPostProcessing) { if (additive != null || derivatives != null) { - log.warn("WAF object had not been closed (probably missed request-end event)"); + log.debug( + LogCollector.SEND_TELEMETRY, + "WAF object had not been closed (probably missed request-end event)"); closeAdditive(); derivatives = null; } diff --git a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFStatsReporter.java b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFStatsReporter.java index 073aea73d03..e2fdb7de5d5 100644 --- a/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFStatsReporter.java +++ b/dd-java-agent/appsec/src/main/java/com/datadog/appsec/powerwaf/PowerWAFStatsReporter.java @@ -35,7 +35,10 @@ public void processTraceSegment( segment.setTagTop(RASP_TOTAL_DURATION_US_TAG, raspMetrics.getTotalRunTimeNs() / 1000L); segment.setTagTop( RASP_TOTAL_DDWAF_RUN_DURATION_US_TAG, raspMetrics.getTotalDdwafRunTimeNs() / 1000L); - segment.setTagTop(RASP_RULE_EVAL, ctx.getRaspMetricsCounter().get()); + final int raspCount = ctx.getRaspMetricsCounter().get(); + if (raspCount > 0) { + segment.setTagTop(RASP_RULE_EVAL, raspCount); + } } String rulesVersion = this.rulesVersion; diff --git a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy index 39b6dbe0219..22f566b1b14 100644 --- a/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy +++ b/dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/powerwaf/PowerWAFModuleSpecification.groovy @@ -1490,6 +1490,7 @@ class PowerWAFModuleSpecification extends DDSpecification { void 'http endpoint fingerprint support'() { given: final flow = Mock(ChangeableFlow) + final fingerprint = '_dd.appsec.fp.http.endpoint' setupWithStubConfigService 'fingerprint_config.json' dataListener = pwafModule.dataSubscriptions.first() ctx.closeAdditive() @@ -1508,12 +1509,15 @@ class PowerWAFModuleSpecification extends DDSpecification { then: 1 * flow.setAction({ it.blocking }) - ctx.derivativeKeys.contains('_dd.appsec.fp.http.endpoint') + 1 * ctx.reportDerivatives({ Map map -> + map.containsKey(fingerprint) && map.get(fingerprint).matches('http-get-.*') + }) } void 'http session fingerprint support'() { given: final flow = Mock(ChangeableFlow) + final fingerprint = '_dd.appsec.fp.session' final sessionId = UUID.randomUUID().toString() setupWithStubConfigService 'fingerprint_config.json' dataListener = pwafModule.dataSubscriptions.first() @@ -1530,7 +1534,9 @@ class PowerWAFModuleSpecification extends DDSpecification { ctx.closeAdditive() then: - ctx.derivativeKeys.contains('_dd.appsec.fp.session') + 1 * ctx.reportDerivatives({ Map map -> + map.containsKey(fingerprint) && map.get(fingerprint).matches('ssn-.*') + }) } private Map getDefaultConfig() { diff --git a/dd-java-agent/appsec/weblog/README.md b/dd-java-agent/appsec/weblog/README.md deleted file mode 100644 index 3e6fdfb5d4a..00000000000 --- a/dd-java-agent/appsec/weblog/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# Build - -```shell -# build webapp -gradle build -``` - -# Database support - -- MongoDB (in-memory, embedded) -- hsqldb, *default* (in-memory) -- mysql `-Dspring.profiles.active=mysql` -- postgresql `-Dspring.profiles.active=postgresql` -- oracle `-Dspring.profiles.active=oracle` - -## Local Mysql server - -```shell -docker run -d \ --e MYSQL_DATABASE=sqreen-app \ --e MYSQL_USER=mysql \ --e MYSQL_PASSWORD=mysql \ --p 3306:3306 \ -mysql/mysql-server:5.7 -``` - -## Local PostgreSQL server - -```shell -docker run -d \ - -e POSTGRES_PASSWORD=postgres \ - -e POSTGRES_USER=postgres \ - -e POSTGRES_DB=sqreen-app \ - -p 5432:5432 \ - postgres:latest -``` - -## Local Oracle server - -TODO: this image is not available anymore, we need to find an alternative - -```shell -docker run -d \ - --shm-size=1g \ - -p 1521:1521 \ - -p 8081:8080 \ - -e ORACLE_PWD=password \ - -v /tmp/oracle:/u01/app/oracle/oradata \ - oracle/database:11.2.0.2-xe -``` - - diff --git a/dd-java-agent/appsec/weblog/weblog-common/build.gradle b/dd-java-agent/appsec/weblog/weblog-common/build.gradle deleted file mode 100644 index 0126a948c8c..00000000000 --- a/dd-java-agent/appsec/weblog/weblog-common/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -apply from: "$rootDir/gradle/java.gradle" - -sourceCompatibility = JavaVersion.VERSION_1_6 -targetCompatibility = JavaVersion.VERSION_1_6 - -dependencies { - api 'javax.servlet:servlet-api:2.4' - implementation group: 'com.google.guava', name: 'guava', version: '20.0' -} diff --git a/dd-java-agent/appsec/weblog/weblog-common/gradle.lockfile b/dd-java-agent/appsec/weblog/weblog-common/gradle.lockfile deleted file mode 100644 index a140a0026f7..00000000000 --- a/dd-java-agent/appsec/weblog/weblog-common/gradle.lockfile +++ /dev/null @@ -1,105 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -ch.qos.logback:logback-classic:1.2.3=testCompileClasspath,testRuntimeClasspath -ch.qos.logback:logback-core:1.2.3=testCompileClasspath,testRuntimeClasspath -com.beust:jcommander:1.78=testRuntimeClasspath -com.github.javaparser:javaparser-core:3.25.1=testCompileClasspath,testRuntimeClasspath -com.github.spotbugs:spotbugs-annotations:4.2.0=compileClasspath,testCompileClasspath,testRuntimeClasspath -com.github.spotbugs:spotbugs-annotations:4.7.3=spotbugs -com.github.spotbugs:spotbugs:4.7.3=spotbugs -com.google.code.findbugs:jsr305:3.0.2=compileClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath -com.google.code.gson:gson:2.9.1=spotbugs -com.google.guava:guava:20.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.thoughtworks.qdox:qdox:1.12.1=testRuntimeClasspath -commons-codec:commons-codec:1.15=spotbugs -de.thetaphi:forbiddenapis:3.1=compileClasspath -info.picocli:picocli:4.6.3=testRuntimeClasspath -javax.servlet:servlet-api:2.4=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -jaxen:jaxen:1.2.0=spotbugs -jline:jline:2.14.6=testRuntimeClasspath -junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath -net.jcip:jcip-annotations:1.0=compileClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath -net.sf.saxon:Saxon-HE:11.4=spotbugs -org.apache.ant:ant-antlr:1.10.12=testRuntimeClasspath -org.apache.ant:ant-antlr:1.9.15=codenarc -org.apache.ant:ant-junit:1.10.12=testRuntimeClasspath -org.apache.ant:ant-junit:1.9.15=codenarc -org.apache.ant:ant-launcher:1.10.12=testRuntimeClasspath -org.apache.ant:ant:1.10.12=testCompileClasspath,testRuntimeClasspath -org.apache.bcel:bcel:6.5.0=spotbugs -org.apache.commons:commons-lang3:3.12.0=pitest,spotbugs -org.apache.commons:commons-text:1.10.0=pitest,spotbugs -org.apache.httpcomponents.client5:httpclient5:5.1.3=spotbugs -org.apache.httpcomponents.core5:httpcore5-h2:5.1.3=spotbugs -org.apache.httpcomponents.core5:httpcore5:5.1.3=spotbugs -org.apache.logging.log4j:log4j-api:2.19.0=spotbugs -org.apache.logging.log4j:log4j-core:2.19.0=spotbugs -org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath -org.codehaus.groovy:groovy-all:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-ant:2.5.14=codenarc -org.codehaus.groovy:groovy-ant:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-astbuilder:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-cli-picocli:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-console:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-datetime:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-docgenerator:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-groovydoc:2.5.14=codenarc -org.codehaus.groovy:groovy-groovydoc:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-groovysh:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-jmx:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-json:2.5.14=codenarc -org.codehaus.groovy:groovy-json:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-jsr223:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-macro:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-nio:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-servlet:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-sql:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-swing:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-templates:2.5.14=codenarc -org.codehaus.groovy:groovy-templates:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-test-junit5:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-test:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-testng:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-xml:2.5.14=codenarc -org.codehaus.groovy:groovy-xml:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy:2.5.14=codenarc -org.codehaus.groovy:groovy:3.0.17=testCompileClasspath,testRuntimeClasspath -org.codenarc:CodeNarc:2.2.0=codenarc -org.dom4j:dom4j:2.1.3=spotbugs -org.gmetrics:GMetrics:1.1=codenarc -org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath -org.hamcrest:hamcrest:2.2=testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-api:5.9.2=testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-engine:5.9.2=testRuntimeClasspath -org.junit.jupiter:junit-jupiter-params:5.9.2=testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter:5.9.2=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-commons:1.9.2=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-engine:1.9.2=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-launcher:1.9.2=testRuntimeClasspath -org.junit:junit-bom:5.9.1=spotbugs -org.junit:junit-bom:5.9.2=testCompileClasspath,testRuntimeClasspath -org.objenesis:objenesis:3.3=testCompileClasspath,testRuntimeClasspath -org.opentest4j:opentest4j:1.2.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-analysis:9.4=spotbugs -org.ow2.asm:asm-commons:9.4=spotbugs -org.ow2.asm:asm-tree:9.4=spotbugs -org.ow2.asm:asm-util:9.4=spotbugs -org.ow2.asm:asm:9.4=spotbugs -org.pitest:pitest-command-line:1.9.11=pitest -org.pitest:pitest-entry:1.9.11=pitest -org.pitest:pitest:1.9.11=pitest -org.slf4j:jcl-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath -org.slf4j:jul-to-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath -org.slf4j:log4j-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-api:1.7.30=testCompileClasspath -org.slf4j:slf4j-api:1.7.32=testRuntimeClasspath -org.slf4j:slf4j-api:2.0.0=spotbugs,spotbugsSlf4j -org.slf4j:slf4j-simple:2.0.0=spotbugsSlf4j -org.spockframework:spock-core:2.2-groovy-3.0=testCompileClasspath,testRuntimeClasspath -org.spockframework:spock-junit4:2.2-groovy-3.0=testCompileClasspath,testRuntimeClasspath -org.testng:testng:7.5=testRuntimeClasspath -org.webjars:jquery:3.5.1=testRuntimeClasspath -org.xmlresolver:xmlresolver:4.4.3=spotbugs -xml-apis:xml-apis:1.4.01=spotbugs -empty=annotationProcessor,spotbugsPlugins,testAnnotationProcessor diff --git a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/VulnerableExecutions.java b/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/VulnerableExecutions.java deleted file mode 100644 index 76a23e58b87..00000000000 --- a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/VulnerableExecutions.java +++ /dev/null @@ -1,107 +0,0 @@ -package io.sqreen.testapp.imitation; - -import com.google.common.collect.ImmutableMap; -import de.thetaphi.forbiddenapis.SuppressForbidden; -import io.sqreen.testapp.imitation.util.exec.ExecUtil; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; -import javax.script.*; - -/** A various process execution and code evaluation vulnerability imitations. */ -public final class VulnerableExecutions { - - /** - * Executes ping command with the arbitrary additional arguments. - * - * @param otherArgs argument to append - * @return an {@link InputStream} containing the command output - */ - public static InputStream ping(String otherArgs) { - return ExecUtil.exec(new String[] {"sh", "-c", "ping -c 1 -w 3 " + otherArgs}); - } - - /** - * Executes ping command with the specified ip address. - * - * @param ipAddress an ip address to use - * @return an {@link InputStream} containing the command output - */ - public static InputStream pingNoShell(String ipAddress) { - return ExecUtil.exec(new String[] {"ping", "-c", "1", "-w", "3", ipAddress}); - } - - private static ScriptEngine groovyEngine = new ScriptEngineManager().getEngineByName("groovy"); - - public static void setGroovyEngine(ScriptEngine groovyEngine) { - VulnerableExecutions.groovyEngine = groovyEngine; - } - - /** - * Performs arbitrary groovy code evaluation. - * - * @param valueToEval a code to evaluate - * @param strategy a strategy to choose - * @param controller reference to the controller/servlet instance - * @return the evaluation result in string format - * @throws ScriptException - */ - public static String eval(final String valueToEval, String strategy, Object controller) - throws ScriptException { - Bindings bindings = new SimpleBindings(); - bindings.put("controller", controller); - - Object res = null; - if ("eval_reader".equals(strategy)) { - Reader reader = new StringReader(valueToEval); - res = groovyEngine.eval(reader, bindings); - } else if ("compile".equals(strategy)) { - // we're doing it with groovy, but could have used another language like js - CompiledScript script = ((Compilable) groovyEngine).compile(valueToEval); - - res = script.eval(bindings); - } else if ("error".equals(strategy)) { - groovyEngine.eval(new ErrorReader()); - } else { // eval_string - res = groovyEngine.eval(valueToEval, bindings); - } - - return (res != null) ? res.toString() : "(null)"; - } - - /** - * Executes the arbitrary system shell command. - * - * @param referer an additional argument to pass - * @return an {@link InputStream} containing the file contents - */ - public static InputStream shellShock(final String referer) { - String[] command = {"bash", "-c", "echo Value of \\$REFERRER: \"$REFERER\""}; - return (referer == null || referer.length() == 0) - ? ExecUtil.exec(command) - : ExecUtil.exec(command, ImmutableMap.of("REFERER", referer)); - } - - private static final class ErrorReader extends Reader { - - @Override - public int read(char[] cbuf, int off, int len) throws IOException { - throw new IOException("not implemented"); - } - - @Override - public void close() throws IOException { - throw new IOException("not implemented"); - } - } - - @SuppressForbidden - public static InputStream exec(String command) { - return ExecUtil.exec(command.split(" ")); - } - - private VulnerableExecutions() { - /**/ - } -} diff --git a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/VulnerableFiles.java b/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/VulnerableFiles.java deleted file mode 100644 index afe02aaaa3e..00000000000 --- a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/VulnerableFiles.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.sqreen.testapp.imitation; - -import com.google.common.io.Closer; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -/** A files-related vulnerability imitations. */ -public final class VulnerableFiles { - - private static final File filesLocation = new File("/tmp"); - - /** - * Stores the input in a temporary file, returning the hash of its contents. The name of the file - * is randomly generated and cannot be obtained by the caller. - * - * @param is the input stream connected to the uploaded file - * @return md5 hash of the file - * @throws IOException - * @throws NoSuchAlgorithmException - */ - public static String store(InputStream is) throws IOException, NoSuchAlgorithmException { - - Closer closer = Closer.create(); - closer.register(is); - - MessageDigest digest = MessageDigest.getInstance("MD5"); - File storedFile = File.createTempFile("sample-app", ".tmp", filesLocation); - FileOutputStream os = closer.register(new FileOutputStream(storedFile)); - - try { - - byte[] byteBuffer = new byte[8 * 1024]; - int byteRead; - - while ((byteRead = is.read(byteBuffer)) != -1) { - // update the digest - digest.update(byteBuffer, 0, byteRead); - // append buffer - os.write(byteBuffer, 0, byteRead); - } - - return String.format("%032x", new BigInteger(1, digest.digest())); - - } finally { - closer.close(); - } - } - - /** - * Returns a {@link File} associated with specified file path. - * - * @param filename a file to read - * @return an {@link File} referring to the specified string - * @throws IOException - */ - public static File getFile(String filename) { - return new File(filename); - } - - private VulnerableFiles() { - /**/ - } -} diff --git a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/VulnerableQuery.java b/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/VulnerableQuery.java deleted file mode 100644 index 0341885946c..00000000000 --- a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/VulnerableQuery.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.sqreen.testapp.imitation; - -/** A various SQL/Mongo-query vulnerability imitations. */ -public final class VulnerableQuery { - - /** - * Builds the SQL/Mongo-query without any parameter preparation or value escaping. Just plain - * concatenation. - * - * @param sqlQuery a main query - * @param criteria criteria to append - * @return built query as a string - */ - public static String build(String sqlQuery, String... criteria) { - return sqlQuery + concatenate(criteria); - } - - private static String concatenate(String[] criteria) { - StringBuilder sb = new StringBuilder(); - for (String criterion : criteria) { - sb.append(criterion); - } - return sb.toString(); - } - - private VulnerableQuery() { - /**/ - } -} diff --git a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/util/exec/ExecUtil.java b/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/util/exec/ExecUtil.java deleted file mode 100644 index b423272b8b1..00000000000 --- a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/util/exec/ExecUtil.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.sqreen.testapp.imitation.util.exec; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** Execution convenience class. */ -public final class ExecUtil { - - /** - * Executes the specified command. - * - * @param params an array containing the command and it's arguments. - * @return the {@link InputStream} containing the command output (it's STDOUT) - */ - public static InputStream exec(String[] params) { - return exec(params, new HashMap()); - } - - /** - * Executes the specified command. - * - * @param params an array containing the command and it's arguments. - * @param env a {@link Map} with environment variables to pass to the command being executed - * @return the {@link InputStream} containing the command output (it's STDOUT) - */ - public static InputStream exec(String[] params, Map env) { - try { - List envp = new LinkedList(); - for (Map.Entry entry : env.entrySet()) { - envp.add(entry.getKey() + "=" + entry.getValue()); - } - - Process p = Runtime.getRuntime().exec(params, envp.toArray(new String[] {})); - - StreamConsumer stderrConsumer = new StreamConsumer(p.getErrorStream(), "STDERR consumer"); - stderrConsumer.start(); - - return p.getInputStream(); - - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private ExecUtil() { - /**/ - } -} diff --git a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/util/exec/StreamConsumer.java b/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/util/exec/StreamConsumer.java deleted file mode 100644 index 3fa162b707d..00000000000 --- a/dd-java-agent/appsec/weblog/weblog-common/src/main/java/io/sqreen/testapp/imitation/util/exec/StreamConsumer.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.sqreen.testapp.imitation.util.exec; - -import com.google.common.io.ByteStreams; -import com.google.common.io.Closeables; -import java.io.IOException; -import java.io.InputStream; - -/** - * A stream consumer which is run in parallel to the command (process) being executed. It is - * necessary to avoid the situation when the process hang up when it's STDERR/STDOUT is buffered by - * OS. - */ -public class StreamConsumer extends Thread { - - private InputStream is; - - public StreamConsumer(InputStream is, String name) { - this.is = is; - setDaemon(true); - setName(name); - } - - public void run() { - try { - // Just copy the stream to NULL - ByteStreams.copy(is, ByteStreams.nullOutputStream()); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } finally { - Closeables.closeQuietly(is); - } - } -} diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1-core-test/src/test/groovy/MongoCore31ClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.1-core-test/src/test/groovy/MongoCore31ClientTest.groovy index 58f291607d6..df22d41839f 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.1-core-test/src/test/groovy/MongoCore31ClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.1-core-test/src/test/groovy/MongoCore31ClientTest.groovy @@ -47,8 +47,9 @@ abstract class MongoCore31ClientTest extends MongoBaseTest { client = null } - def "test create collection"() { + def "test create collection with renameService=#renameService"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) injectSysConfig(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "$renameService") @@ -63,12 +64,12 @@ abstract class MongoCore31ClientTest extends MongoBaseTest { } where: - collectionName = randomCollectionName() renameService << [false, true] } def "test create collection no description"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = new MongoClient("localhost", port).getDatabase(databaseName) when: @@ -80,13 +81,11 @@ abstract class MongoCore31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "create","{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, databaseName) } } - - where: - collectionName = randomCollectionName() } def "test get collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -99,13 +98,11 @@ abstract class MongoCore31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test insert"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -129,13 +126,11 @@ abstract class MongoCore31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test update"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -164,13 +159,11 @@ abstract class MongoCore31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test delete"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -197,13 +190,11 @@ abstract class MongoCore31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test error"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -221,13 +212,11 @@ abstract class MongoCore31ClientTest extends MongoBaseTest { thrown(IllegalArgumentException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } def "test client failure"() { setup: + String collectionName = randomCollectionName() def options = MongoClientOptions.builder().serverSelectionTimeout(10).build() def client = new MongoClient(new ServerAddress("localhost", UNUSABLE_PORT), [], options) @@ -239,9 +228,6 @@ abstract class MongoCore31ClientTest extends MongoBaseTest { thrown(MongoTimeoutException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } } diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoJava31ClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoJava31ClientTest.groovy index 92c1671f6ad..4a798cdc1c5 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoJava31ClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoJava31ClientTest.groovy @@ -49,8 +49,9 @@ abstract class MongoJava31ClientTest extends MongoBaseTest { client = null } - def "test create collection"() { + def "test create collection with renameService=#renameService"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) injectSysConfig(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "$renameService") @@ -65,12 +66,12 @@ abstract class MongoJava31ClientTest extends MongoBaseTest { } where: - collectionName = randomCollectionName() renameService << [false, true] } def "test create collection no description"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = new MongoClient("localhost", port).getDatabase(databaseName) when: @@ -82,13 +83,11 @@ abstract class MongoJava31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, databaseName) } } - - where: - collectionName = randomCollectionName() } def "test get collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -101,13 +100,11 @@ abstract class MongoJava31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test insert"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -131,13 +128,11 @@ abstract class MongoJava31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test update"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -166,13 +161,11 @@ abstract class MongoJava31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test delete"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -199,13 +192,11 @@ abstract class MongoJava31ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test error"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -223,13 +214,11 @@ abstract class MongoJava31ClientTest extends MongoBaseTest { thrown(IllegalArgumentException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } def "test client failure"() { setup: + String collectionName = randomCollectionName() def options = MongoClientOptions.builder().serverSelectionTimeout(10).build() def client = new MongoClient(new ServerAddress("localhost", UNUSABLE_PORT), [], options) @@ -241,9 +230,6 @@ abstract class MongoJava31ClientTest extends MongoBaseTest { thrown(MongoTimeoutException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } } diff --git a/dd-java-agent/instrumentation/mongo/driver-3.10-sync-test/src/test/groovy/MongoSyncClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.10-sync-test/src/test/groovy/MongoSyncClientTest.groovy index cf1d745bb1b..74bb354af88 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.10-sync-test/src/test/groovy/MongoSyncClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.10-sync-test/src/test/groovy/MongoSyncClientTest.groovy @@ -28,8 +28,9 @@ abstract class MongoSyncClientTest extends MongoBaseTest { client = null } - def "test create collection"() { + def "test create collection with renameService=#renameService"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) injectSysConfig(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "$renameService") @@ -45,12 +46,12 @@ abstract class MongoSyncClientTest extends MongoBaseTest { } where: - collectionName = randomCollectionName() renameService << [false, true] } def "test create collection no description"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(databaseName) when: @@ -62,13 +63,11 @@ abstract class MongoSyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, databaseName) } } - - where: - collectionName = randomCollectionName() } def "test get collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -81,13 +80,11 @@ abstract class MongoSyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test insert"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -113,13 +110,11 @@ abstract class MongoSyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test update"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -150,13 +145,11 @@ abstract class MongoSyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test delete"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -185,13 +178,11 @@ abstract class MongoSyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test error"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -209,13 +200,11 @@ abstract class MongoSyncClientTest extends MongoBaseTest { thrown(IllegalArgumentException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } def "test client failure"() { setup: + String collectionName = randomCollectionName() def client = MongoClients.create("mongodb://localhost:$UNUSABLE_PORT/?serverselectiontimeoutms=10") when: @@ -226,9 +215,6 @@ abstract class MongoSyncClientTest extends MongoBaseTest { thrown(MongoTimeoutException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } } diff --git a/dd-java-agent/instrumentation/mongo/driver-3.3-async-test/src/test/groovy/MongoAsyncClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.3-async-test/src/test/groovy/MongoAsyncClientTest.groovy index 7fc37ff3db4..032365e83b7 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.3-async-test/src/test/groovy/MongoAsyncClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.3-async-test/src/test/groovy/MongoAsyncClientTest.groovy @@ -39,6 +39,7 @@ abstract class MongoAsyncClientTest extends MongoBaseTest { def "test create collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -50,13 +51,11 @@ abstract class MongoAsyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}") } } - - where: - collectionName = randomCollectionName() } def "test create collection no description"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(databaseName) when: @@ -68,13 +67,11 @@ abstract class MongoAsyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, databaseName) } } - - where: - collectionName = randomCollectionName() } def "test get collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -88,13 +85,11 @@ abstract class MongoAsyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test insert"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -124,13 +119,11 @@ abstract class MongoAsyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test update"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -168,13 +161,11 @@ abstract class MongoAsyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test delete"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -210,9 +201,6 @@ abstract class MongoAsyncClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } SingleResultCallback toCallback(Closure closure) { diff --git a/dd-java-agent/instrumentation/mongo/driver-3.4/src/test/groovy/MongoJava34ClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.4/src/test/groovy/MongoJava34ClientTest.groovy index bcbf4568e03..424748dabb0 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.4/src/test/groovy/MongoJava34ClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.4/src/test/groovy/MongoJava34ClientTest.groovy @@ -47,8 +47,9 @@ abstract class MongoJava34ClientTest extends MongoBaseTest { client = null } - def "test create collection"() { + def "test create collection with renameService=#renameService"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) injectSysConfig(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "$renameService") @@ -63,12 +64,12 @@ abstract class MongoJava34ClientTest extends MongoBaseTest { } where: - collectionName = randomCollectionName() renameService << [false, true] } def "test create collection no description"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = new MongoClient("localhost", port).getDatabase(databaseName) when: @@ -80,13 +81,11 @@ abstract class MongoJava34ClientTest extends MongoBaseTest { mongoSpan(it, 0, "create","{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, databaseName) } } - - where: - collectionName = randomCollectionName() } def "test get collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -99,13 +98,11 @@ abstract class MongoJava34ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test insert"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -129,13 +126,11 @@ abstract class MongoJava34ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test update"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -164,13 +159,11 @@ abstract class MongoJava34ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test delete"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -197,13 +190,11 @@ abstract class MongoJava34ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}") } } - - where: - collectionName = randomCollectionName() } def "test error"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -221,13 +212,11 @@ abstract class MongoJava34ClientTest extends MongoBaseTest { thrown(IllegalArgumentException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } def "test client failure"() { setup: + String collectionName = randomCollectionName() def options = MongoClientOptions.builder().serverSelectionTimeout(10).build() def client = new MongoClient(new ServerAddress("localhost", UNUSABLE_PORT), [], options) @@ -239,9 +228,6 @@ abstract class MongoJava34ClientTest extends MongoBaseTest { thrown(MongoTimeoutException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } } diff --git a/dd-java-agent/instrumentation/mongo/driver-3.7-core-test/src/test/groovy/MongoCore37ClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.7-core-test/src/test/groovy/MongoCore37ClientTest.groovy index e0f62a296a5..6d424fca2a4 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.7-core-test/src/test/groovy/MongoCore37ClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.7-core-test/src/test/groovy/MongoCore37ClientTest.groovy @@ -28,8 +28,9 @@ abstract class MongoCore37ClientTest extends MongoBaseTest { client = null } - def "test create collection"() { + def "test create collection with renameService=#renameService"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) injectSysConfig(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "$renameService") @@ -45,12 +46,12 @@ abstract class MongoCore37ClientTest extends MongoBaseTest { } where: - collectionName = randomCollectionName() renameService << [false, true] } def "test create collection no description"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(databaseName) when: @@ -62,13 +63,11 @@ abstract class MongoCore37ClientTest extends MongoBaseTest { mongoSpan(it, 0, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, databaseName) } } - - where: - collectionName = randomCollectionName() } def "test get collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -81,13 +80,11 @@ abstract class MongoCore37ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}", false, "some-instance") } } - - where: - collectionName = randomCollectionName() } def "test insert"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -113,13 +110,11 @@ abstract class MongoCore37ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}", false, "some-instance") } } - - where: - collectionName = randomCollectionName() } def "test update"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -150,13 +145,11 @@ abstract class MongoCore37ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}", false, "some-instance") } } - - where: - collectionName = randomCollectionName() } def "test delete"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -185,13 +178,11 @@ abstract class MongoCore37ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\",\"query\":{}}", false, "some-instance") } } - - where: - collectionName = randomCollectionName() } def "test error"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -209,13 +200,11 @@ abstract class MongoCore37ClientTest extends MongoBaseTest { thrown(IllegalArgumentException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } def "test client failure"() { setup: + String collectionName = randomCollectionName() def client = MongoClients.create("mongodb://localhost:$UNUSABLE_PORT/?serverselectiontimeoutms=10") when: @@ -226,9 +215,6 @@ abstract class MongoCore37ClientTest extends MongoBaseTest { thrown(MongoTimeoutException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } } diff --git a/dd-java-agent/instrumentation/mongo/driver-4.0/src/test/groovy/Mongo4ClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-4.0/src/test/groovy/Mongo4ClientTest.groovy index f7b5cfeb585..fa4de78c9a9 100644 --- a/dd-java-agent/instrumentation/mongo/driver-4.0/src/test/groovy/Mongo4ClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-4.0/src/test/groovy/Mongo4ClientTest.groovy @@ -40,8 +40,9 @@ abstract class Mongo4ClientTest extends MongoBaseTest { return '' }.call() - def "test create collection"() { + def "test create collection with renameService=#renameService"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) injectSysConfig(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "$renameService") @@ -57,12 +58,12 @@ abstract class Mongo4ClientTest extends MongoBaseTest { } where: - collectionName = randomCollectionName() renameService << [false, true] } def "test create collection no description"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(databaseName) when: @@ -74,13 +75,11 @@ abstract class Mongo4ClientTest extends MongoBaseTest { mongoSpan(it, 0, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, databaseName) } } - - where: - collectionName = randomCollectionName() } def "test get collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -93,13 +92,11 @@ abstract class Mongo4ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\"$query}") } } - - where: - collectionName = randomCollectionName() } def "test insert"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -125,13 +122,11 @@ abstract class Mongo4ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\"$query}") } } - - where: - collectionName = randomCollectionName() } def "test update"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -162,13 +157,11 @@ abstract class Mongo4ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\"$query}") } } - - where: - collectionName = randomCollectionName() } def "test delete"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -197,13 +190,11 @@ abstract class Mongo4ClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\"$query}") } } - - where: - collectionName = randomCollectionName() } def "test error"() { setup: + String collectionName = randomCollectionName() DDSpan setupSpan = null MongoCollection collection = runUnderTrace("setup") { setupSpan = activeSpan() as DDSpan @@ -221,13 +212,11 @@ abstract class Mongo4ClientTest extends MongoBaseTest { thrown(IllegalArgumentException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } def "test client failure"() { setup: + String collectionName = randomCollectionName() def client = MongoClients.create("mongodb://localhost:$UNUSABLE_PORT/?serverselectiontimeoutms=10") when: @@ -238,9 +227,6 @@ abstract class Mongo4ClientTest extends MongoBaseTest { thrown(MongoTimeoutException) // Unfortunately not caught by our instrumentation. assertTraces(0) {} - - where: - collectionName = randomCollectionName() } } diff --git a/dd-java-agent/instrumentation/mongo/driver-4.0/src/test/groovy/MongoReactiveClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-4.0/src/test/groovy/MongoReactiveClientTest.groovy index 9b1507af454..d47f350c664 100644 --- a/dd-java-agent/instrumentation/mongo/driver-4.0/src/test/groovy/MongoReactiveClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-4.0/src/test/groovy/MongoReactiveClientTest.groovy @@ -80,6 +80,7 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { def "test create collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -91,13 +92,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 0, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}") } } - - where: - collectionName = randomCollectionName() } def "test create collection with parent"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -113,13 +112,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 1, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, "some-description", span(0)) } } - - where: - collectionName = randomCollectionName() } def "test create collection no description"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(databaseName) when: @@ -131,13 +128,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 0, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, databaseName) } } - - where: - collectionName = randomCollectionName() } def "test create collection no description with parent"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(databaseName) when: @@ -153,13 +148,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 1, "create", "{\"create\":\"$collectionName\",\"capped\":\"?\"}", false, databaseName, span(0)) } } - - where: - collectionName = randomCollectionName() } def "test get collection"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -173,13 +166,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\"$query}") } } - - where: - collectionName = randomCollectionName() } def "test get collection with parent"() { setup: + String collectionName = randomCollectionName() MongoDatabase db = client.getDatabase(databaseName) when: @@ -197,13 +188,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 1, "count", "{\"count\":\"$collectionName\"$query}", false, "some-description", span(0)) } } - - where: - collectionName = randomCollectionName() } def "test insert"() { setup: + String collectionName = randomCollectionName() def collection = setupCollection(collectionName) when: @@ -222,13 +211,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\"$query}") } } - - where: - collectionName = randomCollectionName() } def "test insert with parent"() { setup: + String collectionName = randomCollectionName() def collection = setupCollection(collectionName) when: @@ -249,13 +236,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 2, "count", "{\"count\":\"$collectionName\"$query}", false, "some-description", span(0)) } } - - where: - collectionName = randomCollectionName() } def "test update"() { setup: + String collectionName = randomCollectionName() MongoCollection collection = setupCollection(collectionName) insertDocument(collection, new Document("password", "OLDPW"), null) @@ -280,13 +265,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\"$query}") } } - - where: - collectionName = randomCollectionName() } def "test update with parent"() { setup: + String collectionName = randomCollectionName() MongoCollection collection = setupCollection(collectionName) insertDocument(collection, new Document("password", "OLDPW"), null) @@ -313,13 +296,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 2, "count", "{\"count\":\"$collectionName\"$query}", false, "some-description", span(0)) } } - - where: - collectionName = randomCollectionName() } def "test delete"() { setup: + String collectionName = randomCollectionName() MongoCollection collection = setupCollection(collectionName) insertDocument(collection, new Document("password", "SECRET"), null) @@ -342,13 +323,11 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 0, "count", "{\"count\":\"$collectionName\"$query}") } } - - where: - collectionName = randomCollectionName() } def "test delete with parent"() { setup: + String collectionName = randomCollectionName() MongoCollection collection = setupCollection(collectionName) insertDocument(collection, new Document("password", "SECRET"), null) @@ -373,9 +352,6 @@ abstract class MongoReactiveClientTest extends MongoBaseTest { mongoSpan(it, 2, "count", "{\"count\":\"$collectionName\"$query}", false, "some-description", span(0)) } } - - where: - collectionName = randomCollectionName() } def Subscriber toSubscriber(Closure closure) { diff --git a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ExceptionDebuggerIntegrationTest.java b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ExceptionDebuggerIntegrationTest.java index 6c1df76cfb8..3a0fe5b923c 100644 --- a/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ExceptionDebuggerIntegrationTest.java +++ b/dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ExceptionDebuggerIntegrationTest.java @@ -34,6 +34,8 @@ protected ProcessBuilder createProcessBuilder(Path logFilePath, String... params commandParams.add("-Ddd.exception.replay.enabled=true"); // enable exception replay commandParams.add("-Ddd.internal.exception.replay.only.local.root=false"); // for all spans commandParams.add("-Ddd.third.party.excludes=datadog.smoketest"); + // disable DI to make sure exception debugger works alone + commandParams.remove("-Ddd.dynamic.instrumentation.enabled=true"); return ProcessBuilderHelper.createProcessBuilder( commandParams, logFilePath, getAppClass(), params); } diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index f6388e42945..a2f83aca90c 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -1676,6 +1676,11 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) telemetryMetricsEnabled = configProvider.getBoolean(GeneralConfig.TELEMETRY_METRICS_ENABLED, true); + isTelemetryLogCollectionEnabled = + instrumenterConfig.isTelemetryEnabled() + && configProvider.getBoolean( + TELEMETRY_LOG_COLLECTION_ENABLED, DEFAULT_TELEMETRY_LOG_COLLECTION_ENABLED); + isTelemetryDependencyServiceEnabled = configProvider.getBoolean( TELEMETRY_DEPENDENCY_COLLECTION_ENABLED, @@ -2007,24 +2012,6 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) debuggerThirdPartyIncludes = tryMakeImmutableSet(configProvider.getList(THIRD_PARTY_INCLUDES)); debuggerThirdPartyExcludes = tryMakeImmutableSet(configProvider.getList(THIRD_PARTY_EXCLUDES)); - // FIXME: For the initial rollout, we default log collection to true for IAST and CI Visibility - // users. - // FIXME: For progressive rollout, we include by default Java < 11 hosts as product independent - // sample users. - // FIXME:This should be removed once we default to true, and then it can also be moved up - // together with the rest of telemetry config. - final boolean telemetryLogCollectionEnabledDefault = - instrumenterConfig.isTelemetryEnabled() - && (instrumenterConfig.getAppSecActivation() == ProductActivation.FULLY_ENABLED - || instrumenterConfig.getIastActivation() == ProductActivation.FULLY_ENABLED - || instrumenterConfig.isCiVisibilityEnabled() - || debuggerEnabled - || !Platform.isJavaVersionAtLeast(11)) - || DEFAULT_TELEMETRY_LOG_COLLECTION_ENABLED; - isTelemetryLogCollectionEnabled = - configProvider.getBoolean( - TELEMETRY_LOG_COLLECTION_ENABLED, telemetryLogCollectionEnabledDefault); - awsPropagationEnabled = isPropagationEnabled(true, "aws", "aws-sdk"); sqsPropagationEnabled = isPropagationEnabled(true, "sqs"); diff --git a/settings.gradle b/settings.gradle index 66f943b22ad..d332a3df27d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -71,7 +71,6 @@ include ':remote-config:remote-config-api' include ':remote-config:remote-config-core' include ':dd-java-agent:appsec' -include ':dd-java-agent:appsec:weblog:weblog-common' // ci-visibility include ':dd-java-agent:agent-ci-visibility' diff --git a/telemetry/src/main/java/datadog/telemetry/log/LogPeriodicAction.java b/telemetry/src/main/java/datadog/telemetry/log/LogPeriodicAction.java index 279ddd5bc3a..094e9aaae59 100644 --- a/telemetry/src/main/java/datadog/telemetry/log/LogPeriodicAction.java +++ b/telemetry/src/main/java/datadog/telemetry/log/LogPeriodicAction.java @@ -15,7 +15,15 @@ public class LogPeriodicAction implements TelemetryRunnable.TelemetryPeriodicAct * as a filter) */ static final String[] PACKAGE_ALLOW_LIST = { - "datadog.", "com.datadog.", "java.", "javax.", "jakarta.", "jdk.", "sun.", "com.sun." + "datadog.", + "com.datadog.", + "java.", + "javax.", + "jakarta.", + "jdk.", + "sun.", + "com.sun.", + "io.sqreen.powerwaf." }; private static final String UNKNOWN = ""; @@ -45,43 +53,79 @@ public void doIteration(TelemetryService service) { private static String renderStackTrace(Throwable t) { StringBuilder result = new StringBuilder(); - String name = t.getClass().getCanonicalName(); - if (name == null || name.isEmpty()) { - result.append(UNKNOWN); - } else { - result.append(name); - } + StackTraceElement[] previousStackTrace = null; - if (isDataDogCode(t)) { - String msg = t.getMessage(); - result.append(": "); - if (msg == null || msg.isEmpty()) { + while (t != null) { + String name = t.getClass().getCanonicalName(); + if (name == null || name.isEmpty()) { result.append(UNKNOWN); } else { - result.append(msg); + result.append(name); } - } - result.append('\n'); - - final StackTraceElement[] stacktrace = t.getStackTrace(); - int pendingRedacted = 0; - if (stacktrace != null) { - for (final StackTraceElement frame : t.getStackTrace()) { - final String className = frame.getClassName(); - if (shouldRedactClass(className)) { - pendingRedacted++; + + if (isDataDogCode(t)) { + String msg = t.getMessage(); + result.append(": "); + if (msg == null || msg.isEmpty()) { + result.append(UNKNOWN); } else { - writePendingRedacted(result, pendingRedacted); - pendingRedacted = 0; - result.append(" at ").append(frame).append('\n'); + result.append(msg); } } + result.append('\n'); + + final StackTraceElement[] stacktrace = t.getStackTrace(); + int pendingRedacted = 0; + if (stacktrace != null) { + int commonFrames = 0; + if (previousStackTrace != null) { + commonFrames = countCommonFrames(previousStackTrace, stacktrace); + } + int maxIndex = stacktrace.length - commonFrames; + + for (int i = 0; i < maxIndex; i++) { + final StackTraceElement frame = stacktrace[i]; + final String className = frame.getClassName(); + if (shouldRedactClass(className)) { + pendingRedacted++; + } else { + writePendingRedacted(result, pendingRedacted); + pendingRedacted = 0; + result.append(" at ").append(frame).append('\n'); + } + } + writePendingRedacted(result, pendingRedacted); + + if (commonFrames > 0) { + result.append(" ... ").append(commonFrames).append(" more\n"); + } + } + + previousStackTrace = stacktrace; + t = t.getCause(); + if (t != null) { + result.append("Caused by: "); + } } - writePendingRedacted(result, pendingRedacted); return result.toString(); } + private static int countCommonFrames( + StackTraceElement[] previousStackTrace, StackTraceElement[] currentStackTrace) { + int previousIndex = previousStackTrace.length - 1; + int currentIndex = currentStackTrace.length - 1; + int count = 0; + while (previousIndex >= 0 + && currentIndex >= 0 + && previousStackTrace[previousIndex].equals(currentStackTrace[currentIndex])) { + count++; + previousIndex--; + currentIndex--; + } + return count; + } + private static boolean isDataDogCode(Throwable t) { StackTraceElement[] stackTrace = t.getStackTrace(); if (stackTrace == null || stackTrace.length == 0) { @@ -91,7 +135,9 @@ private static boolean isDataDogCode(Throwable t) { if (cn.isEmpty()) { return false; } - return cn.startsWith("datadog.") || cn.startsWith("com.datadog."); + return cn.startsWith("datadog.") + || cn.startsWith("com.datadog.") + || cn.startsWith("io.sqreen.powerwaf."); } private static boolean shouldRedactClass(final String className) { diff --git a/telemetry/src/test/groovy/datadog/telemetry/log/LogPeriodicActionTest.groovy b/telemetry/src/test/groovy/datadog/telemetry/log/LogPeriodicActionTest.groovy index 94c10ac4a37..aa29acdd6a3 100644 --- a/telemetry/src/test/groovy/datadog/telemetry/log/LogPeriodicActionTest.groovy +++ b/telemetry/src/test/groovy/datadog/telemetry/log/LogPeriodicActionTest.groovy @@ -138,14 +138,133 @@ class LogPeriodicActionTest extends DDSpecification { " at (redacted: 2 frames)\n" } + void 'stacktrace with multiple frames and common frames'() { + LogMessage logMessage + + given: + final t = throwable("exception", stacktrace( + frame(""), + frame("datadog.MyClass"), + frame("mycorp.MyClass"), + frame("mycorp.MyClass"), + ), throwable("exception 2", stacktrace( + frame("java.MyClass"), + frame("mycorp.MyClass"), + frame("datadog.MyClass"), + frame("mycorp.MyClass"), + frame("mycorp.MyClass"), + ))) + + when: + LogCollector.get().addLogMessage(LogMessageLevel.ERROR.toString(), "test", t) + periodicAction.doIteration(telemetryService) + + then: + 1 * telemetryService.addLogMessage(_) >> { args -> logMessage = args[0] } + 0 * _ + logMessage.getMessage() == 'test' + logMessage.getStackTrace() == "${MutableException.canonicalName}\n" + + " at (redacted)\n" + + " at datadog.MyClass.method(file:42)\n" + + " at (redacted: 2 frames)\n" + + "Caused by: ${MutableException.canonicalName}\n" + + " at java.MyClass.method(file:42)\n" + + " at (redacted)\n" + + " ... 3 more\n" + } + + void 'stacktrace with common frames only'() { + LogMessage logMessage + + given: + final t = throwable("exception", stacktrace( + frame("java.MyClass"), + frame("mycorp.MyClass"), + frame("datadog.MyClass"), + frame("mycorp.MyClass"), + frame("mycorp.MyClass"), + ), throwable("exception 2", stacktrace( + frame("java.MyClass"), + frame("mycorp.MyClass"), + frame("datadog.MyClass"), + frame("mycorp.MyClass"), + frame("mycorp.MyClass"), + ), throwable("exception 3", stacktrace( + frame("java.MyClass"), + frame("mycorp.MyClass"), + frame("datadog.MyClass"), + frame("mycorp.MyClass"), + frame("mycorp.MyClass"), + )))) + + when: + LogCollector.get().addLogMessage(LogMessageLevel.ERROR.toString(), "test", t) + periodicAction.doIteration(telemetryService) + + then: + 1 * telemetryService.addLogMessage(_) >> { args -> logMessage = args[0] } + 0 * _ + logMessage.getMessage() == 'test' + logMessage.getStackTrace() == "${MutableException.canonicalName}\n" + + " at java.MyClass.method(file:42)\n" + + " at (redacted)\n" + + " at datadog.MyClass.method(file:42)\n" + + " at (redacted: 2 frames)\n" + + "Caused by: ${MutableException.canonicalName}\n" + + " ... 5 more\n" + + "Caused by: ${MutableException.canonicalName}\n" + + " ... 5 more\n" + } + + void 'stacktrace without common frames'() { + LogMessage logMessage + + given: + final t = throwable("exception", stacktrace( + frame("java.MyClass"), + frame("mycorp.MyClass"), + frame("datadog.MyClass"), + frame("mycorp.MyClass"), + frame("mycorp.MyClass"), + ), throwable("exception 2", stacktrace( + frame("java.MyClass"), + frame("org.datadog.Test"), + frame("io.DataTest"), + frame("dd.MainClass"), + ))) + + when: + LogCollector.get().addLogMessage(LogMessageLevel.ERROR.toString(), "test", t) + periodicAction.doIteration(telemetryService) + + then: + 1 * telemetryService.addLogMessage(_) >> { args -> logMessage = args[0] } + 0 * _ + logMessage.getMessage() == 'test' + logMessage.getStackTrace() == "${MutableException.canonicalName}\n" + + " at java.MyClass.method(file:42)\n" + + " at (redacted)\n" + + " at datadog.MyClass.method(file:42)\n" + + " at (redacted: 2 frames)\n" + + "Caused by: ${MutableException.canonicalName}\n" + + " at java.MyClass.method(file:42)\n" + + " at (redacted: 3 frames)\n" + } + static class MutableException extends Exception { - MutableException(String message) { - super(message, null, true, true) + MutableException(String message, Throwable cause) { + super(message, cause, true, true) } } static Throwable throwable(String message, StackTraceElement[] stacktrace) { - final MutableException t = new MutableException(message) + final MutableException t = new MutableException(message, null) + t.setStackTrace(stacktrace) + return t + } + + static Throwable throwable(String message, StackTraceElement[] stacktrace, Throwable cause) { + final MutableException t = new MutableException(message, cause) t.setStackTrace(stacktrace) return t }