/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 com.alibaba.jstorm.daemon.worker; import backtype.storm.Config; import backtype.storm.tuple.MessageId; import backtype.storm.tuple.Tuple; import com.alibaba.jstorm.client.ConfigExtension; import com.alibaba.jstorm.config.Refreshable; import com.alibaba.jstorm.config.RefreshableComponents; import com.alibaba.jstorm.utils.JStormUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Map; import java.util.Set; /** * manage the global state of topology.debug & topology.debug.recv.tuple & topology.debug.sample.rate on the fly * * @author Jark (wuchong.wc@alibaba-inc.com) */ public class JStormDebugger implements Refreshable { private static Logger LOG = LoggerFactory.getLogger(JStormDebugger.class); private JStormDebugger() { RefreshableComponents.registerRefreshable(this); } private static final JStormDebugger INSTANCE = new JStormDebugger(); public static JStormDebugger getInstance() { return INSTANCE; } public static volatile boolean isDebug = false; public static volatile boolean isDebugRecv = false; public static volatile double sampleRate = 1.0d; // PRECISION represent the sampling rate precision , such as: 10000 -> 0.0001 private static final int PRECISION = 10000; public static boolean isDebug(Long root_id) { return isDebug && sample(root_id); } public static boolean isDebug(Set<Long> rootIds) { return isDebug && sample(rootIds); } public static boolean isDebug(Collection<Tuple> anchors) { return isDebug && sample(anchors); } public static boolean isDebug(Object id) { return isDebug && sample(id); } public static boolean isDebugRecv(MessageId msgId) { return msgId != null && isDebugRecv(msgId.getAnchors()); } public static boolean isDebugRecv(Set<Long> rootIds) { return isDebugRecv && sample(rootIds); } public static boolean isDebugRecv(Collection<Tuple> anchors) { return isDebugRecv && sample(anchors); } public static boolean isDebugRecv(Object id) { return isDebugRecv && sample(id); } /** * the tuple debug logs only output `rate`% the tuples with same rootId, should be logged or not logged together. */ private static boolean sample(Long rootId) { if (Double.compare(sampleRate, 1.0d) >= 0) return true; int mod = (int) (Math.abs(rootId) % PRECISION); int threshold = (int) (sampleRate * PRECISION); return mod < threshold; } /** * one of the rootIds has been chosen , the logs should be output */ private static boolean sample(Set<Long> rootIds) { if (Double.compare(sampleRate, 1.0d) >= 0) return true; int threshold = (int) (sampleRate * PRECISION); for (Long id : rootIds) { int mod = (int) (Math.abs(id) % PRECISION); if (mod < threshold) { return true; } } return false; } /** * one of the tuples has been chosen, the logs should be output */ private static boolean sample(Collection<Tuple> anchors) { if (Double.compare(sampleRate, 1.0d) >= 0) return true; for (Tuple t : anchors) { if (sample(t.getMessageId().getAnchors())) { return true; } } return false; } private static boolean sample(Object id) { if (Double.compare(sampleRate, 1.0d) >= 0) return true; return id != null && id instanceof Long && sample((Long) id); } public static void update(Map conf) { boolean _isDebug = JStormUtils.parseBoolean(conf.get(Config.TOPOLOGY_DEBUG), isDebug); if (_isDebug != isDebug) { isDebug = _isDebug; LOG.info("switch topology.debug to {}", _isDebug); } boolean _isDebugRecv = ConfigExtension.isTopologyDebugRecvTuple(conf); if (_isDebugRecv != isDebugRecv) { isDebugRecv = _isDebugRecv; LOG.info("switch topology.debug.recv.tuple to {}", _isDebug); } double _sampleRate = ConfigExtension.getTopologyDebugSampleRate(conf); if (Double.compare(_sampleRate, sampleRate) != 0) { sampleRate = _sampleRate; LOG.info("switch topology.debug.sample.rate to {}", _sampleRate); } } @Override public void refresh(Map conf) { update(conf); } }