diff --git a/src/main/java/com/nhn/pinpoint/web/service/ApplicationMapServiceImpl.java b/src/main/java/com/nhn/pinpoint/web/service/ApplicationMapServiceImpl.java index 7b9b4e2bd..e43b290ba 100644 --- a/src/main/java/com/nhn/pinpoint/web/service/ApplicationMapServiceImpl.java +++ b/src/main/java/com/nhn/pinpoint/web/service/ApplicationMapServiceImpl.java @@ -130,7 +130,6 @@ public class ApplicationMapServiceImpl implements ApplicationMapService { * @param calleeApplicationName * @param from * @param to - * @param foundApplications * @return */ private Set selectCaller(String calleeApplicationName, short calleeServiceType, long from, long to, Set calleeFoundApplications, Set callerFoundApplications) { @@ -180,6 +179,7 @@ public class ApplicationMapServiceImpl implements ApplicationMapService { Application app = hostApplicationMapDao.findApplicationName(stat.getTo(), from, to); if (app != null) { logger.debug("Application info replaced. {} => {}", stat, app); + stat.setTo(app.getApplicationName()); stat.setToServiceType(app.getServiceType()); return true; diff --git a/src/main/java/com/nhn/pinpoint/web/service/ComplexNodeId.java b/src/main/java/com/nhn/pinpoint/web/service/ComplexNodeId.java new file mode 100644 index 000000000..205d76554 --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/service/ComplexNodeId.java @@ -0,0 +1,73 @@ +package com.nhn.pinpoint.web.service; + +/** + * @author emeroad + */ +public class ComplexNodeId implements NodeId { + private Node src; + private Node dest; + + public ComplexNodeId(Node src, Node dest) { + if (src == null) { + throw new NullPointerException("src must not be null"); + } + if (dest == null) { + throw new NullPointerException("dest must not be null"); + } + this.src = src; + this.dest = dest; + } + + public Node getSrc() { + return src; + } + + public void setSrc(Node src) { + if (src == null) { + throw new NullPointerException("src must not be null"); + } + + this.src = src; + } + + public Node getDest() { + return dest; + } + + public void setDest(Node dest) { + if (dest == null) { + throw new NullPointerException("dest must not be null"); + } + + this.dest = dest; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ComplexNodeId that = (ComplexNodeId) o; + + if (!dest.equals(that.dest)) return false; + if (!src.equals(that.src)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = src.hashCode(); + result = 31 * result + dest.hashCode(); + return result; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ComplexNodeId{"); + sb.append("src=").append(src); + sb.append(", dest=").append(dest); + sb.append('}'); + return sb.toString(); + } +} diff --git a/src/main/java/com/nhn/pinpoint/web/service/FilteredApplicationMapServiceImpl.java b/src/main/java/com/nhn/pinpoint/web/service/FilteredApplicationMapServiceImpl.java index 88bb3c2c3..3e7410e1d 100755 --- a/src/main/java/com/nhn/pinpoint/web/service/FilteredApplicationMapServiceImpl.java +++ b/src/main/java/com/nhn/pinpoint/web/service/FilteredApplicationMapServiceImpl.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.collections.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -85,22 +86,15 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap StopWatch watch = new StopWatch(); watch.start(); - List> transactionList = this.traceDao.selectAllSpans(traceIdSet); - List transaction = new ArrayList(); - for (List t : transactionList) { - if (filter.include(t)) { - for (SpanBo span : t) { - transaction.add(span); - } - } - } + List> originalList = this.traceDao.selectAllSpans(traceIdSet); + List filteredTransactionList = filterList(originalList, filter); LinkStatistics statistics = new LinkStatistics(from, to); // TODO fromToFilter처럼. node의 타입에 따른 처리 필요함. // scan transaction list - for (SpanBo span : transaction) { + for (SpanBo span : filteredTransactionList) { if (srcApplicationName.equals(span.getApplicationId()) && srcServiceType == span.getServiceType().getCode()) { List spanEventBoList = span.getSpanEventBoList(); if (spanEventBoList == null) { @@ -127,7 +121,27 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap return statistics; } - @Override + private List filterList(List> transactionList, Filter filter) { + final List filteredResult = new ArrayList(); + for (List transaction : transactionList) { + if (filter.include(transaction)) { + filteredResult.addAll(transaction); + } + } + return filteredResult; + } + + private List> filterList2(List> transactionList, Filter filter) { + final List> filteredResult = new ArrayList>(); + for (List transaction : transactionList) { + if (filter.include(transaction)) { + filteredResult.add(transaction); + } + } + return filteredResult; + } + + @Override public ApplicationMap selectApplicationMap(TransactionId transactionId) { if (transactionId == null) { throw new NullPointerException("transactionId must not be null"); @@ -155,126 +169,58 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap // 개별 객체를 각각 보고 재귀 내용을 삭제함. // 향후 tree base로 충돌구간을 점검하여 없앨 경우 여기서 filter를 치면 안됨. - Collection filterdList = recursiveCallFilter(transactionIdList); - + final Collection recursiveFilterList = recursiveCallFilter(transactionIdList); // FIXME 나중에 List을 순회하면서 실행할 process chain을 두는것도 괜찮을듯. - List> transactionList = this.traceDao.selectAllSpans(filterdList); + final List> originalList = this.traceDao.selectAllSpans(recursiveFilterList); + final List> filterList = filterList2(originalList, filter); - Set statisticsData = new HashSet(); - Map statisticsMap = new HashMap(); - Map transactionSpanMap = new HashMap(); + Set statisticsData = new HashSet(); + Map statisticsMap = new HashMap(); - TimeSeriesStore tr = new TimeSeriesStoreImpl2(from, to); + final TimeSeriesStore timeSeriesStore = new TimeSeriesStoreImpl2(from, to); /** * 통계정보로 변환한다. */ - for (List transaction : transactionList) { - if (!filter.include(transaction)) { - continue; - } - - transactionSpanMap.clear(); + for (List transaction : filterList) { + final Map transactionSpanMap = new HashMap(transactionIdList.size()); for (SpanBo span : transaction) { - transactionSpanMap.put(span.getSpanId(), span); - } + final SpanBo old = transactionSpanMap.put(span.getSpanId(), span); + logger.warn("duplicated span found:{}", old); + } for (SpanBo span : transaction) { - String src; - String dest; - ServiceType srcServiceType; - ServiceType destServiceType; - SpanBo parentSpan = transactionSpanMap.get(span.getParentSpanId()); - - if (span.isRoot() || parentSpan == null) { - src = span.getApplicationId(); - srcServiceType = ServiceType.CLIENT; - } else { - src = parentSpan.getApplicationId(); - srcServiceType = parentSpan.getServiceType(); - } - - dest = span.getApplicationId(); - destServiceType = span.getServiceType(); - - if (!destServiceType.isRecordStatistics() || destServiceType.isRpcClient()) { + final Node srcNode = createNode(span, transactionSpanMap); + final Node destNode = new Node(span.getApplicationId(), span.getServiceType()); + // TODO API의 의미가 link라고 하기는 좀 애매함. 변경필요. + if (destNode.isLink()) { continue; } - String statId = TransactionFlowStatisticsUtils.makeId(src, srcServiceType, dest, destServiceType); - TransactionFlowStatistics stat = (statisticsMap.containsKey(statId) ? statisticsMap.get(statId) : new TransactionFlowStatistics(src, srcServiceType, dest, destServiceType)); + final ComplexNodeId statId = new ComplexNodeId(srcNode, destNode); + TransactionFlowStatistics stat; + if (statisticsMap.containsKey(statId)) { + stat = statisticsMap.get(statId); + } + else { + stat = new TransactionFlowStatistics(srcNode.getName(), srcNode.getServiceType(), destNode.getName(), destNode.getServiceType()); + } - int slot; - if (span.hasException()) { - slot = Histogram.ERROR_SLOT.getSlotTime(); - } else { - slot = destServiceType.getHistogram().findHistogramSlot(span.getElapsed()).getSlotTime(); - } + final int slot = getHistogramSlot(span, destNode.getServiceType()); - stat.addSample(dest, destServiceType.getCode(), (short) slot, 1); + stat.addSample(destNode.getName(), destNode.getServiceType().getCode(), (short) slot, 1); statisticsData.add(stat); statisticsMap.put(statId, stat); // link timeseries statistics추가. - tr.add(statId, span.getCollectorAcceptTime(), slot, 1L, span.hasException()); + timeSeriesStore.add(statId, span.getCollectorAcceptTime(), slot, 1L, span.hasException()); // application timeseries statistics - tr.add(span.getApplicationId(), span.getCollectorAcceptTime(), slot, 1L, span.hasException()); + timeSeriesStore.add(new SimpleNodeId(span.getApplicationId()), span.getCollectorAcceptTime(), slot, 1L, span.hasException()); - /** - * span event의 statistics추가. - */ - List spanEventBoList = span.getSpanEventBoList(); - if (spanEventBoList == null || spanEventBoList.isEmpty()) { - continue; - } - src = span.getApplicationId(); - srcServiceType = span.getServiceType(); - - for (SpanEventBo spanEvent : spanEventBoList) { - dest = spanEvent.getDestinationId(); - destServiceType = spanEvent.getServiceType(); - - if (!destServiceType.isRecordStatistics() /*|| destServiceType.isRpcClient()*/) { - continue; - } - - // rpc client이면서 acceptor가 없으면 unknown으로 변환시킨다. - // 내가 아는 next spanid를 spanid로 가진 span이 있으면 acceptor가 존재하는 셈. - if (destServiceType.isRpcClient()) { - if (transactionSpanMap.containsKey(spanEvent.getNextSpanId())) { - continue; - } else { - destServiceType = ServiceType.UNKNOWN_CLOUD; - } - } - - String statId2 = TransactionFlowStatisticsUtils.makeId(src, srcServiceType, dest, destServiceType); - TransactionFlowStatistics stat2 = (statisticsMap.containsKey(statId2) ? statisticsMap.get(statId2) : new TransactionFlowStatistics(src, srcServiceType, dest, destServiceType)); - - int slot2; - if (spanEvent.hasException()) { - slot2 = Histogram.ERROR_SLOT.getSlotTime(); - } else { - slot2 = destServiceType.getHistogram().findHistogramSlot(spanEvent.getEndElapsed()).getSlotTime(); - } - - // FIXME - // stat2.addSample((dest == null) ? spanEvent.getEndPoint() : dest, destServiceType.getCode(), (short) slot2, 1); - stat2.addSample(spanEvent.getEndPoint(), destServiceType.getCode(), (short) slot2, 1); - - // agent 정보추가. destination의 agent정보 알 수 없음. - statisticsData.add(stat2); - statisticsMap.put(statId2, stat2); - - // link timeseries statistics추가. - tr.add(statId2, span.getStartTime() + spanEvent.getStartElapsed(), slot2, 1L, spanEvent.hasException()); - - // application timeseries statistics - tr.add(spanEvent.getDestinationId(), span.getCollectorAcceptTime(), slot2, 1L, spanEvent.hasException()); - } - } + addNodeFromSpanEvent(statisticsData, statisticsMap, timeSeriesStore, transactionSpanMap, span); + } } // mark agent info @@ -283,7 +229,7 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap } ApplicationMap map = new ApplicationMap(statisticsData).build(); - map.setTimeSeriesStore(tr); + map.setTimeSeriesStore(timeSeriesStore); watch.stop(); logger.debug("Select filtered application map elapsed. {}ms", watch.getTotalTimeMillis()); @@ -291,7 +237,93 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap return map; } - private Collection recursiveCallFilter(List transactionIdList) { + + + + private void addNodeFromSpanEvent(Set statisticsData, Map statisticsMap, TimeSeriesStore timeSeriesStore, Map transactionSpanMap, SpanBo span) { + /** + * span event의 statistics추가. + */ + final List spanEventBoList = span.getSpanEventBoList(); + if (CollectionUtils.isEmpty(spanEventBoList)) { + return; + } + final Node srcNode = new Node(span.getApplicationId(), span.getServiceType()); + + for (SpanEventBo spanEvent : spanEventBoList) { + final String dest = spanEvent.getDestinationId(); + ServiceType destServiceType = spanEvent.getServiceType(); + + if (!destServiceType.isRecordStatistics() /*|| destServiceType.isRpcClient()*/) { + continue; + } + + // rpc client이면서 acceptor가 없으면 unknown으로 변환시킨다. + // 내가 아는 next spanid를 spanid로 가진 span이 있으면 acceptor가 존재하는 셈. + if (destServiceType.isRpcClient()) { + if (transactionSpanMap.containsKey(spanEvent.getNextSpanId())) { + continue; + } else { + destServiceType = ServiceType.UNKNOWN_CLOUD; + } + } + + final NodeId spanEventStatId = new ComplexNodeId(srcNode, new Node(dest, destServiceType)); + TransactionFlowStatistics stat2; + if (statisticsMap.containsKey(spanEventStatId)) { + stat2 = statisticsMap.get(spanEventStatId); + } else { + stat2 = new TransactionFlowStatistics(srcNode.getName(), srcNode.getServiceType(), dest, destServiceType); + } + + final int slot2 = getHistogramSlot(spanEvent, destServiceType); + + // FIXME + // stat2.addSample((dest == null) ? spanEvent.getEndPoint() : dest, destServiceType.getCode(), (short) slot2, 1); + stat2.addSample(spanEvent.getEndPoint(), destServiceType.getCode(), (short) slot2, 1); + + // agent 정보추가. destination의 agent정보 알 수 없음. + statisticsData.add(stat2); + statisticsMap.put(spanEventStatId, stat2); + + // link timeseries statistics추가. + timeSeriesStore.add(spanEventStatId, span.getStartTime() + spanEvent.getStartElapsed(), slot2, 1L, spanEvent.hasException()); + + // application timeseries statistics + timeSeriesStore.add(new SimpleNodeId(spanEvent.getDestinationId()), span.getCollectorAcceptTime(), slot2, 1L, spanEvent.hasException()); + } + } + + private Node createNode(SpanBo span, Map transactionSpanMap) { + SpanBo parentSpan = transactionSpanMap.get(span.getParentSpanId()); + if (span.isRoot() || parentSpan == null) { + String src = span.getApplicationId(); + ServiceType srcServiceType = ServiceType.CLIENT; + return new Node(src, srcServiceType); + } else { + String src = parentSpan.getApplicationId(); + ServiceType serviceType = parentSpan.getServiceType(); + return new Node(src, serviceType); + } + } + + private int getHistogramSlot(SpanEventBo spanEvent, ServiceType serviceType) { + return getHistogramSlot(spanEvent.hasException(), spanEvent.getEndElapsed(), serviceType); + } + + private int getHistogramSlot(SpanBo span, ServiceType serviceType) { + return getHistogramSlot(span.hasException(), span.getElapsed(), serviceType); + } + + private int getHistogramSlot(boolean hasException, int elapsedTime, ServiceType serviceType) { + if (hasException) { + return Histogram.ERROR_SLOT.getSlotTime(); + } else { + return serviceType.getHistogram().findHistogramSlot(elapsedTime).getSlotTime(); + } + } + + private Collection recursiveCallFilter(List transactionIdList) { if (transactionIdList == null) { throw new NullPointerException("transactionIdList must not be null"); } @@ -335,4 +367,5 @@ public class FilteredApplicationMapServiceImpl implements FilteredApplicationMap } return agentSet; } + } diff --git a/src/main/java/com/nhn/pinpoint/web/service/Node.java b/src/main/java/com/nhn/pinpoint/web/service/Node.java new file mode 100644 index 000000000..2f3ed8514 --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/service/Node.java @@ -0,0 +1,79 @@ +package com.nhn.pinpoint.web.service; + +import com.nhn.pinpoint.common.ServiceType; + +/** + * @author emeroad + */ +public class Node { + private String name; + private ServiceType serviceType; + + public Node(String name, ServiceType serviceType) { + if (name == null) { + throw new NullPointerException("name must not be null"); + } + if (serviceType == null) { + throw new NullPointerException("serviceType must not be null"); + } + this.name = name; + this.serviceType = serviceType; + } + + public String getName() { + return name; + } + + public void setName(String name) { + if (name == null) { + throw new NullPointerException("name must not be null"); + } + this.name = name; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public void setServiceType(ServiceType serviceType) { + if (serviceType == null) { + throw new NullPointerException("serviceType must not be null"); + } + + this.serviceType = serviceType; + } + + public boolean isLink() { + // record해야 되거나. rpc콜은 링크이다. + return !serviceType.isRecordStatistics() || serviceType.isRpcClient(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Node node = (Node) o; + + if (!name.equals(node.name)) return false; + if (serviceType != node.serviceType) return false; + + return true; + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + serviceType.hashCode(); + return result; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Node{"); + sb.append("name='").append(name).append('\''); + sb.append(", serviceType=").append(serviceType); + sb.append('}'); + return sb.toString(); + } +} diff --git a/src/main/java/com/nhn/pinpoint/web/service/NodeId.java b/src/main/java/com/nhn/pinpoint/web/service/NodeId.java new file mode 100644 index 000000000..bf17bd2d8 --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/service/NodeId.java @@ -0,0 +1,7 @@ +package com.nhn.pinpoint.web.service; + +/** + * @author emeroad + */ +public interface NodeId { +} diff --git a/src/main/java/com/nhn/pinpoint/web/service/SimpleNodeId.java b/src/main/java/com/nhn/pinpoint/web/service/SimpleNodeId.java new file mode 100644 index 000000000..0eb3a6648 --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/service/SimpleNodeId.java @@ -0,0 +1,44 @@ +package com.nhn.pinpoint.web.service; + +/** + * @author emeroad + */ +public class SimpleNodeId implements NodeId{ + private String key; + + public SimpleNodeId(String key) { + if (key == null) { + throw new NullPointerException("key must not be null"); + } + this.key = key; + } + + public String getKey() { + return key; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SimpleNodeId that = (SimpleNodeId) o; + + if (!key.equals(that.key)) return false; + + return true; + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("SimpleNodeId{"); + sb.append("key='").append(key).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/src/main/java/com/nhn/pinpoint/web/util/Mergeable.java b/src/main/java/com/nhn/pinpoint/web/util/Mergeable.java index 59bf3cfaf..8249756d9 100644 --- a/src/main/java/com/nhn/pinpoint/web/util/Mergeable.java +++ b/src/main/java/com/nhn/pinpoint/web/util/Mergeable.java @@ -1,5 +1,7 @@ package com.nhn.pinpoint.web.util; +import com.nhn.pinpoint.web.service.NodeId; + /** * * @author netspider diff --git a/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStore.java b/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStore.java index a7d44c61d..3ea183f45 100644 --- a/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStore.java +++ b/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStore.java @@ -1,5 +1,6 @@ package com.nhn.pinpoint.web.vo; +import com.nhn.pinpoint.web.service.NodeId; import com.nhn.pinpoint.web.util.JsonSerializable; /** @@ -8,5 +9,5 @@ import com.nhn.pinpoint.web.util.JsonSerializable; * */ public interface TimeSeriesStore extends JsonSerializable { - void add(String key, long timestamp, int responseTimeslot, long callCount, boolean isFailed); + void add(NodeId key, long timestamp, int responseTimeslot, long callCount, boolean isFailed); } diff --git a/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStoreImpl1.java b/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStoreImpl1.java index 267c30095..000f9a1eb 100644 --- a/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStoreImpl1.java +++ b/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStoreImpl1.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import com.nhn.pinpoint.web.service.NodeId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +46,7 @@ public class TimeSeriesStoreImpl1 implements TimeSeriesStore { /** * key = id value = value list */ - private final Map> values = new HashMap>(); + private final Map> values = new HashMap>(); private final long from; private final long to; @@ -61,7 +62,7 @@ public class TimeSeriesStoreImpl1 implements TimeSeriesStore { } @Override - public void add(String key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) { + public void add(NodeId key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) { } }; @@ -93,7 +94,7 @@ public class TimeSeriesStoreImpl1 implements TimeSeriesStore { return list; } - public void add(String key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) { + public void add(NodeId key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) { logger.debug("add sample key={}, timestamp={} responseTimeSlot={}, count={}", key, timestamp, responseTimeslot, callCount, isFailed); List list = values.get(key); @@ -117,9 +118,9 @@ public class TimeSeriesStoreImpl1 implements TimeSeriesStore { } StringBuilder sb = new StringBuilder(); sb.append("{\"values\":{"); - Iterator>> entryIterator = values.entrySet().iterator(); + Iterator>> entryIterator = values.entrySet().iterator(); while (entryIterator.hasNext()) { - Entry> entry = entryIterator.next(); + Entry> entry = entryIterator.next(); sb.append("\"").append(entry.getKey()).append("\":["); Iterator valueIterator = entry.getValue().iterator(); while (valueIterator.hasNext()) { diff --git a/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStoreImpl2.java b/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStoreImpl2.java index 2c032556f..65c7aa3bb 100644 --- a/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStoreImpl2.java +++ b/src/main/java/com/nhn/pinpoint/web/vo/TimeSeriesStoreImpl2.java @@ -3,6 +3,7 @@ package com.nhn.pinpoint.web.vo; import java.util.HashMap; import java.util.Map; +import com.nhn.pinpoint.web.service.NodeId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,7 +20,7 @@ public class TimeSeriesStoreImpl2 implements TimeSeriesStore { private final long from; private final long to; - private final Map data = new HashMap(); + private final Map data = new HashMap(); private final boolean enabled; public TimeSeriesStoreImpl2(long from, long to) { @@ -33,7 +34,7 @@ public class TimeSeriesStoreImpl2 implements TimeSeriesStore { } @Override - public void add(String key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) { + public void add(NodeId key, long timestamp, int responseTimeslot, long callCount, boolean isFailed) { if (!enabled) { logger.debug("store is not enabled."); return;