diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 2c22941c2f..9df613353d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.MailService; @@ -432,10 +431,6 @@ public class ActorSystemContext { @Getter private ClaimDevicesService claimDevicesService; - @Autowired - @Getter - private JsInvokeStats jsInvokeStats; - //TODO: separate context for TbCore and TbRuleEngine @Autowired(required = false) @Getter @@ -595,17 +590,6 @@ public class ActorSystemContext { this.localCacheType = "caffeine".equals(cacheType); } - @Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}") - public void printStats() { - if (statisticsEnabled) { - if (jsInvokeStats.getRequests() > 0 || jsInvokeStats.getResponses() > 0 || jsInvokeStats.getFailures() > 0) { - log.info("Rule Engine JS Invoke Stats: requests [{}] responses [{}] failures [{}]", - jsInvokeStats.getRequests(), jsInvokeStats.getResponses(), jsInvokeStats.getFailures()); - jsInvokeStats.reset(); - } - } - } - @Value("${actors.tenant.create_components_on_init:true}") @Getter private boolean tenantComponentsInitEnabled; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index ee6758df6a..033e10ca9a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -654,27 +654,6 @@ public class DefaultTbContext implements TbContext { } } - @Override - public void logJsEvalRequest() { - if (mainCtx.isStatisticsEnabled()) { - mainCtx.getJsInvokeStats().incrementRequests(); - } - } - - @Override - public void logJsEvalResponse() { - if (mainCtx.isStatisticsEnabled()) { - mainCtx.getJsInvokeStats().incrementResponses(); - } - } - - @Override - public void logJsEvalFailure() { - if (mainCtx.isStatisticsEnabled()) { - mainCtx.getJsInvokeStats().incrementFailures(); - } - } - @Override public String getServiceId() { return mainCtx.getServiceInfoProvider().getServiceId(); diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java deleted file mode 100644 index e160e0e5b5..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright © 2016-2025 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.stats; - -import jakarta.annotation.PostConstruct; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.thingsboard.server.actors.JsInvokeStats; -import org.thingsboard.server.common.stats.StatsCounter; -import org.thingsboard.server.common.stats.StatsFactory; -import org.thingsboard.server.common.stats.StatsType; - -@Service -public class DefaultJsInvokeStats implements JsInvokeStats { - private static final String REQUESTS = "requests"; - private static final String RESPONSES = "responses"; - private static final String FAILURES = "failures"; - - private StatsCounter requestsCounter; - private StatsCounter responsesCounter; - private StatsCounter failuresCounter; - - @Autowired - private StatsFactory statsFactory; - - @PostConstruct - public void init() { - String key = StatsType.JS_INVOKE.getName(); - this.requestsCounter = statsFactory.createStatsCounter(key, REQUESTS); - this.responsesCounter = statsFactory.createStatsCounter(key, RESPONSES); - this.failuresCounter = statsFactory.createStatsCounter(key, FAILURES); - } - - @Override - public void incrementRequests(int amount) { - requestsCounter.add(amount); - } - - @Override - public void incrementResponses(int amount) { - responsesCounter.add(amount); - } - - @Override - public void incrementFailures(int amount) { - failuresCounter.add(amount); - } - - @Override - public int getRequests() { - return requestsCounter.get(); - } - - @Override - public int getResponses() { - return responsesCounter.get(); - } - - @Override - public int getFailures() { - return failuresCounter.get(); - } - - @Override - public void reset() { - requestsCounter.clear(); - responsesCounter.clear(); - failuresCounter.clear(); - } -} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 2ee3c439c1..6c817eaa57 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -502,8 +502,6 @@ actors: statistics: # Enable/disable actor statistics enabled: "${ACTORS_STATISTICS_ENABLED:true}" - # Frequency of printing the JS executor statistics - js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}" # Actors statistic persistence frequency in milliseconds persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}" calculated_fields: diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java index 320093e8f4..91eced64f6 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.cf.ctx.state; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.stats.DefaultStatsFactory; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.service.cf.CalculatedFieldResult; @@ -51,7 +53,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -@SpringBootTest(classes = DefaultTbelInvokeService.class) +@SpringBootTest(classes = {SimpleMeterRegistry.class, DefaultStatsFactory.class, DefaultTbelInvokeService.class}) public class ScriptCalculatedFieldStateTest { private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("5b18e321-3327-4290-b996-d72a65e90382")); diff --git a/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java b/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java index f8563e5e2a..e3383e32a8 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.script; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.thingsboard.common.util.JacksonUtil; @@ -22,7 +23,7 @@ import org.thingsboard.script.api.ScriptType; import org.thingsboard.script.api.tbel.DefaultTbelInvokeService; import org.thingsboard.script.api.tbel.TbelInvokeService; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.common.stats.DefaultStatsFactory; import java.util.Map; import java.util.UUID; @@ -30,7 +31,7 @@ import java.util.concurrent.ExecutionException; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; -@SpringBootTest(classes = DefaultTbelInvokeService.class) +@SpringBootTest(classes = {SimpleMeterRegistry.class, DefaultStatsFactory.class, DefaultTbelInvokeService.class }) public abstract class AbstractTbelInvokeTest { @Autowired diff --git a/application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java b/application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java index 7404427f92..363f21fa10 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java @@ -21,9 +21,13 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.springframework.test.util.ReflectionTestUtils; import org.thingsboard.script.api.ScriptType; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.stats.DefaultStatsFactory; +import org.thingsboard.server.common.stats.StatsCounter; +import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.common.stats.TbApiUsageStateClient; import org.thingsboard.server.gen.js.JsInvokeProtos; @@ -68,6 +72,10 @@ class RemoteJsInvokeServiceTest { remoteJsInvokeService = new RemoteJsInvokeService(Optional.of(apiUsageStateClient), Optional.of(apiUsageReportClient)); jsRequestTemplate = mock(TbQueueRequestTemplate.class); remoteJsInvokeService.requestTemplate = jsRequestTemplate; + StatsFactory statsFactory = mock(StatsFactory.class); + when(statsFactory.createStatsCounter(any(), any())).thenReturn(mock(StatsCounter.class)); + ReflectionTestUtils.setField(remoteJsInvokeService, "statsFactory",statsFactory); + remoteJsInvokeService.init(); } @AfterEach diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/JsInvokeStats.java b/common/actor/src/main/java/org/thingsboard/server/actors/JsInvokeStats.java deleted file mode 100644 index bf81e04b71..0000000000 --- a/common/actor/src/main/java/org/thingsboard/server/actors/JsInvokeStats.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright © 2016-2025 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.actors; - -public interface JsInvokeStats { - default void incrementRequests() { - incrementRequests(1); - } - - void incrementRequests(int amount); - - default void incrementResponses() { - incrementResponses(1); - } - - void incrementResponses(int amount); - - default void incrementFailures() { - incrementFailures(1); - } - - void incrementFailures(int amount); - - int getRequests(); - - int getResponses(); - - int getFailures(); - - void reset(); -} diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java index 7cbac0401d..3d9b855064 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java @@ -20,12 +20,16 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfObject; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.stats.StatsCounter; +import org.thingsboard.server.common.stats.StatsFactory; +import org.thingsboard.server.common.stats.StatsType; import java.util.Map; import java.util.UUID; @@ -34,22 +38,31 @@ import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; import static java.lang.String.format; @Slf4j public abstract class AbstractScriptInvokeService implements ScriptInvokeService { + private static final String REQUESTS = "requests"; + private static final String INVOKE_RESPONSES = "invoke_responses"; + private static final String EVAL_RESPONSES = "eval_responses"; + private static final String FAILURES = "failures"; + private static final String TIMEOUTS = "timeouts"; + protected final Map disabledScripts = new ConcurrentHashMap<>(); - private final AtomicInteger pushedMsgs = new AtomicInteger(0); - private final AtomicInteger invokeMsgs = new AtomicInteger(0); - private final AtomicInteger evalMsgs = new AtomicInteger(0); - protected final AtomicInteger failedMsgs = new AtomicInteger(0); - protected final AtomicInteger timeoutMsgs = new AtomicInteger(0); - private final FutureCallback evalCallback = new ScriptStatCallback<>(evalMsgs, timeoutMsgs, failedMsgs); - private final FutureCallback invokeCallback = new ScriptStatCallback<>(invokeMsgs, timeoutMsgs, failedMsgs); + private StatsCounter requestsCounter; + private StatsCounter invokeResponsesCounter; + private StatsCounter evalResponsesCounter; + private StatsCounter failuresCounter; + private StatsCounter timeoutsCounter; + + private FutureCallback evalCallback; + private FutureCallback invokeCallback; + + @Autowired + private StatsFactory statsFactory; protected ScheduledExecutorService timeoutExecutorService; @@ -78,6 +91,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService protected abstract boolean isScriptPresent(UUID scriptId); protected abstract boolean isExecEnabled(TenantId tenantId); + protected abstract void reportExecution(TenantId tenantId, CustomerId customerId); protected abstract ListenableFuture doEvalScript(TenantId tenantId, ScriptType scriptType, String scriptBody, UUID scriptId, String[] argNames); @@ -87,6 +101,14 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService protected abstract void doRelease(UUID scriptId) throws Exception; public void init() { + String key = getStatsType().getName(); + this.requestsCounter = statsFactory.createStatsCounter(key, REQUESTS); + this.invokeResponsesCounter = statsFactory.createStatsCounter(key, INVOKE_RESPONSES); + this.evalResponsesCounter = statsFactory.createStatsCounter(key, EVAL_RESPONSES); + this.failuresCounter = statsFactory.createStatsCounter(key, FAILURES); + this.timeoutsCounter = statsFactory.createStatsCounter(key, TIMEOUTS); + this.evalCallback = new ScriptStatCallback<>(evalResponsesCounter, timeoutsCounter, failuresCounter); + this.invokeCallback = new ScriptStatCallback<>(invokeResponsesCounter, timeoutsCounter, failuresCounter); if (getMaxEvalRequestsTimeout() > 0 || getMaxInvokeRequestsTimeout() > 0) { timeoutExecutorService = ThingsBoardExecutors.newSingleThreadScheduledExecutor("script-timeout"); } @@ -100,11 +122,11 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService public void printStats() { if (isStatsEnabled()) { - int pushed = pushedMsgs.getAndSet(0); - int invoked = invokeMsgs.getAndSet(0); - int evaluated = evalMsgs.getAndSet(0); - int failed = failedMsgs.getAndSet(0); - int timedOut = timeoutMsgs.getAndSet(0); + int pushed = requestsCounter.getAndClear(); + int invoked = invokeResponsesCounter.getAndClear(); + int evaluated = evalResponsesCounter.getAndClear(); + int failed = failuresCounter.getAndClear(); + int timedOut = timeoutsCounter.getAndClear(); if (pushed > 0 || invoked > 0 || evaluated > 0 || failed > 0 || timedOut > 0) { log.info("{}: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]", getStatsName(), pushed, invoked + evaluated, invoked, evaluated, failed, timedOut); @@ -119,7 +141,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService return error(format("Script body exceeds maximum allowed size of %s symbols", getMaxScriptBodySize())); } UUID scriptId = UUID.randomUUID(); - pushedMsgs.incrementAndGet(); + requestsCounter.increment(); return withTimeoutAndStatsCallback(scriptId, null, doEvalScript(tenantId, scriptType, scriptBody, scriptId, argNames), evalCallback, getMaxEvalRequestsTimeout()); } else { @@ -141,7 +163,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService return Futures.immediateFailedFuture(handleScriptException(scriptId, null, t)); } reportExecution(tenantId, customerId); - pushedMsgs.incrementAndGet(); + requestsCounter.increment(); log.trace("[{}] InvokeScript uuid {} with timeout {}ms", tenantId, scriptId, getMaxInvokeRequestsTimeout()); var task = doInvokeFunction(scriptId, args); @@ -278,4 +300,6 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService private ListenableFuture error(String message) { return Futures.immediateFailedFuture(new RuntimeException(message)); } + + protected abstract StatsType getStatsType(); } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java index 33812b7852..fd6f5bf7d7 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java @@ -19,29 +19,29 @@ import com.google.common.util.concurrent.FutureCallback; import jakarta.annotation.Nullable; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.stats.StatsCounter; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; @Slf4j @AllArgsConstructor public class ScriptStatCallback implements FutureCallback { - private final AtomicInteger successMsgs; - private final AtomicInteger timeoutMsgs; - private final AtomicInteger failedMsgs; + private final StatsCounter successMsgs; + private final StatsCounter timeoutMsgs; + private final StatsCounter failedMsgs; @Override public void onSuccess(@Nullable T result) { - successMsgs.incrementAndGet(); + successMsgs.increment(); } @Override public void onFailure(Throwable t) { if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { - timeoutMsgs.incrementAndGet(); + timeoutMsgs.increment(); } else { - failedMsgs.incrementAndGet(); + failedMsgs.increment(); } } } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java index 4049040c94..b6ddf16e66 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java @@ -26,6 +26,7 @@ import org.thingsboard.script.api.ScriptType; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.stats.StatsType; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.common.stats.TbApiUsageStateClient; @@ -117,4 +118,8 @@ public abstract class AbstractJsInvokeService extends AbstractScriptInvokeServic .hash().toString(); } + @Override + protected StatsType getStatsType() { + return StatsType.JS_INVOKE; + } } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java index 3680e1bfb2..25a7ede547 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java @@ -44,6 +44,7 @@ import org.thingsboard.script.api.TbScriptException; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.stats.StatsType; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.common.stats.TbApiUsageStateClient; @@ -263,4 +264,9 @@ public class DefaultTbelInvokeService extends AbstractScriptInvokeService implem protected long getMaxEvalRequestsTimeout() { return maxInvokeRequestsTimeout * 2; } + + @Override + protected StatsType getStatsType() { + return StatsType.TBEL_INVOKE; + } } diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java index ef9041fab5..126d4dac95 100644 --- a/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java @@ -41,6 +41,10 @@ public class DefaultCounter { return aiCounter.get(); } + public int getAndClear() { + return aiCounter.getAndSet(0); + } + public void add(int delta){ aiCounter.addAndGet(delta); micrometerCounter.increment(delta); diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java index de64d4fe54..90bf1bec32 100644 --- a/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java @@ -20,6 +20,7 @@ public enum StatsType { CORE("core"), TRANSPORT("transport"), JS_INVOKE("jsInvoke"), + TBEL_INVOKE("tbelInvoke"), RATE_EXECUTOR("rateExecutor"), HOUSEKEEPER("housekeeper"), EDGE("edge"), diff --git a/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json b/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json index e04a5c2d84..15ff06ab8c 100644 --- a/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json +++ b/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json @@ -223,8 +223,8 @@ "fill": 1, "fillGradient": 0, "gridPos": { - "h": 12, - "w": 24, + "h": 10, + "w": 12, "x": 0, "y": 10 }, @@ -303,6 +303,100 @@ "align": false, "alignLevel": null } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 10 + }, + "hiddenSeries": false, + "id": 19, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.4", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "sum by(statsName) (increase(tbelInvoke_total[1m]))", + "interval": "", + "legendFormat": "{{statsName}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "TbelInvoke Stats", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } } ], "schemaVersion": 27, diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index e473df4c54..b66c9e13d5 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -375,12 +375,6 @@ public interface TbContext { ScriptEngine createScriptEngine(ScriptLanguage scriptLang, String script, String... argNames); - void logJsEvalRequest(); - - void logJsEvalResponse(); - - void logJsEvalFailure(); - String getServiceId(); EventLoopGroup getSharedEventLoop(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java index c78bc77d72..6a806e7bc1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java @@ -69,10 +69,8 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode clearAlarm(TbContext ctx, TbMsg msg, Alarm alarm) { - ctx.logJsEvalRequest(); ListenableFuture asyncDetails = buildAlarmDetails(msg, alarm.getDetails()); return Futures.transform(asyncDetails, details -> { - ctx.logJsEvalResponse(); AlarmApiCallResult result = ctx.getAlarmService().clearAlarm(ctx.getTenantId(), alarm.getId(), System.currentTimeMillis(), details); if (result.isSuccessful()) { return new TbAlarmResult(false, false, result.isCleared(), result.getAlarm()); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java index 837c5ba692..78411d7cec 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java @@ -118,15 +118,11 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode asyncDetails; boolean buildDetails = !config.isUseMessageAlarmData() || config.isOverwriteAlarmDetails(); if (buildDetails) { - ctx.logJsEvalRequest(); asyncDetails = buildAlarmDetails(msg, null); } else { asyncDetails = Futures.immediateFuture(null); } ListenableFuture asyncAlarm = Futures.transform(asyncDetails, details -> { - if (buildDetails) { - ctx.logJsEvalResponse(); - } Alarm newAlarm; if (msgAlarm != null) { newAlarm = msgAlarm; @@ -147,15 +143,11 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode asyncDetails; boolean buildDetails = !config.isUseMessageAlarmData() || config.isOverwriteAlarmDetails(); if (buildDetails) { - ctx.logJsEvalRequest(); asyncDetails = buildAlarmDetails(msg, existingAlarm.getDetails()); } else { asyncDetails = Futures.immediateFuture(null); } ListenableFuture asyncUpdated = Futures.transform(asyncDetails, details -> { - if (buildDetails) { - ctx.logJsEvalResponse(); - } if (msgAlarm != null) { existingAlarm.setSeverity(msgAlarm.getSeverity()); existingAlarm.setPropagate(msgAlarm.isPropagate()); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java index 7e241832cf..47ecac154f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java @@ -75,18 +75,15 @@ public class TbLogNode implements TbNode { return; } - ctx.logJsEvalRequest(); Futures.addCallback(scriptEngine.executeToStringAsync(msg), new FutureCallback() { @Override public void onSuccess(@Nullable String result) { - ctx.logJsEvalResponse(); log.info(result); ctx.tellSuccess(msg); } @Override public void onFailure(Throwable t) { - ctx.logJsEvalResponse(); ctx.tellFailure(msg, t); } }, MoreExecutors.directExecutor()); //usually js responses runs on js callback executor diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index 2848e7c45a..90ee0c9048 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -160,10 +160,8 @@ public class TbMsgGeneratorNode implements TbNode { prevMsg = ctx.newMsg(queueName, TbMsg.EMPTY_STRING, originatorId, msg.getCustomerId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); } if (initialized.get()) { - ctx.logJsEvalRequest(); return Futures.transformAsync(scriptEngine.executeGenerateAsync(prevMsg), generated -> { log.trace("generate process response, generated {}, config {}", generated, config); - ctx.logJsEvalResponse(); prevMsg = ctx.newMsg(queueName, generated.getType(), originatorId, msg.getCustomerId(), generated.getMetaData(), generated.getData()); return Futures.immediateFuture(prevMsg); }, MoreExecutors.directExecutor()); //usually it runs on js-executor-remote-callback thread pool diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java index 50f168a5af..e36494a2b6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java @@ -60,15 +60,12 @@ public class TbJsFilterNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - ctx.logJsEvalRequest(); withCallback(scriptEngine.executeFilterAsync(msg), filterResult -> { - ctx.logJsEvalResponse(); ctx.tellNext(msg, filterResult ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE); }, t -> { ctx.tellFailure(msg, t); - ctx.logJsEvalFailure(); }, ctx.getDbCallbackExecutor()); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java index 3c27740c84..e039eaeb14 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java @@ -61,17 +61,14 @@ public class TbJsSwitchNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - ctx.logJsEvalRequest(); Futures.addCallback(scriptEngine.executeSwitchAsync(msg), new FutureCallback<>() { @Override public void onSuccess(@Nullable Set result) { - ctx.logJsEvalResponse(); processSwitch(ctx, msg, result); } @Override public void onFailure(Throwable t) { - ctx.logJsEvalFailure(); ctx.tellFailure(msg, t); } }, MoreExecutors.directExecutor()); //usually runs in a callbackExecutor diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java index 74284ed207..9373149cf5 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java @@ -57,13 +57,11 @@ public class TbTransformMsgNode extends TbAbstractTransformNode> transform(TbContext ctx, TbMsg msg) { - ctx.logJsEvalRequest(); return scriptEngine.executeUpdateAsync(msg); } @Override protected void transformFailure(TbContext ctx, TbMsg msg, Throwable t) { - ctx.logJsEvalFailure(); super.transformFailure(ctx, msg, t); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java index 1d4c55a780..6cb8299b67 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java @@ -119,8 +119,8 @@ class TbCreateAlarmNodeTest { delete metadata.prevAlarmDetails; //now metadata is the same as it comes IN this rule node } - - + + return details;"""); assertThat(config.getAlarmDetailsBuildTbel()).isEqualTo(""" \ @@ -131,8 +131,8 @@ class TbCreateAlarmNodeTest { metadata.remove('prevAlarmDetails'); //now metadata is the same as it comes IN this rule node } - - + + return details;"""); assertThat(config.getSeverity()).isEqualTo(AlarmSeverity.CRITICAL.name()); assertThat(config.isPropagate()).isFalse(); @@ -247,9 +247,7 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); then(alarmDetailsScriptMock).should().executeJsonAsync(incomingMsg); - then(ctxMock).should().logJsEvalResponse(); // verify we called createAlarm() with correct AlarmCreateOrUpdateActiveRequest then(alarmServiceMock).should().createAlarm(expectedCreateAlarmRequest); @@ -421,9 +419,7 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); then(alarmDetailsScriptMock).should().executeJsonAsync(incomingMsg); - then(ctxMock).should().logJsEvalResponse(); // verify we called createAlarm() with correct AlarmCreateOrUpdateActiveRequest then(alarmServiceMock).should().createAlarm(expectedCreateAlarmRequest); @@ -616,14 +612,12 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); var dummyMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(alarmDetailsScriptMock).should().executeJsonAsync(dummyMsgCaptor.capture()); TbMsg actualDummyMsg = dummyMsgCaptor.getValue(); assertThat(actualDummyMsg.getType()).isEqualTo(incomingMsg.getType()); assertThat(actualDummyMsg.getData()).isEqualTo(incomingMsg.getData()); assertThat(actualDummyMsg.getMetaData().getData()).containsEntry("prevAlarmDetails", JacksonUtil.toString(oldAlarmDetails)); - then(ctxMock).should().logJsEvalResponse(); // verify we called updateAlarm() with correct AlarmUpdateRequest then(alarmServiceMock).should().updateAlarm(expectedUpdateAlarmRequest); @@ -793,9 +787,7 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script was not evaluated - then(ctxMock).should(never()).logJsEvalRequest(); then(alarmDetailsScriptMock).should(never()).executeJsonAsync(any()); - then(ctxMock).should(never()).logJsEvalResponse(); // verify we called createAlarm() with correct AlarmCreateOrUpdateActiveRequest then(alarmServiceMock).should().createAlarm(expectedCreateAlarmRequest); @@ -985,14 +977,12 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); var dummyMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(alarmDetailsScriptMock).should().executeJsonAsync(dummyMsgCaptor.capture()); TbMsg actualDummyMsg = dummyMsgCaptor.getValue(); assertThat(actualDummyMsg.getType()).isEqualTo(incomingMsg.getType()); assertThat(actualDummyMsg.getData()).isEqualTo(incomingMsg.getData()); assertThat(actualDummyMsg.getMetaData().getData()).containsEntry("prevAlarmDetails", JacksonUtil.toString(oldAlarmDetails)); - then(ctxMock).should().logJsEvalResponse(); // verify we called updateAlarm() with correct AlarmUpdateRequest then(alarmServiceMock).should().updateAlarm(expectedUpdateAlarmRequest); @@ -1171,14 +1161,12 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); var dummyMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(alarmDetailsScriptMock).should().executeJsonAsync(dummyMsgCaptor.capture()); TbMsg actualDummyMsg = dummyMsgCaptor.getValue(); assertThat(actualDummyMsg.getType()).isEqualTo(incomingMsg.getType()); assertThat(actualDummyMsg.getData()).isEqualTo(incomingMsg.getData()); assertThat(actualDummyMsg.getMetaData().getData()).containsEntry("prevAlarmDetails", JacksonUtil.toString(alarmDetails)); - then(ctxMock).should().logJsEvalResponse(); // verify we called updateAlarm() with correct AlarmUpdateRequest then(alarmServiceMock).should().updateAlarm(expectedUpdateAlarmRequest);