mirror of
https://github.com/valitydev/exporter-business-metrics.git
synced 2024-11-06 09:05:22 +00:00
parent
8a5df7bdef
commit
7a6fd021c7
@ -5,7 +5,9 @@ import dev.vality.exporter.businessmetrics.model.PaymentMetricDto;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@ -13,15 +15,40 @@ public class PaymentAggregatedMetricDtoToPaymentMetricDtoList implements Convert
|
||||
List<PaymentMetricDto>> {
|
||||
@Override
|
||||
public List<PaymentMetricDto> convert(PaymentsAggregatedMetricDto source) {
|
||||
if (source.getStatusCounters().size() != source.getAmountCounters().size()) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Status counters size must be equal to amount counters size! Found %s " +
|
||||
"statuses and %s amounts!",
|
||||
source.getStatusCounters().size(),
|
||||
source.getAmountCounters().size()));
|
||||
List<PaymentMetricDto> result = new ArrayList<>();
|
||||
result.add(createPaymentMetricDto(source, "5m", PaymentsAggregatedMetricDto::getCount5m,
|
||||
PaymentsAggregatedMetricDto::getAmount5m));
|
||||
result.add(createPaymentMetricDto(source, "15m", PaymentsAggregatedMetricDto::getCount15m,
|
||||
PaymentsAggregatedMetricDto::getAmount15m));
|
||||
result.add(createPaymentMetricDto(source, "30m", PaymentsAggregatedMetricDto::getCount30m,
|
||||
PaymentsAggregatedMetricDto::getAmount30m));
|
||||
result.add(createPaymentMetricDto(source, "1h", PaymentsAggregatedMetricDto::getCount1h,
|
||||
PaymentsAggregatedMetricDto::getAmount1h));
|
||||
result.add(createPaymentMetricDto(source, "3h", PaymentsAggregatedMetricDto::getCount3h,
|
||||
PaymentsAggregatedMetricDto::getAmount3h));
|
||||
result.add(createPaymentMetricDto(source, "6h", PaymentsAggregatedMetricDto::getCount6h,
|
||||
PaymentsAggregatedMetricDto::getAmount6h));
|
||||
result.add(createPaymentMetricDto(source, "12h", PaymentsAggregatedMetricDto::getCount12h,
|
||||
PaymentsAggregatedMetricDto::getAmount12h));
|
||||
result.add(createPaymentMetricDto(source, "24h", PaymentsAggregatedMetricDto::getCount24h,
|
||||
PaymentsAggregatedMetricDto::getAmount24h));
|
||||
return result;
|
||||
}
|
||||
|
||||
return source.getStatusCounters().keySet().stream().map(duration -> PaymentMetricDto.builder()
|
||||
private PaymentMetricDto createPaymentMetricDto(PaymentsAggregatedMetricDto source, String duration,
|
||||
Function<PaymentsAggregatedMetricDto,
|
||||
String> getStatusCountFunc,
|
||||
Function<PaymentsAggregatedMetricDto,
|
||||
String> getAmountFunc) {
|
||||
var dto = createCommonPart(source);
|
||||
dto.setDuration(duration);
|
||||
dto.setCount(getStatusCountFunc.apply(source));
|
||||
dto.setAmount(getAmountFunc.apply(source));
|
||||
return dto;
|
||||
}
|
||||
|
||||
private PaymentMetricDto createCommonPart(PaymentsAggregatedMetricDto source) {
|
||||
return PaymentMetricDto.builder()
|
||||
.providerId(source.getProviderId())
|
||||
.providerName(source.getProviderName())
|
||||
.terminalId(source.getTerminalId())
|
||||
@ -30,10 +57,6 @@ public class PaymentAggregatedMetricDtoToPaymentMetricDtoList implements Convert
|
||||
.shopName(source.getShopName())
|
||||
.currencyCode(source.getCurrencyCode())
|
||||
.status(source.getStatus())
|
||||
.duration(duration)
|
||||
.count(source.getStatusCounters().get(duration))
|
||||
.amount(source.getAmountCounters().get(duration))
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -5,23 +5,49 @@ import dev.vality.exporter.businessmetrics.model.WithdrawalMetricDto;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Component
|
||||
public class WithdrawalAggregatedMetricDtoToWithdrawalMetricDtoList implements Converter<WithdrawalsAggregatedMetricDto,
|
||||
List<WithdrawalMetricDto>> {
|
||||
@Override
|
||||
public List<WithdrawalMetricDto> convert(WithdrawalsAggregatedMetricDto source) {
|
||||
if (source.getStatusCounters().size() != source.getAmountCounters().size()) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Status counters size must be equal to amount counters size! Found %s " +
|
||||
"statuses and %s amounts!",
|
||||
source.getStatusCounters().size(),
|
||||
source.getAmountCounters().size()));
|
||||
List<WithdrawalMetricDto> result = new ArrayList<>();
|
||||
result.add(createWithdrawalMetricDto(source, "5m", WithdrawalsAggregatedMetricDto::getCount5m,
|
||||
WithdrawalsAggregatedMetricDto::getAmount5m));
|
||||
result.add(createWithdrawalMetricDto(source, "15m", WithdrawalsAggregatedMetricDto::getCount15m,
|
||||
WithdrawalsAggregatedMetricDto::getAmount15m));
|
||||
result.add(createWithdrawalMetricDto(source, "30m", WithdrawalsAggregatedMetricDto::getCount30m,
|
||||
WithdrawalsAggregatedMetricDto::getAmount30m));
|
||||
result.add(createWithdrawalMetricDto(source, "1h", WithdrawalsAggregatedMetricDto::getCount1h,
|
||||
WithdrawalsAggregatedMetricDto::getAmount1h));
|
||||
result.add(createWithdrawalMetricDto(source, "3h", WithdrawalsAggregatedMetricDto::getCount3h,
|
||||
WithdrawalsAggregatedMetricDto::getAmount3h));
|
||||
result.add(createWithdrawalMetricDto(source, "6h", WithdrawalsAggregatedMetricDto::getCount6h,
|
||||
WithdrawalsAggregatedMetricDto::getAmount6h));
|
||||
result.add(createWithdrawalMetricDto(source, "12h", WithdrawalsAggregatedMetricDto::getCount12h,
|
||||
WithdrawalsAggregatedMetricDto::getAmount12h));
|
||||
result.add(createWithdrawalMetricDto(source, "24h", WithdrawalsAggregatedMetricDto::getCount24h,
|
||||
WithdrawalsAggregatedMetricDto::getAmount24h));
|
||||
return result;
|
||||
}
|
||||
|
||||
return source.getStatusCounters().keySet().stream().map(duration -> WithdrawalMetricDto.builder()
|
||||
private WithdrawalMetricDto createWithdrawalMetricDto(WithdrawalsAggregatedMetricDto source, String duration,
|
||||
Function<WithdrawalsAggregatedMetricDto,
|
||||
String> getStatusCountFunc,
|
||||
Function<WithdrawalsAggregatedMetricDto,
|
||||
String> getAmountFunc) {
|
||||
var dto = createCommonPart(source);
|
||||
dto.setDuration(duration);
|
||||
dto.setCount(getStatusCountFunc.apply(source));
|
||||
dto.setAmount(getAmountFunc.apply(source));
|
||||
return dto;
|
||||
}
|
||||
|
||||
private WithdrawalMetricDto createCommonPart(WithdrawalsAggregatedMetricDto source) {
|
||||
return WithdrawalMetricDto.builder()
|
||||
.providerId(source.getProviderId())
|
||||
.providerName(source.getProviderName())
|
||||
.terminalId(source.getTerminalId())
|
||||
@ -30,10 +56,6 @@ public class WithdrawalAggregatedMetricDtoToWithdrawalMetricDtoList implements C
|
||||
.walletName(source.getWalletName())
|
||||
.currencyCode(source.getCurrencyCode())
|
||||
.status(source.getStatus())
|
||||
.duration(duration)
|
||||
.count(source.getStatusCounters().get(duration))
|
||||
.amount(source.getAmountCounters().get(duration))
|
||||
.build())
|
||||
.collect(Collectors.toList());
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@NamedNativeQuery(
|
||||
name = "getPaymentsStatusMetrics",
|
||||
query = """
|
||||
@ -118,22 +119,22 @@ import java.util.Map;
|
||||
@ColumnResult(name = "shopName", type = String.class),
|
||||
@ColumnResult(name = "currencyCode", type = String.class),
|
||||
@ColumnResult(name = "status", type = String.class),
|
||||
@ColumnResult(name = "count5m", type = String.class),
|
||||
@ColumnResult(name = "amount5m", type = String.class),
|
||||
@ColumnResult(name = "count15m", type = String.class),
|
||||
@ColumnResult(name = "amount15m", type = String.class),
|
||||
@ColumnResult(name = "count30m", type = String.class),
|
||||
@ColumnResult(name = "amount30m", type = String.class),
|
||||
@ColumnResult(name = "count1h", type = String.class),
|
||||
@ColumnResult(name = "amount1h", type = String.class),
|
||||
@ColumnResult(name = "count3h", type = String.class),
|
||||
@ColumnResult(name = "amount3h", type = String.class),
|
||||
@ColumnResult(name = "count6h", type = String.class),
|
||||
@ColumnResult(name = "amount6h", type = String.class),
|
||||
@ColumnResult(name = "count12h", type = String.class),
|
||||
@ColumnResult(name = "amount12h", type = String.class),
|
||||
@ColumnResult(name = "count24h", type = String.class),
|
||||
@ColumnResult(name = "amount24h", type = String.class),}))
|
||||
@ColumnResult(name = "count_5m", type = String.class),
|
||||
@ColumnResult(name = "amount_5m", type = String.class),
|
||||
@ColumnResult(name = "count_15m", type = String.class),
|
||||
@ColumnResult(name = "amount_15m", type = String.class),
|
||||
@ColumnResult(name = "count_30m", type = String.class),
|
||||
@ColumnResult(name = "amount_30m", type = String.class),
|
||||
@ColumnResult(name = "count_1h", type = String.class),
|
||||
@ColumnResult(name = "amount_1h", type = String.class),
|
||||
@ColumnResult(name = "count_3h", type = String.class),
|
||||
@ColumnResult(name = "amount_3h", type = String.class),
|
||||
@ColumnResult(name = "count_6h", type = String.class),
|
||||
@ColumnResult(name = "amount_6h", type = String.class),
|
||||
@ColumnResult(name = "count_12h", type = String.class),
|
||||
@ColumnResult(name = "amount_12h", type = String.class),
|
||||
@ColumnResult(name = "count_24h", type = String.class),
|
||||
@ColumnResult(name = "amount_24h", type = String.class),}))
|
||||
@SuppressWarnings("LineLength")
|
||||
public class PaymentsAggregatedMetricDto {
|
||||
|
||||
@ -147,8 +148,22 @@ public class PaymentsAggregatedMetricDto {
|
||||
private String shopName;
|
||||
private String currencyCode;
|
||||
private String status;
|
||||
private Map<String, String> statusCounters;
|
||||
private Map<String, String> amountCounters;
|
||||
private String count5m;
|
||||
private String amount5m;
|
||||
private String count15m;
|
||||
private String amount15m;
|
||||
private String count30m;
|
||||
private String amount30m;
|
||||
private String count1h;
|
||||
private String amount1h;
|
||||
private String count3h;
|
||||
private String amount3h;
|
||||
private String count6h;
|
||||
private String amount6h;
|
||||
private String count12h;
|
||||
private String amount12h;
|
||||
private String count24h;
|
||||
private String amount24h;
|
||||
|
||||
public PaymentsAggregatedMetricDto(String providerId, String providerName, String terminalId, String terminalName, String shopId, String shopName, String currencyCode, String status,
|
||||
String count5m, String amount5m,
|
||||
@ -167,23 +182,25 @@ public class PaymentsAggregatedMetricDto {
|
||||
this.shopName = shopName;
|
||||
this.currencyCode = currencyCode;
|
||||
this.status = status;
|
||||
this.statusCounters = new HashMap<>();
|
||||
statusCounters.put("5m", count5m);
|
||||
statusCounters.put("15m", count15m);
|
||||
statusCounters.put("30m", count30m);
|
||||
statusCounters.put("1h", count1h);
|
||||
statusCounters.put("3h", count3h);
|
||||
statusCounters.put("6h", count6h);
|
||||
statusCounters.put("12h", count12h);
|
||||
statusCounters.put("24h", count24h);
|
||||
this.amountCounters = new HashMap<>();
|
||||
amountCounters.put("5m", amount5m);
|
||||
amountCounters.put("15m", amount15m);
|
||||
amountCounters.put("30m", amount30m);
|
||||
amountCounters.put("1h", amount1h);
|
||||
amountCounters.put("3h", amount3h);
|
||||
amountCounters.put("6h", amount6h);
|
||||
amountCounters.put("12h", amount12h);
|
||||
amountCounters.put("24h", amount24h);
|
||||
this.count5m = count5m;
|
||||
this.amount5m = amount5m;
|
||||
this.count15m = count15m;
|
||||
this.amount15m = amount15m;
|
||||
this.count30m = count30m;
|
||||
this.amount30m = amount30m;
|
||||
this.count1h = count1h;
|
||||
this.amount1h = amount1h;
|
||||
this.count3h = count3h;
|
||||
this.amount3h = amount3h;
|
||||
this.count6h = count6h;
|
||||
this.amount6h = amount6h;
|
||||
this.count12h = count12h;
|
||||
this.amount12h = amount12h;
|
||||
this.count24h = count24h;
|
||||
this.amount24h = amount24h;
|
||||
}
|
||||
|
||||
public PaymentsAggregatedMetricDto() {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@NamedNativeQuery(
|
||||
name = "getWithdrawalsMetrics",
|
||||
query = """
|
||||
@ -104,22 +105,22 @@ import java.util.Map;
|
||||
@ColumnResult(name = "walletName", type = String.class),
|
||||
@ColumnResult(name = "currencyCode", type = String.class),
|
||||
@ColumnResult(name = "status", type = String.class),
|
||||
@ColumnResult(name = "count5m", type = String.class),
|
||||
@ColumnResult(name = "amount5m", type = String.class),
|
||||
@ColumnResult(name = "count15m", type = String.class),
|
||||
@ColumnResult(name = "amount15m", type = String.class),
|
||||
@ColumnResult(name = "count30m", type = String.class),
|
||||
@ColumnResult(name = "amount30m", type = String.class),
|
||||
@ColumnResult(name = "count1h", type = String.class),
|
||||
@ColumnResult(name = "amount1h", type = String.class),
|
||||
@ColumnResult(name = "count3h", type = String.class),
|
||||
@ColumnResult(name = "amount3h", type = String.class),
|
||||
@ColumnResult(name = "count6h", type = String.class),
|
||||
@ColumnResult(name = "amount6h", type = String.class),
|
||||
@ColumnResult(name = "count12h", type = String.class),
|
||||
@ColumnResult(name = "amount12h", type = String.class),
|
||||
@ColumnResult(name = "count24h", type = String.class),
|
||||
@ColumnResult(name = "amount24h", type = String.class)}))
|
||||
@ColumnResult(name = "count_5m", type = String.class),
|
||||
@ColumnResult(name = "amount_5m", type = String.class),
|
||||
@ColumnResult(name = "count_15m", type = String.class),
|
||||
@ColumnResult(name = "amount_15m", type = String.class),
|
||||
@ColumnResult(name = "count_30m", type = String.class),
|
||||
@ColumnResult(name = "amount_30m", type = String.class),
|
||||
@ColumnResult(name = "count_1h", type = String.class),
|
||||
@ColumnResult(name = "amount_1h", type = String.class),
|
||||
@ColumnResult(name = "count_3h", type = String.class),
|
||||
@ColumnResult(name = "amount_3h", type = String.class),
|
||||
@ColumnResult(name = "count_6h", type = String.class),
|
||||
@ColumnResult(name = "amount_6h", type = String.class),
|
||||
@ColumnResult(name = "count_12h", type = String.class),
|
||||
@ColumnResult(name = "amount_12h", type = String.class),
|
||||
@ColumnResult(name = "count_24h", type = String.class),
|
||||
@ColumnResult(name = "amount_24h", type = String.class)}))
|
||||
@SuppressWarnings("LineLength")
|
||||
public class WithdrawalsAggregatedMetricDto {
|
||||
|
||||
@ -133,8 +134,22 @@ public class WithdrawalsAggregatedMetricDto {
|
||||
private String walletName;
|
||||
private String currencyCode;
|
||||
private String status;
|
||||
private Map<String, String> statusCounters;
|
||||
private Map<String, String> amountCounters;
|
||||
private String count5m;
|
||||
private String amount5m;
|
||||
private String count15m;
|
||||
private String amount15m;
|
||||
private String count30m;
|
||||
private String amount30m;
|
||||
private String count1h;
|
||||
private String amount1h;
|
||||
private String count3h;
|
||||
private String amount3h;
|
||||
private String count6h;
|
||||
private String amount6h;
|
||||
private String count12h;
|
||||
private String amount12h;
|
||||
private String count24h;
|
||||
private String amount24h;
|
||||
|
||||
public WithdrawalsAggregatedMetricDto(String providerId, String providerName, String terminalId,
|
||||
String terminalName, String walletId, String walletName,
|
||||
@ -155,23 +170,25 @@ public class WithdrawalsAggregatedMetricDto {
|
||||
this.walletName = walletName;
|
||||
this.currencyCode = currencyCode;
|
||||
this.status = status;
|
||||
this.statusCounters = new HashMap<>();
|
||||
statusCounters.put("5m", count5m);
|
||||
statusCounters.put("15m", count15m);
|
||||
statusCounters.put("30m", count30m);
|
||||
statusCounters.put("1h", count1h);
|
||||
statusCounters.put("3h", count3h);
|
||||
statusCounters.put("6h", count6h);
|
||||
statusCounters.put("12h", count12h);
|
||||
statusCounters.put("24h", count24h);
|
||||
this.amountCounters = new HashMap<>();
|
||||
amountCounters.put("5m", amount5m);
|
||||
amountCounters.put("15m", amount15m);
|
||||
amountCounters.put("30m", amount30m);
|
||||
amountCounters.put("1h", amount1h);
|
||||
amountCounters.put("3h", amount3h);
|
||||
amountCounters.put("6h", amount6h);
|
||||
amountCounters.put("12h", amount12h);
|
||||
amountCounters.put("24h", amount24h);
|
||||
this.count5m = count5m;
|
||||
this.amount5m = amount5m;
|
||||
this.count15m = count15m;
|
||||
this.amount15m = amount15m;
|
||||
this.count30m = count30m;
|
||||
this.amount30m = amount30m;
|
||||
this.count1h = count1h;
|
||||
this.amount1h = amount1h;
|
||||
this.count3h = count3h;
|
||||
this.amount3h = amount3h;
|
||||
this.count6h = count6h;
|
||||
this.amount6h = amount6h;
|
||||
this.count12h = count12h;
|
||||
this.amount12h = amount12h;
|
||||
this.count24h = count24h;
|
||||
this.amount24h = amount24h;
|
||||
}
|
||||
|
||||
public WithdrawalsAggregatedMetricDto() {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -77,8 +75,9 @@ public class PaymentService {
|
||||
Collectors.mapping(
|
||||
Map.Entry::getValue,
|
||||
Collectors.<MultiGauge.Row<?>>toList())));
|
||||
multiGaugePaymentsFinalStatusCount.register(rows.get(PAYMENTS_STATUS_COUNT), true);
|
||||
multiGaugePaymentsAmount.register(rows.get(PAYMENTS_AMOUNT), true);
|
||||
|
||||
multiGaugePaymentsFinalStatusCount.register(rows.getOrDefault(PAYMENTS_STATUS_COUNT, Collections.emptyList()), true);
|
||||
multiGaugePaymentsAmount.register(rows.getOrDefault(PAYMENTS_AMOUNT, Collections.emptyList()), true);
|
||||
var registeredMetricsSize = meterRegistryService.getRegisteredMetricsSize(Metric.PAYMENTS_STATUS_COUNT.getName()) + meterRegistryService.getRegisteredMetricsSize(Metric.PAYMENTS_AMOUNT.getName());
|
||||
log.info("Payments with final statuses metrics have been registered to 'prometheus', " +
|
||||
"registeredMetricsSize = {}, failedCount = {}, capturedCount = {}, otherStatusCount = {}", registeredMetricsSize, failedCount, capturedCount, otherStatusCount);
|
||||
@ -100,7 +99,7 @@ public class PaymentService {
|
||||
Collectors.mapping(
|
||||
Map.Entry::getValue,
|
||||
Collectors.<MultiGauge.Row<?>>toList())));
|
||||
multiGaugePaymentsTransactionCount.register(rows.get(PAYMENTS_TRANSACTION_COUNT), true);
|
||||
multiGaugePaymentsTransactionCount.register(rows.getOrDefault(PAYMENTS_TRANSACTION_COUNT, Collections.emptyList()), true);
|
||||
var registeredMetricsSize = meterRegistryService.getRegisteredMetricsSize(Metric.PAYMENTS_TRANSACTION_COUNT.getName()) + meterRegistryService.getRegisteredMetricsSize(Metric.PAYMENTS_TRANSACTION_COUNT.getName());
|
||||
log.info("Payments with transaction count metrics have been registered to 'prometheus', registeredMetricsSize = {}", registeredMetricsSize);
|
||||
|
||||
|
@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -64,8 +65,8 @@ public class WithdrawalService {
|
||||
Collectors.mapping(
|
||||
Map.Entry::getValue,
|
||||
Collectors.<MultiGauge.Row<?>>toList())));
|
||||
multiGaugeWithdrawalsFinalStatusCount.register(rows.get(WITHDRAWALS_STATUS_COUNT), true);
|
||||
multiGaugeWithdrawalsAmount.register(rows.get(WITHDRAWALS_AMOUNT), true);
|
||||
multiGaugeWithdrawalsFinalStatusCount.register(rows.getOrDefault(WITHDRAWALS_STATUS_COUNT, Collections.emptyList()), true);
|
||||
multiGaugeWithdrawalsAmount.register(rows.getOrDefault(WITHDRAWALS_AMOUNT, Collections.emptyList()), true);
|
||||
var registeredMetricsSize =
|
||||
meterRegistryService.getRegisteredMetricsSize(Metric.WITHDRAWALS_STATUS_COUNT.getName()) +
|
||||
meterRegistryService.getRegisteredMetricsSize(Metric.WITHDRAWALS_AMOUNT.getName());
|
||||
|
@ -30,7 +30,7 @@ management:
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: '${project.name}'
|
||||
name: 'hahah'
|
||||
output:
|
||||
ansi:
|
||||
enabled: always
|
||||
|
Loading…
Reference in New Issue
Block a user