Skip to content

Commit e6fe1bd

Browse files
committed
send to telemetry
Signed-off-by: sezen.leblay <sezen.leblay@datadoghq.com>
1 parent 446f636 commit e6fe1bd

File tree

8 files changed

+113
-31
lines changed

8 files changed

+113
-31
lines changed

dd-java-agent/agent-logging/src/main/java/datadog/trace/logging/ddlogger/DDTelemetryLogger.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ private void telemetryLog(LogLevel level, Marker marker, String msgOrgFormat, Th
4545
if (t == null && marker != LogCollector.SEND_TELEMETRY) {
4646
return;
4747
}
48-
LogCollector.get().addLogMessage(level.name(), msgOrgFormat, t);
48+
LogCollector.get()
49+
.addLogMessage(LogCollector.LogLevel.fromString(level.name()), msgOrgFormat, t);
4950
}
5051

5152
private Throwable getIfThrowable(final Object obj) {

dd-java-agent/appsec/src/main/java/com/datadog/appsec/AppSecSystem.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,13 @@ private static void loadModules(
166166
}
167167
module.config(cfgObject);
168168
cfgObject.commit();
169-
} catch (RuntimeException | AppSecModule.AppSecModuleActivationException t) {
169+
} catch (RuntimeException t) {
170170
log.error("Startup of appsec module {} failed", module.getName(), t);
171171
continue;
172+
} catch (AppSecModule.AppSecModuleActivationException e) {
173+
log.error("Startup of appsec module {} failed", module.getName(), e);
174+
APP_SEC_CONFIG_SERVICE.reportWafHandleActivationError();
175+
continue;
172176
}
173177

174178
for (AppSecModule.DataSubscription sub : module.getDataSubscriptions()) {

dd-java-agent/appsec/src/main/java/com/datadog/appsec/config/AppSecConfigServiceImpl.java

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,21 @@
4747
import datadog.trace.api.Config;
4848
import datadog.trace.api.ProductActivation;
4949
import datadog.trace.api.UserIdCollectionMode;
50+
import datadog.trace.api.telemetry.LogCollector;
5051
import java.io.ByteArrayInputStream;
5152
import java.io.FileInputStream;
5253
import java.io.FileNotFoundException;
5354
import java.io.IOException;
5455
import java.io.InputStream;
5556
import java.util.ArrayList;
57+
import java.util.Arrays;
5658
import java.util.Collections;
5759
import java.util.HashMap;
5860
import java.util.HashSet;
5961
import java.util.List;
6062
import java.util.Map;
6163
import java.util.Set;
6264
import java.util.concurrent.ConcurrentHashMap;
63-
64-
import datadog.trace.api.telemetry.LogCollector;
6565
import okio.Okio;
6666
import org.slf4j.Logger;
6767
import org.slf4j.LoggerFactory;
@@ -101,6 +101,7 @@ public class AppSecConfigServiceImpl implements AppSecConfigService {
101101
private final String DEFAULT_WAF_CONFIG_RULE = "DEFAULT_WAF_CONFIG";
102102
private String currentRuleVersion;
103103
private List<AppSecModule> modulesToUpdateVersionIn;
104+
private final LogCollector telemetryLogger = LogCollector.get();
104105

105106
public AppSecConfigServiceImpl(
106107
Config tracerConfig,
@@ -171,6 +172,16 @@ public String getCurrentRuleVersion() {
171172
return currentRuleVersion;
172173
}
173174

175+
public void reportWafHandleActivationError() {
176+
this.configurationPoller.addUserConfigError(
177+
"Failed to build WAF instance: no valid rules or processors available",
178+
LogCollector.LogLevel.ERROR.name(),
179+
Arrays.asList(
180+
"\"log_type\": \"rc::asm_dd::diagnostic\"",
181+
"\"appsec_config_key\": \"*\"",
182+
"\"rc_config_id\": \"*\""));
183+
}
184+
174185
private class AppSecConfigConfigurationChangesTypedListener implements ProductListener {
175186
@Override
176187
public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollingRateHinter)
@@ -247,9 +258,6 @@ private void handleWafUpdateResultReport(String configKey, Map<String, Object> r
247258
StandardizedLogging.numLoadedRules(log, configKey, countRules(rawConfig));
248259
}
249260

250-
// TODO: Send diagnostics via telemetry
251-
final LogCollector telemetryLogger = LogCollector.get();
252-
253261
initReporter.setReportForPublication(wafDiagnostics);
254262
if (wafDiagnostics.rulesetVersion != null
255263
&& !wafDiagnostics.rulesetVersion.isEmpty()
@@ -261,13 +269,44 @@ private void handleWafUpdateResultReport(String configKey, Map<String, Object> r
261269
modulesToUpdateVersionIn.forEach(module -> module.setRuleVersion(currentRuleVersion));
262270
}
263271
}
272+
if (wafDiagnostics.getNumConfigError() > 0) {
273+
wafDiagnostics
274+
.getAllErrors()
275+
.forEach(
276+
(section, errors) -> {
277+
String error = String.join(", ", errors);
278+
error = "{" + section + " : " + error + "}";
279+
telemetryLogger.addLogMessage(
280+
LogCollector.LogLevel.ERROR,
281+
error,
282+
new AppSecModule.AppSecModuleActivationException(error));
283+
});
284+
}
264285
} catch (InvalidRuleSetException e) {
265286
log.debug(
266287
"Invalid rule during waf config update for config key {}: {}",
267288
configKey,
268289
e.wafDiagnostics);
269-
270-
// TODO: Propagate diagostics back to remote config apply_error
290+
e.wafDiagnostics
291+
.getAllErrors()
292+
.forEach(
293+
(section, errors) -> {
294+
String error = String.join(", ", errors);
295+
error = "{" + section + " : " + error + "}";
296+
telemetryLogger.addLogMessage(
297+
LogCollector.LogLevel.ERROR,
298+
error,
299+
new AppSecModule.AppSecModuleActivationException(error));
300+
});
301+
e.wafDiagnostics
302+
.getAllErrors()
303+
.forEach(
304+
(section, errors) -> {
305+
String error = String.join(", ", errors);
306+
error = "{" + section + " : " + error + "}";
307+
this.configurationPoller.addUserConfigError(
308+
error, LogCollector.LogLevel.ERROR.name(), new ArrayList<>()); // TODO add tags
309+
});
271310

272311
initReporter.setReportForPublication(e.wafDiagnostics);
273312
throw new RuntimeException(e);
@@ -348,10 +387,7 @@ public void init() {
348387
throw new IllegalStateException("Expected default waf config to be available");
349388
}
350389
try {
351-
handleWafUpdateResultReport(
352-
DEFAULT_WAF_CONFIG_RULE,
353-
wafConfig,
354-
defaultConfigActivated ? DEFAULT_CONFIG_LOCATION : tracerConfig.getAppSecRulesFile());
390+
handleWafUpdateResultReport(DEFAULT_WAF_CONFIG_RULE, wafConfig);
355391
} catch (AppSecModule.AppSecModuleActivationException e) {
356392
throw new RuntimeException(e);
357393
}

dd-java-agent/appsec/src/test/groovy/com/datadog/appsec/config/AppSecConfigServiceImplSpecification.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class AppSecConfigServiceImplSpecification extends DDSpecification {
122122
when:
123123
appSecConfigService.init()
124124
then:
125-
2 * config.getAppSecRulesFile() >> (p as String)
125+
1 * config.getAppSecRulesFile() >> (p as String)
126126

127127
when:
128128
appSecConfigService.maybeSubscribeConfigPolling()

internal-api/src/main/java/datadog/trace/api/telemetry/LogCollector.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ public class LogCollector {
2020
private final Map<RawLogMessage, AtomicInteger> rawLogMessages;
2121
private final int maxCapacity;
2222

23+
public enum LogLevel {
24+
DEBUG,
25+
WARN,
26+
ERROR;
27+
28+
public static LogLevel fromString(String level) {
29+
try {
30+
return LogLevel.valueOf(level.toUpperCase());
31+
} catch (IllegalArgumentException e) {
32+
return DEBUG; // Default to DEBUG if the level is unknown
33+
}
34+
}
35+
}
36+
2337
public static LogCollector get() {
2438
return INSTANCE;
2539
}
@@ -34,13 +48,13 @@ private LogCollector() {
3448
this.rawLogMessages = new ConcurrentHashMap<>(maxCapacity);
3549
}
3650

37-
public void addLogMessage(String logLevel, String message, Throwable throwable) {
51+
public void addLogMessage(LogLevel logLevel, String message, Throwable throwable) {
3852
if (rawLogMessages.size() >= maxCapacity) {
3953
// TODO: We could emit a metric for dropped logs.
4054
return;
4155
}
4256
RawLogMessage rawLogMessage =
43-
new RawLogMessage(logLevel, message, throwable, System.currentTimeMillis() / 1000);
57+
new RawLogMessage(logLevel.name(), message, throwable, System.currentTimeMillis() / 1000);
4458
AtomicInteger count = rawLogMessages.computeIfAbsent(rawLogMessage, k -> new AtomicInteger());
4559
count.incrementAndGet();
4660
}

internal-api/src/test/groovy/datadog/trace/api/telemetry/LogCollectorTest.groovy

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class LogCollectorTest extends DDSpecification {
99
def logCollector = new LogCollector(1)
1010

1111
when:
12-
logCollector.addLogMessage("ERROR", "Message 1", null)
12+
logCollector.addLogMessage(LogCollector.LogLevel.ERROR, "Message 1", null)
1313

1414
then:
1515
def log = logCollector.drain().toList().get(0)
@@ -24,27 +24,27 @@ class LogCollectorTest extends DDSpecification {
2424
def logCollector = new LogCollector(3)
2525

2626
when:
27-
logCollector.addLogMessage("ERROR", "Message 1", null)
28-
logCollector.addLogMessage("ERROR", "Message 2", null)
29-
logCollector.addLogMessage("ERROR", "Message 3", null)
30-
logCollector.addLogMessage("ERROR", "Message 4", null)
27+
logCollector.addLogMessage(LogCollector.LogLevel.ERROR, "Message 1", null)
28+
logCollector.addLogMessage(LogCollector.LogLevel.ERROR, "Message 2", null)
29+
logCollector.addLogMessage(LogCollector.LogLevel.ERROR, "Message 3", null)
30+
logCollector.addLogMessage(LogCollector.LogLevel.ERROR, "Message 4", null)
3131

3232
then:
3333
logCollector.rawLogMessages.size() == 3
3434
}
3535

3636
void "grouping messages in LogCollector"() {
3737
when:
38-
LogCollector.get().addLogMessage("ERROR", "First Message", null)
39-
LogCollector.get().addLogMessage("ERROR", "Second Message", null)
40-
LogCollector.get().addLogMessage("ERROR", "Third Message", null)
41-
LogCollector.get().addLogMessage("ERROR", "Forth Message", null)
42-
LogCollector.get().addLogMessage("ERROR", "Second Message", null)
43-
LogCollector.get().addLogMessage("ERROR", "Third Message", null)
44-
LogCollector.get().addLogMessage("ERROR", "Forth Message", null)
45-
LogCollector.get().addLogMessage("ERROR", "Third Message", null)
46-
LogCollector.get().addLogMessage("ERROR", "Forth Message", null)
47-
LogCollector.get().addLogMessage("ERROR", "Forth Message", null)
38+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "First Message", null)
39+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "Second Message", null)
40+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "Third Message", null)
41+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "Forth Message", null)
42+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "Second Message", null)
43+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "Third Message", null)
44+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "Forth Message", null)
45+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "Third Message", null)
46+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "Forth Message", null)
47+
LogCollector.get().addLogMessage(LogCollector.LogLevel.ERROR, "Forth Message", null)
4848

4949
then:
5050
def list = LogCollector.get().drain()

remote-config/remote-config-api/src/main/java/datadog/remoteconfig/ConfigurationPoller.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package datadog.remoteconfig;
22

33
import datadog.remoteconfig.state.ProductListener;
4+
import java.util.List;
45

56
public interface ConfigurationPoller {
67
void addListener(Product product, ProductListener listener);
@@ -23,6 +24,10 @@ void addListener(
2324

2425
void removeListeners(Product product);
2526

27+
default void addUserConfigError(String message, String level, List<String> tags) {
28+
// used in default implementation
29+
}
30+
2631
void addConfigurationEndListener(ConfigurationEndListener listener);
2732

2833
void removeConfigurationEndListener(ConfigurationEndListener listener);

remote-config/remote-config-core/src/main/java/datadog/remoteconfig/DefaultConfigurationPoller.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import cafe.cryptography.curve25519.InvalidEncodingException;
77
import cafe.cryptography.ed25519.Ed25519PublicKey;
88
import cafe.cryptography.ed25519.Ed25519Signature;
9+
import com.squareup.moshi.JsonAdapter;
910
import com.squareup.moshi.Moshi;
1011
import datadog.remoteconfig.state.ParsedConfigKey;
1112
import datadog.remoteconfig.state.ProductListener;
@@ -84,6 +85,7 @@ public class DefaultConfigurationPoller
8485
private PollerRequestFactory requestFactory;
8586
private RemoteConfigResponse.Factory responseFactory;
8687
private boolean fatalOnInitialization = false;
88+
private Moshi moshi = new Moshi.Builder().build();
8789

8890
public DefaultConfigurationPoller(
8991
Config config,
@@ -184,6 +186,26 @@ public synchronized <T> void addFileListener(
184186
this.fileListeners.put(file, useDeserializer(deserializer, listener));
185187
}
186188

189+
@Override
190+
public synchronized void addUserConfigError(String message, String level, List<String> tags) {
191+
JsonAdapter<ReportableExceptionBuilder> adapter =
192+
moshi.adapter(ReportableExceptionBuilder.class);
193+
String error = adapter.toJson(new ReportableExceptionBuilder(message, level, tags));
194+
this.productStates.get(Product.ASM_DD).recordError(new ReportableException(error));
195+
}
196+
197+
private static class ReportableExceptionBuilder {
198+
private String message;
199+
private String level;
200+
private List<String> tags;
201+
202+
public ReportableExceptionBuilder(String message, String level, List<String> tags) {
203+
this.message = message;
204+
this.level = level;
205+
this.tags = tags;
206+
}
207+
}
208+
187209
@Override
188210
public synchronized void addConfigurationEndListener(ConfigurationEndListener listener) {
189211
this.configurationEndListeners.add(listener);

0 commit comments

Comments
 (0)