Merge pull request #9699 from thingsboard/improvements/monitoring
Monitoring service improvements
This commit is contained in:
commit
db22c014bd
@ -21,4 +21,8 @@ public interface MonitoringTarget {
|
|||||||
|
|
||||||
UUID getDeviceId();
|
UUID getDeviceId();
|
||||||
|
|
||||||
|
String getBaseUrl();
|
||||||
|
|
||||||
|
boolean isCheckDomainIps();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,9 +23,8 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
public abstract class TransportMonitoringConfig implements MonitoringConfig<TransportMonitoringTarget> {
|
public abstract class TransportMonitoringConfig implements MonitoringConfig<TransportMonitoringTarget> {
|
||||||
|
|
||||||
private int requestTimeoutMs;
|
|
||||||
|
|
||||||
private List<TransportMonitoringTarget> targets;
|
private List<TransportMonitoringTarget> targets;
|
||||||
|
private int requestTimeoutMs;
|
||||||
|
|
||||||
public abstract TransportType getTransportType();
|
public abstract TransportType getTransportType();
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ public class TransportMonitoringTarget implements MonitoringTarget {
|
|||||||
|
|
||||||
private String baseUrl;
|
private String baseUrl;
|
||||||
private DeviceConfig device; // set manually during initialization
|
private DeviceConfig device; // set manually during initialization
|
||||||
|
private boolean checkDomainIps;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID getDeviceId() {
|
public UUID getDeviceId() {
|
||||||
|
|||||||
@ -25,4 +25,8 @@ public class Latencies {
|
|||||||
return String.format("%sRequest", key);
|
return String.format("%sRequest", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String wsUpdate(String key) {
|
||||||
|
return String.format("%sWsUpdate", key);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,53 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.monitoring.data;
|
package org.thingsboard.monitoring.data;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.AtomicDouble;
|
import lombok.Data;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
@Data(staticConstructor = "of")
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class Latency {
|
public class Latency {
|
||||||
|
|
||||||
private final String key;
|
private final String key;
|
||||||
private final AtomicDouble latencySum = new AtomicDouble();
|
private final double value;
|
||||||
private final AtomicInteger counter = new AtomicInteger();
|
|
||||||
|
|
||||||
public synchronized void report(double latencyInMs) {
|
public String getFormattedValue() {
|
||||||
latencySum.addAndGet(latencyInMs);
|
return String.format("%,.2f ms", value);
|
||||||
counter.incrementAndGet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized double getAvg() {
|
|
||||||
return latencySum.get() / counter.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isNotEmpty() {
|
|
||||||
return counter.get() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void reset() {
|
|
||||||
latencySum.set(0.0);
|
|
||||||
counter.set(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Latency snapshot() {
|
|
||||||
Latency snapshot = new Latency(key);
|
|
||||||
snapshot.latencySum.set(latencySum.get());
|
|
||||||
snapshot.counter.set(counter.get());
|
|
||||||
return snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Latency{" +
|
|
||||||
"key='" + key + '\'' +
|
|
||||||
", avgLatency=" + getAvg() +
|
|
||||||
'}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,11 +21,11 @@ import java.util.Collection;
|
|||||||
|
|
||||||
public class HighLatencyNotification implements Notification {
|
public class HighLatencyNotification implements Notification {
|
||||||
|
|
||||||
private final Collection<Latency> latencies;
|
private final Collection<Latency> highLatencies;
|
||||||
private final int thresholdMs;
|
private final int thresholdMs;
|
||||||
|
|
||||||
public HighLatencyNotification(Collection<Latency> latencies, int thresholdMs) {
|
public HighLatencyNotification(Collection<Latency> highLatencies, int thresholdMs) {
|
||||||
this.latencies = latencies;
|
this.highLatencies = highLatencies;
|
||||||
this.thresholdMs = thresholdMs;
|
this.thresholdMs = thresholdMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,8 +33,8 @@ public class HighLatencyNotification implements Notification {
|
|||||||
public String getText() {
|
public String getText() {
|
||||||
StringBuilder text = new StringBuilder();
|
StringBuilder text = new StringBuilder();
|
||||||
text.append("Some of the latencies are higher than ").append(thresholdMs).append(" ms:\n");
|
text.append("Some of the latencies are higher than ").append(thresholdMs).append(" ms:\n");
|
||||||
latencies.forEach(latency -> {
|
highLatencies.forEach(latency -> {
|
||||||
text.append(String.format("[%s] %,.2f ms\n", latency.getKey(), latency.getAvg()));
|
text.append(String.format("[%s] %s\n", latency.getKey(), latency.getFormattedValue()));
|
||||||
});
|
});
|
||||||
return text.toString();
|
return text.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.monitoring.service;
|
package org.thingsboard.monitoring.service;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -30,13 +31,17 @@ import org.thingsboard.monitoring.util.TbStopWatch;
|
|||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class BaseHealthChecker<C extends MonitoringConfig, T extends MonitoringTarget> {
|
public abstract class BaseHealthChecker<C extends MonitoringConfig, T extends MonitoringTarget> {
|
||||||
|
|
||||||
|
@Getter
|
||||||
protected final C config;
|
protected final C config;
|
||||||
|
@Getter
|
||||||
protected final T target;
|
protected final T target;
|
||||||
|
|
||||||
private Object info;
|
private Object info;
|
||||||
@ -48,6 +53,9 @@ public abstract class BaseHealthChecker<C extends MonitoringConfig, T extends Mo
|
|||||||
@Value("${monitoring.check_timeout_ms}")
|
@Value("${monitoring.check_timeout_ms}")
|
||||||
private int resultCheckTimeoutMs;
|
private int resultCheckTimeoutMs;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final Map<String, BaseHealthChecker<C, T>> associates = new HashMap<>();
|
||||||
|
|
||||||
public static final String TEST_TELEMETRY_KEY = "testData";
|
public static final String TEST_TELEMETRY_KEY = "testData";
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@ -84,6 +92,10 @@ public abstract class BaseHealthChecker<C extends MonitoringConfig, T extends Mo
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
reporter.serviceFailure(MonitoredServiceKey.GENERAL, e);
|
reporter.serviceFailure(MonitoredServiceKey.GENERAL, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
associates.values().forEach(healthChecker -> {
|
||||||
|
healthChecker.check(wsClient);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkWsUpdate(WsClient wsClient, String testValue) {
|
private void checkWsUpdate(WsClient wsClient, String testValue) {
|
||||||
@ -96,10 +108,9 @@ public abstract class BaseHealthChecker<C extends MonitoringConfig, T extends Mo
|
|||||||
} else if (!update.toString().equals(testValue)) {
|
} else if (!update.toString().equals(testValue)) {
|
||||||
throw new ServiceFailureException("Was expecting value " + testValue + " but got " + update);
|
throw new ServiceFailureException("Was expecting value " + testValue + " but got " + update);
|
||||||
}
|
}
|
||||||
reporter.reportLatency(Latencies.WS_UPDATE, stopWatch.getTime());
|
reporter.reportLatency(Latencies.wsUpdate(getKey()), stopWatch.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected abstract void initClient() throws Exception;
|
protected abstract void initClient() throws Exception;
|
||||||
|
|
||||||
protected abstract String createTestPayload(String testValue);
|
protected abstract String createTestPayload(String testValue);
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.monitoring.service;
|
package org.thingsboard.monitoring.service;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
@ -29,14 +30,22 @@ import org.thingsboard.monitoring.service.transport.TransportHealthChecker;
|
|||||||
import org.thingsboard.monitoring.util.TbStopWatch;
|
import org.thingsboard.monitoring.util.TbStopWatch;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class BaseMonitoringService<C extends MonitoringConfig<T>, T extends MonitoringTarget> {
|
public abstract class BaseMonitoringService<C extends MonitoringConfig<T>, T extends MonitoringTarget> {
|
||||||
|
|
||||||
@Autowired
|
@Autowired(required = false)
|
||||||
private List<C> configs;
|
private List<C> configs;
|
||||||
private final List<BaseHealthChecker<C, T>> healthCheckers = new LinkedList<>();
|
private final List<BaseHealthChecker<C, T>> healthCheckers = new LinkedList<>();
|
||||||
private final List<UUID> devices = new LinkedList<>();
|
private final List<UUID> devices = new LinkedList<>();
|
||||||
@ -54,18 +63,32 @@ public abstract class BaseMonitoringService<C extends MonitoringConfig<T>, T ext
|
|||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void init() {
|
private void init() {
|
||||||
|
if (configs == null || configs.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
tbClient.logIn();
|
tbClient.logIn();
|
||||||
configs.forEach(config -> {
|
configs.forEach(config -> {
|
||||||
config.getTargets().forEach(target -> {
|
config.getTargets().forEach(target -> {
|
||||||
BaseHealthChecker<C, T> healthChecker = (BaseHealthChecker<C, T>) createHealthChecker(config, target);
|
BaseHealthChecker<C, T> healthChecker = initHealthChecker(target, config);
|
||||||
log.info("Initializing {}", healthChecker.getClass().getSimpleName());
|
|
||||||
healthChecker.initialize(tbClient);
|
|
||||||
devices.add(target.getDeviceId());
|
|
||||||
healthCheckers.add(healthChecker);
|
healthCheckers.add(healthChecker);
|
||||||
|
|
||||||
|
if (target.isCheckDomainIps()) {
|
||||||
|
getAssociatedUrls(target.getBaseUrl()).forEach(url -> {
|
||||||
|
healthChecker.getAssociates().put(url, initHealthChecker(createTarget(url), config));
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BaseHealthChecker<C, T> initHealthChecker(T target, C config) {
|
||||||
|
BaseHealthChecker<C, T> healthChecker = (BaseHealthChecker<C, T>) createHealthChecker(config, target);
|
||||||
|
log.info("Initializing {} for {}", healthChecker.getClass().getSimpleName(), target.getBaseUrl());
|
||||||
|
healthChecker.initialize(tbClient);
|
||||||
|
devices.add(target.getDeviceId());
|
||||||
|
return healthChecker;
|
||||||
|
}
|
||||||
|
|
||||||
public final void runChecks() {
|
public final void runChecks() {
|
||||||
if (healthCheckers.isEmpty()) {
|
if (healthCheckers.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@ -78,9 +101,8 @@ public abstract class BaseMonitoringService<C extends MonitoringConfig<T>, T ext
|
|||||||
|
|
||||||
try (WsClient wsClient = wsClientFactory.createClient(accessToken)) {
|
try (WsClient wsClient = wsClientFactory.createClient(accessToken)) {
|
||||||
wsClient.subscribeForTelemetry(devices, TransportHealthChecker.TEST_TELEMETRY_KEY).waitForReply();
|
wsClient.subscribeForTelemetry(devices, TransportHealthChecker.TEST_TELEMETRY_KEY).waitForReply();
|
||||||
|
|
||||||
for (BaseHealthChecker<C, T> healthChecker : healthCheckers) {
|
for (BaseHealthChecker<C, T> healthChecker : healthCheckers) {
|
||||||
healthChecker.check(wsClient);
|
check(healthChecker, wsClient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reporter.reportLatencies(tbClient);
|
reporter.reportLatencies(tbClient);
|
||||||
@ -94,8 +116,61 @@ public abstract class BaseMonitoringService<C extends MonitoringConfig<T>, T ext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void check(BaseHealthChecker<C, T> healthChecker, WsClient wsClient) throws Exception {
|
||||||
|
healthChecker.check(wsClient);
|
||||||
|
|
||||||
|
T target = healthChecker.getTarget();
|
||||||
|
if (target.isCheckDomainIps()) {
|
||||||
|
Set<String> associatedUrls = getAssociatedUrls(target.getBaseUrl());
|
||||||
|
Map<String, BaseHealthChecker<C, T>> associates = healthChecker.getAssociates();
|
||||||
|
Set<String> prevAssociatedUrls = new HashSet<>(associates.keySet());
|
||||||
|
|
||||||
|
boolean changed = false;
|
||||||
|
for (String url : associatedUrls) {
|
||||||
|
if (!prevAssociatedUrls.contains(url)) {
|
||||||
|
BaseHealthChecker<C, T> associate = initHealthChecker(createTarget(url), healthChecker.getConfig());
|
||||||
|
associates.put(url, associate);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String url : prevAssociatedUrls) {
|
||||||
|
if (!associatedUrls.contains(url)) {
|
||||||
|
stopHealthChecker(healthChecker);
|
||||||
|
associates.remove(url);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
log.info("Updated IPs for {}: {} (old list: {})", target.getBaseUrl(), associatedUrls, prevAssociatedUrls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private Set<String> getAssociatedUrls(String baseUrl) {
|
||||||
|
URI url = new URI(baseUrl);
|
||||||
|
return Arrays.stream(InetAddress.getAllByName(url.getHost()))
|
||||||
|
.map(InetAddress::getHostAddress)
|
||||||
|
.map(ip -> {
|
||||||
|
try {
|
||||||
|
return new URI(url.getScheme(), null, ip, url.getPort(), "", null, null).toString();
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopHealthChecker(BaseHealthChecker<C, T> healthChecker) throws Exception {
|
||||||
|
healthChecker.destroyClient();
|
||||||
|
devices.remove(healthChecker.getTarget().getDeviceId());
|
||||||
|
log.info("Stopped {} for {}", healthChecker.getClass().getSimpleName(), healthChecker.getTarget().getBaseUrl());
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract BaseHealthChecker<?, ?> createHealthChecker(C config, T target);
|
protected abstract BaseHealthChecker<?, ?> createHealthChecker(C config, T target);
|
||||||
|
|
||||||
|
protected abstract T createTarget(String baseUrl);
|
||||||
|
|
||||||
protected abstract String getName();
|
protected abstract String getName();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,25 +63,20 @@ public class MonitoringReporter {
|
|||||||
private String reportingAssetId;
|
private String reportingAssetId;
|
||||||
|
|
||||||
public void reportLatencies(TbClient tbClient) {
|
public void reportLatencies(TbClient tbClient) {
|
||||||
List<Latency> latencies = this.latencies.values().stream()
|
|
||||||
.filter(Latency::isNotEmpty)
|
|
||||||
.map(latency -> {
|
|
||||||
Latency snapshot = latency.snapshot();
|
|
||||||
latency.reset();
|
|
||||||
return snapshot;
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
if (latencies.isEmpty()) {
|
if (latencies.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info("Latencies:\n{}", latencies.stream().map(latency -> latency.getKey() + ": " + latency.getAvg() + " ms")
|
log.debug("Latencies:\n{}", latencies.values().stream().map(latency -> latency.getKey() + ": " + latency.getFormattedValue())
|
||||||
.collect(Collectors.joining("\n")) + "\n");
|
.collect(Collectors.joining("\n")) + "\n");
|
||||||
|
|
||||||
if (!latencyReportingEnabled) return;
|
if (!latencyReportingEnabled) return;
|
||||||
|
|
||||||
if (latencies.stream().anyMatch(latency -> latency.getAvg() >= (double) latencyThresholdMs)) {
|
List<Latency> highLatencies = latencies.values().stream()
|
||||||
HighLatencyNotification highLatencyNotification = new HighLatencyNotification(latencies, latencyThresholdMs);
|
.filter(latency -> latency.getValue() >= (double) latencyThresholdMs)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (!highLatencies.isEmpty()) {
|
||||||
|
HighLatencyNotification highLatencyNotification = new HighLatencyNotification(highLatencies, latencyThresholdMs);
|
||||||
notificationService.sendNotification(highLatencyNotification);
|
notificationService.sendNotification(highLatencyNotification);
|
||||||
|
log.warn("{}", highLatencyNotification.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -99,10 +94,11 @@ public class MonitoringReporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ObjectNode msg = JacksonUtil.newObjectNode();
|
ObjectNode msg = JacksonUtil.newObjectNode();
|
||||||
latencies.forEach(latency -> {
|
latencies.values().forEach(latency -> {
|
||||||
msg.set(latency.getKey(), new DoubleNode(latency.getAvg()));
|
msg.set(latency.getKey(), new DoubleNode(latency.getValue()));
|
||||||
});
|
});
|
||||||
tbClient.saveEntityTelemetry(new AssetId(UUID.fromString(reportingAssetId)), "time", msg);
|
tbClient.saveEntityTelemetry(new AssetId(UUID.fromString(reportingAssetId)), "time", msg);
|
||||||
|
latencies.clear();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to report latencies: {}", e.getMessage());
|
log.error("Failed to report latencies: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
@ -112,7 +108,7 @@ public class MonitoringReporter {
|
|||||||
String latencyKey = key + "Latency";
|
String latencyKey = key + "Latency";
|
||||||
double latencyInMs = (double) latencyInNanos / 1000_000;
|
double latencyInMs = (double) latencyInNanos / 1000_000;
|
||||||
log.trace("Reporting latency [{}]: {} ms", key, latencyInMs);
|
log.trace("Reporting latency [{}]: {} ms", key, latencyInMs);
|
||||||
latencies.computeIfAbsent(latencyKey, k -> new Latency(latencyKey)).report(latencyInMs);
|
latencies.put(latencyKey, Latency.of(latencyKey, latencyInMs));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void serviceFailure(Object serviceKey, Throwable error) {
|
public void serviceFailure(Object serviceKey, Throwable error) {
|
||||||
|
|||||||
@ -33,6 +33,13 @@ public final class TransportsMonitoringService extends BaseMonitoringService<Tra
|
|||||||
return applicationContext.getBean(config.getTransportType().getServiceClass(), config, target);
|
return applicationContext.getBean(config.getTransportType().getServiceClass(), config, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TransportMonitoringTarget createTarget(String baseUrl) {
|
||||||
|
TransportMonitoringTarget target = new TransportMonitoringTarget();
|
||||||
|
target.setBaseUrl(baseUrl);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getName() {
|
protected String getName() {
|
||||||
return "transports check";
|
return "transports check";
|
||||||
|
|||||||
436
monitoring/src/main/resources/root_rule_chain.json
Normal file
436
monitoring/src/main/resources/root_rule_chain.json
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
{
|
||||||
|
"ruleChain": {
|
||||||
|
"additionalInfo": null,
|
||||||
|
"name": "Root Rule Chain",
|
||||||
|
"type": "CORE",
|
||||||
|
"firstRuleNodeId": null,
|
||||||
|
"root": false,
|
||||||
|
"debugMode": false,
|
||||||
|
"configuration": null,
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"firstNodeIndex": 12,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": null,
|
||||||
|
"layoutX": 1202,
|
||||||
|
"layoutY": 221
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||||
|
"name": "Save Timeseries",
|
||||||
|
"debugMode": true,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"defaultTTL": 0
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"layoutX": 1000,
|
||||||
|
"layoutY": 167
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
|
||||||
|
"name": "Save Attributes",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 1,
|
||||||
|
"configuration": {
|
||||||
|
"scope": "CLIENT_SCOPE",
|
||||||
|
"notifyDevice": false,
|
||||||
|
"sendAttributesUpdatedNotification": false,
|
||||||
|
"updateAttributesOnlyOnValueChange": false
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"layoutX": 566,
|
||||||
|
"layoutY": 302
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
|
||||||
|
"name": "Message Type Switch",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"version": 0
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"layoutX": 1000,
|
||||||
|
"layoutY": 381
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.action.TbLogNode",
|
||||||
|
"name": "Log RPC from Device",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"scriptLang": "TBEL",
|
||||||
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
|
||||||
|
"tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"layoutX": 1000,
|
||||||
|
"layoutY": 494
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.action.TbLogNode",
|
||||||
|
"name": "Log Other",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"scriptLang": "TBEL",
|
||||||
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
|
||||||
|
"tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"layoutX": 1000,
|
||||||
|
"layoutY": 583
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
|
||||||
|
"name": "RPC Call Request",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"timeoutInSeconds": 60
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"layoutX": 255,
|
||||||
|
"layoutY": 301
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.filter.TbOriginatorTypeFilterNode",
|
||||||
|
"name": "Is Entity Group",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"originatorTypes": [
|
||||||
|
"ENTITY_GROUP"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"layoutX": 319,
|
||||||
|
"layoutY": 109
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode",
|
||||||
|
"name": "Post attributes or RPC request",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"messageTypes": [
|
||||||
|
"POST_ATTRIBUTES_REQUEST",
|
||||||
|
"RPC_CALL_FROM_SERVER_TO_DEVICE"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"layoutX": 627,
|
||||||
|
"layoutY": 108
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.transform.TbDuplicateMsgToGroupNode",
|
||||||
|
"name": "Duplicate To Group Entities",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"entityGroupId": null,
|
||||||
|
"entityGroupIsMessageOriginator": true
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
|
||||||
|
"layoutX": 45,
|
||||||
|
"layoutY": 359
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
|
||||||
|
"name": "Device Profile Node",
|
||||||
|
"debugMode": true,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"persistAlarmRulesState": false,
|
||||||
|
"fetchAlarmRulesStateOnStart": false
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": "",
|
||||||
|
"layoutX": 160,
|
||||||
|
"layoutY": 631
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.filter.TbJsFilterNode",
|
||||||
|
"name": "Test JS script",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"scriptLang": "JS",
|
||||||
|
"jsScript": "var test = {\n a: 'a',\n b: 'b'\n};\nreturn test.a === 'a' && test.b === 'b';",
|
||||||
|
"tbelScript": "return msg.temperature > 20;"
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": "",
|
||||||
|
"layoutX": 427,
|
||||||
|
"layoutY": 541
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.filter.TbJsFilterNode",
|
||||||
|
"name": "Test TBEL script",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"scriptLang": "TBEL",
|
||||||
|
"jsScript": "return msg.temperature > 20;",
|
||||||
|
"tbelScript": "var a = \"a\";\nvar b = \"b\";\nreturn a.equals(\"a\") && b.equals(\"b\");"
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": "",
|
||||||
|
"layoutX": 40,
|
||||||
|
"layoutY": 252
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode",
|
||||||
|
"name": "Add arrival timestamp",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"scriptLang": "TBEL",
|
||||||
|
"jsScript": "return {msg: msg, metadata: metadata, msgType: msgType};",
|
||||||
|
"tbelScript": "metadata.arrivalTs = Date.now();\nreturn {msg: msg, metadata: metadata, msgType: msgType};"
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": "",
|
||||||
|
"layoutX": 1467,
|
||||||
|
"layoutY": 267
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode",
|
||||||
|
"name": "Calculate additional latencies",
|
||||||
|
"debugMode": true,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"scriptLang": "TBEL",
|
||||||
|
"jsScript": "return {msg: msg, metadata: metadata, msgType: msgType};",
|
||||||
|
"tbelScript": "var arrivalLatency = metadata.arrivalTs - metadata.ts;\nvar processingTime = Date.now() - metadata.arrivalTs;\nmsg = {\n arrivalLatency: arrivalLatency,\n processingTime: processingTime\n};\nreturn {msg: msg, metadata: metadata, msgType: msgType};"
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": "",
|
||||||
|
"layoutX": 1438,
|
||||||
|
"layoutY": 403
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.transform.TbChangeOriginatorNode",
|
||||||
|
"name": "To latencies asset",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"originatorSource": "ENTITY",
|
||||||
|
"entityType": "ASSET",
|
||||||
|
"entityNamePattern": "[Monitoring] Latencies",
|
||||||
|
"relationsQuery": {
|
||||||
|
"direction": "FROM",
|
||||||
|
"maxLevel": 1,
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"relationType": "Contains",
|
||||||
|
"entityTypes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fetchLastLevelOnly": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": null,
|
||||||
|
"layoutX": 1458,
|
||||||
|
"layoutY": 505
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||||
|
"name": "Save Timeseries",
|
||||||
|
"debugMode": true,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"defaultTTL": 0
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": "",
|
||||||
|
"layoutX": 928,
|
||||||
|
"layoutY": 266
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.filter.TbCheckMessageNode",
|
||||||
|
"name": "Has testData",
|
||||||
|
"debugMode": false,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"messageNames": [
|
||||||
|
"testData"
|
||||||
|
],
|
||||||
|
"metadataNames": [],
|
||||||
|
"checkAllKeys": true
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"additionalInfo": {
|
||||||
|
"description": null,
|
||||||
|
"layoutX": 1203,
|
||||||
|
"layoutY": 327
|
||||||
|
},
|
||||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||||
|
"name": "Save Timeseries with TTL",
|
||||||
|
"debugMode": true,
|
||||||
|
"singletonMode": false,
|
||||||
|
"configurationVersion": 0,
|
||||||
|
"configuration": {
|
||||||
|
"defaultTTL": 180,
|
||||||
|
"skipLatestPersistence": null,
|
||||||
|
"useServerTs": null
|
||||||
|
},
|
||||||
|
"externalId": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": [
|
||||||
|
{
|
||||||
|
"fromIndex": 2,
|
||||||
|
"toIndex": 1,
|
||||||
|
"type": "Post attributes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 2,
|
||||||
|
"toIndex": 3,
|
||||||
|
"type": "RPC Request from Device"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 2,
|
||||||
|
"toIndex": 4,
|
||||||
|
"type": "Other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 2,
|
||||||
|
"toIndex": 5,
|
||||||
|
"type": "RPC Request to Device"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 2,
|
||||||
|
"toIndex": 16,
|
||||||
|
"type": "Post telemetry"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 6,
|
||||||
|
"toIndex": 2,
|
||||||
|
"type": "False"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 6,
|
||||||
|
"toIndex": 7,
|
||||||
|
"type": "True"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 7,
|
||||||
|
"toIndex": 2,
|
||||||
|
"type": "False"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 7,
|
||||||
|
"toIndex": 8,
|
||||||
|
"type": "True"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 8,
|
||||||
|
"toIndex": 2,
|
||||||
|
"type": "Success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 9,
|
||||||
|
"toIndex": 10,
|
||||||
|
"type": "Success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 10,
|
||||||
|
"toIndex": 11,
|
||||||
|
"type": "True"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 11,
|
||||||
|
"toIndex": 6,
|
||||||
|
"type": "True"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 12,
|
||||||
|
"toIndex": 9,
|
||||||
|
"type": "Success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 13,
|
||||||
|
"toIndex": 14,
|
||||||
|
"type": "Success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 14,
|
||||||
|
"toIndex": 15,
|
||||||
|
"type": "Success"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 16,
|
||||||
|
"toIndex": 0,
|
||||||
|
"type": "False"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 16,
|
||||||
|
"toIndex": 17,
|
||||||
|
"type": "True"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 17,
|
||||||
|
"toIndex": 13,
|
||||||
|
"type": "Success"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ruleChainConnections": null
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -51,8 +51,10 @@ monitoring:
|
|||||||
# MQTT QoS
|
# MQTT QoS
|
||||||
qos: '${MQTT_QOS_LEVEL:1}'
|
qos: '${MQTT_QOS_LEVEL:1}'
|
||||||
targets:
|
targets:
|
||||||
# MQTT transport base url, tcp://DOMAIN:1883 by default
|
# MQTT transport base url, tcp://DOMAIN:1883 by default
|
||||||
- base_url: '${MQTT_TRANSPORT_BASE_URL:tcp://${monitoring.domain}:1883}'
|
- base_url: '${MQTT_TRANSPORT_BASE_URL:tcp://${monitoring.domain}:1883}'
|
||||||
|
# Whether to monitor IPs associated with the domain from base url
|
||||||
|
check_domain_ips: '${MQTT_TRANSPORT_CHECK_DOMAIN_IPS:false}'
|
||||||
# To add more targets, use following environment variables:
|
# To add more targets, use following environment variables:
|
||||||
# monitoring.transports.mqtt.targets[1].base_url, monitoring.transports.mqtt.targets[2].base_url, etc.
|
# monitoring.transports.mqtt.targets[1].base_url, monitoring.transports.mqtt.targets[2].base_url, etc.
|
||||||
|
|
||||||
@ -62,8 +64,10 @@ monitoring:
|
|||||||
# CoAP request timeout in milliseconds
|
# CoAP request timeout in milliseconds
|
||||||
request_timeout_ms: '${COAP_REQUEST_TIMEOUT_MS:4000}'
|
request_timeout_ms: '${COAP_REQUEST_TIMEOUT_MS:4000}'
|
||||||
targets:
|
targets:
|
||||||
# CoAP transport base url, coap://DOMAIN by default
|
# CoAP transport base url, coap://DOMAIN by default
|
||||||
- base_url: '${COAP_TRANSPORT_BASE_URL:coap://${monitoring.domain}}'
|
- base_url: '${COAP_TRANSPORT_BASE_URL:coap://${monitoring.domain}}'
|
||||||
|
# Whether to monitor IPs associated with the domain from base url
|
||||||
|
check_domain_ips: '${COAP_TRANSPORT_CHECK_DOMAIN_IPS:false}'
|
||||||
# To add more targets, use following environment variables:
|
# To add more targets, use following environment variables:
|
||||||
# monitoring.transports.coap.targets[1].base_url, monitoring.transports.coap.targets[2].base_url, etc.
|
# monitoring.transports.coap.targets[1].base_url, monitoring.transports.coap.targets[2].base_url, etc.
|
||||||
|
|
||||||
@ -73,8 +77,10 @@ monitoring:
|
|||||||
# HTTP request timeout in milliseconds
|
# HTTP request timeout in milliseconds
|
||||||
request_timeout_ms: '${HTTP_REQUEST_TIMEOUT_MS:4000}'
|
request_timeout_ms: '${HTTP_REQUEST_TIMEOUT_MS:4000}'
|
||||||
targets:
|
targets:
|
||||||
# HTTP transport base url, http://DOMAIN by default
|
# HTTP transport base url, http://DOMAIN by default
|
||||||
- base_url: '${HTTP_TRANSPORT_BASE_URL:http://${monitoring.domain}}'
|
- base_url: '${HTTP_TRANSPORT_BASE_URL:http://${monitoring.domain}}'
|
||||||
|
# Whether to monitor IPs associated with the domain from base url
|
||||||
|
check_domain_ips: '${HTTP_TRANSPORT_CHECK_DOMAIN_IPS:false}'
|
||||||
# To add more targets, use following environment variables:
|
# To add more targets, use following environment variables:
|
||||||
# monitoring.transports.http.targets[1].base_url, monitoring.transports.http.targets[2].base_url, etc.
|
# monitoring.transports.http.targets[1].base_url, monitoring.transports.http.targets[2].base_url, etc.
|
||||||
|
|
||||||
@ -84,8 +90,10 @@ monitoring:
|
|||||||
# LwM2M request timeout in milliseconds
|
# LwM2M request timeout in milliseconds
|
||||||
request_timeout_ms: '${LWM2M_REQUEST_TIMEOUT_MS:4000}'
|
request_timeout_ms: '${LWM2M_REQUEST_TIMEOUT_MS:4000}'
|
||||||
targets:
|
targets:
|
||||||
# LwM2M transport base url, coap://DOMAIN:5685 by default
|
# LwM2M transport base url, coap://DOMAIN:5685 by default
|
||||||
- base_url: '${LWM2M_TRANSPORT_BASE_URL:coap://${monitoring.domain}:5685}'
|
- base_url: '${LWM2M_TRANSPORT_BASE_URL:coap://${monitoring.domain}:5685}'
|
||||||
|
# Whether to monitor IPs associated with the domain from base url
|
||||||
|
check_domain_ips: '${LWM2M_TRANSPORT_CHECK_DOMAIN_IPS:false}'
|
||||||
# To add more targets, use following environment variables:
|
# To add more targets, use following environment variables:
|
||||||
# monitoring.transports.lwm2m.targets[1].base_url, monitoring.transports.lwm2m.targets[2].base_url, etc.
|
# monitoring.transports.lwm2m.targets[1].base_url, monitoring.transports.lwm2m.targets[2].base_url, etc.
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user