12 changed files with 588 additions and 27 deletions
@ -0,0 +1,117 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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.housekeeper.stats; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.scheduling.annotation.Scheduled; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.stats.DefaultCounter; |
|||
import org.thingsboard.server.common.stats.StatsCounter; |
|||
import org.thingsboard.server.common.stats.StatsFactory; |
|||
import org.thingsboard.server.common.stats.StatsType; |
|||
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask; |
|||
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType; |
|||
import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.EnumMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class HousekeeperStatsService { |
|||
|
|||
private final Map<HousekeeperTaskType, HousekeeperStats> stats = new EnumMap<>(HousekeeperTaskType.class); |
|||
|
|||
public HousekeeperStatsService(StatsFactory statsFactory) { |
|||
for (HousekeeperTaskType taskType : HousekeeperTaskType.values()) { |
|||
stats.put(taskType, new HousekeeperStats(taskType, statsFactory)); |
|||
} |
|||
} |
|||
|
|||
@Scheduled(initialDelay = 60, fixedDelay = 60, timeUnit = TimeUnit.SECONDS) |
|||
private void reportStats() { |
|||
String statsStr = stats.values().stream().map(stats -> { |
|||
String countersStr = stats.getCounters().stream() |
|||
.filter(counter -> counter.get() > 0) |
|||
.map(counter -> counter.getName() + " = " + counter.get()) |
|||
.collect(Collectors.joining(", ")); |
|||
if (countersStr.isEmpty()) { |
|||
return null; |
|||
} else { |
|||
return stats.getTaskType() + " {" + countersStr + "}"; |
|||
} |
|||
}).filter(Objects::nonNull).collect(Collectors.joining("; ")); |
|||
|
|||
if (!statsStr.isEmpty()) { |
|||
stats.values().forEach(HousekeeperStats::reset); |
|||
log.info("Housekeeper stats: {}", statsStr); |
|||
} |
|||
} |
|||
|
|||
public void reportProcessed(HousekeeperTask task, ToHousekeeperServiceMsg msg) { |
|||
HousekeeperStats stats = this.stats.get(task.getTaskType()); |
|||
if (msg.getTask().getErrorsCount() == 0) { |
|||
stats.getProcessedCounter().increment(); |
|||
} else { |
|||
stats.getReprocessedCounter().increment(); |
|||
} |
|||
} |
|||
|
|||
public void reportFailure(HousekeeperTask task, ToHousekeeperServiceMsg msg) { |
|||
HousekeeperStats stats = this.stats.get(task.getTaskType()); |
|||
if (msg.getTask().getErrorsCount() == 0) { |
|||
stats.getFailedProcessingCounter().increment(); |
|||
} else { |
|||
stats.getFailedReprocessingCounter().increment(); |
|||
} |
|||
} |
|||
|
|||
@Getter |
|||
static class HousekeeperStats { |
|||
private final HousekeeperTaskType taskType; |
|||
private final List<StatsCounter> counters = new ArrayList<>(); |
|||
|
|||
private final StatsCounter processedCounter; |
|||
private final StatsCounter failedProcessingCounter; |
|||
private final StatsCounter reprocessedCounter; |
|||
private final StatsCounter failedReprocessingCounter; |
|||
|
|||
public HousekeeperStats(HousekeeperTaskType taskType, StatsFactory statsFactory) { |
|||
this.taskType = taskType; |
|||
this.processedCounter = register("processed", statsFactory); |
|||
this.failedProcessingCounter = register("failedProcessing", statsFactory); |
|||
this.reprocessedCounter = register("reprocessed", statsFactory); |
|||
this.failedReprocessingCounter = register("failedReprocessing", statsFactory); |
|||
} |
|||
|
|||
private StatsCounter register(String statsName, StatsFactory statsFactory) { |
|||
StatsCounter counter = statsFactory.createStatsCounter(StatsType.HOUSEKEEPER.getName(), statsName, Map.of("taskType", taskType.name())); |
|||
counters.add(counter); |
|||
return counter; |
|||
} |
|||
|
|||
public void reset() { |
|||
counters.forEach(DefaultCounter::clear); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,390 @@ |
|||
{ |
|||
"annotations": { |
|||
"list": [ |
|||
{ |
|||
"builtIn": 1, |
|||
"datasource": "-- Grafana --", |
|||
"enable": true, |
|||
"hide": true, |
|||
"iconColor": "rgba(0, 211, 255, 1)", |
|||
"name": "Annotations & Alerts", |
|||
"target": { |
|||
"limit": 100, |
|||
"matchAny": false, |
|||
"tags": [], |
|||
"type": "dashboard" |
|||
}, |
|||
"type": "dashboard" |
|||
} |
|||
] |
|||
}, |
|||
"editable": true, |
|||
"fiscalYearStartMonth": 0, |
|||
"graphTooltip": 0, |
|||
"id": 2, |
|||
"links": [], |
|||
"liveNow": false, |
|||
"panels": [ |
|||
{ |
|||
"fieldConfig": { |
|||
"defaults": { |
|||
"color": { |
|||
"mode": "palette-classic" |
|||
}, |
|||
"custom": { |
|||
"axisLabel": "", |
|||
"axisPlacement": "auto", |
|||
"barAlignment": 0, |
|||
"drawStyle": "line", |
|||
"fillOpacity": 0, |
|||
"gradientMode": "none", |
|||
"hideFrom": { |
|||
"legend": false, |
|||
"tooltip": false, |
|||
"viz": false |
|||
}, |
|||
"lineInterpolation": "smooth", |
|||
"lineWidth": 1, |
|||
"pointSize": 5, |
|||
"scaleDistribution": { |
|||
"type": "linear" |
|||
}, |
|||
"showPoints": "auto", |
|||
"spanNulls": false, |
|||
"stacking": { |
|||
"group": "A", |
|||
"mode": "none" |
|||
}, |
|||
"thresholdsStyle": { |
|||
"mode": "off" |
|||
} |
|||
}, |
|||
"decimals": 0, |
|||
"mappings": [], |
|||
"thresholds": { |
|||
"mode": "absolute", |
|||
"steps": [ |
|||
{ |
|||
"color": "green", |
|||
"value": null |
|||
}, |
|||
{ |
|||
"color": "red", |
|||
"value": 80 |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
"overrides": [] |
|||
}, |
|||
"gridPos": { |
|||
"h": 10, |
|||
"w": 12, |
|||
"x": 0, |
|||
"y": 0 |
|||
}, |
|||
"id": 2, |
|||
"options": { |
|||
"legend": { |
|||
"calcs": [], |
|||
"displayMode": "list", |
|||
"placement": "bottom" |
|||
}, |
|||
"tooltip": { |
|||
"mode": "single", |
|||
"sort": "none" |
|||
} |
|||
}, |
|||
"targets": [ |
|||
{ |
|||
"datasource": { |
|||
"type": "prometheus", |
|||
"uid": "9BonzvTSz" |
|||
}, |
|||
"exemplar": true, |
|||
"expr": "sum by (taskType) (increase(housekeeper_total{instance=\"192.168.3.27:8080\",statsName=\"processed\"}[1m]))", |
|||
"interval": "", |
|||
"legendFormat": "{{taskType}}", |
|||
"refId": "A" |
|||
} |
|||
], |
|||
"title": "Processed", |
|||
"type": "timeseries" |
|||
}, |
|||
{ |
|||
"fieldConfig": { |
|||
"defaults": { |
|||
"color": { |
|||
"mode": "palette-classic" |
|||
}, |
|||
"custom": { |
|||
"axisLabel": "", |
|||
"axisPlacement": "auto", |
|||
"barAlignment": 0, |
|||
"drawStyle": "line", |
|||
"fillOpacity": 0, |
|||
"gradientMode": "none", |
|||
"hideFrom": { |
|||
"legend": false, |
|||
"tooltip": false, |
|||
"viz": false |
|||
}, |
|||
"lineInterpolation": "smooth", |
|||
"lineWidth": 1, |
|||
"pointSize": 5, |
|||
"scaleDistribution": { |
|||
"type": "linear" |
|||
}, |
|||
"showPoints": "auto", |
|||
"spanNulls": false, |
|||
"stacking": { |
|||
"group": "A", |
|||
"mode": "none" |
|||
}, |
|||
"thresholdsStyle": { |
|||
"mode": "off" |
|||
} |
|||
}, |
|||
"decimals": 0, |
|||
"mappings": [], |
|||
"thresholds": { |
|||
"mode": "absolute", |
|||
"steps": [ |
|||
{ |
|||
"color": "green", |
|||
"value": null |
|||
}, |
|||
{ |
|||
"color": "red", |
|||
"value": 80 |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
"overrides": [] |
|||
}, |
|||
"gridPos": { |
|||
"h": 10, |
|||
"w": 12, |
|||
"x": 12, |
|||
"y": 0 |
|||
}, |
|||
"id": 4, |
|||
"options": { |
|||
"legend": { |
|||
"calcs": [], |
|||
"displayMode": "list", |
|||
"placement": "bottom" |
|||
}, |
|||
"tooltip": { |
|||
"mode": "single", |
|||
"sort": "none" |
|||
} |
|||
}, |
|||
"targets": [ |
|||
{ |
|||
"datasource": { |
|||
"type": "prometheus", |
|||
"uid": "9BonzvTSz" |
|||
}, |
|||
"exemplar": true, |
|||
"expr": "sum by (taskType) (increase(housekeeper_total{instance=\"192.168.3.27:8080\",statsName=\"failedProcessing\"}[1m]))", |
|||
"interval": "", |
|||
"legendFormat": "{{taskType}}", |
|||
"refId": "A" |
|||
} |
|||
], |
|||
"title": "Processing failures", |
|||
"type": "timeseries" |
|||
}, |
|||
{ |
|||
"fieldConfig": { |
|||
"defaults": { |
|||
"color": { |
|||
"mode": "palette-classic" |
|||
}, |
|||
"custom": { |
|||
"axisLabel": "", |
|||
"axisPlacement": "auto", |
|||
"barAlignment": 0, |
|||
"drawStyle": "line", |
|||
"fillOpacity": 0, |
|||
"gradientMode": "none", |
|||
"hideFrom": { |
|||
"legend": false, |
|||
"tooltip": false, |
|||
"viz": false |
|||
}, |
|||
"lineInterpolation": "smooth", |
|||
"lineWidth": 1, |
|||
"pointSize": 5, |
|||
"scaleDistribution": { |
|||
"type": "linear" |
|||
}, |
|||
"showPoints": "auto", |
|||
"spanNulls": false, |
|||
"stacking": { |
|||
"group": "A", |
|||
"mode": "none" |
|||
}, |
|||
"thresholdsStyle": { |
|||
"mode": "off" |
|||
} |
|||
}, |
|||
"decimals": 0, |
|||
"mappings": [], |
|||
"thresholds": { |
|||
"mode": "absolute", |
|||
"steps": [ |
|||
{ |
|||
"color": "green", |
|||
"value": null |
|||
}, |
|||
{ |
|||
"color": "red", |
|||
"value": 80 |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
"overrides": [] |
|||
}, |
|||
"gridPos": { |
|||
"h": 9, |
|||
"w": 12, |
|||
"x": 0, |
|||
"y": 10 |
|||
}, |
|||
"id": 5, |
|||
"options": { |
|||
"legend": { |
|||
"calcs": [], |
|||
"displayMode": "list", |
|||
"placement": "bottom" |
|||
}, |
|||
"tooltip": { |
|||
"mode": "single", |
|||
"sort": "none" |
|||
} |
|||
}, |
|||
"targets": [ |
|||
{ |
|||
"datasource": { |
|||
"type": "prometheus", |
|||
"uid": "9BonzvTSz" |
|||
}, |
|||
"exemplar": true, |
|||
"expr": "sum by (taskType) (increase(housekeeper_total{instance=\"192.168.3.27:8080\",statsName=\"reprocessed\"}[1m]))", |
|||
"interval": "", |
|||
"legendFormat": "{{taskType}}", |
|||
"refId": "A" |
|||
} |
|||
], |
|||
"title": "Reprocessed", |
|||
"type": "timeseries" |
|||
}, |
|||
{ |
|||
"fieldConfig": { |
|||
"defaults": { |
|||
"color": { |
|||
"mode": "palette-classic" |
|||
}, |
|||
"custom": { |
|||
"axisLabel": "", |
|||
"axisPlacement": "auto", |
|||
"barAlignment": 0, |
|||
"drawStyle": "line", |
|||
"fillOpacity": 0, |
|||
"gradientMode": "none", |
|||
"hideFrom": { |
|||
"legend": false, |
|||
"tooltip": false, |
|||
"viz": false |
|||
}, |
|||
"lineInterpolation": "smooth", |
|||
"lineWidth": 1, |
|||
"pointSize": 5, |
|||
"scaleDistribution": { |
|||
"type": "linear" |
|||
}, |
|||
"showPoints": "auto", |
|||
"spanNulls": false, |
|||
"stacking": { |
|||
"group": "A", |
|||
"mode": "none" |
|||
}, |
|||
"thresholdsStyle": { |
|||
"mode": "off" |
|||
} |
|||
}, |
|||
"decimals": 0, |
|||
"mappings": [], |
|||
"thresholds": { |
|||
"mode": "absolute", |
|||
"steps": [ |
|||
{ |
|||
"color": "green", |
|||
"value": null |
|||
}, |
|||
{ |
|||
"color": "red", |
|||
"value": 80 |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
"overrides": [] |
|||
}, |
|||
"gridPos": { |
|||
"h": 9, |
|||
"w": 12, |
|||
"x": 12, |
|||
"y": 10 |
|||
}, |
|||
"id": 6, |
|||
"options": { |
|||
"legend": { |
|||
"calcs": [], |
|||
"displayMode": "list", |
|||
"placement": "bottom" |
|||
}, |
|||
"tooltip": { |
|||
"mode": "single", |
|||
"sort": "none" |
|||
} |
|||
}, |
|||
"targets": [ |
|||
{ |
|||
"datasource": { |
|||
"type": "prometheus", |
|||
"uid": "9BonzvTSz" |
|||
}, |
|||
"exemplar": true, |
|||
"expr": "sum by (taskType) (increase(housekeeper_total{instance=\"192.168.3.27:8080\",statsName=\"failedReprocessing\"}[1m]))", |
|||
"interval": "", |
|||
"legendFormat": "{{taskType}}", |
|||
"refId": "A" |
|||
} |
|||
], |
|||
"title": "Reprocessing failures", |
|||
"type": "timeseries" |
|||
} |
|||
], |
|||
"refresh": "10s", |
|||
"schemaVersion": 35, |
|||
"style": "dark", |
|||
"tags": [], |
|||
"templating": { |
|||
"list": [] |
|||
}, |
|||
"time": { |
|||
"from": "now-15m", |
|||
"to": "now" |
|||
}, |
|||
"timepicker": {}, |
|||
"timezone": "", |
|||
"title": "Housekeeper", |
|||
"uid": "JFJb7voIz", |
|||
"version": 5, |
|||
"weekStart": "" |
|||
} |
|||
Loading…
Reference in new issue