init
This commit is contained in:
		
							
								
								
									
										72
									
								
								sentinel/sentinel-core/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								sentinel/sentinel-core/pom.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
			
		||||
    <modelVersion>4.0.0</modelVersion>
 | 
			
		||||
 | 
			
		||||
    <parent>
 | 
			
		||||
        <groupId>com.alibaba.csp</groupId>
 | 
			
		||||
        <artifactId>sentinel-parent</artifactId>
 | 
			
		||||
        <version>1.8.3</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
    <artifactId>sentinel-core</artifactId>
 | 
			
		||||
    <packaging>jar</packaging>
 | 
			
		||||
    <description>The core of Sentinel</description>
 | 
			
		||||
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>junit</groupId>
 | 
			
		||||
            <artifactId>junit</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
            <exclusions>
 | 
			
		||||
                <exclusion>
 | 
			
		||||
                    <groupId>org.hamcrest</groupId>
 | 
			
		||||
                    <artifactId>hamcrest-core</artifactId>
 | 
			
		||||
                </exclusion>
 | 
			
		||||
                <exclusion>
 | 
			
		||||
                    <groupId>org.hamcrest</groupId>
 | 
			
		||||
                    <artifactId>hamcrest-libray</artifactId>
 | 
			
		||||
                </exclusion>
 | 
			
		||||
            </exclusions>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.mockito</groupId>
 | 
			
		||||
            <artifactId>mockito-core</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.awaitility</groupId>
 | 
			
		||||
            <artifactId>awaitility</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.hamcrest</groupId>
 | 
			
		||||
            <artifactId>java-hamcrest</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.powermock</groupId>
 | 
			
		||||
            <artifactId>powermock-module-junit4</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.powermock</groupId>
 | 
			
		||||
            <artifactId>powermock-api-mockito2</artifactId>
 | 
			
		||||
            <scope>test</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
    <build>
 | 
			
		||||
        <plugins>
 | 
			
		||||
            <plugin>
 | 
			
		||||
                <groupId>org.apache.maven.plugins</groupId>
 | 
			
		||||
                <artifactId>maven-jar-plugin</artifactId>
 | 
			
		||||
                <configuration>
 | 
			
		||||
                    <archive>
 | 
			
		||||
                        <manifestEntries>
 | 
			
		||||
                            <Implementation-Version>${project.version}</Implementation-Version>
 | 
			
		||||
                        </manifestEntries>
 | 
			
		||||
                    </archive>
 | 
			
		||||
                </configuration>
 | 
			
		||||
            </plugin>
 | 
			
		||||
        </plugins>
 | 
			
		||||
    </build>
 | 
			
		||||
</project>
 | 
			
		||||
@@ -0,0 +1,98 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.NullContext;
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The entry for asynchronous resources.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 0.2.0
 | 
			
		||||
 */
 | 
			
		||||
public class AsyncEntry extends CtEntry {
 | 
			
		||||
 | 
			
		||||
    private Context asyncContext;
 | 
			
		||||
 | 
			
		||||
    AsyncEntry(ResourceWrapper resourceWrapper, ProcessorSlot<Object> chain, Context context) {
 | 
			
		||||
        super(resourceWrapper, chain, context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Remove current entry from local context, but does not exit.
 | 
			
		||||
     */
 | 
			
		||||
    void cleanCurrentEntryInLocal() {
 | 
			
		||||
        if (context instanceof NullContext) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        Context originalContext = context;
 | 
			
		||||
        if (originalContext != null) {
 | 
			
		||||
            Entry curEntry = originalContext.getCurEntry();
 | 
			
		||||
            if (curEntry == this) {
 | 
			
		||||
                Entry parent = this.parent;
 | 
			
		||||
                originalContext.setCurEntry(parent);
 | 
			
		||||
                if (parent != null) {
 | 
			
		||||
                    ((CtEntry)parent).child = null;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                String curEntryName = curEntry == null ? "none"
 | 
			
		||||
                    : curEntry.resourceWrapper.getName() + "@" + curEntry.hashCode();
 | 
			
		||||
                String msg = String.format("Bad async context state, expected entry: %s, but actual: %s",
 | 
			
		||||
                    getResourceWrapper().getName() + "@" + hashCode(), curEntryName);
 | 
			
		||||
                throw new IllegalStateException(msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Context getAsyncContext() {
 | 
			
		||||
        return asyncContext;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The async context should not be initialized until the node for current resource has been set to current entry.
 | 
			
		||||
     */
 | 
			
		||||
    void initAsyncContext() {
 | 
			
		||||
        if (asyncContext == null) {
 | 
			
		||||
            if (context instanceof NullContext) {
 | 
			
		||||
                asyncContext = context;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            this.asyncContext = Context.newAsyncContext(context.getEntranceNode(), context.getName())
 | 
			
		||||
                .setOrigin(context.getOrigin())
 | 
			
		||||
                .setCurEntry(this);
 | 
			
		||||
        } else {
 | 
			
		||||
            RecordLog.warn(
 | 
			
		||||
                "[AsyncEntry] Duplicate initialize of async context for entry: " + resourceWrapper.getName());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void clearEntryContext() {
 | 
			
		||||
        super.clearEntryContext();
 | 
			
		||||
        this.asyncContext = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Entry trueExit(int count, Object... args) throws ErrorEntryFreeException {
 | 
			
		||||
        exitForContext(asyncContext, count, args);
 | 
			
		||||
 | 
			
		||||
        return parent;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,86 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.node.ClusterNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.DefaultNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.EntranceNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.VersionUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Universal constants of Sentinel.
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @author youji.zj
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public final class Constants {
 | 
			
		||||
 | 
			
		||||
    public static final String SENTINEL_VERSION = VersionUtil.getVersion("1.8.3");
 | 
			
		||||
 | 
			
		||||
    public final static int MAX_CONTEXT_NAME_SIZE = 2000;
 | 
			
		||||
    public final static int MAX_SLOT_CHAIN_SIZE = 6000;
 | 
			
		||||
 | 
			
		||||
    public final static String ROOT_ID = "machine-root";
 | 
			
		||||
    public final static String CONTEXT_DEFAULT_NAME = "sentinel_default_context";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A virtual resource identifier for total inbound statistics (since 1.5.0).
 | 
			
		||||
     */
 | 
			
		||||
    public final static String TOTAL_IN_RESOURCE_NAME = "__total_inbound_traffic__";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A virtual resource identifier for cpu usage statistics (since 1.6.1).
 | 
			
		||||
     */
 | 
			
		||||
    public final static String CPU_USAGE_RESOURCE_NAME = "__cpu_usage__";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A virtual resource identifier for system load statistics (since 1.6.1).
 | 
			
		||||
     */
 | 
			
		||||
    public final static String SYSTEM_LOAD_RESOURCE_NAME = "__system_load__";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Global ROOT statistic node that represents the universal parent node.
 | 
			
		||||
     */
 | 
			
		||||
    public final static DefaultNode ROOT = new EntranceNode(new StringResourceWrapper(ROOT_ID, EntryType.IN),
 | 
			
		||||
        new ClusterNode(ROOT_ID, ResourceTypeConstants.COMMON));
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Global statistic node for inbound traffic. Usually used for {@code SystemRule} checking.
 | 
			
		||||
     */
 | 
			
		||||
    public final static ClusterNode ENTRY_NODE = new ClusterNode(TOTAL_IN_RESOURCE_NAME, ResourceTypeConstants.COMMON);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The global switch for Sentinel.
 | 
			
		||||
     */
 | 
			
		||||
    public static volatile boolean ON = true;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Order of default processor slots
 | 
			
		||||
     */
 | 
			
		||||
    public static final int ORDER_NODE_SELECTOR_SLOT = -10000;
 | 
			
		||||
    public static final int ORDER_CLUSTER_BUILDER_SLOT = -9000;
 | 
			
		||||
    public static final int ORDER_LOG_SLOT = -8000;
 | 
			
		||||
    public static final int ORDER_STATISTIC_SLOT = -7000;
 | 
			
		||||
    public static final int ORDER_AUTHORITY_SLOT = -6000;
 | 
			
		||||
    public static final int ORDER_SYSTEM_SLOT = -5000;
 | 
			
		||||
    public static final int ORDER_FLOW_SLOT = -2000;
 | 
			
		||||
    public static final int ORDER_DEGRADE_SLOT = -1000;
 | 
			
		||||
 | 
			
		||||
    private Constants() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,154 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import java.util.LinkedList;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.ContextUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.NullContext;
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.Node;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.function.BiConsumer;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Linked entry within current context.
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
class CtEntry extends Entry {
 | 
			
		||||
 | 
			
		||||
    protected Entry parent = null;
 | 
			
		||||
    protected Entry child = null;
 | 
			
		||||
 | 
			
		||||
    protected ProcessorSlot<Object> chain;
 | 
			
		||||
    protected Context context;
 | 
			
		||||
    protected LinkedList<BiConsumer<Context, Entry>> exitHandlers;
 | 
			
		||||
 | 
			
		||||
    CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot<Object> chain, Context context) {
 | 
			
		||||
        super(resourceWrapper);
 | 
			
		||||
        this.chain = chain;
 | 
			
		||||
        this.context = context;
 | 
			
		||||
 | 
			
		||||
        setUpEntryFor(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setUpEntryFor(Context context) {
 | 
			
		||||
        // The entry should not be associated to NullContext.
 | 
			
		||||
        if (context instanceof NullContext) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.parent = context.getCurEntry();
 | 
			
		||||
        if (parent != null) {
 | 
			
		||||
            ((CtEntry) parent).child = this;
 | 
			
		||||
        }
 | 
			
		||||
        context.setCurEntry(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void exit(int count, Object... args) throws ErrorEntryFreeException {
 | 
			
		||||
        trueExit(count, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Note: the exit handlers will be called AFTER onExit of slot chain.
 | 
			
		||||
     */
 | 
			
		||||
    private void callExitHandlersAndCleanUp(Context ctx) {
 | 
			
		||||
        if (exitHandlers != null && !exitHandlers.isEmpty()) {
 | 
			
		||||
            for (BiConsumer<Context, Entry> handler : this.exitHandlers) {
 | 
			
		||||
                try {
 | 
			
		||||
                    handler.accept(ctx, this);
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    RecordLog.warn("Error occurred when invoking entry exit handler, current entry: "
 | 
			
		||||
                        + resourceWrapper.getName(), e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            exitHandlers = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void exitForContext(Context context, int count, Object... args) throws ErrorEntryFreeException {
 | 
			
		||||
        if (context != null) {
 | 
			
		||||
            // Null context should exit without clean-up.
 | 
			
		||||
            if (context instanceof NullContext) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (context.getCurEntry() != this) {
 | 
			
		||||
                String curEntryNameInContext = context.getCurEntry() == null ? null
 | 
			
		||||
                    : context.getCurEntry().getResourceWrapper().getName();
 | 
			
		||||
                // Clean previous call stack.
 | 
			
		||||
                CtEntry e = (CtEntry) context.getCurEntry();
 | 
			
		||||
                while (e != null) {
 | 
			
		||||
                    e.exit(count, args);
 | 
			
		||||
                    e = (CtEntry) e.parent;
 | 
			
		||||
                }
 | 
			
		||||
                String errorMessage = String.format("The order of entry exit can't be paired with the order of entry"
 | 
			
		||||
                        + ", current entry in context: <%s>, but expected: <%s>", curEntryNameInContext,
 | 
			
		||||
                    resourceWrapper.getName());
 | 
			
		||||
                throw new ErrorEntryFreeException(errorMessage);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Go through the onExit hook of all slots.
 | 
			
		||||
                if (chain != null) {
 | 
			
		||||
                    chain.exit(context, resourceWrapper, count, args);
 | 
			
		||||
                }
 | 
			
		||||
                // Go through the existing terminate handlers (associated to this invocation).
 | 
			
		||||
                callExitHandlersAndCleanUp(context);
 | 
			
		||||
 | 
			
		||||
                // Restore the call stack.
 | 
			
		||||
                context.setCurEntry(parent);
 | 
			
		||||
                if (parent != null) {
 | 
			
		||||
                    ((CtEntry) parent).child = null;
 | 
			
		||||
                }
 | 
			
		||||
                if (parent == null) {
 | 
			
		||||
                    // Default context (auto entered) will be exited automatically.
 | 
			
		||||
                    if (ContextUtil.isDefaultContext(context)) {
 | 
			
		||||
                        ContextUtil.exit();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // Clean the reference of context in current entry to avoid duplicate exit.
 | 
			
		||||
                clearEntryContext();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void clearEntryContext() {
 | 
			
		||||
        this.context = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void whenTerminate(BiConsumer<Context, Entry> handler) {
 | 
			
		||||
        if (this.exitHandlers == null) {
 | 
			
		||||
            this.exitHandlers = new LinkedList<>();
 | 
			
		||||
        }
 | 
			
		||||
        this.exitHandlers.add(handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Entry trueExit(int count, Object... args) throws ErrorEntryFreeException {
 | 
			
		||||
        exitForContext(context, count, args);
 | 
			
		||||
 | 
			
		||||
        return parent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Node getLastNode() {
 | 
			
		||||
        return parent == null ? null : parent.getCurNode();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,356 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.ContextUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.NullContext;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.MethodResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.SlotChainProvider;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.Rule;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@inheritDoc}
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author leyou(lihao)
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @see Sph
 | 
			
		||||
 */
 | 
			
		||||
public class CtSph implements Sph {
 | 
			
		||||
 | 
			
		||||
    private static final Object[] OBJECTS0 = new Object[0];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Same resource({@link ResourceWrapper#equals(Object)}) will share the same
 | 
			
		||||
     * {@link ProcessorSlotChain}, no matter in which {@link Context}.
 | 
			
		||||
     */
 | 
			
		||||
    private static volatile Map<ResourceWrapper, ProcessorSlotChain> chainMap
 | 
			
		||||
        = new HashMap<ResourceWrapper, ProcessorSlotChain>();
 | 
			
		||||
 | 
			
		||||
    private static final Object LOCK = new Object();
 | 
			
		||||
 | 
			
		||||
    private AsyncEntry asyncEntryWithNoChain(ResourceWrapper resourceWrapper, Context context) {
 | 
			
		||||
        AsyncEntry entry = new AsyncEntry(resourceWrapper, null, context);
 | 
			
		||||
        entry.initAsyncContext();
 | 
			
		||||
        // The async entry will be removed from current context as soon as it has been created.
 | 
			
		||||
        entry.cleanCurrentEntryInLocal();
 | 
			
		||||
        return entry;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private AsyncEntry asyncEntryWithPriorityInternal(ResourceWrapper resourceWrapper, int count, boolean prioritized,
 | 
			
		||||
                                                      Object... args) throws BlockException {
 | 
			
		||||
        Context context = ContextUtil.getContext();
 | 
			
		||||
        if (context instanceof NullContext) {
 | 
			
		||||
            // The {@link NullContext} indicates that the amount of context has exceeded the threshold,
 | 
			
		||||
            // so here init the entry only. No rule checking will be done.
 | 
			
		||||
            return asyncEntryWithNoChain(resourceWrapper, context);
 | 
			
		||||
        }
 | 
			
		||||
        if (context == null) {
 | 
			
		||||
            // Using default context.
 | 
			
		||||
            context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Global switch is turned off, so no rule checking will be done.
 | 
			
		||||
        if (!Constants.ON) {
 | 
			
		||||
            return asyncEntryWithNoChain(resourceWrapper, context);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
 | 
			
		||||
 | 
			
		||||
        // Means processor cache size exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE}, so no rule checking will be done.
 | 
			
		||||
        if (chain == null) {
 | 
			
		||||
            return asyncEntryWithNoChain(resourceWrapper, context);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        AsyncEntry asyncEntry = new AsyncEntry(resourceWrapper, chain, context);
 | 
			
		||||
        try {
 | 
			
		||||
            chain.entry(context, resourceWrapper, null, count, prioritized, args);
 | 
			
		||||
            // Initiate the async context only when the entry successfully passed the slot chain.
 | 
			
		||||
            asyncEntry.initAsyncContext();
 | 
			
		||||
            // The asynchronous call may take time in background, and current context should not be hanged on it.
 | 
			
		||||
            // So we need to remove current async entry from current context.
 | 
			
		||||
            asyncEntry.cleanCurrentEntryInLocal();
 | 
			
		||||
        } catch (BlockException e1) {
 | 
			
		||||
            // When blocked, the async entry will be exited on current context.
 | 
			
		||||
            // The async context will not be initialized.
 | 
			
		||||
            asyncEntry.exitForContext(context, count, args);
 | 
			
		||||
            throw e1;
 | 
			
		||||
        } catch (Throwable e1) {
 | 
			
		||||
            // This should not happen, unless there are errors existing in Sentinel internal.
 | 
			
		||||
            // When this happens, async context is not initialized.
 | 
			
		||||
            RecordLog.warn("Sentinel unexpected exception in asyncEntryInternal", e1);
 | 
			
		||||
 | 
			
		||||
            asyncEntry.cleanCurrentEntryInLocal();
 | 
			
		||||
        }
 | 
			
		||||
        return asyncEntry;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private AsyncEntry asyncEntryInternal(ResourceWrapper resourceWrapper, int count, Object... args)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        return asyncEntryWithPriorityInternal(resourceWrapper, count, false, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        Context context = ContextUtil.getContext();
 | 
			
		||||
        if (context instanceof NullContext) {
 | 
			
		||||
            // The {@link NullContext} indicates that the amount of context has exceeded the threshold,
 | 
			
		||||
            // so here init the entry only. No rule checking will be done.
 | 
			
		||||
            return new CtEntry(resourceWrapper, null, context);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (context == null) {
 | 
			
		||||
            // Using default context.
 | 
			
		||||
            context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Global switch is close, no rule checking will do.
 | 
			
		||||
        if (!Constants.ON) {
 | 
			
		||||
            return new CtEntry(resourceWrapper, null, context);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * Means amount of resources (slot chain) exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE},
 | 
			
		||||
         * so no rule checking will be done.
 | 
			
		||||
         */
 | 
			
		||||
        if (chain == null) {
 | 
			
		||||
            return new CtEntry(resourceWrapper, null, context);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Entry e = new CtEntry(resourceWrapper, chain, context);
 | 
			
		||||
        try {
 | 
			
		||||
            chain.entry(context, resourceWrapper, null, count, prioritized, args);
 | 
			
		||||
        } catch (BlockException e1) {
 | 
			
		||||
            e.exit(count, args);
 | 
			
		||||
            throw e1;
 | 
			
		||||
        } catch (Throwable e1) {
 | 
			
		||||
            // This should not happen, unless there are errors existing in Sentinel internal.
 | 
			
		||||
            RecordLog.info("Sentinel unexpected exception", e1);
 | 
			
		||||
        }
 | 
			
		||||
        return e;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Do all {@link Rule}s checking about the resource.
 | 
			
		||||
     *
 | 
			
		||||
     * <p>Each distinct resource will use a {@link ProcessorSlot} to do rules checking. Same resource will use
 | 
			
		||||
     * same {@link ProcessorSlot} globally. </p>
 | 
			
		||||
     *
 | 
			
		||||
     * <p>Note that total {@link ProcessorSlot} count must not exceed {@link Constants#MAX_SLOT_CHAIN_SIZE},
 | 
			
		||||
     * otherwise no rules checking will do. In this condition, all requests will pass directly, with no checking
 | 
			
		||||
     * or exception.</p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param resourceWrapper resource name
 | 
			
		||||
     * @param count           tokens needed
 | 
			
		||||
     * @param args            arguments of user method call
 | 
			
		||||
     * @return {@link Entry} represents this call
 | 
			
		||||
     * @throws BlockException if any rule's threshold is exceeded
 | 
			
		||||
     */
 | 
			
		||||
    public Entry entry(ResourceWrapper resourceWrapper, int count, Object... args) throws BlockException {
 | 
			
		||||
        return entryWithPriority(resourceWrapper, count, false, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get {@link ProcessorSlotChain} of the resource. new {@link ProcessorSlotChain} will
 | 
			
		||||
     * be created if the resource doesn't relate one.
 | 
			
		||||
     *
 | 
			
		||||
     * <p>Same resource({@link ResourceWrapper#equals(Object)}) will share the same
 | 
			
		||||
     * {@link ProcessorSlotChain} globally, no matter in which {@link Context}.<p/>
 | 
			
		||||
     *
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Note that total {@link ProcessorSlot} count must not exceed {@link Constants#MAX_SLOT_CHAIN_SIZE},
 | 
			
		||||
     * otherwise null will return.
 | 
			
		||||
     * </p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param resourceWrapper target resource
 | 
			
		||||
     * @return {@link ProcessorSlotChain} of the resource
 | 
			
		||||
     */
 | 
			
		||||
    ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
 | 
			
		||||
        ProcessorSlotChain chain = chainMap.get(resourceWrapper);
 | 
			
		||||
        if (chain == null) {
 | 
			
		||||
            synchronized (LOCK) {
 | 
			
		||||
                chain = chainMap.get(resourceWrapper);
 | 
			
		||||
                if (chain == null) {
 | 
			
		||||
                    // Entry size limit.
 | 
			
		||||
                    if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
 | 
			
		||||
                        return null;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    chain = SlotChainProvider.newSlotChain();
 | 
			
		||||
                    Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
 | 
			
		||||
                        chainMap.size() + 1);
 | 
			
		||||
                    newMap.putAll(chainMap);
 | 
			
		||||
                    newMap.put(resourceWrapper, chain);
 | 
			
		||||
                    chainMap = newMap;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return chain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get current size of created slot chains.
 | 
			
		||||
     *
 | 
			
		||||
     * @return size of created slot chains
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    public static int entrySize() {
 | 
			
		||||
        return chainMap.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reset the slot chain map. Only for internal test.
 | 
			
		||||
     *
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    static void resetChainMap() {
 | 
			
		||||
        chainMap.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Only for internal test.
 | 
			
		||||
     *
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    static Map<ResourceWrapper, ProcessorSlotChain> getChainMap() {
 | 
			
		||||
        return chainMap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This class is used for skip context name checking.
 | 
			
		||||
     */
 | 
			
		||||
    private final static class InternalContextUtil extends ContextUtil {
 | 
			
		||||
        static Context internalEnter(String name) {
 | 
			
		||||
            return trueEnter(name, "");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static Context internalEnter(String name, String origin) {
 | 
			
		||||
            return trueEnter(name, origin);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(String name) throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, EntryType.OUT);
 | 
			
		||||
        return entry(resource, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(Method method) throws BlockException {
 | 
			
		||||
        MethodResourceWrapper resource = new MethodResourceWrapper(method, EntryType.OUT);
 | 
			
		||||
        return entry(resource, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(Method method, EntryType type) throws BlockException {
 | 
			
		||||
        MethodResourceWrapper resource = new MethodResourceWrapper(method, type);
 | 
			
		||||
        return entry(resource, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(String name, EntryType type) throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, type);
 | 
			
		||||
        return entry(resource, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(Method method, EntryType type, int count) throws BlockException {
 | 
			
		||||
        MethodResourceWrapper resource = new MethodResourceWrapper(method, type);
 | 
			
		||||
        return entry(resource, count, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(String name, EntryType type, int count) throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, type);
 | 
			
		||||
        return entry(resource, count, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(Method method, int count) throws BlockException {
 | 
			
		||||
        MethodResourceWrapper resource = new MethodResourceWrapper(method, EntryType.OUT);
 | 
			
		||||
        return entry(resource, count, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(String name, int count) throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, EntryType.OUT);
 | 
			
		||||
        return entry(resource, count, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(Method method, EntryType type, int count, Object... args) throws BlockException {
 | 
			
		||||
        MethodResourceWrapper resource = new MethodResourceWrapper(method, type);
 | 
			
		||||
        return entry(resource, count, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entry(String name, EntryType type, int count, Object... args) throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, type);
 | 
			
		||||
        return entry(resource, count, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public AsyncEntry asyncEntry(String name, EntryType type, int count, Object... args) throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, type);
 | 
			
		||||
        return asyncEntryInternal(resource, count, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entryWithPriority(String name, EntryType type, int count, boolean prioritized) throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, type);
 | 
			
		||||
        return entryWithPriority(resource, count, prioritized);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entryWithPriority(String name, EntryType type, int count, boolean prioritized, Object... args)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, type);
 | 
			
		||||
        return entryWithPriority(resource, count, prioritized, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entryWithType(String name, int resourceType, EntryType entryType, int count, Object[] args)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        return entryWithType(name, resourceType, entryType, count, false, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Entry entryWithType(String name, int resourceType, EntryType entryType, int count, boolean prioritized,
 | 
			
		||||
                               Object[] args) throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, entryType, resourceType);
 | 
			
		||||
        return entryWithPriority(resource, count, prioritized, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public AsyncEntry asyncEntryWithType(String name, int resourceType, EntryType entryType, int count,
 | 
			
		||||
                                         boolean prioritized, Object[] args) throws BlockException {
 | 
			
		||||
        StringResourceWrapper resource = new StringResourceWrapper(name, entryType, resourceType);
 | 
			
		||||
        return asyncEntryWithPriorityInternal(resource, count, prioritized, args);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,192 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.TimeUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.function.BiConsumer;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.ContextUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.Node;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Each {@link SphU}#entry() will return an {@link Entry}. This class holds information of current invocation:<br/>
 | 
			
		||||
 *
 | 
			
		||||
 * <ul>
 | 
			
		||||
 * <li>createTime, the create time of this entry, using for rt statistics.</li>
 | 
			
		||||
 * <li>current {@link Node}, that is statistics of the resource in current context.</li>
 | 
			
		||||
 * <li>origin {@link Node}, that is statistics for the specific origin. Usually the
 | 
			
		||||
 * origin could be the Service Consumer's app name, see
 | 
			
		||||
 * {@link ContextUtil#enter(String name, String origin)} </li>
 | 
			
		||||
 * <li>{@link ResourceWrapper}, that is resource name.</li>
 | 
			
		||||
 * <br/>
 | 
			
		||||
 * </ul>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * A invocation tree will be created if we invoke SphU#entry() multi times in the same {@link Context},
 | 
			
		||||
 * so parent or child entry may be held by this to form the tree. Since {@link Context} always holds
 | 
			
		||||
 * the current entry in the invocation tree, every {@link Entry#exit()} call should modify
 | 
			
		||||
 * {@link Context#setCurEntry(Entry)} as parent entry of this.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author leyou(lihao)
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @see SphU
 | 
			
		||||
 * @see Context
 | 
			
		||||
 * @see ContextUtil
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Entry implements AutoCloseable {
 | 
			
		||||
 | 
			
		||||
    private static final Object[] OBJECTS0 = new Object[0];
 | 
			
		||||
 | 
			
		||||
    private final long createTimestamp;
 | 
			
		||||
    private long completeTimestamp;
 | 
			
		||||
 | 
			
		||||
    private Node curNode;
 | 
			
		||||
    /**
 | 
			
		||||
     * {@link Node} of the specific origin, Usually the origin is the Service Consumer.
 | 
			
		||||
     */
 | 
			
		||||
    private Node originNode;
 | 
			
		||||
 | 
			
		||||
    private Throwable error;
 | 
			
		||||
    private BlockException blockError;
 | 
			
		||||
 | 
			
		||||
    protected final ResourceWrapper resourceWrapper;
 | 
			
		||||
 | 
			
		||||
    public Entry(ResourceWrapper resourceWrapper) {
 | 
			
		||||
        this.resourceWrapper = resourceWrapper;
 | 
			
		||||
        this.createTimestamp = TimeUtil.currentTimeMillis();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ResourceWrapper getResourceWrapper() {
 | 
			
		||||
        return resourceWrapper;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Complete the current resource entry and restore the entry stack in context.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws ErrorEntryFreeException if entry in current context does not match current entry
 | 
			
		||||
     */
 | 
			
		||||
    public void exit() throws ErrorEntryFreeException {
 | 
			
		||||
        exit(1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void exit(int count) throws ErrorEntryFreeException {
 | 
			
		||||
        exit(count, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Equivalent to {@link #exit()}. Support try-with-resources since JDK 1.7.
 | 
			
		||||
     *
 | 
			
		||||
     * @since 1.5.0
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() {
 | 
			
		||||
        exit();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Exit this entry. This method should invoke if and only if once at the end of the resource protection.
 | 
			
		||||
     *
 | 
			
		||||
     * @param count tokens to release.
 | 
			
		||||
     * @param args extra parameters
 | 
			
		||||
     * @throws ErrorEntryFreeException, if {@link Context#getCurEntry()} is not this entry.
 | 
			
		||||
     */
 | 
			
		||||
    public abstract void exit(int count, Object... args) throws ErrorEntryFreeException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Exit this entry.
 | 
			
		||||
     *
 | 
			
		||||
     * @param count tokens to release.
 | 
			
		||||
     * @param args extra parameters
 | 
			
		||||
     * @return next available entry after exit, that is the parent entry.
 | 
			
		||||
     * @throws ErrorEntryFreeException, if {@link Context#getCurEntry()} is not this entry.
 | 
			
		||||
     */
 | 
			
		||||
    protected abstract Entry trueExit(int count, Object... args) throws ErrorEntryFreeException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get related {@link Node} of the parent {@link Entry}.
 | 
			
		||||
     *
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    public abstract Node getLastNode();
 | 
			
		||||
 | 
			
		||||
    public long getCreateTimestamp() {
 | 
			
		||||
        return createTimestamp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getCompleteTimestamp() {
 | 
			
		||||
        return completeTimestamp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Entry setCompleteTimestamp(long completeTimestamp) {
 | 
			
		||||
        this.completeTimestamp = completeTimestamp;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Node getCurNode() {
 | 
			
		||||
        return curNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setCurNode(Node node) {
 | 
			
		||||
        this.curNode = node;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BlockException getBlockError() {
 | 
			
		||||
        return blockError;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Entry setBlockError(BlockException blockError) {
 | 
			
		||||
        this.blockError = blockError;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Throwable getError() {
 | 
			
		||||
        return error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setError(Throwable error) {
 | 
			
		||||
        this.error = error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get origin {@link Node} of the this {@link Entry}.
 | 
			
		||||
     *
 | 
			
		||||
     * @return origin {@link Node} of the this {@link Entry}, may be null if no origin specified by
 | 
			
		||||
     * {@link ContextUtil#enter(String name, String origin)}.
 | 
			
		||||
     */
 | 
			
		||||
    public Node getOriginNode() {
 | 
			
		||||
        return originNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setOriginNode(Node originNode) {
 | 
			
		||||
        this.originNode = originNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Like {@code CompletableFuture} since JDK 8, it guarantees specified handler
 | 
			
		||||
     * is invoked when this entry terminated (exited), no matter it's blocked or permitted.
 | 
			
		||||
     * Use it when you did some STATEFUL operations on entries.
 | 
			
		||||
     * 
 | 
			
		||||
     * @param handler handler function on the invocation terminates
 | 
			
		||||
     * @since 1.8.0
 | 
			
		||||
     */
 | 
			
		||||
    public abstract void whenTerminate(BiConsumer<Context, Entry> handler);
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An enum marks resource invocation direction.
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Yanming Zhou
 | 
			
		||||
 */
 | 
			
		||||
public enum EntryType {
 | 
			
		||||
    /**
 | 
			
		||||
     * Inbound traffic
 | 
			
		||||
     */
 | 
			
		||||
    IN,
 | 
			
		||||
    /**
 | 
			
		||||
     * Outbound traffic
 | 
			
		||||
     */
 | 
			
		||||
    OUT;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.init.InitExecutor;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Sentinel Env. This class will trigger all initialization for Sentinel.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * NOTE: to prevent deadlocks, other classes' static code block or static field should
 | 
			
		||||
 * NEVER refer to this class.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 */
 | 
			
		||||
public class Env {
 | 
			
		||||
 | 
			
		||||
    public static final Sph sph = new CtSph();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        // If init fails, the process will exit.
 | 
			
		||||
        InitExecutor.doInit();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,28 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents order mismatch of resource entry and resource exit (pair mismatch).
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 */
 | 
			
		||||
public class ErrorEntryFreeException extends RuntimeException {
 | 
			
		||||
 | 
			
		||||
    public ErrorEntryFreeException(String s) {
 | 
			
		||||
        super(s);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 *
 | 
			
		||||
 *      https://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.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.7.0
 | 
			
		||||
 */
 | 
			
		||||
public final class ResourceTypeConstants {
 | 
			
		||||
 | 
			
		||||
    public static final int COMMON = 0;
 | 
			
		||||
    public static final int COMMON_WEB = 1;
 | 
			
		||||
    public static final int COMMON_RPC = 2;
 | 
			
		||||
    public static final int COMMON_API_GATEWAY = 3;
 | 
			
		||||
    public static final int COMMON_DB_SQL = 4;
 | 
			
		||||
 | 
			
		||||
    private ResourceTypeConstants() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,196 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The basic interface for recording statistics and performing rule checking for resources.
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author leyou
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public interface Sph extends SphResourceTypeSupport {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the unique name of the protected resource
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(String name) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method the protected method
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(Method method) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method     the protected method
 | 
			
		||||
     * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(Method method, int batchCount) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name       the unique string for the resource
 | 
			
		||||
     * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(String name, int batchCount) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method      the protected method
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(Method method, EntryType trafficType) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(String name, EntryType trafficType) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method      the protected method
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(Method method, EntryType trafficType, int batchCount) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(String name, EntryType trafficType, int batchCount) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method      the protected method
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args        parameters of the method for flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data).
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(Method method, EntryType trafficType, int batchCount, Object... args) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args        args for parameter flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entry(String name, EntryType trafficType, int batchCount, Object... args) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a protected asynchronous resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args        args for parameter flow control or customized slots
 | 
			
		||||
     * @return created asynchronous entry
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    AsyncEntry asyncEntry(String name, EntryType trafficType, int batchCount, Object... args) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a protected resource with priority.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param prioritized whether the entry is prioritized
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     * @since 1.4.0
 | 
			
		||||
     */
 | 
			
		||||
    Entry entryWithPriority(String name, EntryType trafficType, int batchCount, boolean prioritized)
 | 
			
		||||
        throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a protected resource with priority.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param prioritized whether the entry is prioritized
 | 
			
		||||
     * @param args        args for parameter flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     * @since 1.5.0
 | 
			
		||||
     */
 | 
			
		||||
    Entry entryWithPriority(String name, EntryType trafficType, int batchCount, boolean prioritized, Object... args)
 | 
			
		||||
        throws BlockException;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,226 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.ContextUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.Rule;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Conceptually, physical or logical resource that need protection should be
 | 
			
		||||
 * surrounded by an entry. The requests to this resource will be blocked if any
 | 
			
		||||
 * criteria is met, eg. when any {@link Rule}'s threshold is exceeded. Once blocked,
 | 
			
		||||
 * {@link SphO}#entry() will return false.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * To configure the criteria, we can use <code>XXXRuleManager.loadRules()</code> to add rules. eg.
 | 
			
		||||
 * {@link FlowRuleManager#loadRules(List)}, {@link DegradeRuleManager#loadRules(List)},
 | 
			
		||||
 * {@link SystemRuleManager#loadRules(List)}.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Following code is an example. {@code "abc"} represent a unique name for the
 | 
			
		||||
 * protected resource:
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * public void foo() {
 | 
			
		||||
 *    if (SphO.entry("abc")) {
 | 
			
		||||
 *        try {
 | 
			
		||||
 *            // business logic
 | 
			
		||||
 *        } finally {
 | 
			
		||||
 *            SphO.exit(); // must exit()
 | 
			
		||||
 *        }
 | 
			
		||||
 *    } else {
 | 
			
		||||
 *        // failed to enter the protected resource.
 | 
			
		||||
 *    }
 | 
			
		||||
 * }
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * Make sure {@code SphO.entry()} and {@link SphO#exit()} be paired in the same thread,
 | 
			
		||||
 * otherwise {@link ErrorEntryFreeException} will be thrown.
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author leyou
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @see SphU
 | 
			
		||||
 */
 | 
			
		||||
public class SphO {
 | 
			
		||||
 | 
			
		||||
    private static final Object[] OBJECTS0 = new Object[0];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the unique name of the protected resource
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(String name) {
 | 
			
		||||
        return entry(name, EntryType.OUT, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checking all {@link Rule}s about the protected method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method the protected method
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(Method method) {
 | 
			
		||||
        return entry(method, EntryType.OUT, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checking all {@link Rule}s about the protected method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method     the protected method
 | 
			
		||||
     * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(Method method, int batchCount) {
 | 
			
		||||
        return entry(method, EntryType.OUT, batchCount, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name       the unique string for the resource
 | 
			
		||||
     * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(String name, int batchCount) {
 | 
			
		||||
        return entry(name, EntryType.OUT, batchCount, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checking all {@link Rule}s about the protected method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method the protected method
 | 
			
		||||
     * @param type   the resource is an inbound or an outbound method. This is used
 | 
			
		||||
     *               to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *               only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(Method method, EntryType type) {
 | 
			
		||||
        return entry(method, type, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the unique name for the protected resource
 | 
			
		||||
     * @param type the resource is an inbound or an outbound method. This is used
 | 
			
		||||
     *             to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *             only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(String name, EntryType type) {
 | 
			
		||||
        return entry(name, type, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checking all {@link Rule}s about the protected method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method the protected method
 | 
			
		||||
     * @param type   the resource is an inbound or an outbound method. This is used
 | 
			
		||||
     *               to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *               only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param count  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(Method method, EntryType type, int count) {
 | 
			
		||||
        return entry(method, type, count, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name  the unique name for the protected resource
 | 
			
		||||
     * @param type  the resource is an inbound or an outbound method. This is used
 | 
			
		||||
     *              to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *              only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param count the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(String name, EntryType type, int count) {
 | 
			
		||||
        return entry(name, type, count, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args        args for parameter flow control or customized slots
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(String name, EntryType trafficType, int batchCount, Object... args) {
 | 
			
		||||
        try {
 | 
			
		||||
            Env.sph.entry(name, trafficType, batchCount, args);
 | 
			
		||||
        } catch (BlockException e) {
 | 
			
		||||
            return false;
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            RecordLog.warn("SphO fatal error", e);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given method resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method      the protected method
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args        args for parameter flow control or customized slots
 | 
			
		||||
     * @return true if no rule's threshold is exceeded, otherwise return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean entry(Method method, EntryType trafficType, int batchCount, Object... args) {
 | 
			
		||||
        try {
 | 
			
		||||
            Env.sph.entry(method, trafficType, batchCount, args);
 | 
			
		||||
        } catch (BlockException e) {
 | 
			
		||||
            return false;
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            RecordLog.warn("SphO fatal error", e);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void exit(int count, Object... args) {
 | 
			
		||||
        ContextUtil.getContext().getCurEntry().exit(count, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void exit(int count) {
 | 
			
		||||
        ContextUtil.getContext().getCurEntry().exit(count, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void exit() {
 | 
			
		||||
        exit(1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,77 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 *
 | 
			
		||||
 *      https://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.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.7.0
 | 
			
		||||
 */
 | 
			
		||||
public interface SphResourceTypeSupport {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource with provided classification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         the unique name of the protected resource
 | 
			
		||||
     * @param resourceType the classification of the resource
 | 
			
		||||
     * @param trafficType  the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                     to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                     only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount   the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args         args for parameter flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entryWithType(String name, int resourceType, EntryType trafficType, int batchCount, Object[] args)
 | 
			
		||||
        throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource with the provided classification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         the unique name of the protected resource
 | 
			
		||||
     * @param resourceType classification of the resource (e.g. Web or RPC)
 | 
			
		||||
     * @param trafficType  the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                     to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                     only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount   the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param prioritized  whether the entry is prioritized
 | 
			
		||||
     * @param args         args for parameter flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    Entry entryWithType(String name, int resourceType, EntryType trafficType, int batchCount, boolean prioritized,
 | 
			
		||||
                        Object[] args) throws BlockException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource that indicates an async invocation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         the unique name for the protected resource
 | 
			
		||||
     * @param resourceType classification of the resource (e.g. Web or RPC)
 | 
			
		||||
     * @param trafficType  the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                     to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                     only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount   the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param prioritized  whether the entry is prioritized
 | 
			
		||||
     * @param args         args for parameter flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met
 | 
			
		||||
     */
 | 
			
		||||
    AsyncEntry asyncEntryWithType(String name, int resourceType, EntryType trafficType, int batchCount,
 | 
			
		||||
                                  boolean prioritized,
 | 
			
		||||
                                  Object[] args) throws BlockException;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,368 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.Rule;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>The fundamental Sentinel API for recording statistics and performing rule checking for resources.</p>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Conceptually, physical or logical resource that need protection should be
 | 
			
		||||
 * surrounded by an entry. The requests to this resource will be blocked if any
 | 
			
		||||
 * criteria is met, eg. when any {@link Rule}'s threshold is exceeded. Once blocked,
 | 
			
		||||
 * a {@link BlockException} will be thrown.
 | 
			
		||||
 * </p>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * To configure the criteria, we can use <code>XxxRuleManager.loadRules()</code> to load rules.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Following code is an example, {@code "abc"} represent a unique name for the
 | 
			
		||||
 * protected resource:
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 *  public void foo() {
 | 
			
		||||
 *     Entry entry = null;
 | 
			
		||||
 *     try {
 | 
			
		||||
 *        entry = SphU.entry("abc");
 | 
			
		||||
 *        // resource that need protection
 | 
			
		||||
 *     } catch (BlockException blockException) {
 | 
			
		||||
 *         // when goes there, it is blocked
 | 
			
		||||
 *         // add blocked handle logic here
 | 
			
		||||
 *     } catch (Throwable bizException) {
 | 
			
		||||
 *         // business exception
 | 
			
		||||
 *         Tracer.trace(bizException);
 | 
			
		||||
 *     } finally {
 | 
			
		||||
 *         // ensure finally be executed
 | 
			
		||||
 *         if (entry != null){
 | 
			
		||||
 *             entry.exit();
 | 
			
		||||
 *         }
 | 
			
		||||
 *     }
 | 
			
		||||
 *  }
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Make sure {@code SphU.entry()} and {@link Entry#exit()} be paired in the same thread,
 | 
			
		||||
 * otherwise {@link ErrorEntryFreeException} will be thrown.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @see SphO
 | 
			
		||||
 */
 | 
			
		||||
public class SphU {
 | 
			
		||||
 | 
			
		||||
    private static final Object[] OBJECTS0 = new Object[0];
 | 
			
		||||
 | 
			
		||||
    private SphU() {}
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the unique name of the protected resource
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(String name) throws BlockException {
 | 
			
		||||
        return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checking all {@link Rule}s about the protected method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method the protected method
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(Method method) throws BlockException {
 | 
			
		||||
        return Env.sph.entry(method, EntryType.OUT, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checking all {@link Rule}s about the protected method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method     the protected method
 | 
			
		||||
     * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(Method method, int batchCount) throws BlockException {
 | 
			
		||||
        return Env.sph.entry(method, EntryType.OUT, batchCount, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name       the unique string for the resource
 | 
			
		||||
     * @param batchCount the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(String name, int batchCount) throws BlockException {
 | 
			
		||||
        return Env.sph.entry(name, EntryType.OUT, batchCount, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checking all {@link Rule}s about the protected method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method      the protected method
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(Method method, EntryType trafficType) throws BlockException {
 | 
			
		||||
        return Env.sph.entry(method, trafficType, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(String name, EntryType trafficType) throws BlockException {
 | 
			
		||||
        return Env.sph.entry(name, trafficType, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checking all {@link Rule}s about the protected method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method      the protected method
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(Method method, EntryType trafficType, int batchCount) throws BlockException {
 | 
			
		||||
        return Env.sph.entry(method, trafficType, batchCount, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(String name, EntryType trafficType, int batchCount) throws BlockException {
 | 
			
		||||
        return Env.sph.entry(name, trafficType, batchCount, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checking all {@link Rule}s about the protected method.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method      the protected method
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args        args for parameter flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(Method method, EntryType trafficType, int batchCount, Object... args)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        return Env.sph.entry(method, trafficType, batchCount, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args        args for parameter flow control
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(String name, EntryType trafficType, int batchCount, Object... args)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        return Env.sph.entry(name, trafficType, batchCount, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and check all rules of the resource that indicates an async invocation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the unique name of the protected resource
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    public static AsyncEntry asyncEntry(String name) throws BlockException {
 | 
			
		||||
        return Env.sph.asyncEntry(name, EntryType.OUT, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and check all rules of the resource that indicates an async invocation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    public static AsyncEntry asyncEntry(String name, EntryType trafficType) throws BlockException {
 | 
			
		||||
        return Env.sph.asyncEntry(name, trafficType, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and check all rules of the resource that indicates an async invocation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param batchCount  the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args        args for parameter flow control
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    public static AsyncEntry asyncEntry(String name, EntryType trafficType, int batchCount, Object... args)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        return Env.sph.asyncEntry(name, trafficType, batchCount, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource. The entry is prioritized.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the unique name for the protected resource
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 1.4.0
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entryWithPriority(String name) throws BlockException {
 | 
			
		||||
        return Env.sph.entryWithPriority(name, EntryType.OUT, 1, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource. The entry is prioritized.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name        the unique name for the protected resource
 | 
			
		||||
     * @param trafficType the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                    to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                    only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 1.4.0
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entryWithPriority(String name, EntryType trafficType) throws BlockException {
 | 
			
		||||
        return Env.sph.entryWithPriority(name, trafficType, 1, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         the unique name for the protected resource
 | 
			
		||||
     * @param resourceType classification of the resource (e.g. Web or RPC)
 | 
			
		||||
     * @param trafficType  the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                     to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                     only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(String name, int resourceType, EntryType trafficType) throws BlockException {
 | 
			
		||||
        return Env.sph.entryWithType(name, resourceType, trafficType, 1, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         the unique name for the protected resource
 | 
			
		||||
     * @param trafficType  the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                     to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                     only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param resourceType classification of the resource (e.g. Web or RPC)
 | 
			
		||||
     * @param args         args for parameter flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    public static Entry entry(String name, int resourceType, EntryType trafficType, Object[] args)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        return Env.sph.entryWithType(name, resourceType, trafficType, 1, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource that indicates an async invocation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         the unique name for the protected resource
 | 
			
		||||
     * @param trafficType  the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                     to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                     only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param resourceType classification of the resource (e.g. Web or RPC)
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    public static AsyncEntry asyncEntry(String name, int resourceType, EntryType trafficType)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        return Env.sph.asyncEntryWithType(name, resourceType, trafficType, 1, false, OBJECTS0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource that indicates an async invocation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         the unique name for the protected resource
 | 
			
		||||
     * @param trafficType  the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                     to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                     only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param resourceType classification of the resource (e.g. Web or RPC)
 | 
			
		||||
     * @param args         args for parameter flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    public static AsyncEntry asyncEntry(String name, int resourceType, EntryType trafficType, Object[] args)
 | 
			
		||||
        throws BlockException {
 | 
			
		||||
        return Env.sph.asyncEntryWithType(name, resourceType, trafficType, 1, false, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Record statistics and perform rule checking for the given resource that indicates an async invocation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name         the unique name for the protected resource
 | 
			
		||||
     * @param trafficType  the traffic type (inbound, outbound or internal). This is used
 | 
			
		||||
     *                     to mark whether it can be blocked when the system is unstable,
 | 
			
		||||
     *                     only inbound traffic could be blocked by {@link SystemRule}
 | 
			
		||||
     * @param resourceType classification of the resource (e.g. Web or RPC)
 | 
			
		||||
     * @param batchCount   the amount of calls within the invocation (e.g. batchCount=2 means request for 2 tokens)
 | 
			
		||||
     * @param args         args for parameter flow control or customized slots
 | 
			
		||||
     * @return the {@link Entry} of this invocation (used for mark the invocation complete and get context data)
 | 
			
		||||
     * @throws BlockException if the block criteria is met (e.g. metric exceeded the threshold of any rules)
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    public static AsyncEntry asyncEntry(String name, int resourceType, EntryType trafficType, int batchCount,
 | 
			
		||||
                                        Object[] args) throws BlockException {
 | 
			
		||||
        return Env.sph.asyncEntryWithType(name, resourceType, trafficType, batchCount, false, args);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,226 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.ContextUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.NullContext;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.AssertUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.function.Predicate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class is used to record other exceptions except block exception.
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public class Tracer {
 | 
			
		||||
 | 
			
		||||
    protected static Class<? extends Throwable>[] traceClasses;
 | 
			
		||||
    protected static Class<? extends Throwable>[] ignoreClasses;
 | 
			
		||||
 | 
			
		||||
    protected static Predicate<Throwable> exceptionPredicate;
 | 
			
		||||
 | 
			
		||||
    protected Tracer() {}
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Trace provided {@link Throwable} to the resource entry in current context.
 | 
			
		||||
     *
 | 
			
		||||
     * @param e exception to record
 | 
			
		||||
     */
 | 
			
		||||
    public static void trace(Throwable e) {
 | 
			
		||||
        traceContext(e, ContextUtil.getContext());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Trace provided {@link Throwable} to current entry in current context.
 | 
			
		||||
     *
 | 
			
		||||
     * @param e     exception to record
 | 
			
		||||
     * @param count exception count to add
 | 
			
		||||
     */
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public static void trace(Throwable e, int count) {
 | 
			
		||||
        traceContext(e, count, ContextUtil.getContext());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Trace provided {@link Throwable} to current entry of given entrance context.
 | 
			
		||||
     *
 | 
			
		||||
     * @param e     exception to record
 | 
			
		||||
     * @param context target entrance context
 | 
			
		||||
     * @since 1.8.0
 | 
			
		||||
     */
 | 
			
		||||
    public static void traceContext(Throwable e, Context context) {
 | 
			
		||||
        if (!shouldTrace(e)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (context == null || context instanceof NullContext) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        traceEntryInternal(e, context.getCurEntry());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Trace provided {@link Throwable} and add exception count to current entry in provided context.
 | 
			
		||||
     *
 | 
			
		||||
     * @param e     exception to record
 | 
			
		||||
     * @param count exception count to add
 | 
			
		||||
     * @since 1.4.2
 | 
			
		||||
     */
 | 
			
		||||
    @Deprecated
 | 
			
		||||
    public static void traceContext(Throwable e, int count, Context context) {
 | 
			
		||||
        if (!shouldTrace(e)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (context == null || context instanceof NullContext) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        traceEntryInternal(e, context.getCurEntry());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Trace provided {@link Throwable} to the given resource entry.
 | 
			
		||||
     *
 | 
			
		||||
     * @param e exception to record
 | 
			
		||||
     * @since 1.4.2
 | 
			
		||||
     */
 | 
			
		||||
    public static void traceEntry(Throwable e, Entry entry) {
 | 
			
		||||
        if (!shouldTrace(e)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        traceEntryInternal(e, entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void traceEntryInternal(/*@NeedToTrace*/ Throwable e, Entry entry) {
 | 
			
		||||
        if (entry == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        entry.setError(e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set exception to trace. If not set, all Exception except for {@link BlockException} will be traced.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Note that if both {@link #setExceptionsToIgnore(Class[])} and this method is set,
 | 
			
		||||
     * the ExceptionsToIgnore will be of higher precedence.
 | 
			
		||||
     * </p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param traceClasses the list of exception classes to trace.
 | 
			
		||||
     * @since 1.6.1
 | 
			
		||||
     */
 | 
			
		||||
    @SafeVarargs
 | 
			
		||||
    public static void setExceptionsToTrace(Class<? extends Throwable>... traceClasses) {
 | 
			
		||||
        checkNotNull(traceClasses);
 | 
			
		||||
        Tracer.traceClasses = traceClasses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get exception classes to trace.
 | 
			
		||||
     *
 | 
			
		||||
     * @return an array of exception classes to trace.
 | 
			
		||||
     * @since 1.6.1
 | 
			
		||||
     */
 | 
			
		||||
    public static Class<? extends Throwable>[] getExceptionsToTrace() {
 | 
			
		||||
        return traceClasses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set exceptions to ignore. if not set, all Exception except for {@link BlockException} will be traced.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Note that if both {@link #setExceptionsToTrace(Class[])} and this method is set,
 | 
			
		||||
     * the ExceptionsToIgnore will be of higher precedence.
 | 
			
		||||
     * </p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param ignoreClasses the list of exception classes to ignore.
 | 
			
		||||
     * @since 1.6.1
 | 
			
		||||
     */
 | 
			
		||||
    @SafeVarargs
 | 
			
		||||
    public static void setExceptionsToIgnore(Class<? extends Throwable>... ignoreClasses) {
 | 
			
		||||
        checkNotNull(ignoreClasses);
 | 
			
		||||
        Tracer.ignoreClasses = ignoreClasses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get exception classes to ignore.
 | 
			
		||||
     *
 | 
			
		||||
     * @return an array of exception classes to ignore.
 | 
			
		||||
     * @since 1.6.1
 | 
			
		||||
     */
 | 
			
		||||
    public static Class<? extends Throwable>[] getExceptionsToIgnore() {
 | 
			
		||||
        return ignoreClasses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get exception predicate
 | 
			
		||||
     * @return the exception predicate.
 | 
			
		||||
     */
 | 
			
		||||
    public static Predicate<? extends Throwable> getExceptionPredicate() {
 | 
			
		||||
        return exceptionPredicate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * set an exception predicate which indicates the exception should be traced(return true) or ignored(return false)
 | 
			
		||||
     * except for {@link BlockException}
 | 
			
		||||
     * @param exceptionPredicate the exception predicate
 | 
			
		||||
     */
 | 
			
		||||
    public static void setExceptionPredicate(Predicate<Throwable> exceptionPredicate) {
 | 
			
		||||
        AssertUtil.notNull(exceptionPredicate, "exception predicate must not be null");
 | 
			
		||||
        Tracer.exceptionPredicate = exceptionPredicate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void checkNotNull(Class<? extends Throwable>[] classes) {
 | 
			
		||||
        AssertUtil.notNull(classes, "trace or ignore classes must not be null");
 | 
			
		||||
        for (Class<? extends Throwable> clazz : classes) {
 | 
			
		||||
            AssertUtil.notNull(clazz, "trace or ignore classes must not be null");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check whether the throwable should be traced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param t the throwable to check.
 | 
			
		||||
     * @return true if the throwable should be traced, else return false.
 | 
			
		||||
     */
 | 
			
		||||
    protected static boolean shouldTrace(Throwable t) {
 | 
			
		||||
        if (t == null || t instanceof BlockException) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (exceptionPredicate != null) {
 | 
			
		||||
            return exceptionPredicate.test(t);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ignoreClasses != null) {
 | 
			
		||||
            for (Class<? extends Throwable> clazz : ignoreClasses) {
 | 
			
		||||
                if (clazz != null && clazz.isAssignableFrom(t.getClass())) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (traceClasses != null) {
 | 
			
		||||
            for (Class<? extends Throwable> clazz : traceClasses) {
 | 
			
		||||
                if (clazz != null && clazz.isAssignableFrom(t.getClass())) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,106 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2020 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.annotation;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.EntryType;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The annotation indicates a definition of Sentinel resource.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @author zhaoyuguang
 | 
			
		||||
 * @since 0.1.1
 | 
			
		||||
 */
 | 
			
		||||
@Target({ElementType.METHOD, ElementType.TYPE})
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
@Inherited
 | 
			
		||||
public @interface SentinelResource {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return name of the Sentinel resource
 | 
			
		||||
     */
 | 
			
		||||
    String value() default "";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return the entry type (inbound or outbound), outbound by default
 | 
			
		||||
     */
 | 
			
		||||
    EntryType entryType() default EntryType.OUT;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return the classification (type) of the resource
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    int resourceType() default 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return name of the block exception function, empty by default
 | 
			
		||||
     */
 | 
			
		||||
    String blockHandler() default "";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The {@code blockHandler} is located in the same class with the original method by default.
 | 
			
		||||
     * However, if some methods share the same signature and intend to set the same block handler,
 | 
			
		||||
     * then users can set the class where the block handler exists. Note that the block handler method
 | 
			
		||||
     * must be static.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the class where the block handler exists, should not provide more than one classes
 | 
			
		||||
     */
 | 
			
		||||
    Class<?>[] blockHandlerClass() default {};
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return name of the fallback function, empty by default
 | 
			
		||||
     */
 | 
			
		||||
    String fallback() default "";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The {@code defaultFallback} is used as the default universal fallback method.
 | 
			
		||||
     * It should not accept any parameters, and the return type should be compatible
 | 
			
		||||
     * with the original method.
 | 
			
		||||
     *
 | 
			
		||||
     * @return name of the default fallback method, empty by default
 | 
			
		||||
     * @since 1.6.0
 | 
			
		||||
     */
 | 
			
		||||
    String defaultFallback() default "";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The {@code fallback} is located in the same class with the original method by default.
 | 
			
		||||
     * However, if some methods share the same signature and intend to set the same fallback,
 | 
			
		||||
     * then users can set the class where the fallback function exists. Note that the shared fallback method
 | 
			
		||||
     * must be static.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the class where the fallback method is located (only single class)
 | 
			
		||||
     * @since 1.6.0
 | 
			
		||||
     */
 | 
			
		||||
    Class<?>[] fallbackClass() default {};
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return the list of exception classes to trace, {@link Throwable} by default
 | 
			
		||||
     * @since 1.5.1
 | 
			
		||||
     */
 | 
			
		||||
    Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Indicates the exceptions to be ignored. Note that {@code exceptionsToTrace} should
 | 
			
		||||
     * not appear with {@code exceptionsToIgnore} at the same time, or {@code exceptionsToIgnore}
 | 
			
		||||
     * will be of higher precedence.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the list of exception classes to ignore, empty by default
 | 
			
		||||
     * @since 1.6.0
 | 
			
		||||
     */
 | 
			
		||||
    Class<? extends Throwable>[] exceptionsToIgnore() default {};
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,274 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.cluster.client.ClusterTokenClient;
 | 
			
		||||
import com.alibaba.csp.sentinel.cluster.client.TokenClientProvider;
 | 
			
		||||
import com.alibaba.csp.sentinel.cluster.server.EmbeddedClusterTokenServer;
 | 
			
		||||
import com.alibaba.csp.sentinel.cluster.server.EmbeddedClusterTokenServerProvider;
 | 
			
		||||
import com.alibaba.csp.sentinel.init.InitExecutor;
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
 | 
			
		||||
import com.alibaba.csp.sentinel.property.PropertyListener;
 | 
			
		||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.TimeUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Global state manager for Sentinel cluster.
 | 
			
		||||
 * This enables switching between cluster token client and server mode.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public final class ClusterStateManager {
 | 
			
		||||
 | 
			
		||||
    public static final int CLUSTER_CLIENT = 0;
 | 
			
		||||
    public static final int CLUSTER_SERVER = 1;
 | 
			
		||||
    public static final int CLUSTER_NOT_STARTED = -1;
 | 
			
		||||
 | 
			
		||||
    private static volatile int mode = CLUSTER_NOT_STARTED;
 | 
			
		||||
    private static volatile long lastModified = -1;
 | 
			
		||||
 | 
			
		||||
    private static volatile SentinelProperty<Integer> stateProperty = new DynamicSentinelProperty<Integer>();
 | 
			
		||||
    private static final PropertyListener<Integer> PROPERTY_LISTENER = new ClusterStatePropertyListener();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        InitExecutor.doInit();
 | 
			
		||||
        stateProperty.addListener(PROPERTY_LISTENER);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void registerProperty(SentinelProperty<Integer> property) {
 | 
			
		||||
        synchronized (PROPERTY_LISTENER) {
 | 
			
		||||
            RecordLog.info("[ClusterStateManager] Registering new property to cluster state manager");
 | 
			
		||||
            stateProperty.removeListener(PROPERTY_LISTENER);
 | 
			
		||||
            property.addListener(PROPERTY_LISTENER);
 | 
			
		||||
            stateProperty = property;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int getMode() {
 | 
			
		||||
        return mode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isClient() {
 | 
			
		||||
        return mode == CLUSTER_CLIENT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isServer() {
 | 
			
		||||
        return mode == CLUSTER_SERVER;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Set current mode to client mode. If Sentinel currently works in server mode,
 | 
			
		||||
     * it will be turned off. Then the cluster client will be started.
 | 
			
		||||
     * </p>
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean setToClient() {
 | 
			
		||||
        if (mode == CLUSTER_CLIENT) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        mode = CLUSTER_CLIENT;
 | 
			
		||||
        sleepIfNeeded();
 | 
			
		||||
        lastModified = TimeUtil.currentTimeMillis();
 | 
			
		||||
        return startClient();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static boolean startClient() {
 | 
			
		||||
        try {
 | 
			
		||||
            EmbeddedClusterTokenServer server = EmbeddedClusterTokenServerProvider.getServer();
 | 
			
		||||
            if (server != null) {
 | 
			
		||||
                server.stop();
 | 
			
		||||
            }
 | 
			
		||||
            ClusterTokenClient tokenClient = TokenClientProvider.getClient();
 | 
			
		||||
            if (tokenClient != null) {
 | 
			
		||||
                tokenClient.start();
 | 
			
		||||
                RecordLog.info("[ClusterStateManager] Changing cluster mode to client");
 | 
			
		||||
                return true;
 | 
			
		||||
            } else {
 | 
			
		||||
                RecordLog.warn("[ClusterStateManager] Cannot change to client (no client SPI found)");
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            RecordLog.warn("[ClusterStateManager] Error when changing cluster mode to client", ex);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static boolean stopClient() {
 | 
			
		||||
        try {
 | 
			
		||||
            ClusterTokenClient tokenClient = TokenClientProvider.getClient();
 | 
			
		||||
            if (tokenClient != null) {
 | 
			
		||||
                tokenClient.stop();
 | 
			
		||||
                RecordLog.info("[ClusterStateManager] Stopping the cluster token client");
 | 
			
		||||
                return true;
 | 
			
		||||
            } else {
 | 
			
		||||
                RecordLog.warn("[ClusterStateManager] Cannot stop cluster token client (no server SPI found)");
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            RecordLog.warn("[ClusterStateManager] Error when stopping cluster token client", ex);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Set current mode to server mode. If Sentinel currently works in client mode,
 | 
			
		||||
     * it will be turned off. Then the cluster server will be started.
 | 
			
		||||
     * </p>
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean setToServer() {
 | 
			
		||||
        if (mode == CLUSTER_SERVER) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        mode = CLUSTER_SERVER;
 | 
			
		||||
        sleepIfNeeded();
 | 
			
		||||
        lastModified = TimeUtil.currentTimeMillis();
 | 
			
		||||
        return startServer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static boolean startServer() {
 | 
			
		||||
        try {
 | 
			
		||||
            ClusterTokenClient tokenClient = TokenClientProvider.getClient();
 | 
			
		||||
            if (tokenClient != null) {
 | 
			
		||||
                tokenClient.stop();
 | 
			
		||||
            }
 | 
			
		||||
            EmbeddedClusterTokenServer server = EmbeddedClusterTokenServerProvider.getServer();
 | 
			
		||||
            if (server != null) {
 | 
			
		||||
                server.start();
 | 
			
		||||
                RecordLog.info("[ClusterStateManager] Changing cluster mode to server");
 | 
			
		||||
                return true;
 | 
			
		||||
            } else {
 | 
			
		||||
                RecordLog.warn("[ClusterStateManager] Cannot change to server (no server SPI found)");
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            RecordLog.warn("[ClusterStateManager] Error when changing cluster mode to server", ex);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static boolean stopServer() {
 | 
			
		||||
        try {
 | 
			
		||||
            EmbeddedClusterTokenServer server = EmbeddedClusterTokenServerProvider.getServer();
 | 
			
		||||
            if (server != null) {
 | 
			
		||||
                server.stop();
 | 
			
		||||
                RecordLog.info("[ClusterStateManager] Stopping the cluster server");
 | 
			
		||||
                return true;
 | 
			
		||||
            } else {
 | 
			
		||||
                RecordLog.warn("[ClusterStateManager] Cannot stop server (no server SPI found)");
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            RecordLog.warn("[ClusterStateManager] Error when stopping server", ex);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The interval between two change operations should be greater than {@code MIN_INTERVAL} (by default 10s).
 | 
			
		||||
     * Or we need to wait for a while.
 | 
			
		||||
     */
 | 
			
		||||
    private static void sleepIfNeeded() {
 | 
			
		||||
        if (lastModified <= 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        long now = TimeUtil.currentTimeMillis();
 | 
			
		||||
        long durationPast = now - lastModified;
 | 
			
		||||
        long estimated = durationPast - MIN_INTERVAL;
 | 
			
		||||
        if (estimated < 0) {
 | 
			
		||||
            try {
 | 
			
		||||
                Thread.sleep(-estimated);
 | 
			
		||||
            } catch (InterruptedException e) {
 | 
			
		||||
                e.printStackTrace();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static long getLastModified() {
 | 
			
		||||
        return lastModified;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class ClusterStatePropertyListener implements PropertyListener<Integer> {
 | 
			
		||||
        @Override
 | 
			
		||||
        public synchronized void configLoad(Integer value) {
 | 
			
		||||
            applyStateInternal(value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public synchronized void configUpdate(Integer value) {
 | 
			
		||||
            applyStateInternal(value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static boolean applyStateInternal(Integer state) {
 | 
			
		||||
        if (state == null || state < CLUSTER_NOT_STARTED) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (state == mode) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            switch (state) {
 | 
			
		||||
                case CLUSTER_CLIENT:
 | 
			
		||||
                    return setToClient();
 | 
			
		||||
                case CLUSTER_SERVER:
 | 
			
		||||
                    return setToServer();
 | 
			
		||||
                case CLUSTER_NOT_STARTED:
 | 
			
		||||
                    setStop();
 | 
			
		||||
                    return true;
 | 
			
		||||
                default:
 | 
			
		||||
                    RecordLog.warn("[ClusterStateManager] Ignoring unknown cluster state: " + state);
 | 
			
		||||
                    return false;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            RecordLog.warn("[ClusterStateManager] Fatal error when applying state: " + state, t);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void setStop() {
 | 
			
		||||
        if (mode == CLUSTER_NOT_STARTED) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        RecordLog.info("[ClusterStateManager] Changing cluster mode to not-started");
 | 
			
		||||
        mode = CLUSTER_NOT_STARTED;
 | 
			
		||||
 | 
			
		||||
        sleepIfNeeded();
 | 
			
		||||
        lastModified = TimeUtil.currentTimeMillis();
 | 
			
		||||
 | 
			
		||||
        stopClient();
 | 
			
		||||
        stopServer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Apply given state to cluster mode.
 | 
			
		||||
     *
 | 
			
		||||
     * @param state valid state to apply
 | 
			
		||||
     */
 | 
			
		||||
    public static void applyState(Integer state) {
 | 
			
		||||
        stateProperty.updateValue(state);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void markToServer() {
 | 
			
		||||
        mode = CLUSTER_SERVER;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final int MIN_INTERVAL = 5 * 1000;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,98 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Result entity of acquiring cluster flow token.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class TokenResult {
 | 
			
		||||
 | 
			
		||||
    private Integer status;
 | 
			
		||||
 | 
			
		||||
    private int remaining;
 | 
			
		||||
    private int waitInMs;
 | 
			
		||||
 | 
			
		||||
    private long tokenId;
 | 
			
		||||
 | 
			
		||||
    private Map<String, String> attachments;
 | 
			
		||||
 | 
			
		||||
    public TokenResult() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TokenResult(Integer status) {
 | 
			
		||||
        this.status = status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getTokenId() {
 | 
			
		||||
        return tokenId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setTokenId(long tokenId) {
 | 
			
		||||
        this.tokenId = tokenId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Integer getStatus() {
 | 
			
		||||
        return status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TokenResult setStatus(Integer status) {
 | 
			
		||||
        this.status = status;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getRemaining() {
 | 
			
		||||
        return remaining;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TokenResult setRemaining(int remaining) {
 | 
			
		||||
        this.remaining = remaining;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getWaitInMs() {
 | 
			
		||||
        return waitInMs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TokenResult setWaitInMs(int waitInMs) {
 | 
			
		||||
        this.waitInMs = waitInMs;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Map<String, String> getAttachments() {
 | 
			
		||||
        return attachments;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TokenResult setAttachments(Map<String, String> attachments) {
 | 
			
		||||
        this.attachments = attachments;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return "TokenResult{" +
 | 
			
		||||
                "status=" + status +
 | 
			
		||||
                ", remaining=" + remaining +
 | 
			
		||||
                ", waitInMs=" + waitInMs +
 | 
			
		||||
                ", attachments=" + attachments +
 | 
			
		||||
                ", tokenId=" + tokenId +
 | 
			
		||||
                '}';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,73 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public final class TokenResultStatus {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Bad client request.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int BAD_REQUEST = -4;
 | 
			
		||||
    /**
 | 
			
		||||
     * Too many request in server.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int TOO_MANY_REQUEST = -2;
 | 
			
		||||
    /**
 | 
			
		||||
     * Server or client unexpected failure (due to transport or serialization failure).
 | 
			
		||||
     */
 | 
			
		||||
    public static final int FAIL = -1;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Token acquired.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int OK = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Token acquire failed (blocked).
 | 
			
		||||
     */
 | 
			
		||||
    public static final int BLOCKED = 1;
 | 
			
		||||
    /**
 | 
			
		||||
     * Should wait for next buckets.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int SHOULD_WAIT = 2;
 | 
			
		||||
    /**
 | 
			
		||||
     * Token acquire failed (no rule exists).
 | 
			
		||||
     */
 | 
			
		||||
    public static final int NO_RULE_EXISTS = 3;
 | 
			
		||||
    /**
 | 
			
		||||
     * Token acquire failed (reference resource is not available).
 | 
			
		||||
     */
 | 
			
		||||
    public static final int NO_REF_RULE_EXISTS = 4;
 | 
			
		||||
    /**
 | 
			
		||||
     * Token acquire failed (strategy not available).
 | 
			
		||||
     */
 | 
			
		||||
    public static final int NOT_AVAILABLE = 5;
 | 
			
		||||
    /**
 | 
			
		||||
     * Token is successfully released.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int RELEASE_OK = 6;
 | 
			
		||||
    /**
 | 
			
		||||
     * Token already is released before the request arrives.
 | 
			
		||||
     */
 | 
			
		||||
    public static final int ALREADY_RELEASE=7;
 | 
			
		||||
 | 
			
		||||
    private TokenResultStatus() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A simple descriptor for Sentinel token server.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class TokenServerDescriptor {
 | 
			
		||||
 | 
			
		||||
    private final String host;
 | 
			
		||||
    private final int port;
 | 
			
		||||
 | 
			
		||||
    private String type = "default";
 | 
			
		||||
 | 
			
		||||
    public TokenServerDescriptor(String host, int port) {
 | 
			
		||||
        this.host = host;
 | 
			
		||||
        this.port = port;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getHost() {
 | 
			
		||||
        return host;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getPort() {
 | 
			
		||||
        return port;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getType() {
 | 
			
		||||
        return type;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TokenServerDescriptor setType(String type) {
 | 
			
		||||
        this.type = type;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return "TokenServerDescriptor{" +
 | 
			
		||||
            "host='" + host + '\'' +
 | 
			
		||||
            ", port=" + port +
 | 
			
		||||
            ", type='" + type + '\'' +
 | 
			
		||||
            '}';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,63 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster;
 | 
			
		||||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service interface of flow control.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public interface TokenService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Request tokens from remote token server.
 | 
			
		||||
     *
 | 
			
		||||
     * @param ruleId the unique rule ID
 | 
			
		||||
     * @param acquireCount token count to acquire
 | 
			
		||||
     * @param prioritized whether the request is prioritized
 | 
			
		||||
     * @return result of the token request
 | 
			
		||||
     */
 | 
			
		||||
    TokenResult requestToken(Long ruleId, int acquireCount, boolean prioritized);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Request tokens for a specific parameter from remote token server.
 | 
			
		||||
     *
 | 
			
		||||
     * @param ruleId the unique rule ID
 | 
			
		||||
     * @param acquireCount token count to acquire
 | 
			
		||||
     * @param params parameter list
 | 
			
		||||
     * @return result of the token request
 | 
			
		||||
     */
 | 
			
		||||
    TokenResult requestParamToken(Long ruleId, int acquireCount, Collection<Object> params);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Request acquire concurrent tokens from remote token server.
 | 
			
		||||
     *
 | 
			
		||||
     * @param clientAddress the address of the request belong.
 | 
			
		||||
     * @param ruleId ruleId the unique rule ID
 | 
			
		||||
     * @param acquireCount token count to acquire
 | 
			
		||||
     * @return result of the token request
 | 
			
		||||
     */
 | 
			
		||||
    TokenResult requestConcurrentToken(String clientAddress,Long ruleId,int acquireCount);
 | 
			
		||||
    /**
 | 
			
		||||
     * Request release concurrent tokens from remote token server asynchronously.
 | 
			
		||||
     *
 | 
			
		||||
     * @param tokenId the unique token ID
 | 
			
		||||
     */
 | 
			
		||||
    void releaseConcurrentToken(Long tokenId);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,56 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster.client;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.cluster.TokenServerDescriptor;
 | 
			
		||||
import com.alibaba.csp.sentinel.cluster.TokenService;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Token client interface for distributed flow control.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public interface ClusterTokenClient extends TokenService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get descriptor of current token server.
 | 
			
		||||
     *
 | 
			
		||||
     * @return current token server if connected, otherwise null
 | 
			
		||||
     */
 | 
			
		||||
    TokenServerDescriptor currentServer();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Start the token client.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws Exception some error occurs
 | 
			
		||||
     */
 | 
			
		||||
    void start() throws Exception;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stop the token client.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws Exception some error occurs
 | 
			
		||||
     */
 | 
			
		||||
    void stop() throws Exception;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get state of the cluster token client.
 | 
			
		||||
     *
 | 
			
		||||
     * @return state of the cluster token client
 | 
			
		||||
     */
 | 
			
		||||
    int getState();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster.client;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Provider for a universal {@link ClusterTokenClient} instance.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public final class TokenClientProvider {
 | 
			
		||||
 | 
			
		||||
    private static ClusterTokenClient client = null;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        // Not strictly thread-safe, but it's OK since it will be resolved only once.
 | 
			
		||||
        resolveTokenClientInstance();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ClusterTokenClient getClient() {
 | 
			
		||||
        return client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void resolveTokenClientInstance() {
 | 
			
		||||
        ClusterTokenClient resolvedClient = SpiLoader.of(ClusterTokenClient.class).loadFirstInstance();
 | 
			
		||||
        if (resolvedClient == null) {
 | 
			
		||||
            RecordLog.info(
 | 
			
		||||
                "[TokenClientProvider] No existing cluster token client, cluster client mode will not be activated");
 | 
			
		||||
        } else {
 | 
			
		||||
            client = resolvedClient;
 | 
			
		||||
            RecordLog.info("[TokenClientProvider] Cluster token client resolved: {}",
 | 
			
		||||
                client.getClass().getCanonicalName());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isClientSpiAvailable() {
 | 
			
		||||
        return getClient() != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private TokenClientProvider() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster.log;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.eagleeye.EagleEye;
 | 
			
		||||
import com.alibaba.csp.sentinel.eagleeye.StatLogger;
 | 
			
		||||
import com.alibaba.csp.sentinel.log.LogBase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public final class ClusterClientStatLogUtil {
 | 
			
		||||
 | 
			
		||||
    private static final String FILE_NAME = "sentinel-cluster-client.log";
 | 
			
		||||
 | 
			
		||||
    private static StatLogger statLogger;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        String path = LogBase.getLogBaseDir() + FILE_NAME;
 | 
			
		||||
 | 
			
		||||
        statLogger = EagleEye.statLoggerBuilder("sentinel-cluster-client-record")
 | 
			
		||||
            .intervalSeconds(1)
 | 
			
		||||
            .entryDelimiter('|')
 | 
			
		||||
            .keyDelimiter(',')
 | 
			
		||||
            .valueDelimiter(',')
 | 
			
		||||
            .maxEntryCount(5000)
 | 
			
		||||
            .configLogFilePath(path)
 | 
			
		||||
            .maxFileSizeMB(300)
 | 
			
		||||
            .maxBackupIndex(3)
 | 
			
		||||
            .buildSingleton();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void log(String msg) {
 | 
			
		||||
        statLogger.stat(msg).count();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void log(String msg, int count) {
 | 
			
		||||
        statLogger.stat(msg).count(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ClusterClientStatLogUtil() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,59 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster.log;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.eagleeye.EagleEye;
 | 
			
		||||
import com.alibaba.csp.sentinel.eagleeye.StatLogger;
 | 
			
		||||
import com.alibaba.csp.sentinel.log.LogBase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public final class ClusterStatLogUtil {
 | 
			
		||||
 | 
			
		||||
    private static final String FILE_NAME = "sentinel-cluster.log";
 | 
			
		||||
 | 
			
		||||
    private static StatLogger statLogger;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        String path = LogBase.getLogBaseDir() + FILE_NAME;
 | 
			
		||||
 | 
			
		||||
        statLogger = EagleEye.statLoggerBuilder("sentinel-cluster-record")
 | 
			
		||||
            .intervalSeconds(1)
 | 
			
		||||
            .entryDelimiter('|')
 | 
			
		||||
            .keyDelimiter(',')
 | 
			
		||||
            .valueDelimiter(',')
 | 
			
		||||
            .maxEntryCount(5000)
 | 
			
		||||
            .configLogFilePath(path)
 | 
			
		||||
            .maxFileSizeMB(300)
 | 
			
		||||
            .maxBackupIndex(3)
 | 
			
		||||
            .buildSingleton();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void log(String msg) {
 | 
			
		||||
        statLogger.stat(msg).count();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void log(String msg, int count) {
 | 
			
		||||
        statLogger.stat(msg).count(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ClusterStatLogUtil() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster.server;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Token server interface for distributed flow control.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public interface ClusterTokenServer {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Start the Sentinel cluster server.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws Exception if any error occurs
 | 
			
		||||
     */
 | 
			
		||||
    void start() throws Exception;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stop the Sentinel cluster server.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws Exception if any error occurs
 | 
			
		||||
     */
 | 
			
		||||
    void stop() throws Exception;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster.server;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.cluster.TokenService;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Embedded token server interface that can work in embedded mode.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public interface EmbeddedClusterTokenServer extends ClusterTokenServer, TokenService {
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,53 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.cluster.server;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public final class EmbeddedClusterTokenServerProvider {
 | 
			
		||||
 | 
			
		||||
    private static EmbeddedClusterTokenServer server = null;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        resolveInstance();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void resolveInstance() {
 | 
			
		||||
        EmbeddedClusterTokenServer s = SpiLoader.of(EmbeddedClusterTokenServer.class).loadFirstInstance();
 | 
			
		||||
        if (s == null) {
 | 
			
		||||
            RecordLog.warn("[EmbeddedClusterTokenServerProvider] No existing cluster token server, cluster server mode will not be activated");
 | 
			
		||||
        } else {
 | 
			
		||||
            server = s;
 | 
			
		||||
            RecordLog.info("[EmbeddedClusterTokenServerProvider] Cluster token server resolved: {}",
 | 
			
		||||
                server.getClass().getCanonicalName());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static EmbeddedClusterTokenServer getServer() {
 | 
			
		||||
        return server;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isServerSpiAvailable() {
 | 
			
		||||
        return getServer() != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private EmbeddedClusterTokenServerProvider() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,50 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.concurrent;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.ThreadFactory;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Wrapped thread factory for better use.
 | 
			
		||||
 */
 | 
			
		||||
public class NamedThreadFactory implements ThreadFactory {
 | 
			
		||||
 | 
			
		||||
    private final ThreadGroup group;
 | 
			
		||||
    private final AtomicInteger threadNumber = new AtomicInteger(1);
 | 
			
		||||
 | 
			
		||||
    private final String namePrefix;
 | 
			
		||||
    private final boolean daemon;
 | 
			
		||||
 | 
			
		||||
    public NamedThreadFactory(String namePrefix, boolean daemon) {
 | 
			
		||||
        this.daemon = daemon;
 | 
			
		||||
        SecurityManager s = System.getSecurityManager();
 | 
			
		||||
        group = (s != null) ? s.getThreadGroup() :
 | 
			
		||||
            Thread.currentThread().getThreadGroup();
 | 
			
		||||
        this.namePrefix = namePrefix;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public NamedThreadFactory(String namePrefix) {
 | 
			
		||||
        this(namePrefix, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Thread newThread(Runnable r) {
 | 
			
		||||
        Thread t = new Thread(group, r, namePrefix + "-thread-" + threadNumber.getAndIncrement(), 0);
 | 
			
		||||
        t.setDaemon(daemon);
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,345 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.config;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.AssertUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.StringUtil;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The universal local configuration center of Sentinel. The config is retrieved from command line arguments
 | 
			
		||||
 * and customized properties file by default.
 | 
			
		||||
 *
 | 
			
		||||
 * @author leyou
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @author Lin Liang
 | 
			
		||||
 */
 | 
			
		||||
public final class SentinelConfig {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The default application type.
 | 
			
		||||
     *
 | 
			
		||||
     * @since 1.6.0
 | 
			
		||||
     */
 | 
			
		||||
    public static final int APP_TYPE_COMMON = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parameter value for using context classloader.
 | 
			
		||||
     */
 | 
			
		||||
    private static final String CLASSLOADER_CONTEXT = "context";
 | 
			
		||||
 | 
			
		||||
    private static final Map<String, String> props = new ConcurrentHashMap<>();
 | 
			
		||||
 | 
			
		||||
    private static int appType = APP_TYPE_COMMON;
 | 
			
		||||
    private static String appName = "";
 | 
			
		||||
 | 
			
		||||
    public static final String PROJECT_NAME_PROP_KEY = "project.name";
 | 
			
		||||
    public static final String APP_NAME_PROP_KEY = "csp.sentinel.app.name";
 | 
			
		||||
    public static final String APP_TYPE_PROP_KEY = "csp.sentinel.app.type";
 | 
			
		||||
    public static final String CHARSET = "csp.sentinel.charset";
 | 
			
		||||
    public static final String SINGLE_METRIC_FILE_SIZE = "csp.sentinel.metric.file.single.size";
 | 
			
		||||
    public static final String TOTAL_METRIC_FILE_COUNT = "csp.sentinel.metric.file.total.count";
 | 
			
		||||
    public static final String COLD_FACTOR = "csp.sentinel.flow.cold.factor";
 | 
			
		||||
    public static final String STATISTIC_MAX_RT = "csp.sentinel.statistic.max.rt";
 | 
			
		||||
    public static final String SPI_CLASSLOADER = "csp.sentinel.spi.classloader";
 | 
			
		||||
    public static final String METRIC_FLUSH_INTERVAL = "csp.sentinel.metric.flush.interval";
 | 
			
		||||
 | 
			
		||||
    public static final String DEFAULT_CHARSET = "UTF-8";
 | 
			
		||||
    public static final long DEFAULT_SINGLE_METRIC_FILE_SIZE = 1024 * 1024 * 50;
 | 
			
		||||
    public static final int DEFAULT_TOTAL_METRIC_FILE_COUNT = 6;
 | 
			
		||||
    public static final int DEFAULT_COLD_FACTOR = 3;
 | 
			
		||||
    public static final int DEFAULT_STATISTIC_MAX_RT = 5000;
 | 
			
		||||
    public static final long DEFAULT_METRIC_FLUSH_INTERVAL = 1L;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        try {
 | 
			
		||||
            initialize();
 | 
			
		||||
            loadProps();
 | 
			
		||||
            resolveAppName();
 | 
			
		||||
            resolveAppType();
 | 
			
		||||
            RecordLog.info("[SentinelConfig] Application type resolved: {}", appType);
 | 
			
		||||
        } catch (Throwable ex) {
 | 
			
		||||
            RecordLog.warn("[SentinelConfig] Failed to initialize", ex);
 | 
			
		||||
            ex.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void resolveAppType() {
 | 
			
		||||
        try {
 | 
			
		||||
            String type = getConfig(APP_TYPE_PROP_KEY);
 | 
			
		||||
            if (type == null) {
 | 
			
		||||
                appType = APP_TYPE_COMMON;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            appType = Integer.parseInt(type);
 | 
			
		||||
            if (appType < 0) {
 | 
			
		||||
                appType = APP_TYPE_COMMON;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            appType = APP_TYPE_COMMON;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void initialize() {
 | 
			
		||||
        // Init default properties.
 | 
			
		||||
        setConfig(CHARSET, DEFAULT_CHARSET);
 | 
			
		||||
        setConfig(SINGLE_METRIC_FILE_SIZE, String.valueOf(DEFAULT_SINGLE_METRIC_FILE_SIZE));
 | 
			
		||||
        setConfig(TOTAL_METRIC_FILE_COUNT, String.valueOf(DEFAULT_TOTAL_METRIC_FILE_COUNT));
 | 
			
		||||
        setConfig(COLD_FACTOR, String.valueOf(DEFAULT_COLD_FACTOR));
 | 
			
		||||
        setConfig(STATISTIC_MAX_RT, String.valueOf(DEFAULT_STATISTIC_MAX_RT));
 | 
			
		||||
        setConfig(METRIC_FLUSH_INTERVAL, String.valueOf(DEFAULT_METRIC_FLUSH_INTERVAL));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void loadProps() {
 | 
			
		||||
        Properties properties = SentinelConfigLoader.getProperties();
 | 
			
		||||
        for (Object key : properties.keySet()) {
 | 
			
		||||
            setConfig((String) key, (String) properties.get(key));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get config value of the specific key.
 | 
			
		||||
     *
 | 
			
		||||
     * @param key config key
 | 
			
		||||
     * @return the config value.
 | 
			
		||||
     */
 | 
			
		||||
    public static String getConfig(String key) {
 | 
			
		||||
        AssertUtil.notNull(key, "key cannot be null");
 | 
			
		||||
        return props.get(key);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Get config value of the specific key.
 | 
			
		||||
     *
 | 
			
		||||
     * @param key config key
 | 
			
		||||
     * @param envVariableKey Get the value of the environment variable with the given key
 | 
			
		||||
     * @return the config value.
 | 
			
		||||
     */
 | 
			
		||||
    public static String getConfig(String key, boolean envVariableKey) {
 | 
			
		||||
        AssertUtil.notNull(key, "key cannot be null");
 | 
			
		||||
        if (envVariableKey) {
 | 
			
		||||
            String value = System.getenv(key);
 | 
			
		||||
            if (StringUtil.isNotEmpty(value)) {
 | 
			
		||||
                return value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return getConfig(key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void setConfig(String key, String value) {
 | 
			
		||||
        AssertUtil.notNull(key, "key cannot be null");
 | 
			
		||||
        AssertUtil.notNull(value, "value cannot be null");
 | 
			
		||||
        props.put(key, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String removeConfig(String key) {
 | 
			
		||||
        AssertUtil.notNull(key, "key cannot be null");
 | 
			
		||||
        return props.remove(key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void setConfigIfAbsent(String key, String value) {
 | 
			
		||||
        AssertUtil.notNull(key, "key cannot be null");
 | 
			
		||||
        AssertUtil.notNull(value, "value cannot be null");
 | 
			
		||||
        String v = props.get(key);
 | 
			
		||||
        if (v == null) {
 | 
			
		||||
            props.put(key, value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String getAppName() {
 | 
			
		||||
        return appName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get application type.
 | 
			
		||||
     *
 | 
			
		||||
     * @return application type, common (0) by default
 | 
			
		||||
     * @since 1.6.0
 | 
			
		||||
     */
 | 
			
		||||
    public static int getAppType() {
 | 
			
		||||
        return appType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String charset() {
 | 
			
		||||
        return props.get(CHARSET);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the metric log flush interval in second
 | 
			
		||||
     * @return  the metric log flush interval in second
 | 
			
		||||
     * @since 1.8.1
 | 
			
		||||
     */
 | 
			
		||||
    public static long metricLogFlushIntervalSec() {
 | 
			
		||||
        String flushIntervalStr = SentinelConfig.getConfig(METRIC_FLUSH_INTERVAL);
 | 
			
		||||
        if (flushIntervalStr == null) {
 | 
			
		||||
            return DEFAULT_METRIC_FLUSH_INTERVAL;
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            return Long.parseLong(flushIntervalStr);
 | 
			
		||||
        } catch (Throwable throwable) {
 | 
			
		||||
            RecordLog.warn("[SentinelConfig] Parse the metricLogFlushInterval fail, use default value: "
 | 
			
		||||
                    + DEFAULT_METRIC_FLUSH_INTERVAL, throwable);
 | 
			
		||||
            return DEFAULT_METRIC_FLUSH_INTERVAL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static long singleMetricFileSize() {
 | 
			
		||||
        try {
 | 
			
		||||
            return Long.parseLong(props.get(SINGLE_METRIC_FILE_SIZE));
 | 
			
		||||
        } catch (Throwable throwable) {
 | 
			
		||||
            RecordLog.warn("[SentinelConfig] Parse singleMetricFileSize fail, use default value: "
 | 
			
		||||
                    + DEFAULT_SINGLE_METRIC_FILE_SIZE, throwable);
 | 
			
		||||
            return DEFAULT_SINGLE_METRIC_FILE_SIZE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int totalMetricFileCount() {
 | 
			
		||||
        try {
 | 
			
		||||
            return Integer.parseInt(props.get(TOTAL_METRIC_FILE_COUNT));
 | 
			
		||||
        } catch (Throwable throwable) {
 | 
			
		||||
            RecordLog.warn("[SentinelConfig] Parse totalMetricFileCount fail, use default value: "
 | 
			
		||||
                    + DEFAULT_TOTAL_METRIC_FILE_COUNT, throwable);
 | 
			
		||||
            return DEFAULT_TOTAL_METRIC_FILE_COUNT;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int coldFactor() {
 | 
			
		||||
        try {
 | 
			
		||||
            int coldFactor = Integer.parseInt(props.get(COLD_FACTOR));
 | 
			
		||||
            // check the cold factor larger than 1
 | 
			
		||||
            if (coldFactor <= 1) {
 | 
			
		||||
                coldFactor = DEFAULT_COLD_FACTOR;
 | 
			
		||||
                RecordLog.warn("cold factor=" + coldFactor + ", should be larger than 1, use default value: "
 | 
			
		||||
                        + DEFAULT_COLD_FACTOR);
 | 
			
		||||
            }
 | 
			
		||||
            return coldFactor;
 | 
			
		||||
        } catch (Throwable throwable) {
 | 
			
		||||
            RecordLog.warn("[SentinelConfig] Parse coldFactor fail, use default value: "
 | 
			
		||||
                    + DEFAULT_COLD_FACTOR, throwable);
 | 
			
		||||
            return DEFAULT_COLD_FACTOR;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>Get the max RT value that Sentinel could accept for system BBR strategy.</p>
 | 
			
		||||
     *
 | 
			
		||||
     * @return the max allowed RT value
 | 
			
		||||
     * @since 1.4.1
 | 
			
		||||
     */
 | 
			
		||||
    public static int statisticMaxRt() {
 | 
			
		||||
        String v = props.get(STATISTIC_MAX_RT);
 | 
			
		||||
        try {
 | 
			
		||||
            if (StringUtil.isEmpty(v)) {
 | 
			
		||||
                return DEFAULT_STATISTIC_MAX_RT;
 | 
			
		||||
            }
 | 
			
		||||
            return Integer.parseInt(v);
 | 
			
		||||
        } catch (Throwable throwable) {
 | 
			
		||||
            RecordLog.warn("[SentinelConfig] Invalid statisticMaxRt value: {}, using the default value instead: "
 | 
			
		||||
                    + DEFAULT_STATISTIC_MAX_RT, v, throwable);
 | 
			
		||||
            SentinelConfig.setConfig(STATISTIC_MAX_RT, String.valueOf(DEFAULT_STATISTIC_MAX_RT));
 | 
			
		||||
            return DEFAULT_STATISTIC_MAX_RT;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function for resolving project name. The order is elaborated below:
 | 
			
		||||
     *
 | 
			
		||||
     * <ol>
 | 
			
		||||
     * <li>Resolve the value from {@code CSP_SENTINEL_APP_NAME} system environment;</li>
 | 
			
		||||
     * <li>Resolve the value from {@code csp.sentinel.app.name} system property;</li>
 | 
			
		||||
     * <li>Resolve the value from {@code project.name} system property (for compatibility);</li>
 | 
			
		||||
     * <li>Resolve the value from {@code sun.java.command} system property, then remove path, arguments and ".jar" or ".JAR"
 | 
			
		||||
     * suffix, use the result as app name. Note that whitespace in file name or path is not allowed, or a
 | 
			
		||||
     * wrong app name may be gotten, For example:
 | 
			
		||||
     * <p>
 | 
			
		||||
     * <code>
 | 
			
		||||
     * "test.Main" -> test.Main<br/>
 | 
			
		||||
     * "/target/test.Main" -> test.Main<br/>
 | 
			
		||||
     * "/target/test.Main args1" -> test.Main<br/>
 | 
			
		||||
     * "Main.jar" -> Main<br/>
 | 
			
		||||
     * "/target/Main.JAR args1" -> Main<br/>
 | 
			
		||||
     * "Mai n.jar" -> Mai // whitespace in file name is not allowed<br/>
 | 
			
		||||
     * </code>
 | 
			
		||||
     * </p>
 | 
			
		||||
     * </li>
 | 
			
		||||
     * </ol>
 | 
			
		||||
     */
 | 
			
		||||
    private static void resolveAppName() {
 | 
			
		||||
        // Priority: system env -> csp.sentinel.app.name -> project.name -> main class (or jar) name
 | 
			
		||||
        String envKey = toEnvKey(APP_NAME_PROP_KEY);
 | 
			
		||||
        String n = System.getenv(envKey);
 | 
			
		||||
        if (!StringUtil.isBlank(n)) {
 | 
			
		||||
            appName = n;
 | 
			
		||||
            RecordLog.info("App name resolved from system env {}: {}", envKey, appName);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        n = props.get(APP_NAME_PROP_KEY);
 | 
			
		||||
        if (!StringUtil.isBlank(n)) {
 | 
			
		||||
            appName = n;
 | 
			
		||||
            RecordLog.info("App name resolved from property {}: {}", APP_NAME_PROP_KEY, appName);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        n = props.get(PROJECT_NAME_PROP_KEY);
 | 
			
		||||
        if (!StringUtil.isBlank(n)) {
 | 
			
		||||
            appName = n;
 | 
			
		||||
            RecordLog.info("App name resolved from property {}: {}", PROJECT_NAME_PROP_KEY, appName);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        // Parse sun.java.command property by default.
 | 
			
		||||
        String command = System.getProperty("sun.java.command");
 | 
			
		||||
        if (StringUtil.isBlank(command)) {
 | 
			
		||||
            RecordLog.warn("Cannot resolve default appName from property sun.java.command");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        command = command.split("\\s")[0];
 | 
			
		||||
        String separator = File.separator;
 | 
			
		||||
        if (command.contains(separator)) {
 | 
			
		||||
            String[] strs;
 | 
			
		||||
            if ("\\".equals(separator)) {
 | 
			
		||||
                // Handle separator in Windows.
 | 
			
		||||
                strs = command.split("\\\\");
 | 
			
		||||
            } else {
 | 
			
		||||
                strs = command.split(separator);
 | 
			
		||||
            }
 | 
			
		||||
            command = strs[strs.length - 1];
 | 
			
		||||
        }
 | 
			
		||||
        if (command.toLowerCase().endsWith(".jar")) {
 | 
			
		||||
            command = command.substring(0, command.length() - 4);
 | 
			
		||||
        }
 | 
			
		||||
        appName = command;
 | 
			
		||||
        RecordLog.info("App name resolved from default: {}", appName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String toEnvKey(/*@NotBlank*/ String propKey) {
 | 
			
		||||
        return propKey.toUpperCase().replace('.', '_');
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether use context classloader via config parameter
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether use context classloader
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean shouldUseContextClassloader() {
 | 
			
		||||
        String classloaderConf = SentinelConfig.getConfig(SentinelConfig.SPI_CLASSLOADER);
 | 
			
		||||
        return CLASSLOADER_CONTEXT.equalsIgnoreCase(classloaderConf);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private SentinelConfig() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,86 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.config;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.AppNameUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.ConfigUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.StringUtil;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
import java.util.concurrent.CopyOnWriteArraySet;
 | 
			
		||||
 | 
			
		||||
import static com.alibaba.csp.sentinel.util.ConfigUtil.addSeparator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>The loader that responsible for loading Sentinel common configurations.</p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author lianglin
 | 
			
		||||
 * @since 1.7.0
 | 
			
		||||
 */
 | 
			
		||||
public final class SentinelConfigLoader {
 | 
			
		||||
 | 
			
		||||
    public static final String SENTINEL_CONFIG_ENV_KEY = "CSP_SENTINEL_CONFIG_FILE";
 | 
			
		||||
    public static final String SENTINEL_CONFIG_PROPERTY_KEY = "csp.sentinel.config.file";
 | 
			
		||||
 | 
			
		||||
    private static final String DEFAULT_SENTINEL_CONFIG_FILE = "classpath:sentinel.properties";
 | 
			
		||||
 | 
			
		||||
    private static Properties properties = new Properties();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        try {
 | 
			
		||||
            load();
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            RecordLog.warn("[SentinelConfigLoader] Failed to initialize configuration items", t);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void load() {
 | 
			
		||||
        // Order: system property -> system env -> default file (classpath:sentinel.properties) -> legacy path
 | 
			
		||||
        String fileName = System.getProperty(SENTINEL_CONFIG_PROPERTY_KEY);
 | 
			
		||||
        if (StringUtil.isBlank(fileName)) {
 | 
			
		||||
            fileName = System.getenv(SENTINEL_CONFIG_ENV_KEY);
 | 
			
		||||
            if (StringUtil.isBlank(fileName)) {
 | 
			
		||||
                fileName = DEFAULT_SENTINEL_CONFIG_FILE;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Properties p = ConfigUtil.loadProperties(fileName);
 | 
			
		||||
        if (p != null && !p.isEmpty()) {
 | 
			
		||||
            RecordLog.info("[SentinelConfigLoader] Loading Sentinel config from {}", fileName);
 | 
			
		||||
            properties.putAll(p);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (Map.Entry<Object, Object> entry : new CopyOnWriteArraySet<>(System.getProperties().entrySet())) {
 | 
			
		||||
            String configKey = entry.getKey().toString();
 | 
			
		||||
            String newConfigValue = entry.getValue().toString();
 | 
			
		||||
            String oldConfigValue = properties.getProperty(configKey);
 | 
			
		||||
            properties.put(configKey, newConfigValue);
 | 
			
		||||
            if (oldConfigValue != null) {
 | 
			
		||||
                RecordLog.info("[SentinelConfigLoader] JVM parameter overrides {}: {} -> {}",
 | 
			
		||||
                        configKey, oldConfigValue, newConfigValue);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static Properties getProperties() {
 | 
			
		||||
        return properties;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,201 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.context;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.Entry;
 | 
			
		||||
import com.alibaba.csp.sentinel.SphO;
 | 
			
		||||
import com.alibaba.csp.sentinel.SphU;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.DefaultNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.EntranceNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.Node;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class holds metadata of current invocation:<br/>
 | 
			
		||||
 *
 | 
			
		||||
 * <ul>
 | 
			
		||||
 * <li>the {@link EntranceNode}: the root of the current invocation
 | 
			
		||||
 * tree.</li>
 | 
			
		||||
 * <li>the current {@link Entry}: the current invocation point.</li>
 | 
			
		||||
 * <li>the current {@link Node}: the statistics related to the
 | 
			
		||||
 * {@link Entry}.</li>
 | 
			
		||||
 * <li>the origin: The origin is useful when we want to control different
 | 
			
		||||
 * invoker/consumer separately. Usually the origin could be the Service Consumer's app name
 | 
			
		||||
 * or origin IP. </li>
 | 
			
		||||
 * </ul>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Each {@link SphU}#entry() or {@link SphO}#entry() should be in a {@link Context},
 | 
			
		||||
 * if we don't invoke {@link ContextUtil}#enter() explicitly, DEFAULT context will be used.
 | 
			
		||||
 * </p>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * A invocation tree will be created if we invoke {@link SphU}#entry() multi times in
 | 
			
		||||
 * the same context.
 | 
			
		||||
 * </p>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Same resource in different context will count separately, see {@link NodeSelectorSlot}.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author leyou(lihao)
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @see ContextUtil
 | 
			
		||||
 * @see NodeSelectorSlot
 | 
			
		||||
 */
 | 
			
		||||
public class Context {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Context name.
 | 
			
		||||
     */
 | 
			
		||||
    private final String name;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The entrance node of current invocation tree.
 | 
			
		||||
     */
 | 
			
		||||
    private DefaultNode entranceNode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Current processing entry.
 | 
			
		||||
     */
 | 
			
		||||
    private Entry curEntry;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The origin of this context (usually indicate different invokers, e.g. service consumer name or origin IP).
 | 
			
		||||
     */
 | 
			
		||||
    private String origin = "";
 | 
			
		||||
 | 
			
		||||
    private final boolean async;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new async context.
 | 
			
		||||
     *
 | 
			
		||||
     * @param entranceNode entrance node of the context
 | 
			
		||||
     * @param name context name
 | 
			
		||||
     * @return the new created context
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    public static Context newAsyncContext(DefaultNode entranceNode, String name) {
 | 
			
		||||
        return new Context(name, entranceNode, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Context(DefaultNode entranceNode, String name) {
 | 
			
		||||
        this(name, entranceNode, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Context(String name, DefaultNode entranceNode, boolean async) {
 | 
			
		||||
        this.name = name;
 | 
			
		||||
        this.entranceNode = entranceNode;
 | 
			
		||||
        this.async = async;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isAsync() {
 | 
			
		||||
        return async;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Node getCurNode() {
 | 
			
		||||
        return curEntry == null ? null : curEntry.getCurNode();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Context setCurNode(Node node) {
 | 
			
		||||
        this.curEntry.setCurNode(node);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Entry getCurEntry() {
 | 
			
		||||
        return curEntry;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Context setCurEntry(Entry curEntry) {
 | 
			
		||||
        this.curEntry = curEntry;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getOrigin() {
 | 
			
		||||
        return origin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Context setOrigin(String origin) {
 | 
			
		||||
        this.origin = origin;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public double getOriginTotalQps() {
 | 
			
		||||
        return getOriginNode() == null ? 0 : getOriginNode().totalQps();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public double getOriginBlockQps() {
 | 
			
		||||
        return getOriginNode() == null ? 0 : getOriginNode().blockQps();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public double getOriginPassReqQps() {
 | 
			
		||||
        return getOriginNode() == null ? 0 : getOriginNode().successQps();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public double getOriginPassQps() {
 | 
			
		||||
        return getOriginNode() == null ? 0 : getOriginNode().passQps();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getOriginTotalRequest() {
 | 
			
		||||
        return getOriginNode() == null ? 0 : getOriginNode().totalRequest();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getOriginBlockRequest() {
 | 
			
		||||
        return getOriginNode() == null ? 0 : getOriginNode().blockRequest();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public double getOriginAvgRt() {
 | 
			
		||||
        return getOriginNode() == null ? 0 : getOriginNode().avgRt();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getOriginCurThreadNum() {
 | 
			
		||||
        return getOriginNode() == null ? 0 : getOriginNode().curThreadNum();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DefaultNode getEntranceNode() {
 | 
			
		||||
        return entranceNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the parent {@link Node} of the current.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the parent node of the current.
 | 
			
		||||
     */
 | 
			
		||||
    public Node getLastNode() {
 | 
			
		||||
        if (curEntry != null && curEntry.getLastNode() != null) {
 | 
			
		||||
            return curEntry.getLastNode();
 | 
			
		||||
        } else {
 | 
			
		||||
            return entranceNode;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Node getOriginNode() {
 | 
			
		||||
        return curEntry == null ? null : curEntry.getOriginNode();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return "Context{" +
 | 
			
		||||
            "name='" + name + '\'' +
 | 
			
		||||
            ", entranceNode=" + entranceNode +
 | 
			
		||||
            ", curEntry=" + curEntry +
 | 
			
		||||
            ", origin='" + origin + '\'' +
 | 
			
		||||
            ", async=" + async +
 | 
			
		||||
            '}';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,26 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.context;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 */
 | 
			
		||||
public class ContextNameDefineException extends RuntimeException {
 | 
			
		||||
 | 
			
		||||
    public ContextNameDefineException(String message) {
 | 
			
		||||
        super(message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,281 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.context;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.locks.ReentrantLock;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.Constants;
 | 
			
		||||
import com.alibaba.csp.sentinel.EntryType;
 | 
			
		||||
import com.alibaba.csp.sentinel.SphO;
 | 
			
		||||
import com.alibaba.csp.sentinel.SphU;
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.DefaultNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.EntranceNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.Node;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Utility class to get or create {@link Context} in current thread.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Each {@link SphU}#entry() or {@link SphO}#entry() should be in a {@link Context}.
 | 
			
		||||
 * If we don't invoke {@link ContextUtil}#enter() explicitly, DEFAULT context will be used.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author leyou(lihao)
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public class ContextUtil {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Store the context in ThreadLocal for easy access.
 | 
			
		||||
     */
 | 
			
		||||
    private static ThreadLocal<Context> contextHolder = new ThreadLocal<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Holds all {@link EntranceNode}. Each {@link EntranceNode} is associated with a distinct context name.
 | 
			
		||||
     */
 | 
			
		||||
    private static volatile Map<String, DefaultNode> contextNameNodeMap = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
    private static final ReentrantLock LOCK = new ReentrantLock();
 | 
			
		||||
    private static final Context NULL_CONTEXT = new NullContext();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        // Cache the entrance node for default context.
 | 
			
		||||
        initDefaultContext();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void initDefaultContext() {
 | 
			
		||||
        String defaultContextName = Constants.CONTEXT_DEFAULT_NAME;
 | 
			
		||||
        EntranceNode node = new EntranceNode(new StringResourceWrapper(defaultContextName, EntryType.IN), null);
 | 
			
		||||
        Constants.ROOT.addChild(node);
 | 
			
		||||
        contextNameNodeMap.put(defaultContextName, node);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Not thread-safe, only for test.
 | 
			
		||||
     */
 | 
			
		||||
    static void resetContextMap() {
 | 
			
		||||
        if (contextNameNodeMap != null) {
 | 
			
		||||
            RecordLog.warn("Context map cleared and reset to initial state");
 | 
			
		||||
            contextNameNodeMap.clear();
 | 
			
		||||
            initDefaultContext();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Enter the invocation context, which marks as the entrance of an invocation chain.
 | 
			
		||||
     * The context is wrapped with {@code ThreadLocal}, meaning that each thread has it's own {@link Context}.
 | 
			
		||||
     * New context will be created if current thread doesn't have one.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * A context will be bound with an {@link EntranceNode}, which represents the entrance statistic node
 | 
			
		||||
     * of the invocation chain. New {@link EntranceNode} will be created if
 | 
			
		||||
     * current context does't have one. Note that same context name will share
 | 
			
		||||
     * same {@link EntranceNode} globally.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The origin node will be created in {@link com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot}.
 | 
			
		||||
     * Note that each distinct {@code origin} of different resources will lead to creating different new
 | 
			
		||||
     * {@link Node}, meaning that total amount of created origin statistic nodes will be:<br/>
 | 
			
		||||
     * {@code distinct resource name amount * distinct origin count}.<br/>
 | 
			
		||||
     * So when there are too many origins, memory footprint should be carefully considered.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Same resource in different context will count separately, see {@link NodeSelectorSlot}.
 | 
			
		||||
     * </p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param name   the context name
 | 
			
		||||
     * @param origin the origin of this invocation, usually the origin could be the Service
 | 
			
		||||
     *               Consumer's app name. The origin is useful when we want to control different
 | 
			
		||||
     *               invoker/consumer separately.
 | 
			
		||||
     * @return The invocation context of the current thread
 | 
			
		||||
     */
 | 
			
		||||
    public static Context enter(String name, String origin) {
 | 
			
		||||
        if (Constants.CONTEXT_DEFAULT_NAME.equals(name)) {
 | 
			
		||||
            throw new ContextNameDefineException(
 | 
			
		||||
                "The " + Constants.CONTEXT_DEFAULT_NAME + " can't be permit to defined!");
 | 
			
		||||
        }
 | 
			
		||||
        return trueEnter(name, origin);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected static Context trueEnter(String name, String origin) {
 | 
			
		||||
        Context context = contextHolder.get();
 | 
			
		||||
        if (context == null) {
 | 
			
		||||
            Map<String, DefaultNode> localCacheNameMap = contextNameNodeMap;
 | 
			
		||||
            DefaultNode node = localCacheNameMap.get(name);
 | 
			
		||||
            if (node == null) {
 | 
			
		||||
                if (localCacheNameMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
 | 
			
		||||
                    setNullContext();
 | 
			
		||||
                    return NULL_CONTEXT;
 | 
			
		||||
                } else {
 | 
			
		||||
                    LOCK.lock();
 | 
			
		||||
                    try {
 | 
			
		||||
                        node = contextNameNodeMap.get(name);
 | 
			
		||||
                        if (node == null) {
 | 
			
		||||
                            if (contextNameNodeMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
 | 
			
		||||
                                setNullContext();
 | 
			
		||||
                                return NULL_CONTEXT;
 | 
			
		||||
                            } else {
 | 
			
		||||
                                node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);
 | 
			
		||||
                                // Add entrance node.
 | 
			
		||||
                                Constants.ROOT.addChild(node);
 | 
			
		||||
 | 
			
		||||
                                Map<String, DefaultNode> newMap = new HashMap<>(contextNameNodeMap.size() + 1);
 | 
			
		||||
                                newMap.putAll(contextNameNodeMap);
 | 
			
		||||
                                newMap.put(name, node);
 | 
			
		||||
                                contextNameNodeMap = newMap;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } finally {
 | 
			
		||||
                        LOCK.unlock();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            context = new Context(node, name);
 | 
			
		||||
            context.setOrigin(origin);
 | 
			
		||||
            contextHolder.set(context);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return context;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static boolean shouldWarn = true;
 | 
			
		||||
 | 
			
		||||
    private static void setNullContext() {
 | 
			
		||||
        contextHolder.set(NULL_CONTEXT);
 | 
			
		||||
        // Don't need to be thread-safe.
 | 
			
		||||
        if (shouldWarn) {
 | 
			
		||||
            RecordLog.warn("[SentinelStatusChecker] WARN: Amount of context exceeds the threshold "
 | 
			
		||||
                + Constants.MAX_CONTEXT_NAME_SIZE + ". Entries in new contexts will NOT take effect!");
 | 
			
		||||
            shouldWarn = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Enter the invocation context, which marks as the entrance of an invocation chain.
 | 
			
		||||
     * The context is wrapped with {@code ThreadLocal}, meaning that each thread has it's own {@link Context}.
 | 
			
		||||
     * New context will be created if current thread doesn't have one.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * A context will be bound with an {@link EntranceNode}, which represents the entrance statistic node
 | 
			
		||||
     * of the invocation chain. New {@link EntranceNode} will be created if
 | 
			
		||||
     * current context does't have one. Note that same context name will share
 | 
			
		||||
     * same {@link EntranceNode} globally.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Same resource in different context will count separately, see {@link NodeSelectorSlot}.
 | 
			
		||||
     * </p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the context name
 | 
			
		||||
     * @return The invocation context of the current thread
 | 
			
		||||
     */
 | 
			
		||||
    public static Context enter(String name) {
 | 
			
		||||
        return enter(name, "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Exit context of current thread, that is removing {@link Context} in the
 | 
			
		||||
     * ThreadLocal.
 | 
			
		||||
     */
 | 
			
		||||
    public static void exit() {
 | 
			
		||||
        Context context = contextHolder.get();
 | 
			
		||||
        if (context != null && context.getCurEntry() == null) {
 | 
			
		||||
            contextHolder.set(null);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get current size of context entrance node map.
 | 
			
		||||
     *
 | 
			
		||||
     * @return current size of context entrance node map
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    public static int contextSize() {
 | 
			
		||||
        return contextNameNodeMap.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if provided context is a default auto-created context.
 | 
			
		||||
     *
 | 
			
		||||
     * @param context context to check
 | 
			
		||||
     * @return true if it is a default context, otherwise false
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isDefaultContext(Context context) {
 | 
			
		||||
        if (context == null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return Constants.CONTEXT_DEFAULT_NAME.equals(context.getName());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get {@link Context} of current thread.
 | 
			
		||||
     *
 | 
			
		||||
     * @return context of current thread. Null value will be return if current
 | 
			
		||||
     * thread does't have context.
 | 
			
		||||
     */
 | 
			
		||||
    public static Context getContext() {
 | 
			
		||||
        return contextHolder.get();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Replace current context with the provided context.
 | 
			
		||||
     * This is mainly designed for context switching (e.g. in asynchronous invocation).
 | 
			
		||||
     * </p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Note: When switching context manually, remember to restore the original context.
 | 
			
		||||
     * For common scenarios, you can use {@link #runOnContext(Context, Runnable)}.
 | 
			
		||||
     * </p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param newContext new context to set
 | 
			
		||||
     * @return old context
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    static Context replaceContext(Context newContext) {
 | 
			
		||||
        Context backupContext = contextHolder.get();
 | 
			
		||||
        if (newContext == null) {
 | 
			
		||||
            contextHolder.remove();
 | 
			
		||||
        } else {
 | 
			
		||||
            contextHolder.set(newContext);
 | 
			
		||||
        }
 | 
			
		||||
        return backupContext;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the code within provided context.
 | 
			
		||||
     * This is mainly designed for context switching (e.g. in asynchronous invocation).
 | 
			
		||||
     *
 | 
			
		||||
     * @param context the context
 | 
			
		||||
     * @param f       lambda to run within the context
 | 
			
		||||
     * @since 0.2.0
 | 
			
		||||
     */
 | 
			
		||||
    public static void runOnContext(Context context, Runnable f) {
 | 
			
		||||
        Context curContext = replaceContext(context);
 | 
			
		||||
        try {
 | 
			
		||||
            f.run();
 | 
			
		||||
        } finally {
 | 
			
		||||
            replaceContext(curContext);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.context;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.Constants;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * If total {@link Context} exceed {@link Constants#MAX_CONTEXT_NAME_SIZE}, a
 | 
			
		||||
 * {@link NullContext} will get when invoke {@link ContextUtil}.enter(), means
 | 
			
		||||
 * no rules checking will do.
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 */
 | 
			
		||||
public class NullContext extends Context {
 | 
			
		||||
 | 
			
		||||
    public NullContext() {
 | 
			
		||||
        super(null, "null_context_internal");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,90 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
class BaseLoggerBuilder<T extends BaseLoggerBuilder<T>> {
 | 
			
		||||
 | 
			
		||||
    protected final String loggerName;
 | 
			
		||||
 | 
			
		||||
    protected String filePath = null;
 | 
			
		||||
 | 
			
		||||
    protected long maxFileSize = 1024;
 | 
			
		||||
 | 
			
		||||
    protected char entryDelimiter = '|';
 | 
			
		||||
 | 
			
		||||
    protected int maxBackupIndex = 3;
 | 
			
		||||
 | 
			
		||||
    BaseLoggerBuilder(String loggerName) {
 | 
			
		||||
        this.loggerName = loggerName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public T logFilePath(String logFilePath) {
 | 
			
		||||
        return configLogFilePath(logFilePath, EagleEye.EAGLEEYE_LOG_DIR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public T appFilePath(String appFilePath) {
 | 
			
		||||
        return configLogFilePath(appFilePath, EagleEye.APP_LOG_DIR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public T baseLogFilePath(String baseLogFilePath) {
 | 
			
		||||
        return configLogFilePath(baseLogFilePath, EagleEye.BASE_LOG_DIR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    private T configLogFilePath(String filePathToConfig, String basePath) {
 | 
			
		||||
        EagleEyeCoreUtils.checkNotNullEmpty(filePathToConfig, "filePath");
 | 
			
		||||
        if (filePathToConfig.charAt(0) != '/') {
 | 
			
		||||
            filePathToConfig = basePath + filePathToConfig;
 | 
			
		||||
        }
 | 
			
		||||
        this.filePath = filePathToConfig;
 | 
			
		||||
        return (T)this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    public T configLogFilePath(String filePath) {
 | 
			
		||||
        EagleEyeCoreUtils.checkNotNullEmpty(filePath, "filePath");
 | 
			
		||||
        this.filePath = filePath;
 | 
			
		||||
        return (T)this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    public T maxFileSizeMB(long maxFileSizeMB) {
 | 
			
		||||
        if (maxFileSize < 10) {
 | 
			
		||||
            throw new IllegalArgumentException("Invalid maxFileSizeMB");
 | 
			
		||||
        }
 | 
			
		||||
        this.maxFileSize = maxFileSizeMB * 1024 * 1024;
 | 
			
		||||
        return (T)this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    public T maxBackupIndex(int maxBackupIndex) {
 | 
			
		||||
        if (maxBackupIndex < 1) {
 | 
			
		||||
            throw new IllegalArgumentException("");
 | 
			
		||||
        }
 | 
			
		||||
        this.maxBackupIndex = maxBackupIndex;
 | 
			
		||||
        return (T)this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    public T entryDelimiter(char entryDelimiter) {
 | 
			
		||||
        this.entryDelimiter = entryDelimiter;
 | 
			
		||||
        return (T)this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    String getLoggerName() {
 | 
			
		||||
        return loggerName;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,235 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.io.StringWriter;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.nio.charset.Charset;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
public final class EagleEye {
 | 
			
		||||
 | 
			
		||||
    public static final String CLASS_LOCATION = getEagleEyeLocation();
 | 
			
		||||
 | 
			
		||||
    static final String USER_HOME = locateUserHome();
 | 
			
		||||
 | 
			
		||||
    static final String BASE_LOG_DIR = locateBaseLogPath();
 | 
			
		||||
 | 
			
		||||
    static final String EAGLEEYE_LOG_DIR = locateEagleEyeLogPath();
 | 
			
		||||
 | 
			
		||||
    static final String APP_LOG_DIR = locateAppLogPath();
 | 
			
		||||
 | 
			
		||||
    static final Charset DEFAULT_CHARSET = getDefaultOutputCharset();
 | 
			
		||||
 | 
			
		||||
    static final String EAGLEEYE_SELF_LOG_FILE = EagleEye.EAGLEEYE_LOG_DIR + "eagleeye-self.log";
 | 
			
		||||
 | 
			
		||||
    // 200MB
 | 
			
		||||
    static final long MAX_SELF_LOG_FILE_SIZE = 200 * 1024 * 1024;
 | 
			
		||||
 | 
			
		||||
    static EagleEyeAppender selfAppender = createSelfLogger();
 | 
			
		||||
 | 
			
		||||
    static private TokenBucket exceptionBucket = new TokenBucket(10, TimeUnit.SECONDS.toMillis(10));
 | 
			
		||||
 | 
			
		||||
    static String getEagleEyeLocation() {
 | 
			
		||||
        try {
 | 
			
		||||
            URL resource = EagleEye.class.getProtectionDomain().getCodeSource().getLocation();
 | 
			
		||||
            if (resource != null) {
 | 
			
		||||
                return resource.toString();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            // ignore
 | 
			
		||||
        }
 | 
			
		||||
        return "unknown location";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Charset getDefaultOutputCharset() {
 | 
			
		||||
        Charset cs;
 | 
			
		||||
        String charsetName = EagleEyeCoreUtils.getSystemProperty("EAGLEEYE.CHARSET");
 | 
			
		||||
        if (EagleEyeCoreUtils.isNotBlank(charsetName)) {
 | 
			
		||||
            charsetName = charsetName.trim();
 | 
			
		||||
            try {
 | 
			
		||||
                cs = Charset.forName(charsetName);
 | 
			
		||||
                if (cs != null) {
 | 
			
		||||
                    return cs;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                // quietly
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            cs = Charset.forName("GB18030");
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            try {
 | 
			
		||||
                cs = Charset.forName("GBK");
 | 
			
		||||
            } catch (Exception e2) {
 | 
			
		||||
                cs = Charset.forName("UTF-8");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return cs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String locateUserHome() {
 | 
			
		||||
        String userHome = EagleEyeCoreUtils.getSystemProperty("user.home");
 | 
			
		||||
        if (EagleEyeCoreUtils.isNotBlank(userHome)) {
 | 
			
		||||
            if (!userHome.endsWith(File.separator)) {
 | 
			
		||||
                userHome += File.separator;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            userHome = "/tmp/";
 | 
			
		||||
        }
 | 
			
		||||
        return userHome;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String locateBaseLogPath() {
 | 
			
		||||
        String tmpPath = EagleEyeCoreUtils.getSystemProperty("JM.LOG.PATH");
 | 
			
		||||
        if (EagleEyeCoreUtils.isNotBlank(tmpPath)) {
 | 
			
		||||
            if (!tmpPath.endsWith(File.separator)) {
 | 
			
		||||
                tmpPath += File.separator;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            tmpPath = USER_HOME + "logs" + File.separator;
 | 
			
		||||
        }
 | 
			
		||||
        return tmpPath;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String locateEagleEyeLogPath() {
 | 
			
		||||
        String tmpPath = EagleEyeCoreUtils.getSystemProperty("EAGLEEYE.LOG.PATH");
 | 
			
		||||
        if (EagleEyeCoreUtils.isNotBlank(tmpPath)) {
 | 
			
		||||
            if (!tmpPath.endsWith(File.separator)) {
 | 
			
		||||
                tmpPath += File.separator;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            tmpPath = BASE_LOG_DIR + "eagleeye" + File.separator;
 | 
			
		||||
        }
 | 
			
		||||
        return tmpPath;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String locateAppLogPath() {
 | 
			
		||||
        String appName = EagleEyeCoreUtils.getSystemProperty("project.name");
 | 
			
		||||
        if (EagleEyeCoreUtils.isNotBlank(appName)) {
 | 
			
		||||
            return USER_HOME + appName + File.separator + "logs" + File.separator;
 | 
			
		||||
        } else {
 | 
			
		||||
            return EAGLEEYE_LOG_DIR;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static private final EagleEyeAppender createSelfLogger() {
 | 
			
		||||
        EagleEyeRollingFileAppender selfAppender = new EagleEyeRollingFileAppender(EAGLEEYE_SELF_LOG_FILE,
 | 
			
		||||
            EagleEyeCoreUtils.getSystemPropertyForLong("EAGLEEYE.LOG.SELF.FILESIZE", MAX_SELF_LOG_FILE_SIZE),
 | 
			
		||||
            false);
 | 
			
		||||
        return new SyncAppender(selfAppender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        initEagleEye();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void initEagleEye() {
 | 
			
		||||
        try {
 | 
			
		||||
            selfLog("[INFO] EagleEye started (" + CLASS_LOCATION + ")" + ", classloader="
 | 
			
		||||
                + EagleEye.class.getClassLoader());
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            selfLog("[INFO] EagleEye started (" + CLASS_LOCATION + ")");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            EagleEyeLogDaemon.start();
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            selfLog("[ERROR] fail to start EagleEyeLogDaemon", e);
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            StatLogController.start();
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            selfLog("[ERROR] fail to start StatLogController", e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void shutdown() {
 | 
			
		||||
        selfLog("[WARN] EagleEye is shutting down (" + CLASS_LOCATION + ")");
 | 
			
		||||
 | 
			
		||||
        EagleEye.flush();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            StatLogController.stop();
 | 
			
		||||
            EagleEye.selfLog("[INFO] StatLogController stopped");
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            selfLog("[ERROR] fail to stop StatLogController", e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            EagleEyeLogDaemon.stop();
 | 
			
		||||
            EagleEye.selfLog("[INFO] EagleEyeLogDaemon stopped");
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            selfLog("[ERROR] fail to stop EagleEyeLogDaemon", e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        EagleEye.selfLog("[WARN] EagleEye shutdown successfully (" + CLASS_LOCATION + ")");
 | 
			
		||||
        try {
 | 
			
		||||
            selfAppender.close();
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            // ignore
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private EagleEye() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static public StatLogger statLogger(String loggerName) {
 | 
			
		||||
        return statLoggerBuilder(loggerName).buildSingleton();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static public StatLoggerBuilder statLoggerBuilder(String loggerName) {
 | 
			
		||||
        return new StatLoggerBuilder(loggerName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void setEagelEyeSelfAppender(EagleEyeAppender appender) {
 | 
			
		||||
        selfAppender = appender;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void selfLog(String log) {
 | 
			
		||||
        try {
 | 
			
		||||
            String timestamp = EagleEyeCoreUtils.formatTime(System.currentTimeMillis());
 | 
			
		||||
            String line = "[" + timestamp + "] " + log + EagleEyeCoreUtils.NEWLINE;
 | 
			
		||||
            selfAppender.append(line);
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void selfLog(String log, Throwable e) {
 | 
			
		||||
        long now = System.currentTimeMillis();
 | 
			
		||||
        if (exceptionBucket.accept(now)) {
 | 
			
		||||
            try {
 | 
			
		||||
                String timestamp = EagleEyeCoreUtils.formatTime(now);
 | 
			
		||||
                StringWriter sw = new StringWriter(4096);
 | 
			
		||||
                PrintWriter pw = new PrintWriter(sw, false);
 | 
			
		||||
                pw.append('[').append(timestamp).append("] ").append(log).append(EagleEyeCoreUtils.NEWLINE);
 | 
			
		||||
                e.printStackTrace(pw);
 | 
			
		||||
                pw.println();
 | 
			
		||||
                pw.flush();
 | 
			
		||||
                selfAppender.append(sw.toString());
 | 
			
		||||
            } catch (Throwable t) {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static public void flush() {
 | 
			
		||||
        EagleEyeLogDaemon.flushAndWait();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,45 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
public abstract class EagleEyeAppender {
 | 
			
		||||
 | 
			
		||||
    public abstract void append(String log);
 | 
			
		||||
 | 
			
		||||
    public void flush() {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void rollOver() {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void reload() {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void close() {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void cleanup() {
 | 
			
		||||
        // do nothing
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getOutputLocation() {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,197 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.concurrent.ExecutorService;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
final class EagleEyeCoreUtils {
 | 
			
		||||
 | 
			
		||||
    public static final String EMPTY_STRING = "";
 | 
			
		||||
    public static final String NEWLINE = "\r\n";
 | 
			
		||||
 | 
			
		||||
    public static final String[] EMPTY_STRING_ARRAY = new String[0];
 | 
			
		||||
 | 
			
		||||
    public static boolean isBlank(String str) {
 | 
			
		||||
        int strLen;
 | 
			
		||||
        if (str == null || (strLen = str.length()) == 0) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 0; i < strLen; i++) {
 | 
			
		||||
            if ((!Character.isWhitespace(str.charAt(i)))) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String checkNotNullEmpty(String value, String name) throws IllegalArgumentException {
 | 
			
		||||
        if (isBlank(value)) {
 | 
			
		||||
            throw new IllegalArgumentException(name + " is null or empty");
 | 
			
		||||
        }
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T> T checkNotNull(T value, String name) throws IllegalArgumentException {
 | 
			
		||||
        if (value == null) {
 | 
			
		||||
            throw new IllegalArgumentException(name + " is null");
 | 
			
		||||
        }
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <T> T defaultIfNull(T value, T defaultValue) {
 | 
			
		||||
        return (value == null) ? defaultValue : value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isNotBlank(String str) {
 | 
			
		||||
        return !isBlank(str);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isNotEmpty(String str) {
 | 
			
		||||
        return str != null && str.length() > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String trim(String str) {
 | 
			
		||||
        return str == null ? null : str.trim();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String[] split(String str, char separatorChar) {
 | 
			
		||||
        return splitWorker(str, separatorChar, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String[] splitWorker(String str, char separatorChar, boolean preserveAllTokens) {
 | 
			
		||||
        if (str == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        int len = str.length();
 | 
			
		||||
        if (len == 0) {
 | 
			
		||||
            return EMPTY_STRING_ARRAY;
 | 
			
		||||
        }
 | 
			
		||||
        List<String> list = new ArrayList<String>();
 | 
			
		||||
        int i = 0, start = 0;
 | 
			
		||||
        boolean match = false;
 | 
			
		||||
        boolean lastMatch = false;
 | 
			
		||||
        while (i < len) {
 | 
			
		||||
            if (str.charAt(i) == separatorChar) {
 | 
			
		||||
                if (match || preserveAllTokens) {
 | 
			
		||||
                    list.add(str.substring(start, i));
 | 
			
		||||
                    match = false;
 | 
			
		||||
                    lastMatch = true;
 | 
			
		||||
                }
 | 
			
		||||
                start = ++i;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            lastMatch = false;
 | 
			
		||||
            match = true;
 | 
			
		||||
            i++;
 | 
			
		||||
        }
 | 
			
		||||
        if (match || (preserveAllTokens && lastMatch)) {
 | 
			
		||||
            list.add(str.substring(start, i));
 | 
			
		||||
        }
 | 
			
		||||
        return list.toArray(new String[list.size()]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static StringBuilder appendWithBlankCheck(String str, String defaultValue, StringBuilder appender) {
 | 
			
		||||
        if (isNotBlank(str)) {
 | 
			
		||||
            appender.append(str);
 | 
			
		||||
        } else {
 | 
			
		||||
            appender.append(defaultValue);
 | 
			
		||||
        }
 | 
			
		||||
        return appender;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static StringBuilder appendWithNullCheck(Object obj, String defaultValue, StringBuilder appender) {
 | 
			
		||||
        if (obj != null) {
 | 
			
		||||
            appender.append(obj.toString());
 | 
			
		||||
        } else {
 | 
			
		||||
            appender.append(defaultValue);
 | 
			
		||||
        }
 | 
			
		||||
        return appender;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static StringBuilder appendLog(String str, StringBuilder appender, char delimiter) {
 | 
			
		||||
        if (str != null) {
 | 
			
		||||
            int len = str.length();
 | 
			
		||||
            appender.ensureCapacity(appender.length() + len);
 | 
			
		||||
            for (int i = 0; i < len; i++) {
 | 
			
		||||
                char c = str.charAt(i);
 | 
			
		||||
                if (c == '\n' || c == '\r' || c == delimiter) {
 | 
			
		||||
                    c = ' ';
 | 
			
		||||
                }
 | 
			
		||||
                appender.append(c);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return appender;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final ThreadLocal<FastDateFormat> dateFmt = new ThreadLocal<FastDateFormat>() {
 | 
			
		||||
        @Override
 | 
			
		||||
        protected FastDateFormat initialValue() {
 | 
			
		||||
            return new FastDateFormat();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    public static String formatTime(long timestamp) {
 | 
			
		||||
        return dateFmt.get().format(timestamp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String getSystemProperty(String key) {
 | 
			
		||||
        try {
 | 
			
		||||
            return System.getProperty(key);
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static long getSystemPropertyForLong(String key, long defaultValue) {
 | 
			
		||||
        try {
 | 
			
		||||
            return Long.parseLong(System.getProperty(key));
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            return defaultValue;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isHexNumeric(char ch) {
 | 
			
		||||
        return (ch >= 'a' && ch <= 'f') || (ch >= '0' && ch <= '9');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isNumeric(char ch) {
 | 
			
		||||
        return ch >= '0' && ch <= '9';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void shutdownThreadPool(ExecutorService pool, long awaitTimeMillis) {
 | 
			
		||||
        try {
 | 
			
		||||
            pool.shutdown();
 | 
			
		||||
 | 
			
		||||
            boolean done = false;
 | 
			
		||||
            if (awaitTimeMillis > 0) {
 | 
			
		||||
                try {
 | 
			
		||||
                    done = pool.awaitTermination(awaitTimeMillis, TimeUnit.MILLISECONDS);
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!done) {
 | 
			
		||||
                pool.shutdownNow();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            // quietly
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,140 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.CopyOnWriteArrayList;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicBoolean;
 | 
			
		||||
 | 
			
		||||
class EagleEyeLogDaemon implements Runnable {
 | 
			
		||||
 | 
			
		||||
    private static final long LOG_CHECK_INTERVAL = TimeUnit.SECONDS.toMillis(20);
 | 
			
		||||
 | 
			
		||||
    private static AtomicBoolean running = new AtomicBoolean(false);
 | 
			
		||||
 | 
			
		||||
    private static Thread worker = null;
 | 
			
		||||
 | 
			
		||||
    private static final CopyOnWriteArrayList<EagleEyeAppender> watchedAppenders
 | 
			
		||||
        = new CopyOnWriteArrayList<EagleEyeAppender>();
 | 
			
		||||
 | 
			
		||||
    static EagleEyeAppender watch(EagleEyeAppender appender) {
 | 
			
		||||
        watchedAppenders.addIfAbsent(appender);
 | 
			
		||||
        return appender;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static boolean unwatch(EagleEyeAppender appender) {
 | 
			
		||||
        return watchedAppenders.remove(appender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void run() {
 | 
			
		||||
        while (running.get()) {
 | 
			
		||||
 | 
			
		||||
            cleanupFiles();
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                Thread.sleep(LOG_CHECK_INTERVAL);
 | 
			
		||||
            } catch (InterruptedException e) {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            flushAndReload();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void cleanupFiles() {
 | 
			
		||||
        for (EagleEyeAppender watchedAppender : watchedAppenders) {
 | 
			
		||||
            try {
 | 
			
		||||
                watchedAppender.cleanup();
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                EagleEye.selfLog("[ERROR] fail to cleanup: " + watchedAppender, e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            EagleEye.selfAppender.cleanup();
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            // quietly
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void flushAndReload() {
 | 
			
		||||
        for (EagleEyeAppender watchedAppender : watchedAppenders) {
 | 
			
		||||
            try {
 | 
			
		||||
                watchedAppender.reload();
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                EagleEye.selfLog("[ERROR] fail to reload: " + watchedAppender, e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            EagleEye.selfAppender.reload();
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            // quietly
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void start() {
 | 
			
		||||
        if (running.compareAndSet(false, true)) {
 | 
			
		||||
            Thread worker = new Thread(new EagleEyeLogDaemon());
 | 
			
		||||
            worker.setDaemon(true);
 | 
			
		||||
            worker.setName("EagleEye-LogDaemon-Thread");
 | 
			
		||||
            worker.start();
 | 
			
		||||
            EagleEyeLogDaemon.worker = worker;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void stop() {
 | 
			
		||||
        if (running.compareAndSet(true, false)) {
 | 
			
		||||
 | 
			
		||||
            closeAppenders();
 | 
			
		||||
 | 
			
		||||
            final Thread worker = EagleEyeLogDaemon.worker;
 | 
			
		||||
            if (worker != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    worker.interrupt();
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    // ignore
 | 
			
		||||
                }
 | 
			
		||||
                try {
 | 
			
		||||
                    worker.join(1000);
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    // ignore
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void closeAppenders() {
 | 
			
		||||
        for (EagleEyeAppender watchedAppender : watchedAppenders) {
 | 
			
		||||
            try {
 | 
			
		||||
                watchedAppender.close();
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                EagleEye.selfLog("[ERROR] fail to close: " + watchedAppender, e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void flushAndWait() {
 | 
			
		||||
        for (EagleEyeAppender watchedAppender : watchedAppenders) {
 | 
			
		||||
            try {
 | 
			
		||||
                watchedAppender.flush();
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                EagleEye.selfLog("[ERROR] fail to flush: " + watchedAppender, e);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private EagleEyeLogDaemon() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,332 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedOutputStream;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.FilenameFilter;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.RandomAccessFile;
 | 
			
		||||
import java.nio.channels.FileLock;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicBoolean;
 | 
			
		||||
 | 
			
		||||
class EagleEyeRollingFileAppender extends EagleEyeAppender {
 | 
			
		||||
 | 
			
		||||
    private static final long LOG_FLUSH_INTERVAL = TimeUnit.SECONDS.toMillis(1);
 | 
			
		||||
 | 
			
		||||
    private static final int DEFAULT_BUFFER_SIZE = 4 * 1024; // 4KB
 | 
			
		||||
 | 
			
		||||
    private final int maxBackupIndex = 3;
 | 
			
		||||
 | 
			
		||||
    private final long maxFileSize;
 | 
			
		||||
 | 
			
		||||
    private final int bufferSize = DEFAULT_BUFFER_SIZE;
 | 
			
		||||
 | 
			
		||||
    private final String filePath;
 | 
			
		||||
 | 
			
		||||
    private final AtomicBoolean isRolling = new AtomicBoolean(false);
 | 
			
		||||
 | 
			
		||||
    private BufferedOutputStream bos = null;
 | 
			
		||||
 | 
			
		||||
    private long nextFlushTime = 0L;
 | 
			
		||||
 | 
			
		||||
    private long lastRollOverTime = 0L;
 | 
			
		||||
 | 
			
		||||
    private long outputByteSize = 0L;
 | 
			
		||||
 | 
			
		||||
    private final boolean selfLogEnabled;
 | 
			
		||||
 | 
			
		||||
    private boolean multiProcessDetected = false;
 | 
			
		||||
 | 
			
		||||
    private static final String DELETE_FILE_SUFFIX = ".deleted";
 | 
			
		||||
 | 
			
		||||
    public EagleEyeRollingFileAppender(String filePath, long maxFileSize) {
 | 
			
		||||
        this(filePath, maxFileSize, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public EagleEyeRollingFileAppender(String filePath, long maxFileSize, boolean selfLogEnabled) {
 | 
			
		||||
        this.filePath = filePath;
 | 
			
		||||
        this.maxFileSize = maxFileSize;
 | 
			
		||||
        this.selfLogEnabled = selfLogEnabled;
 | 
			
		||||
        setFile();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setFile() {
 | 
			
		||||
        try {
 | 
			
		||||
            File logFile = new File(filePath);
 | 
			
		||||
            if (!logFile.exists()) {
 | 
			
		||||
                File parentFile = logFile.getParentFile();
 | 
			
		||||
                if (!parentFile.exists() && !parentFile.mkdirs()) {
 | 
			
		||||
                    doSelfLog("[ERROR] Fail to mkdirs: " + parentFile.getAbsolutePath());
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                try {
 | 
			
		||||
                    if (!logFile.createNewFile()) {
 | 
			
		||||
                        doSelfLog("[ERROR] Fail to create file, it exists: " + logFile.getAbsolutePath());
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    doSelfLog(
 | 
			
		||||
                        "[ERROR] Fail to create file: " + logFile.getAbsolutePath() + ", error=" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (!logFile.isFile() || !logFile.canWrite()) {
 | 
			
		||||
                doSelfLog("[ERROR] Invalid file, exists=" + logFile.exists() + ", isFile=" + logFile.isFile()
 | 
			
		||||
                    + ", canWrite=" + logFile.canWrite() + ", path=" + logFile.getAbsolutePath());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            FileOutputStream ostream = new FileOutputStream(logFile, true);
 | 
			
		||||
            // true
 | 
			
		||||
            // O_APPEND
 | 
			
		||||
            this.bos = new BufferedOutputStream(ostream, bufferSize);
 | 
			
		||||
            this.lastRollOverTime = System.currentTimeMillis();
 | 
			
		||||
            this.outputByteSize = logFile.length();
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            doSelfLog("[ERROR] Fail to create file to write: " + filePath + ", error=" + e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void append(String log) {
 | 
			
		||||
        BufferedOutputStream bos = this.bos;
 | 
			
		||||
        if (bos != null) {
 | 
			
		||||
            try {
 | 
			
		||||
                waitUntilRollFinish();
 | 
			
		||||
 | 
			
		||||
                byte[] bytes = log.getBytes(EagleEye.DEFAULT_CHARSET);
 | 
			
		||||
                int len = bytes.length;
 | 
			
		||||
                if (len > DEFAULT_BUFFER_SIZE && this.multiProcessDetected) {
 | 
			
		||||
                    len = DEFAULT_BUFFER_SIZE;
 | 
			
		||||
                    bytes[len - 1] = '\n';
 | 
			
		||||
                }
 | 
			
		||||
                bos.write(bytes, 0, len);
 | 
			
		||||
                outputByteSize += len;
 | 
			
		||||
 | 
			
		||||
                if (outputByteSize >= maxFileSize) {
 | 
			
		||||
                    rollOver();
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (System.currentTimeMillis() >= nextFlushTime) {
 | 
			
		||||
                        flush();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                doSelfLog("[ERROR] fail to write log to file " + filePath + ", error=" + e.getMessage());
 | 
			
		||||
                close();
 | 
			
		||||
                setFile();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void flush() {
 | 
			
		||||
        final BufferedOutputStream bos = this.bos;
 | 
			
		||||
        if (bos != null) {
 | 
			
		||||
            try {
 | 
			
		||||
                bos.flush();
 | 
			
		||||
                nextFlushTime = System.currentTimeMillis() + LOG_FLUSH_INTERVAL;
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                doSelfLog("[WARN] Fail to flush OutputStream: " + filePath + ", " + e.getMessage());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void rollOver() {
 | 
			
		||||
        final String lockFilePath = filePath + ".lock";
 | 
			
		||||
        final File lockFile = new File(lockFilePath);
 | 
			
		||||
 | 
			
		||||
        RandomAccessFile raf = null;
 | 
			
		||||
        FileLock fileLock = null;
 | 
			
		||||
 | 
			
		||||
        if (!isRolling.compareAndSet(false, true)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            raf = new RandomAccessFile(lockFile, "rw");
 | 
			
		||||
            fileLock = raf.getChannel().tryLock();
 | 
			
		||||
 | 
			
		||||
            if (fileLock != null) {
 | 
			
		||||
                File target;
 | 
			
		||||
                File file;
 | 
			
		||||
                final int maxBackupIndex = this.maxBackupIndex;
 | 
			
		||||
 | 
			
		||||
                reload();
 | 
			
		||||
                if (outputByteSize >= maxFileSize) {
 | 
			
		||||
                    file = new File(filePath + '.' + maxBackupIndex);
 | 
			
		||||
                    if (file.exists()) {
 | 
			
		||||
                        target = new File(filePath + '.' + maxBackupIndex + DELETE_FILE_SUFFIX);
 | 
			
		||||
                        if (!file.renameTo(target) && !file.delete()) {
 | 
			
		||||
                            doSelfLog("[ERROR] Fail to delete or rename file: " + file.getAbsolutePath() + " to "
 | 
			
		||||
                                + target.getAbsolutePath());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    for (int i = maxBackupIndex - 1; i >= 1; i--) {
 | 
			
		||||
                        file = new File(filePath + '.' + i);
 | 
			
		||||
                        if (file.exists()) {
 | 
			
		||||
                            target = new File(filePath + '.' + (i + 1));
 | 
			
		||||
                            if (!file.renameTo(target) && !file.delete()) {
 | 
			
		||||
                                doSelfLog("[ERROR] Fail to delete or rename file: " + file.getAbsolutePath() + " to "
 | 
			
		||||
                                    + target.getAbsolutePath());
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    target = new File(filePath + "." + 1);
 | 
			
		||||
 | 
			
		||||
                    close();
 | 
			
		||||
 | 
			
		||||
                    file = new File(filePath);
 | 
			
		||||
                    if (file.renameTo(target)) {
 | 
			
		||||
                        doSelfLog("[INFO] File rolled to " + target.getAbsolutePath() + ", "
 | 
			
		||||
                            + TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis() - lastRollOverTime)
 | 
			
		||||
                            + " minutes since last roll");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        doSelfLog("[WARN] Fail to rename file: " + file.getAbsolutePath() + " to "
 | 
			
		||||
                            + target.getAbsolutePath());
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    setFile();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            doSelfLog("[ERROR] Fail rollover file: " + filePath + ", error=" + e.getMessage());
 | 
			
		||||
        } finally {
 | 
			
		||||
            isRolling.set(false);
 | 
			
		||||
 | 
			
		||||
            if (fileLock != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    fileLock.release();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    doSelfLog("[ERROR] Fail to release file lock: " + lockFilePath + ", error=" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (raf != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    raf.close();
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    doSelfLog("[WARN] Fail to close file lock: " + lockFilePath + ", error=" + e.getMessage());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (fileLock != null) {
 | 
			
		||||
                if (!lockFile.delete() && lockFile.exists()) {
 | 
			
		||||
                    doSelfLog("[WARN] Fail to delete file lock: " + lockFilePath);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() {
 | 
			
		||||
        BufferedOutputStream bos = this.bos;
 | 
			
		||||
        if (bos != null) {
 | 
			
		||||
            try {
 | 
			
		||||
                bos.close();
 | 
			
		||||
            } catch (IOException e) {
 | 
			
		||||
                doSelfLog("[WARN] Fail to close OutputStream: " + e.getMessage());
 | 
			
		||||
            }
 | 
			
		||||
            this.bos = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void reload() {
 | 
			
		||||
        flush();
 | 
			
		||||
        File logFile = new File(filePath);
 | 
			
		||||
        long fileSize = logFile.length();
 | 
			
		||||
        boolean fileNotExists = fileSize <= 0 && !logFile.exists();
 | 
			
		||||
 | 
			
		||||
        if (this.bos == null || fileSize < outputByteSize || fileNotExists) {
 | 
			
		||||
            doSelfLog("[INFO] Log file rolled over by outside: " + filePath + ", force reload");
 | 
			
		||||
            close();
 | 
			
		||||
            setFile();
 | 
			
		||||
        } else if (fileSize > outputByteSize) {
 | 
			
		||||
            this.outputByteSize = fileSize;
 | 
			
		||||
            if (!this.multiProcessDetected) {
 | 
			
		||||
                this.multiProcessDetected = true;
 | 
			
		||||
                if (selfLogEnabled) {
 | 
			
		||||
                    doSelfLog("[WARN] Multi-process file write detected: " + filePath);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void cleanup() {
 | 
			
		||||
        try {
 | 
			
		||||
            File logFile = new File(filePath);
 | 
			
		||||
            File parentDir = logFile.getParentFile();
 | 
			
		||||
            if (parentDir != null && parentDir.isDirectory()) {
 | 
			
		||||
                final String baseFileName = logFile.getName();
 | 
			
		||||
                File[] filesToDelete = parentDir.listFiles(new FilenameFilter() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public boolean accept(File dir, String name) {
 | 
			
		||||
                        if (name != null && name.startsWith(baseFileName) && name.endsWith(DELETE_FILE_SUFFIX)) {
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                if (filesToDelete != null && filesToDelete.length > 0) {
 | 
			
		||||
                    for (File f : filesToDelete) {
 | 
			
		||||
                        boolean success = f.delete() || !f.exists();
 | 
			
		||||
                        if (success) {
 | 
			
		||||
                            doSelfLog("[INFO] Deleted log file: " + f.getAbsolutePath());
 | 
			
		||||
                        } else if (f.exists()) {
 | 
			
		||||
                            doSelfLog("[ERROR] Fail to delete log file: " + f.getAbsolutePath());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            doSelfLog("[ERROR] Fail to cleanup log file, error=" + e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void waitUntilRollFinish() {
 | 
			
		||||
        while (isRolling.get()) {
 | 
			
		||||
            try {
 | 
			
		||||
                Thread.sleep(1L);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                // quietly
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void doSelfLog(String log) {
 | 
			
		||||
        if (selfLogEnabled) {
 | 
			
		||||
            EagleEye.selfLog(log);
 | 
			
		||||
        } else {
 | 
			
		||||
            System.out.println("[EagleEye]" + log);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getOutputLocation() {
 | 
			
		||||
        return filePath;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return "EagleEyeRollingFileAppender [filePath=" + filePath + "]";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,81 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.TimeZone;
 | 
			
		||||
 | 
			
		||||
class FastDateFormat {
 | 
			
		||||
 | 
			
		||||
    private final SimpleDateFormat fmt = createSimpleDateFormat();
 | 
			
		||||
 | 
			
		||||
    private char[] buffer = new char[23];
 | 
			
		||||
 | 
			
		||||
    private long lastSecond = -1;
 | 
			
		||||
    private long lastMillis = -1;
 | 
			
		||||
 | 
			
		||||
    public String format(long timestamp) {
 | 
			
		||||
        formatToBuffer(timestamp);
 | 
			
		||||
        return new String(buffer, 0, 23);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String format(Date date) {
 | 
			
		||||
        return format(date.getTime());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void formatAndAppendTo(long timestamp, StringBuilder appender) {
 | 
			
		||||
        formatToBuffer(timestamp);
 | 
			
		||||
        appender.append(buffer, 0, 23);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void formatToBuffer(long timestamp) {
 | 
			
		||||
        if (timestamp == lastMillis) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        long diff = timestamp - lastSecond;
 | 
			
		||||
        if (diff >= 0 && diff < 1000) {
 | 
			
		||||
            int ms = (int)(timestamp % 1000);
 | 
			
		||||
            buffer[22] = (char)(ms % 10 + '0');
 | 
			
		||||
            ms /= 10;
 | 
			
		||||
            buffer[21] = (char)(ms % 10 + '0');
 | 
			
		||||
            buffer[20] = (char)(ms / 10 + '0');
 | 
			
		||||
            lastMillis = timestamp;
 | 
			
		||||
        } else {
 | 
			
		||||
            String result = fmt.format(new Date(timestamp));
 | 
			
		||||
            result.getChars(0, result.length(), buffer, 0);
 | 
			
		||||
            lastSecond = timestamp / 1000 * 1000;
 | 
			
		||||
            lastMillis = timestamp;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    String formatWithoutMs(long timestamp) {
 | 
			
		||||
        long diff = timestamp - lastSecond;
 | 
			
		||||
        if (diff < 0 || diff >= 1000) {
 | 
			
		||||
            String result = fmt.format(new Date(timestamp));
 | 
			
		||||
            result.getChars(0, result.length(), buffer, 0);
 | 
			
		||||
            lastSecond = timestamp / 1000 * 1000;
 | 
			
		||||
            lastMillis = timestamp;
 | 
			
		||||
        }
 | 
			
		||||
        return new String(buffer, 0, 19);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private SimpleDateFormat createSimpleDateFormat() {
 | 
			
		||||
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
 | 
			
		||||
        fmt.setTimeZone(TimeZone.getDefault());
 | 
			
		||||
        return fmt;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,179 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public final class StatEntry {
 | 
			
		||||
 | 
			
		||||
    private final StatLogger statLogger;
 | 
			
		||||
 | 
			
		||||
    private final String[] keys;
 | 
			
		||||
    private transient int hash;
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String key) {
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = new String[] {key};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String key1, String key2) {
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = new String[] {key1, key2};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String key1, String key2, String key3) {
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = new String[] {key1, key2, key3};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String key1, String key2, String key3, String key4) {
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = new String[] {key1, key2, key3, key4};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String key1, String key2, String key3, String key4, String key5) {
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = new String[] {key1, key2, key3, key4, key5};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String key1, String key2, String key3, String key4, String key5,
 | 
			
		||||
                     String key6) {
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = new String[] {key1, key2, key3, key4, key5, key6};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String key1, String key2, String key3, String key4, String key5,
 | 
			
		||||
                     String key6, String key7) {
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = new String[] {key1, key2, key3, key4, key5, key6, key7};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String key1, String key2, String key3, String key4, String key5,
 | 
			
		||||
                     String key6, String key7, String key8) {
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = new String[] {key1, key2, key3, key4, key5, key6, key7, key8};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String key1, String... moreKeys) {
 | 
			
		||||
        String[] keys = new String[1 + moreKeys.length];
 | 
			
		||||
        keys[0] = key1;
 | 
			
		||||
        for (int i = 0; i < moreKeys.length; ++i) {
 | 
			
		||||
            keys[i + 1] = moreKeys[i];
 | 
			
		||||
        }
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = keys;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, List<String> keys) {
 | 
			
		||||
        if (keys == null || keys.isEmpty()) {
 | 
			
		||||
            throw new IllegalArgumentException("keys empty or null: " + keys);
 | 
			
		||||
        }
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = keys.toArray(new String[keys.size()]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry(StatLogger statLogger, String[] keys) {
 | 
			
		||||
        if (keys == null || keys.length == 0) {
 | 
			
		||||
            throw new IllegalArgumentException("keys empty or null");
 | 
			
		||||
        }
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.keys = Arrays.copyOf(keys, keys.length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String[] getKeys() {
 | 
			
		||||
        return keys;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void appendTo(StringBuilder appender, char delimiter) {
 | 
			
		||||
        final int len = keys.length;
 | 
			
		||||
        if (len > 0) {
 | 
			
		||||
            appender.append(keys[0]);
 | 
			
		||||
            for (int i = 1; i < len; ++i) {
 | 
			
		||||
                appender.append(delimiter).append(keys[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        StringBuilder sb = new StringBuilder(64);
 | 
			
		||||
        sb.append("StatKeys [");
 | 
			
		||||
        appendTo(sb, ',');
 | 
			
		||||
        sb.append("]");
 | 
			
		||||
        return sb.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int hashCode() {
 | 
			
		||||
        if (hash == 0) {
 | 
			
		||||
            int result = 1;
 | 
			
		||||
            result = 31 * result + Arrays.hashCode(keys);
 | 
			
		||||
            hash = result;
 | 
			
		||||
        }
 | 
			
		||||
        return hash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean equals(Object obj) {
 | 
			
		||||
        if (this == obj) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (obj == null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (getClass() != obj.getClass()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        StatEntry other = (StatEntry)obj;
 | 
			
		||||
        if (hash != 0 && other.hash != 0 && hash != other.hash) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (!Arrays.equals(keys, other.keys)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    StatEntryFunc getFunc(final StatEntryFuncFactory factory) {
 | 
			
		||||
        return this.statLogger.getRollingData().getStatEntryFunc(this, factory);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void count() {
 | 
			
		||||
        count(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void count(long count) {
 | 
			
		||||
        getFunc(StatEntryFuncFactory.COUNT_SUM).count(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void countAndSum(long valueToSum) {
 | 
			
		||||
        countAndSum(1, valueToSum);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void countAndSum(long count, long valueToSum) {
 | 
			
		||||
        getFunc(StatEntryFuncFactory.COUNT_SUM).countAndSum(count, valueToSum);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void minMax(long candidate) {
 | 
			
		||||
        minMax(candidate, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void minMax(long candidate, String ref) {
 | 
			
		||||
        getFunc(StatEntryFuncFactory.MIN_MAX).minMax(candidate, ref);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,205 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.atomic.AtomicReference;
 | 
			
		||||
import java.util.concurrent.atomic.LongAdder;
 | 
			
		||||
 | 
			
		||||
interface StatEntryFunc {
 | 
			
		||||
 | 
			
		||||
    void appendTo(StringBuilder appender, char delimiter);
 | 
			
		||||
 | 
			
		||||
    int getStatType();
 | 
			
		||||
 | 
			
		||||
    Object[] getValues();
 | 
			
		||||
 | 
			
		||||
    void count(long count);
 | 
			
		||||
 | 
			
		||||
    void countAndSum(long count, long value);
 | 
			
		||||
 | 
			
		||||
    void arrayAdd(long... values);
 | 
			
		||||
 | 
			
		||||
    void arraySet(long... values);
 | 
			
		||||
 | 
			
		||||
    void minMax(long candidate, String ref);
 | 
			
		||||
 | 
			
		||||
    void batchAdd(long... values);
 | 
			
		||||
 | 
			
		||||
    void strArray(String... values);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum StatEntryFuncFactory {
 | 
			
		||||
    COUNT_SUM {
 | 
			
		||||
        @Override
 | 
			
		||||
        StatEntryFunc create() {
 | 
			
		||||
            return new StatEntryFuncCountAndSum();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    MIN_MAX {
 | 
			
		||||
        @Override
 | 
			
		||||
        StatEntryFunc create() {
 | 
			
		||||
            return new StatEntryFuncMinMax();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    abstract StatEntryFunc create();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class StatEntryFuncCountAndSum implements StatEntryFunc {
 | 
			
		||||
 | 
			
		||||
    private LongAdder count = new LongAdder();
 | 
			
		||||
    private LongAdder value = new LongAdder();
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void appendTo(StringBuilder appender, char delimiter) {
 | 
			
		||||
        appender.append(count.sum()).append(delimiter).append(value.sum());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object[] getValues() {
 | 
			
		||||
        return new Object[] {count.sum(), value.sum()};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getStatType() {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void count(long count) {
 | 
			
		||||
        this.count.add(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void countAndSum(long count, long value) {
 | 
			
		||||
        this.count.add(count);
 | 
			
		||||
        this.value.add(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void arrayAdd(long... values) {
 | 
			
		||||
        throw new IllegalStateException("arrayAdd() is unavailable if countAndSum() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void arraySet(long... values) {
 | 
			
		||||
        throw new IllegalStateException("arraySet() is unavailable if countAndSum() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void minMax(long candidate, String ref) {
 | 
			
		||||
        throw new IllegalStateException("minMax() is unavailable if countAndSum() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void batchAdd(long... values) {
 | 
			
		||||
        throw new IllegalStateException("batchAdd() is unavailable if countAndSum() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void strArray(String... values) {
 | 
			
		||||
        throw new IllegalStateException("strArray() is unavailable if countAndSum() has been called");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class StatEntryFuncMinMax implements StatEntryFunc {
 | 
			
		||||
 | 
			
		||||
    private AtomicReference<ValueRef> max = new AtomicReference<ValueRef>(new ValueRef(Long.MIN_VALUE, null));
 | 
			
		||||
    private AtomicReference<ValueRef> min = new AtomicReference<ValueRef>(new ValueRef(Long.MAX_VALUE, null));
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void appendTo(StringBuilder appender, char delimiter) {
 | 
			
		||||
        ValueRef lmax = max.get();
 | 
			
		||||
        ValueRef lmin = min.get();
 | 
			
		||||
 | 
			
		||||
        appender.append(lmax.value).append(delimiter);
 | 
			
		||||
        if (lmax.ref != null) {
 | 
			
		||||
            appender.append(lmax.ref);
 | 
			
		||||
        }
 | 
			
		||||
        appender.append(delimiter);
 | 
			
		||||
 | 
			
		||||
        appender.append(lmin.value).append(delimiter);
 | 
			
		||||
        if (lmin.ref != null) {
 | 
			
		||||
            appender.append(lmin.ref);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Object[] getValues() {
 | 
			
		||||
        ValueRef lmax = max.get();
 | 
			
		||||
        ValueRef lmin = min.get();
 | 
			
		||||
        return new Object[] {lmax.value, lmax.ref, lmin.value, lmin.ref};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getStatType() {
 | 
			
		||||
        return 4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void count(long count) {
 | 
			
		||||
        throw new IllegalStateException("count() is unavailable if minMax() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void countAndSum(long count, long value) {
 | 
			
		||||
        throw new IllegalStateException("countAndSum() is unavailable if minMax() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void arrayAdd(long... values) {
 | 
			
		||||
        throw new IllegalStateException("arrayAdd() is unavailable if minMax() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void arraySet(long... values) {
 | 
			
		||||
        throw new IllegalStateException("arraySet() is unavailable if minMax() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void batchAdd(long... values) {
 | 
			
		||||
        throw new IllegalStateException("batchAdd() is unavailable if minMax() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void minMax(long candidate, String ref) {
 | 
			
		||||
        ValueRef lmax = max.get();
 | 
			
		||||
        if (lmax.value <= candidate) {
 | 
			
		||||
            final ValueRef cmax = new ValueRef(candidate, ref);
 | 
			
		||||
            while (!max.compareAndSet(lmax, cmax) && (lmax = max.get()).value <= candidate) { ; }
 | 
			
		||||
        }
 | 
			
		||||
        ValueRef lmin = min.get();
 | 
			
		||||
        if (lmin.value >= candidate) {
 | 
			
		||||
            final ValueRef cmin = new ValueRef(candidate, ref);
 | 
			
		||||
            while (!min.compareAndSet(lmin, cmin) && (lmin = min.get()).value >= candidate) { ; }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void strArray(String... values) {
 | 
			
		||||
        throw new IllegalStateException("strArray() is unavailable if minMax() has been called");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final class ValueRef {
 | 
			
		||||
        final long value;
 | 
			
		||||
        final String ref;
 | 
			
		||||
 | 
			
		||||
        ValueRef(long value, String ref) {
 | 
			
		||||
            this.value = value;
 | 
			
		||||
            this.ref = ref;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,190 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Map.Entry;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicBoolean;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory;
 | 
			
		||||
 | 
			
		||||
class StatLogController {
 | 
			
		||||
 | 
			
		||||
    private static final Map<String, StatLogger> statLoggers = new ConcurrentHashMap<String, StatLogger>();
 | 
			
		||||
 | 
			
		||||
    private static final int STAT_ENTRY_COOL_DOWN_MILLIS = 200;
 | 
			
		||||
 | 
			
		||||
    private static final ScheduledThreadPoolExecutor rollerThreadPool =
 | 
			
		||||
        new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(
 | 
			
		||||
            "EagleEye-StatLogController-roller", true));
 | 
			
		||||
 | 
			
		||||
    private static final ScheduledThreadPoolExecutor writerThreadPool =
 | 
			
		||||
        new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(
 | 
			
		||||
            "EagleEye-StatLogController-writer", true));
 | 
			
		||||
 | 
			
		||||
    private static AtomicBoolean running = new AtomicBoolean(false);
 | 
			
		||||
 | 
			
		||||
    static StatLogger createLoggerIfNotExists(StatLoggerBuilder builder) {
 | 
			
		||||
        String loggerName = builder.getLoggerName();
 | 
			
		||||
        StatLogger statLogger = statLoggers.get(loggerName);
 | 
			
		||||
        if (statLogger == null) {
 | 
			
		||||
            synchronized (StatLogController.class) {
 | 
			
		||||
                if ((statLogger = statLoggers.get(loggerName)) == null) {
 | 
			
		||||
                    statLogger = builder.create();
 | 
			
		||||
                    statLoggers.put(loggerName, statLogger);
 | 
			
		||||
 | 
			
		||||
                    writerThreadPool.setMaximumPoolSize(Math.max(1, statLoggers.size()));
 | 
			
		||||
 | 
			
		||||
                    scheduleNextRollingTask(statLogger);
 | 
			
		||||
                    EagleEye.selfLog("[INFO] created statLogger[" + statLogger.getLoggerName() +
 | 
			
		||||
                        "]: " + statLogger.getAppender());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return statLogger;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static Map<String, StatLogger> getAllStatLoggers() {
 | 
			
		||||
        return Collections.unmodifiableMap(statLoggers);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void scheduleNextRollingTask(StatLogger statLogger) {
 | 
			
		||||
        if (!running.get()) {
 | 
			
		||||
            EagleEye.selfLog("[INFO] stopped rolling statLogger[" + statLogger.getLoggerName() + "]");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        StatLogRollingTask rollingTask = new StatLogRollingTask(statLogger);
 | 
			
		||||
 | 
			
		||||
        long rollingTimeMillis = statLogger.getRollingData().getRollingTimeMillis();
 | 
			
		||||
        long delayMillis = rollingTimeMillis - System.currentTimeMillis();
 | 
			
		||||
        if (delayMillis > 5) {
 | 
			
		||||
            rollerThreadPool.schedule(rollingTask, delayMillis, TimeUnit.MILLISECONDS);
 | 
			
		||||
        } else if (-delayMillis > statLogger.getIntervalMillis()) {
 | 
			
		||||
            EagleEye.selfLog("[WARN] unusual delay of statLogger[" + statLogger.getLoggerName() +
 | 
			
		||||
                "], delay=" + (-delayMillis) + "ms, submit now");
 | 
			
		||||
            rollerThreadPool.submit(rollingTask);
 | 
			
		||||
        } else {
 | 
			
		||||
            rollerThreadPool.submit(rollingTask);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void scheduleWriteTask(StatRollingData statRollingData) {
 | 
			
		||||
        if (statRollingData != null) {
 | 
			
		||||
            try {
 | 
			
		||||
                StatLogWriteTask task = new StatLogWriteTask(statRollingData);
 | 
			
		||||
                writerThreadPool.schedule(task, STAT_ENTRY_COOL_DOWN_MILLIS, TimeUnit.MILLISECONDS);
 | 
			
		||||
            } catch (Throwable t) {
 | 
			
		||||
                EagleEye.selfLog("[ERROR] fail to roll statLogger[" +
 | 
			
		||||
                    statRollingData.getStatLogger().getLoggerName() + "]", t);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class StatLogRollingTask implements Runnable {
 | 
			
		||||
 | 
			
		||||
        final StatLogger statLogger;
 | 
			
		||||
 | 
			
		||||
        StatLogRollingTask(StatLogger statLogger) {
 | 
			
		||||
            this.statLogger = statLogger;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void run() {
 | 
			
		||||
            scheduleWriteTask(statLogger.rolling());
 | 
			
		||||
            scheduleNextRollingTask(statLogger);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class StatLogWriteTask implements Runnable {
 | 
			
		||||
 | 
			
		||||
        final StatRollingData statRollingData;
 | 
			
		||||
 | 
			
		||||
        StatLogWriteTask(StatRollingData statRollingData) {
 | 
			
		||||
            this.statRollingData = statRollingData;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void run() {
 | 
			
		||||
            final StatRollingData data = statRollingData;
 | 
			
		||||
            final StatLogger logger = data.getStatLogger();
 | 
			
		||||
            try {
 | 
			
		||||
                final FastDateFormat fmt = new FastDateFormat();
 | 
			
		||||
                final StringBuilder buffer = new StringBuilder(256);
 | 
			
		||||
                final String timeStr = fmt.formatWithoutMs(data.getTimeSlot());
 | 
			
		||||
 | 
			
		||||
                final EagleEyeAppender appender = logger.getAppender();
 | 
			
		||||
                final Set<Entry<StatEntry, StatEntryFunc>> entrySet = data.getStatEntrySet();
 | 
			
		||||
                final char entryDelimiter = logger.getEntryDelimiter();
 | 
			
		||||
                final char keyDelimiter = logger.getKeyDelimiter();
 | 
			
		||||
                final char valueDelimiter = logger.getValueDelimiter();
 | 
			
		||||
 | 
			
		||||
                for (Entry<StatEntry, StatEntryFunc> entry : entrySet) {
 | 
			
		||||
                    buffer.delete(0, buffer.length());
 | 
			
		||||
                    StatEntryFunc func = entry.getValue();
 | 
			
		||||
                    // time|statType|keys|values
 | 
			
		||||
                    buffer.append(timeStr).append(entryDelimiter);
 | 
			
		||||
                    buffer.append(func.getStatType()).append(entryDelimiter);
 | 
			
		||||
                    entry.getKey().appendTo(buffer, keyDelimiter);
 | 
			
		||||
                    buffer.append(entryDelimiter);
 | 
			
		||||
                    func.appendTo(buffer, valueDelimiter);
 | 
			
		||||
                    buffer.append(EagleEyeCoreUtils.NEWLINE);
 | 
			
		||||
                    appender.append(buffer.toString());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                appender.flush();
 | 
			
		||||
            } catch (Throwable t) {
 | 
			
		||||
                EagleEye.selfLog("[WARN] fail to write statLogger[" +
 | 
			
		||||
                    logger.getLoggerName() + "]", t);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void start() {
 | 
			
		||||
        if (running.compareAndSet(false, true)) {
 | 
			
		||||
            rollerThreadPool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
 | 
			
		||||
            writerThreadPool.setExecuteExistingDelayedTasksAfterShutdownPolicy(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void stop() {
 | 
			
		||||
        if (running.compareAndSet(true, false)) {
 | 
			
		||||
            EagleEyeCoreUtils.shutdownThreadPool(rollerThreadPool, 0);
 | 
			
		||||
            EagleEye.selfLog("[INFO] StatLoggerController: roller ThreadPool shutdown successfully");
 | 
			
		||||
 | 
			
		||||
            for (StatLogger statLogger : statLoggers.values()) {
 | 
			
		||||
                new StatLogRollingTask(statLogger).run();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                Thread.sleep(STAT_ENTRY_COOL_DOWN_MILLIS);
 | 
			
		||||
            } catch (InterruptedException e) {
 | 
			
		||||
                // quietly
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            EagleEyeCoreUtils.shutdownThreadPool(writerThreadPool, 2000);
 | 
			
		||||
            EagleEye.selfLog("[INFO] StatLoggerController: writer ThreadPool shutdown successfully");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private StatLogController() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,145 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicReference;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author jifeng
 | 
			
		||||
 */
 | 
			
		||||
public final class StatLogger {
 | 
			
		||||
 | 
			
		||||
    private final String loggerName;
 | 
			
		||||
 | 
			
		||||
    private final EagleEyeAppender appender;
 | 
			
		||||
 | 
			
		||||
    private final AtomicReference<StatRollingData> ref;
 | 
			
		||||
 | 
			
		||||
    private final long intervalMillis;
 | 
			
		||||
 | 
			
		||||
    private final int maxEntryCount;
 | 
			
		||||
 | 
			
		||||
    private final char entryDelimiter;
 | 
			
		||||
    private final char keyDelimiter;
 | 
			
		||||
    private final char valueDelimiter;
 | 
			
		||||
 | 
			
		||||
    StatLogger(String loggerName, EagleEyeAppender appender, long intervalMillis, int maxEntryCount,
 | 
			
		||||
               char entryDelimiter, char keyDelimiter, char valueDelimiter) {
 | 
			
		||||
        this.loggerName = loggerName;
 | 
			
		||||
        this.appender = appender;
 | 
			
		||||
        this.intervalMillis = intervalMillis;
 | 
			
		||||
        this.maxEntryCount = maxEntryCount;
 | 
			
		||||
        this.entryDelimiter = entryDelimiter;
 | 
			
		||||
        this.keyDelimiter = keyDelimiter;
 | 
			
		||||
        this.valueDelimiter = valueDelimiter;
 | 
			
		||||
        this.ref = new AtomicReference<StatRollingData>();
 | 
			
		||||
        rolling();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getLoggerName() {
 | 
			
		||||
        return loggerName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EagleEyeAppender getAppender() {
 | 
			
		||||
        return appender;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    StatRollingData getRollingData() {
 | 
			
		||||
        return ref.get();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long getIntervalMillis() {
 | 
			
		||||
        return intervalMillis;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getMaxEntryCount() {
 | 
			
		||||
        return maxEntryCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char getEntryDelimiter() {
 | 
			
		||||
        return entryDelimiter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char getKeyDelimiter() {
 | 
			
		||||
        return keyDelimiter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    char getValueDelimiter() {
 | 
			
		||||
        return valueDelimiter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    StatRollingData rolling() {
 | 
			
		||||
        do {
 | 
			
		||||
            long now = System.currentTimeMillis();
 | 
			
		||||
            long timeSlot = now - now % intervalMillis;
 | 
			
		||||
 | 
			
		||||
            StatRollingData prevData = ref.get();
 | 
			
		||||
            long rollingTimeMillis = timeSlot + intervalMillis;
 | 
			
		||||
            int initialCapacity = prevData != null ? prevData.getStatCount() : 16;
 | 
			
		||||
            StatRollingData nextData = new StatRollingData(
 | 
			
		||||
                this, initialCapacity, timeSlot, rollingTimeMillis);
 | 
			
		||||
            if (ref.compareAndSet(prevData, nextData)) {
 | 
			
		||||
                return prevData;
 | 
			
		||||
            }
 | 
			
		||||
        } while (true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String key) {
 | 
			
		||||
        return new StatEntry(this, key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String key1, String key2) {
 | 
			
		||||
        return new StatEntry(this, key1, key2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String key1, String key2, String key3) {
 | 
			
		||||
        return new StatEntry(this, key1, key2, key3);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String key1, String key2, String key3, String key4) {
 | 
			
		||||
        return new StatEntry(this, key1, key2, key3, key4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String key1, String key2, String key3, String key4, String key5) {
 | 
			
		||||
        return new StatEntry(this, key1, key2, key3, key4, key5);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String key1, String key2, String key3, String key4, String key5, String key6) {
 | 
			
		||||
        return new StatEntry(this, key1, key2, key3, key4, key5, key6);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String key1, String key2, String key3, String key4, String key5, String key6, String key7) {
 | 
			
		||||
        return new StatEntry(this, key1, key2, key3, key4, key5, key6, key7);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String key1, String key2, String key3, String key4, String key5, String key6, String key7,
 | 
			
		||||
                          String key8) {
 | 
			
		||||
        return new StatEntry(this, key1, key2, key3, key4, key5, key6, key7, key8);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String key1, String... moreKeys) {
 | 
			
		||||
        return new StatEntry(this, key1, moreKeys);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(List<String> keys) {
 | 
			
		||||
        return new StatEntry(this, keys);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatEntry stat(String[] keys) {
 | 
			
		||||
        return new StatEntry(this, keys);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,113 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author jifeng
 | 
			
		||||
 */
 | 
			
		||||
public final class StatLoggerBuilder extends BaseLoggerBuilder<StatLoggerBuilder> {
 | 
			
		||||
 | 
			
		||||
    private int intervalSeconds = 60;
 | 
			
		||||
 | 
			
		||||
    private int maxEntryCount = 20000;
 | 
			
		||||
 | 
			
		||||
    private char keyDelimiter = ',';
 | 
			
		||||
 | 
			
		||||
    private char valueDelimiter = ',';
 | 
			
		||||
 | 
			
		||||
    private EagleEyeAppender appender = null;
 | 
			
		||||
 | 
			
		||||
    StatLoggerBuilder(String loggerName) {
 | 
			
		||||
        super(loggerName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatLoggerBuilder intervalSeconds(int intervalSeconds) {
 | 
			
		||||
        validateInterval(intervalSeconds);
 | 
			
		||||
        this.intervalSeconds = intervalSeconds;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatLoggerBuilder maxEntryCount(int maxEntryCount) {
 | 
			
		||||
        if (maxEntryCount < 1) {
 | 
			
		||||
            throw new IllegalArgumentException("Max entry count should be at least 1: " + maxEntryCount);
 | 
			
		||||
        }
 | 
			
		||||
        this.maxEntryCount = maxEntryCount;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatLoggerBuilder keyDelimiter(char keyDelimiter) {
 | 
			
		||||
        this.keyDelimiter = keyDelimiter;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatLoggerBuilder valueDelimiter(char valueDelimiter) {
 | 
			
		||||
        this.valueDelimiter = valueDelimiter;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    StatLoggerBuilder appender(EagleEyeAppender appender) {
 | 
			
		||||
        this.appender = appender;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    StatLogger create() {
 | 
			
		||||
        long intervalMillis = TimeUnit.SECONDS.toMillis(this.intervalSeconds);
 | 
			
		||||
 | 
			
		||||
        String filePath;
 | 
			
		||||
        if (this.filePath == null) {
 | 
			
		||||
            filePath = EagleEye.EAGLEEYE_LOG_DIR + "stat-" + loggerName + ".log";
 | 
			
		||||
        } else if (this.filePath.endsWith("/") || this.filePath.endsWith("\\")) {
 | 
			
		||||
            filePath = this.filePath + "stat-" + loggerName + ".log";
 | 
			
		||||
        } else {
 | 
			
		||||
            filePath = this.filePath;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        EagleEyeAppender appender = this.appender;
 | 
			
		||||
        if (appender == null) {
 | 
			
		||||
            EagleEyeRollingFileAppender rfAppender = new EagleEyeRollingFileAppender(filePath, maxFileSize);
 | 
			
		||||
            appender = new SyncAppender(rfAppender);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        EagleEyeLogDaemon.watch(appender);
 | 
			
		||||
        return new StatLogger(loggerName, appender, intervalMillis, maxEntryCount,
 | 
			
		||||
            entryDelimiter, keyDelimiter, valueDelimiter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public StatLogger buildSingleton() {
 | 
			
		||||
        return StatLogController.createLoggerIfNotExists(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void validateInterval(final long intervalSeconds) throws IllegalArgumentException {
 | 
			
		||||
        if (intervalSeconds < 1) {
 | 
			
		||||
            throw new IllegalArgumentException("Interval cannot be less than 1" + intervalSeconds);
 | 
			
		||||
        } else if (intervalSeconds < 60) {
 | 
			
		||||
            if (60 % intervalSeconds != 0) {
 | 
			
		||||
                throw new IllegalArgumentException("Invalid second interval (cannot divide by 60): " + intervalSeconds);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (intervalSeconds <= 5 * 60) {
 | 
			
		||||
            if (intervalSeconds % 60 != 0) {
 | 
			
		||||
                throw new IllegalArgumentException("Invalid second interval (cannot divide by 60): " + intervalSeconds);
 | 
			
		||||
            }
 | 
			
		||||
            if (60 % intervalSeconds != 0) {
 | 
			
		||||
                throw new IllegalArgumentException("Invalid second interval (cannot divide by 60): " + intervalSeconds);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (intervalSeconds > 5 * 60) {
 | 
			
		||||
            throw new IllegalArgumentException("Interval should be less than 5 min: " + intervalSeconds);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,108 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Map.Entry;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.locks.ReentrantLock;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author jifeng
 | 
			
		||||
 */
 | 
			
		||||
final class StatRollingData {
 | 
			
		||||
 | 
			
		||||
    private final StatLogger statLogger;
 | 
			
		||||
 | 
			
		||||
    private final long timeSlot;
 | 
			
		||||
 | 
			
		||||
    private final long rollingTimeMillis;
 | 
			
		||||
 | 
			
		||||
    private final ReentrantLock writeLock;
 | 
			
		||||
 | 
			
		||||
    private final Map<StatEntry, StatEntryFunc> statMap;
 | 
			
		||||
 | 
			
		||||
    StatRollingData(StatLogger statLogger, int initialCapacity, long timeSlot, long rollingTimeMillis) {
 | 
			
		||||
        this(statLogger, timeSlot, rollingTimeMillis,
 | 
			
		||||
            new ConcurrentHashMap<StatEntry, StatEntryFunc>(
 | 
			
		||||
                Math.min(initialCapacity, statLogger.getMaxEntryCount())));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private StatRollingData(StatLogger statLogger, long timeSlot, long rollingTimeMillis,
 | 
			
		||||
                            Map<StatEntry, StatEntryFunc> statMap) {
 | 
			
		||||
        this.statLogger = statLogger;
 | 
			
		||||
        this.timeSlot = timeSlot;
 | 
			
		||||
        this.rollingTimeMillis = rollingTimeMillis;
 | 
			
		||||
        this.writeLock = new ReentrantLock();
 | 
			
		||||
        this.statMap = statMap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    StatEntryFunc getStatEntryFunc(
 | 
			
		||||
        final StatEntry statEntry, final StatEntryFuncFactory factory) {
 | 
			
		||||
        StatEntryFunc func = statMap.get(statEntry);
 | 
			
		||||
        if (func == null) {
 | 
			
		||||
            StatRollingData clone = null;
 | 
			
		||||
            writeLock.lock();
 | 
			
		||||
            try {
 | 
			
		||||
                int entryCount = statMap.size();
 | 
			
		||||
                if (entryCount < statLogger.getMaxEntryCount()) {
 | 
			
		||||
                    func = statMap.get(statEntry);
 | 
			
		||||
                    if (func == null) {
 | 
			
		||||
                        func = factory.create();
 | 
			
		||||
                        statMap.put(statEntry, func);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    Map<StatEntry, StatEntryFunc> cloneStatMap =
 | 
			
		||||
                        new HashMap<StatEntry, StatEntryFunc>(statMap);
 | 
			
		||||
                    statMap.clear();
 | 
			
		||||
 | 
			
		||||
                    func = factory.create();
 | 
			
		||||
                    statMap.put(statEntry, func);
 | 
			
		||||
                    clone = new StatRollingData(statLogger, timeSlot, rollingTimeMillis, cloneStatMap);
 | 
			
		||||
                }
 | 
			
		||||
            } finally {
 | 
			
		||||
                writeLock.unlock();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (clone != null) {
 | 
			
		||||
                StatLogController.scheduleWriteTask(clone);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return func;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    StatLogger getStatLogger() {
 | 
			
		||||
        return statLogger;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long getRollingTimeMillis() {
 | 
			
		||||
        return rollingTimeMillis;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    long getTimeSlot() {
 | 
			
		||||
        return timeSlot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getStatCount() {
 | 
			
		||||
        return statMap.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Set<Entry<StatEntry, StatEntryFunc>> getStatEntrySet() {
 | 
			
		||||
        return statMap.entrySet();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,79 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author jifeng
 | 
			
		||||
 */
 | 
			
		||||
final class SyncAppender extends EagleEyeAppender {
 | 
			
		||||
 | 
			
		||||
    private final EagleEyeAppender delegate;
 | 
			
		||||
    private final Object lock = new Object();
 | 
			
		||||
 | 
			
		||||
    public SyncAppender(EagleEyeAppender delegate) {
 | 
			
		||||
        this.delegate = delegate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void append(String log) {
 | 
			
		||||
        synchronized (lock) {
 | 
			
		||||
            delegate.append(log);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void flush() {
 | 
			
		||||
        synchronized (lock) {
 | 
			
		||||
            delegate.flush();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void rollOver() {
 | 
			
		||||
        synchronized (lock) {
 | 
			
		||||
            delegate.rollOver();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void reload() {
 | 
			
		||||
        synchronized (lock) {
 | 
			
		||||
            delegate.reload();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() {
 | 
			
		||||
        synchronized (lock) {
 | 
			
		||||
            delegate.close();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void cleanup() {
 | 
			
		||||
        delegate.cleanup();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getOutputLocation() {
 | 
			
		||||
        return delegate.getOutputLocation();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return "SyncAppender [appender=" + delegate + "]";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.eagleeye;
 | 
			
		||||
 | 
			
		||||
import java.util.concurrent.atomic.AtomicLong;
 | 
			
		||||
 | 
			
		||||
class TokenBucket {
 | 
			
		||||
 | 
			
		||||
    private final long maxTokens;
 | 
			
		||||
 | 
			
		||||
    private final long intervalMillis;
 | 
			
		||||
 | 
			
		||||
    private volatile long nextUpdate;
 | 
			
		||||
 | 
			
		||||
    private AtomicLong tokens;
 | 
			
		||||
 | 
			
		||||
    public TokenBucket(long maxTokens, long intervalMillis) {
 | 
			
		||||
        if (maxTokens <= 0) {
 | 
			
		||||
            throw new IllegalArgumentException("maxTokens should > 0, but given: " + maxTokens);
 | 
			
		||||
        }
 | 
			
		||||
        if (intervalMillis < 1000) {
 | 
			
		||||
            throw new IllegalArgumentException("intervalMillis should be at least 1000, but given: " + intervalMillis);
 | 
			
		||||
        }
 | 
			
		||||
        this.maxTokens = maxTokens;
 | 
			
		||||
        this.intervalMillis = intervalMillis;
 | 
			
		||||
        this.nextUpdate = System.currentTimeMillis() / 1000 * 1000 + intervalMillis;
 | 
			
		||||
        this.tokens = new AtomicLong(maxTokens);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean accept(long now) {
 | 
			
		||||
        long currTokens;
 | 
			
		||||
        if (now > nextUpdate) {
 | 
			
		||||
            currTokens = tokens.get();
 | 
			
		||||
            if (tokens.compareAndSet(currTokens, maxTokens)) {
 | 
			
		||||
                nextUpdate = System.currentTimeMillis() / 1000 * 1000 + intervalMillis;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        do {
 | 
			
		||||
            currTokens = tokens.get();
 | 
			
		||||
        } while (currTokens > 0 && !tokens.compareAndSet(currTokens, currTokens - 1));
 | 
			
		||||
 | 
			
		||||
        return currTokens > 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,104 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.init;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.ServiceLoader;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicBoolean;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Load registered init functions and execute in order.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public final class InitExecutor {
 | 
			
		||||
 | 
			
		||||
    private static AtomicBoolean initialized = new AtomicBoolean(false);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If one {@link InitFunc} throws an exception, the init process
 | 
			
		||||
     * will immediately be interrupted and the application will exit.
 | 
			
		||||
     *
 | 
			
		||||
     * The initialization will be executed only once.
 | 
			
		||||
     */
 | 
			
		||||
    public static void doInit() {
 | 
			
		||||
        if (!initialized.compareAndSet(false, true)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            List<InitFunc> initFuncs = SpiLoader.of(InitFunc.class).loadInstanceListSorted();
 | 
			
		||||
            List<OrderWrapper> initList = new ArrayList<OrderWrapper>();
 | 
			
		||||
            for (InitFunc initFunc : initFuncs) {
 | 
			
		||||
                RecordLog.info("[InitExecutor] Found init func: {}", initFunc.getClass().getCanonicalName());
 | 
			
		||||
                insertSorted(initList, initFunc);
 | 
			
		||||
            }
 | 
			
		||||
            for (OrderWrapper w : initList) {
 | 
			
		||||
                w.func.init();
 | 
			
		||||
                RecordLog.info("[InitExecutor] Executing {} with order {}",
 | 
			
		||||
                    w.func.getClass().getCanonicalName(), w.order);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception ex) {
 | 
			
		||||
            RecordLog.warn("[InitExecutor] WARN: Initialization failed", ex);
 | 
			
		||||
            ex.printStackTrace();
 | 
			
		||||
        } catch (Error error) {
 | 
			
		||||
            RecordLog.warn("[InitExecutor] ERROR: Initialization failed with fatal error", error);
 | 
			
		||||
            error.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void insertSorted(List<OrderWrapper> list, InitFunc func) {
 | 
			
		||||
        int order = resolveOrder(func);
 | 
			
		||||
        int idx = 0;
 | 
			
		||||
        for (; idx < list.size(); idx++) {
 | 
			
		||||
            if (list.get(idx).getOrder() > order) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        list.add(idx, new OrderWrapper(order, func));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int resolveOrder(InitFunc func) {
 | 
			
		||||
        if (!func.getClass().isAnnotationPresent(InitOrder.class)) {
 | 
			
		||||
            return InitOrder.LOWEST_PRECEDENCE;
 | 
			
		||||
        } else {
 | 
			
		||||
            return func.getClass().getAnnotation(InitOrder.class).value();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private InitExecutor() {}
 | 
			
		||||
 | 
			
		||||
    private static class OrderWrapper {
 | 
			
		||||
        private final int order;
 | 
			
		||||
        private final InitFunc func;
 | 
			
		||||
 | 
			
		||||
        OrderWrapper(int order, InitFunc func) {
 | 
			
		||||
            this.order = order;
 | 
			
		||||
            this.func = func;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int getOrder() {
 | 
			
		||||
            return order;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        InitFunc getFunc() {
 | 
			
		||||
            return func;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.init;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public interface InitFunc {
 | 
			
		||||
 | 
			
		||||
    void init() throws Exception;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,41 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.init;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.Documented;
 | 
			
		||||
import java.lang.annotation.ElementType;
 | 
			
		||||
import java.lang.annotation.Retention;
 | 
			
		||||
import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
@Target({ElementType.TYPE})
 | 
			
		||||
@Documented
 | 
			
		||||
public @interface InitOrder {
 | 
			
		||||
 | 
			
		||||
    int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
 | 
			
		||||
    int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The order value. Lowest precedence by default.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the order value
 | 
			
		||||
     */
 | 
			
		||||
    int value() default LOWEST_PRECEDENCE;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,145 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.log;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
 | 
			
		||||
import static com.alibaba.csp.sentinel.util.ConfigUtil.addSeparator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>The base config class for logging.</p>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * The default log base directory is {@code ${user.home}/logs/csp/}. We can use the {@link #LOG_DIR}
 | 
			
		||||
 * property to override it. The default log file name dose not contain pid, but if multi-instances of the same service
 | 
			
		||||
 * are running in the same machine, we may want to distinguish the log file by process ID number.
 | 
			
		||||
 * In this case, {@link #LOG_NAME_USE_PID} property could be configured as "true" to turn on this switch.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public class LogBase {
 | 
			
		||||
 | 
			
		||||
    public static final String LOG_DIR = "csp.sentinel.log.dir";
 | 
			
		||||
    public static final String LOG_NAME_USE_PID = "csp.sentinel.log.use.pid";
 | 
			
		||||
    public static final String LOG_OUTPUT_TYPE = "csp.sentinel.log.output.type";
 | 
			
		||||
    public static final String LOG_CHARSET = "csp.sentinel.log.charset";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Output biz log (e.g. RecordLog and CommandCenterLog) to file.
 | 
			
		||||
     */
 | 
			
		||||
    public static final String LOG_OUTPUT_TYPE_FILE = "file";
 | 
			
		||||
    /**
 | 
			
		||||
     * Output biz log (e.g. RecordLog and CommandCenterLog) to console.
 | 
			
		||||
     */
 | 
			
		||||
    public static final String LOG_OUTPUT_TYPE_CONSOLE = "console";
 | 
			
		||||
    public static final String LOG_CHARSET_UTF8 = "utf-8";
 | 
			
		||||
 | 
			
		||||
    private static final String DIR_NAME = "logs" + File.separator + "csp";
 | 
			
		||||
    private static final String USER_HOME = "user.home";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private static boolean logNameUsePid;
 | 
			
		||||
    private static String logOutputType;
 | 
			
		||||
    private static String logBaseDir;
 | 
			
		||||
    private static String logCharSet;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        try {
 | 
			
		||||
            initializeDefault();
 | 
			
		||||
            loadProperties();
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            System.err.println("[LogBase] FATAL ERROR when initializing logging config");
 | 
			
		||||
            t.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void initializeDefault() {
 | 
			
		||||
        logNameUsePid = false;
 | 
			
		||||
        logOutputType = LOG_OUTPUT_TYPE_FILE;
 | 
			
		||||
        logBaseDir = addSeparator(System.getProperty(USER_HOME)) + DIR_NAME + File.separator;
 | 
			
		||||
        logCharSet = LOG_CHARSET_UTF8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void loadProperties() {
 | 
			
		||||
        Properties properties = LogConfigLoader.getProperties();
 | 
			
		||||
 | 
			
		||||
        logOutputType = properties.get(LOG_OUTPUT_TYPE) == null ? logOutputType : properties.getProperty(LOG_OUTPUT_TYPE);
 | 
			
		||||
        if (!LOG_OUTPUT_TYPE_FILE.equalsIgnoreCase(logOutputType) && !LOG_OUTPUT_TYPE_CONSOLE.equalsIgnoreCase(logOutputType)) {
 | 
			
		||||
            logOutputType = LOG_OUTPUT_TYPE_FILE;
 | 
			
		||||
        }
 | 
			
		||||
        System.out.println("INFO: Sentinel log output type is: " + logOutputType);
 | 
			
		||||
 | 
			
		||||
        logCharSet = properties.getProperty(LOG_CHARSET) == null ? logCharSet : properties.getProperty(LOG_CHARSET);
 | 
			
		||||
        System.out.println("INFO: Sentinel log charset is: " + logCharSet);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        logBaseDir = properties.getProperty(LOG_DIR) == null ? logBaseDir : properties.getProperty(LOG_DIR);
 | 
			
		||||
        logBaseDir = addSeparator(logBaseDir);
 | 
			
		||||
        File dir = new File(logBaseDir);
 | 
			
		||||
        if (!dir.exists()) {
 | 
			
		||||
            if (!dir.mkdirs()) {
 | 
			
		||||
                System.err.println("ERROR: create Sentinel log base directory error: " + logBaseDir);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        System.out.println("INFO: Sentinel log base directory is: " + logBaseDir);
 | 
			
		||||
 | 
			
		||||
        String usePid = properties.getProperty(LOG_NAME_USE_PID);
 | 
			
		||||
        logNameUsePid = "true".equalsIgnoreCase(usePid);
 | 
			
		||||
        System.out.println("INFO: Sentinel log name use pid is: " + logNameUsePid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether log file name should contain pid. This switch is configured by {@link #LOG_NAME_USE_PID} system property.
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if log file name should contain pid, return true, otherwise false
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isLogNameUsePid() {
 | 
			
		||||
        return logNameUsePid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the log file base directory path, which is guaranteed ended with {@link File#separator}.
 | 
			
		||||
     *
 | 
			
		||||
     * @return log file base directory path
 | 
			
		||||
     */
 | 
			
		||||
    public static String getLogBaseDir() {
 | 
			
		||||
        return logBaseDir;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the log file output type.
 | 
			
		||||
     *
 | 
			
		||||
     * @return log output type, "file" by default
 | 
			
		||||
     */
 | 
			
		||||
    public static String getLogOutputType() {
 | 
			
		||||
        return logOutputType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the log file charset.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the log file charset, "utf-8" by default
 | 
			
		||||
     */
 | 
			
		||||
    public static String getLogCharset() {
 | 
			
		||||
        return logCharSet;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,76 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.log;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.util.ConfigUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.StringUtil;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
import java.util.concurrent.CopyOnWriteArraySet;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>The loader that responsible for loading Sentinel log configurations.</p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author lianglin
 | 
			
		||||
 * @since 1.7.0
 | 
			
		||||
 */
 | 
			
		||||
public class LogConfigLoader {
 | 
			
		||||
 | 
			
		||||
    public static final String LOG_CONFIG_ENV_KEY = "CSP_SENTINEL_CONFIG_FILE";
 | 
			
		||||
    public static final String LOG_CONFIG_PROPERTY_KEY = "csp.sentinel.config.file";
 | 
			
		||||
 | 
			
		||||
    private static final String DEFAULT_LOG_CONFIG_FILE = "classpath:sentinel.properties";
 | 
			
		||||
 | 
			
		||||
    private static final Properties properties = new Properties();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        try {
 | 
			
		||||
            load();
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            // NOTE: do not use RecordLog here, or there will be circular class dependency!
 | 
			
		||||
            System.err.println("[LogConfigLoader] Failed to initialize configuration items");
 | 
			
		||||
            t.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void load() {
 | 
			
		||||
        // Order: system property -> system env -> default file (classpath:sentinel.properties) -> legacy path
 | 
			
		||||
        String fileName = System.getProperty(LOG_CONFIG_PROPERTY_KEY);
 | 
			
		||||
        if (StringUtil.isBlank(fileName)) {
 | 
			
		||||
            fileName = System.getenv(LOG_CONFIG_ENV_KEY);
 | 
			
		||||
            if (StringUtil.isBlank(fileName)) {
 | 
			
		||||
                fileName = DEFAULT_LOG_CONFIG_FILE;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Properties p = ConfigUtil.loadProperties(fileName);
 | 
			
		||||
        if (p != null && !p.isEmpty()) {
 | 
			
		||||
            properties.putAll(p);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CopyOnWriteArraySet<Map.Entry<Object, Object>> copy = new CopyOnWriteArraySet<>(System.getProperties().entrySet());
 | 
			
		||||
        for (Map.Entry<Object, Object> entry : copy) {
 | 
			
		||||
            String configKey = entry.getKey().toString();
 | 
			
		||||
            String newConfigValue = entry.getValue().toString();
 | 
			
		||||
            properties.put(configKey, newConfigValue);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Properties getProperties() {
 | 
			
		||||
        return properties;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 *
 | 
			
		||||
 *      https://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.csp.sentinel.log;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author xue8
 | 
			
		||||
 * @since 1.7.2
 | 
			
		||||
 */
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
@Target(ElementType.TYPE)
 | 
			
		||||
@Documented
 | 
			
		||||
public @interface LogTarget {
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the logger name.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the logger name. Record logger by default
 | 
			
		||||
     */
 | 
			
		||||
    String value() default RecordLog.LOGGER_NAME;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,118 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 *
 | 
			
		||||
 *      https://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.csp.sentinel.log;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>The universal logger SPI interface.</p>
 | 
			
		||||
 * <p>Notice: the placeholder only supports the most popular placeholder convention (slf4j).
 | 
			
		||||
 * So, if you're not using slf4j, you should create adapters compatible with placeholders "{}".</p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author xue8
 | 
			
		||||
 * @since 1.7.2
 | 
			
		||||
 */
 | 
			
		||||
public interface Logger {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log a message at the INFO level according to the specified format
 | 
			
		||||
     * and arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @param format    the format string
 | 
			
		||||
     * @param arguments a list of arguments
 | 
			
		||||
     */
 | 
			
		||||
    void info(String format, Object... arguments);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log an exception (throwable) at the INFO level with an
 | 
			
		||||
     * accompanying message.
 | 
			
		||||
     *
 | 
			
		||||
     * @param msg the message accompanying the exception
 | 
			
		||||
     * @param e   the exception (throwable) to log
 | 
			
		||||
     */
 | 
			
		||||
    void info(String msg, Throwable e);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log a message at the WARN level according to the specified format
 | 
			
		||||
     * and arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @param format    the format string
 | 
			
		||||
     * @param arguments a list of arguments
 | 
			
		||||
     */
 | 
			
		||||
    void warn(String format, Object... arguments);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log an exception (throwable) at the WARN level with an
 | 
			
		||||
     * accompanying message.
 | 
			
		||||
     *
 | 
			
		||||
     * @param msg the message accompanying the exception
 | 
			
		||||
     * @param e   the exception (throwable) to log
 | 
			
		||||
     */
 | 
			
		||||
    void warn(String msg, Throwable e);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log a message at the TRACE level according to the specified format
 | 
			
		||||
     * and arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @param format    the format string
 | 
			
		||||
     * @param arguments a list of arguments
 | 
			
		||||
     */
 | 
			
		||||
    void trace(String format, Object... arguments);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log an exception (throwable) at the TRACE level with an
 | 
			
		||||
     * accompanying message.
 | 
			
		||||
     *
 | 
			
		||||
     * @param msg the message accompanying the exception
 | 
			
		||||
     * @param e   the exception (throwable) to log
 | 
			
		||||
     */
 | 
			
		||||
    void trace(String msg, Throwable e);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log a message at the DEBUG level according to the specified format
 | 
			
		||||
     * and arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @param format    the format string
 | 
			
		||||
     * @param arguments a list of arguments
 | 
			
		||||
     */
 | 
			
		||||
    void debug(String format, Object... arguments);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log an exception (throwable) at the DEBUG level with an
 | 
			
		||||
     * accompanying message.
 | 
			
		||||
     *
 | 
			
		||||
     * @param msg the message accompanying the exception
 | 
			
		||||
     * @param e   the exception (throwable) to log
 | 
			
		||||
     */
 | 
			
		||||
    void debug(String msg, Throwable e);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log a message at the ERROR level according to the specified format
 | 
			
		||||
     * and arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @param format    the format string
 | 
			
		||||
     * @param arguments a list of arguments
 | 
			
		||||
     */
 | 
			
		||||
    void error(String format, Object... arguments);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Log an exception (throwable) at the ERROR level with an
 | 
			
		||||
     * accompanying message.
 | 
			
		||||
     *
 | 
			
		||||
     * @param msg the message accompanying the exception
 | 
			
		||||
     * @param e   the exception (throwable) to log
 | 
			
		||||
     */
 | 
			
		||||
    void error(String msg, Throwable e);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,72 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2020 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 *
 | 
			
		||||
 *      https://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.csp.sentinel.log;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.ServiceLoader;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.util.StringUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * SPI provider of Sentinel {@link Logger}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.7.2
 | 
			
		||||
 */
 | 
			
		||||
public final class LoggerSpiProvider {
 | 
			
		||||
 | 
			
		||||
    private static final Map<String, Logger> LOGGER_MAP = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        // NOTE: this class SHOULD NOT depend on any other Sentinel classes
 | 
			
		||||
        // except the util classes to avoid circular dependency.
 | 
			
		||||
        try {
 | 
			
		||||
            resolveLoggers();
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            System.err.println("Failed to resolve Sentinel Logger SPI");
 | 
			
		||||
            t.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Logger getLogger(String name) {
 | 
			
		||||
        if (name == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return LOGGER_MAP.get(name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void resolveLoggers() {
 | 
			
		||||
        // NOTE: Here we cannot use {@code SpiLoader} directly because it depends on the RecordLog.
 | 
			
		||||
        ServiceLoader<Logger> loggerLoader = ServiceLoader.load(Logger.class);
 | 
			
		||||
 | 
			
		||||
        for (Logger logger : loggerLoader) {
 | 
			
		||||
            LogTarget annotation = logger.getClass().getAnnotation(LogTarget.class);
 | 
			
		||||
            if (annotation == null) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            String name = annotation.value();
 | 
			
		||||
            // Load first encountered logger if multiple loggers are associated with the same name.
 | 
			
		||||
            if (StringUtil.isNotBlank(name) && !LOGGER_MAP.containsKey(name)) {
 | 
			
		||||
                LOGGER_MAP.put(name, logger);
 | 
			
		||||
                System.out.println("Sentinel Logger SPI loaded for <" + name + ">: "
 | 
			
		||||
                    + logger.getClass().getCanonicalName());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private LoggerSpiProvider() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,88 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.log;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.jul.JavaLoggingAdapter;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The basic biz logger of Sentinel.
 | 
			
		||||
 *
 | 
			
		||||
 * @author youji.zj
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public class RecordLog {
 | 
			
		||||
 | 
			
		||||
    public static final String LOGGER_NAME = "sentinelRecordLogger";
 | 
			
		||||
    public static final String DEFAULT_LOG_FILENAME = "sentinel-record.log";
 | 
			
		||||
 | 
			
		||||
    private static com.alibaba.csp.sentinel.log.Logger logger = null;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        try {
 | 
			
		||||
            // Load user-defined logger implementation first.
 | 
			
		||||
            logger = LoggerSpiProvider.getLogger(LOGGER_NAME);
 | 
			
		||||
            if (logger == null) {
 | 
			
		||||
                // If no customized loggers are provided, we use the default logger based on JUL.
 | 
			
		||||
                logger = new JavaLoggingAdapter(LOGGER_NAME, DEFAULT_LOG_FILENAME);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            System.err.println("Error: failed to initialize Sentinel RecordLog");
 | 
			
		||||
            t.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void info(String format, Object... arguments) {
 | 
			
		||||
        logger.info(format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void info(String msg, Throwable e) {
 | 
			
		||||
        logger.info(msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void warn(String format, Object... arguments) {
 | 
			
		||||
        logger.warn(format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void warn(String msg, Throwable e) {
 | 
			
		||||
        logger.warn(msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void trace(String format, Object... arguments) {
 | 
			
		||||
        logger.trace(format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void trace(String msg, Throwable e) {
 | 
			
		||||
        logger.trace(msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void debug(String format, Object... arguments) {
 | 
			
		||||
        logger.debug(format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void debug(String msg, Throwable e) {
 | 
			
		||||
        logger.debug(msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void error(String format, Object... arguments) {
 | 
			
		||||
        logger.error(format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void error(String msg, Throwable e) {
 | 
			
		||||
        logger.error(msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private RecordLog() {}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,128 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 *
 | 
			
		||||
 *      https://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.csp.sentinel.log.jul;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.logging.Handler;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.LogBase;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.PidUtil;
 | 
			
		||||
 | 
			
		||||
import static com.alibaba.csp.sentinel.log.LogBase.LOG_OUTPUT_TYPE_CONSOLE;
 | 
			
		||||
import static com.alibaba.csp.sentinel.log.LogBase.LOG_OUTPUT_TYPE_FILE;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The default logger based on java.util.logging.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.7.2
 | 
			
		||||
 */
 | 
			
		||||
public class BaseJulLogger {
 | 
			
		||||
 | 
			
		||||
    protected void log(Logger logger, Handler handler, Level level, String detail, Object... params) {
 | 
			
		||||
        if (detail == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        disableOtherHandlers(logger, handler);
 | 
			
		||||
 | 
			
		||||
        // Compatible with slf4j placeholder format "{}".
 | 
			
		||||
        FormattingTuple formattingTuple = MessageFormatter.arrayFormat(detail, params);
 | 
			
		||||
        String message = formattingTuple.getMessage();
 | 
			
		||||
        logger.log(level, message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void log(Logger logger, Handler handler, Level level, String detail, Throwable throwable) {
 | 
			
		||||
        if (detail == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        disableOtherHandlers(logger, handler);
 | 
			
		||||
        logger.log(level, detail, throwable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected Handler makeLoggingHandler(String logName, Logger heliumRecordLog) {
 | 
			
		||||
        CspFormatter formatter = new CspFormatter();
 | 
			
		||||
        String logCharSet = LogBase.getLogCharset();
 | 
			
		||||
        Handler handler = null;
 | 
			
		||||
 | 
			
		||||
        // Create handler according to logOutputType, set formatter to CspFormatter, set encoding to LOG_CHARSET
 | 
			
		||||
        switch (LogBase.getLogOutputType()) {
 | 
			
		||||
            case LOG_OUTPUT_TYPE_FILE:
 | 
			
		||||
                String fileName = LogBase.getLogBaseDir() + logName;
 | 
			
		||||
                if (LogBase.isLogNameUsePid()) {
 | 
			
		||||
                    fileName += ".pid" + PidUtil.getPid();
 | 
			
		||||
                }
 | 
			
		||||
                try {
 | 
			
		||||
                    handler = new DateFileLogHandler(fileName + ".%d", 1024 * 1024 * 200, 4, true);
 | 
			
		||||
                    handler.setFormatter(formatter);
 | 
			
		||||
                    handler.setEncoding(logCharSet);
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case LOG_OUTPUT_TYPE_CONSOLE:
 | 
			
		||||
                try {
 | 
			
		||||
                    handler = new ConsoleHandler();
 | 
			
		||||
                    handler.setFormatter(formatter);
 | 
			
		||||
                    handler.setEncoding(logCharSet);
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    e.printStackTrace();
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (handler != null) {
 | 
			
		||||
            disableOtherHandlers(heliumRecordLog, handler);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set log level to INFO by default
 | 
			
		||||
        heliumRecordLog.setLevel(Level.INFO);
 | 
			
		||||
        return handler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Remove all current handlers from the logger and attach it with the given log handler.
 | 
			
		||||
     *
 | 
			
		||||
     * @param logger  logger
 | 
			
		||||
     * @param handler the log handler
 | 
			
		||||
     */
 | 
			
		||||
    static void disableOtherHandlers(Logger logger, Handler handler) {
 | 
			
		||||
        if (logger == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        synchronized (logger) {
 | 
			
		||||
            Handler[] handlers = logger.getHandlers();
 | 
			
		||||
            if (handlers == null) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (handlers.length == 1 && handlers[0].equals(handler)) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            logger.setUseParentHandlers(false);
 | 
			
		||||
            // Remove all current handlers.
 | 
			
		||||
            for (Handler h : handlers) {
 | 
			
		||||
                logger.removeHandler(h);
 | 
			
		||||
            }
 | 
			
		||||
            // Attach the given handler.
 | 
			
		||||
            logger.addHandler(handler);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,85 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.log.jul;
 | 
			
		||||
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
import java.util.logging.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This Handler publishes log records to console by using {@link java.util.logging.StreamHandler}.
 | 
			
		||||
 *
 | 
			
		||||
 * Print log of WARNING level or above to System.err,
 | 
			
		||||
 * and print log of INFO level or below to System.out.
 | 
			
		||||
 *
 | 
			
		||||
 * To use this handler, add the following VM argument:
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * -Dcsp.sentinel.log.output.type=console
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * @author cdfive
 | 
			
		||||
 */
 | 
			
		||||
class ConsoleHandler extends Handler {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A Handler which publishes log records to System.out.
 | 
			
		||||
     */
 | 
			
		||||
    private StreamHandler stdoutHandler;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A Handler which publishes log records to System.err.
 | 
			
		||||
     */
 | 
			
		||||
    private StreamHandler stderrHandler;
 | 
			
		||||
 | 
			
		||||
    public ConsoleHandler() {
 | 
			
		||||
        this.stdoutHandler = new StreamHandler(System.out, new CspFormatter());
 | 
			
		||||
        this.stderrHandler = new StreamHandler(System.err, new CspFormatter());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
 | 
			
		||||
        this.stdoutHandler.setFormatter(newFormatter);
 | 
			
		||||
        this.stderrHandler.setFormatter(newFormatter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public synchronized void setEncoding(String encoding) throws SecurityException, UnsupportedEncodingException {
 | 
			
		||||
        this.stdoutHandler.setEncoding(encoding);
 | 
			
		||||
        this.stderrHandler.setEncoding(encoding);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void publish(LogRecord record) {
 | 
			
		||||
        if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
 | 
			
		||||
            stderrHandler.publish(record);
 | 
			
		||||
            stderrHandler.flush();
 | 
			
		||||
        } else {
 | 
			
		||||
            stdoutHandler.publish(record);
 | 
			
		||||
            stdoutHandler.flush();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void flush() {
 | 
			
		||||
        stdoutHandler.flush();
 | 
			
		||||
        stderrHandler.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() throws SecurityException {
 | 
			
		||||
        stdoutHandler.close();
 | 
			
		||||
        stderrHandler.close();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.log.jul;
 | 
			
		||||
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.io.StringWriter;
 | 
			
		||||
import java.text.DateFormat;
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.logging.Formatter;
 | 
			
		||||
import java.util.logging.LogRecord;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author xuyue
 | 
			
		||||
 */
 | 
			
		||||
class CspFormatter extends Formatter {
 | 
			
		||||
 | 
			
		||||
    private final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public SimpleDateFormat initialValue() {
 | 
			
		||||
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String format(LogRecord record) {
 | 
			
		||||
        final DateFormat df = dateFormatThreadLocal.get();
 | 
			
		||||
        StringBuilder builder = new StringBuilder(1000);
 | 
			
		||||
        builder.append(df.format(new Date(record.getMillis()))).append(" ");
 | 
			
		||||
        builder.append(record.getLevel().getName()).append(" ");
 | 
			
		||||
        builder.append(formatMessage(record));
 | 
			
		||||
 | 
			
		||||
        String throwable = "";
 | 
			
		||||
        if (record.getThrown() != null) {
 | 
			
		||||
            StringWriter sw = new StringWriter();
 | 
			
		||||
            PrintWriter pw = new PrintWriter(sw);
 | 
			
		||||
            pw.println();
 | 
			
		||||
            record.getThrown().printStackTrace(pw);
 | 
			
		||||
            pw.close();
 | 
			
		||||
            throwable = sw.toString();
 | 
			
		||||
        }
 | 
			
		||||
        builder.append(throwable);
 | 
			
		||||
        if ("".equals(throwable)) {
 | 
			
		||||
            builder.append("\n");
 | 
			
		||||
        }
 | 
			
		||||
        return builder.toString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,148 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.log.jul;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.util.Calendar;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.logging.FileHandler;
 | 
			
		||||
import java.util.logging.Formatter;
 | 
			
		||||
import java.util.logging.Handler;
 | 
			
		||||
import java.util.logging.LogRecord;
 | 
			
		||||
 | 
			
		||||
class DateFileLogHandler extends Handler {
 | 
			
		||||
 | 
			
		||||
    private final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
 | 
			
		||||
        @Override
 | 
			
		||||
        public SimpleDateFormat initialValue() {
 | 
			
		||||
            return new SimpleDateFormat("yyyy-MM-dd");
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private volatile FileHandler handler;
 | 
			
		||||
 | 
			
		||||
    private final String pattern;
 | 
			
		||||
    private final int limit;
 | 
			
		||||
    private final int count;
 | 
			
		||||
    private final boolean append;
 | 
			
		||||
 | 
			
		||||
    private volatile boolean initialized = false;
 | 
			
		||||
 | 
			
		||||
    private volatile long startDate = System.currentTimeMillis();
 | 
			
		||||
    private volatile long endDate;
 | 
			
		||||
 | 
			
		||||
    private final Object monitor = new Object();
 | 
			
		||||
 | 
			
		||||
    DateFileLogHandler(String pattern, int limit, int count, boolean append) throws SecurityException {
 | 
			
		||||
        this.pattern = pattern;
 | 
			
		||||
        this.limit = limit;
 | 
			
		||||
        this.count = count;
 | 
			
		||||
        this.append = append;
 | 
			
		||||
        rotateDate();
 | 
			
		||||
        this.initialized = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void close() throws SecurityException {
 | 
			
		||||
        handler.close();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void flush() {
 | 
			
		||||
        handler.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void publish(LogRecord record) {
 | 
			
		||||
        if (shouldRotate(record)) {
 | 
			
		||||
            synchronized (monitor) {
 | 
			
		||||
                if (shouldRotate(record)) {
 | 
			
		||||
                    rotateDate();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (System.currentTimeMillis() - startDate > 25 * 60 * 60 * 1000) {
 | 
			
		||||
            String msg = record.getMessage();
 | 
			
		||||
            record.setMessage("missed file rolling at: " + new Date(endDate) + "\n" + msg);
 | 
			
		||||
        }
 | 
			
		||||
        handler.publish(record);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean shouldRotate(LogRecord record) {
 | 
			
		||||
        if (endDate <= record.getMillis() || !logFileExits()) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setFormatter(Formatter newFormatter) {
 | 
			
		||||
        super.setFormatter(newFormatter);
 | 
			
		||||
        if (handler != null) { handler.setFormatter(newFormatter); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean logFileExits() {
 | 
			
		||||
        try {
 | 
			
		||||
            SimpleDateFormat format = dateFormatThreadLocal.get();
 | 
			
		||||
            String fileName = pattern.replace("%d", format.format(new Date()));
 | 
			
		||||
            // When file count is not 1, the first log file name will end with ".0"
 | 
			
		||||
            if (count != 1) {
 | 
			
		||||
                fileName += ".0";
 | 
			
		||||
            }
 | 
			
		||||
            File logFile = new File(fileName);
 | 
			
		||||
            return logFile.exists();
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void rotateDate() {
 | 
			
		||||
        this.startDate = System.currentTimeMillis();
 | 
			
		||||
        if (handler != null) {
 | 
			
		||||
            handler.close();
 | 
			
		||||
        }
 | 
			
		||||
        SimpleDateFormat format = dateFormatThreadLocal.get();
 | 
			
		||||
        String newPattern = pattern.replace("%d", format.format(new Date()));
 | 
			
		||||
        // Get current date.
 | 
			
		||||
        Calendar next = Calendar.getInstance();
 | 
			
		||||
        // Begin of next date.
 | 
			
		||||
        next.set(Calendar.HOUR_OF_DAY, 0);
 | 
			
		||||
        next.set(Calendar.MINUTE, 0);
 | 
			
		||||
        next.set(Calendar.SECOND, 0);
 | 
			
		||||
        next.set(Calendar.MILLISECOND, 0);
 | 
			
		||||
        next.add(Calendar.DATE, 1);
 | 
			
		||||
        this.endDate = next.getTimeInMillis();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            this.handler = new FileHandler(newPattern, limit, count, append);
 | 
			
		||||
            if (initialized) {
 | 
			
		||||
                handler.setEncoding(this.getEncoding());
 | 
			
		||||
                handler.setErrorManager(this.getErrorManager());
 | 
			
		||||
                handler.setFilter(this.getFilter());
 | 
			
		||||
                handler.setFormatter(this.getFormatter());
 | 
			
		||||
                handler.setLevel(this.getLevel());
 | 
			
		||||
            }
 | 
			
		||||
        } catch (SecurityException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,55 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// Copyright notice: This code was copied from SLF4J which licensed under the MIT License.
 | 
			
		||||
package com.alibaba.csp.sentinel.log.jul;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Holds the results of formatting done by {@link MessageFormatter}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Joern Huxhorn
 | 
			
		||||
 */
 | 
			
		||||
public class FormattingTuple {
 | 
			
		||||
 | 
			
		||||
    static public FormattingTuple NULL = new FormattingTuple(null);
 | 
			
		||||
 | 
			
		||||
    private String message;
 | 
			
		||||
    private Throwable throwable;
 | 
			
		||||
    private Object[] argArray;
 | 
			
		||||
 | 
			
		||||
    public FormattingTuple(String message) {
 | 
			
		||||
        this(message, null, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public FormattingTuple(String message, Object[] argArray, Throwable throwable) {
 | 
			
		||||
        this.message = message;
 | 
			
		||||
        this.throwable = throwable;
 | 
			
		||||
        this.argArray = argArray;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getMessage() {
 | 
			
		||||
        return message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Object[] getArgArray() {
 | 
			
		||||
        return argArray;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Throwable getThrowable() {
 | 
			
		||||
        return throwable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,104 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 *
 | 
			
		||||
 *      https://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.csp.sentinel.log.jul;
 | 
			
		||||
 | 
			
		||||
import java.util.logging.Handler;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.Logger;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.AssertUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * JUL adapter for Sentinel {@link Logger} SPI.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.7.2
 | 
			
		||||
 */
 | 
			
		||||
public class JavaLoggingAdapter extends BaseJulLogger implements Logger {
 | 
			
		||||
 | 
			
		||||
    private final String loggerName;
 | 
			
		||||
    private final String fileNamePattern;
 | 
			
		||||
 | 
			
		||||
    private final java.util.logging.Logger julLogger;
 | 
			
		||||
    private final Handler logHandler;
 | 
			
		||||
 | 
			
		||||
    public JavaLoggingAdapter(String loggerName, String fileNamePattern) {
 | 
			
		||||
        AssertUtil.assertNotBlank(loggerName, "loggerName cannot be blank");
 | 
			
		||||
        AssertUtil.assertNotBlank(fileNamePattern, "fileNamePattern cannot be blank");
 | 
			
		||||
        this.loggerName = loggerName;
 | 
			
		||||
        this.fileNamePattern = fileNamePattern;
 | 
			
		||||
 | 
			
		||||
        this.julLogger = java.util.logging.Logger.getLogger(loggerName);
 | 
			
		||||
        this.logHandler = makeLoggingHandler(fileNamePattern, julLogger);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void info(String format, Object... arguments) {
 | 
			
		||||
        log(julLogger, logHandler, Level.INFO, format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void info(String msg, Throwable e) {
 | 
			
		||||
        log(julLogger, logHandler, Level.INFO, msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void warn(String format, Object... arguments) {
 | 
			
		||||
        log(julLogger, logHandler, Level.WARNING, format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void warn(String msg, Throwable e) {
 | 
			
		||||
        log(julLogger, logHandler, Level.WARNING, msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void trace(String format, Object... arguments) {
 | 
			
		||||
        log(julLogger, logHandler, Level.TRACE, format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void trace(String msg, Throwable e) {
 | 
			
		||||
        log(julLogger, logHandler, Level.TRACE, msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void debug(String format, Object... arguments) {
 | 
			
		||||
        log(julLogger, logHandler, Level.DEBUG, format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void debug(String msg, Throwable e) {
 | 
			
		||||
        log(julLogger, logHandler, Level.DEBUG, msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void error(String format, Object... arguments) {
 | 
			
		||||
        log(julLogger, logHandler, Level.ERROR, format, arguments);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void error(String msg, Throwable e) {
 | 
			
		||||
        log(julLogger, logHandler, Level.ERROR, msg, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getLoggerName() {
 | 
			
		||||
        return loggerName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getFileNamePattern() {
 | 
			
		||||
        return fileNamePattern;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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
 | 
			
		||||
 *
 | 
			
		||||
 *      https://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.csp.sentinel.log.jul;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * JUL logging levels.
 | 
			
		||||
 *
 | 
			
		||||
 * @author xue8
 | 
			
		||||
 */
 | 
			
		||||
public class Level extends java.util.logging.Level {
 | 
			
		||||
    private static final String defaultBundle = "sun.util.logging.resources.logging";
 | 
			
		||||
 | 
			
		||||
    public static final Level ERROR = new Level("ERROR", 1000);
 | 
			
		||||
    public static final Level WARNING = new Level("WARNING", 900);
 | 
			
		||||
    public static final Level INFO = new Level("INFO", 800);
 | 
			
		||||
    public static final Level DEBUG = new Level("DEBUG", 700);
 | 
			
		||||
    public static final Level TRACE = new Level("TRACE", 600);
 | 
			
		||||
 | 
			
		||||
    protected Level(String name, int value) {
 | 
			
		||||
        super(name, value, defaultBundle);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,417 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// Copyright notice: This code was copied from SLF4J which licensed under the MIT License.
 | 
			
		||||
package com.alibaba.csp.sentinel.log.jul;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// contributors: lizongbo: proposed special treatment of array parameter values
 | 
			
		||||
// Joern Huxhorn: pointed out double[] omission, suggested deep array copy
 | 
			
		||||
 | 
			
		||||
import java.text.MessageFormat;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Formats messages according to very simple substitution rules. Substitutions
 | 
			
		||||
 * can be made 1, 2 or more arguments.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * For example,
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * MessageFormatter.format("Hi {}.", "there")
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * will return the string "Hi there.".
 | 
			
		||||
 * <p>
 | 
			
		||||
 * The {} pair is called the <em>formatting anchor</em>. It serves to designate
 | 
			
		||||
 * the location where arguments need to be substituted within the message
 | 
			
		||||
 * pattern.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * In case your message contains the '{' or the '}' character, you do not have
 | 
			
		||||
 * to do anything special unless the '}' character immediately follows '{'. For
 | 
			
		||||
 * example,
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * MessageFormatter.format("Set {1,2,3} is not equal to {}.", "1,2");
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * will return the string "Set {1,2,3} is not equal to 1,2.".
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * If for whatever reason you need to place the string "{}" in the message
 | 
			
		||||
 * without its <em>formatting anchor</em> meaning, then you need to escape the
 | 
			
		||||
 * '{' character with '\', that is the backslash character. Only the '{'
 | 
			
		||||
 * character should be escaped. There is no need to escape the '}' character.
 | 
			
		||||
 * For example,
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * MessageFormatter.format("Set \\{} is not equal to {}.", "1,2");
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * will return the string "Set {} is not equal to 1,2.".
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * The escaping behavior just described can be overridden by escaping the escape
 | 
			
		||||
 * character '\'. Calling
 | 
			
		||||
 *
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * MessageFormatter.format("File name is C:\\\\{}.", "file.zip");
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * will return the string "File name is C:\file.zip".
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * The formatting conventions are different than those of {@link MessageFormat}
 | 
			
		||||
 * which ships with the Java platform. This is justified by the fact that
 | 
			
		||||
 * SLF4J's implementation is 10 times faster than that of {@link MessageFormat}.
 | 
			
		||||
 * This local performance difference is both measurable and significant in the
 | 
			
		||||
 * larger context of the complete logging processing chain.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * See also {@link #format(String, Object)},
 | 
			
		||||
 * {@link #format(String, Object, Object)} and
 | 
			
		||||
 * {@link #arrayFormat(String, Object[])} methods for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Ceki Gülcü
 | 
			
		||||
 * @author Joern Huxhorn
 | 
			
		||||
 */
 | 
			
		||||
final public class MessageFormatter {
 | 
			
		||||
    static final char DELIM_START = '{';
 | 
			
		||||
    static final char DELIM_STOP = '}';
 | 
			
		||||
    static final String DELIM_STR = "{}";
 | 
			
		||||
    private static final char ESCAPE_CHAR = '\\';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs single argument substitution for the 'messagePattern' passed as
 | 
			
		||||
     * parameter.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * For example,
 | 
			
		||||
     *
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * MessageFormatter.format("Hi {}.", "there");
 | 
			
		||||
     * </pre>
 | 
			
		||||
     *
 | 
			
		||||
     * will return the string "Hi there.".
 | 
			
		||||
     * <p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param messagePattern
 | 
			
		||||
     *          The message pattern which will be parsed and formatted
 | 
			
		||||
     * @param arg
 | 
			
		||||
     *          The argument to be substituted in place of the formatting anchor
 | 
			
		||||
     * @return The formatted message
 | 
			
		||||
     */
 | 
			
		||||
    final public static FormattingTuple format(String messagePattern, Object arg) {
 | 
			
		||||
        return arrayFormat(messagePattern, new Object[] { arg });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * Performs a two argument substitution for the 'messagePattern' passed as
 | 
			
		||||
     * parameter.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * For example,
 | 
			
		||||
     *
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * MessageFormatter.format("Hi {}. My name is {}.", "Alice", "Bob");
 | 
			
		||||
     * </pre>
 | 
			
		||||
     *
 | 
			
		||||
     * will return the string "Hi Alice. My name is Bob.".
 | 
			
		||||
     *
 | 
			
		||||
     * @param messagePattern
 | 
			
		||||
     *          The message pattern which will be parsed and formatted
 | 
			
		||||
     * @param arg1
 | 
			
		||||
     *          The argument to be substituted in place of the first formatting
 | 
			
		||||
     *          anchor
 | 
			
		||||
     * @param arg2
 | 
			
		||||
     *          The argument to be substituted in place of the second formatting
 | 
			
		||||
     *          anchor
 | 
			
		||||
     * @return The formatted message
 | 
			
		||||
     */
 | 
			
		||||
    final public static FormattingTuple format(final String messagePattern, Object arg1, Object arg2) {
 | 
			
		||||
        return arrayFormat(messagePattern, new Object[] { arg1, arg2 });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    static final Throwable getThrowableCandidate(Object[] argArray) {
 | 
			
		||||
        if (argArray == null || argArray.length == 0) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final Object lastEntry = argArray[argArray.length - 1];
 | 
			
		||||
        if (lastEntry instanceof Throwable) {
 | 
			
		||||
            return (Throwable) lastEntry;
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray) {
 | 
			
		||||
        Throwable throwableCandidate = getThrowableCandidate(argArray);
 | 
			
		||||
        Object[] args = argArray;
 | 
			
		||||
        if (throwableCandidate != null) {
 | 
			
		||||
            args = trimmedCopy(argArray);
 | 
			
		||||
        }
 | 
			
		||||
        return arrayFormat(messagePattern, args, throwableCandidate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Object[] trimmedCopy(Object[] argArray) {
 | 
			
		||||
        if (argArray == null || argArray.length == 0) {
 | 
			
		||||
            throw new IllegalStateException("non-sensical empty or null argument array");
 | 
			
		||||
        }
 | 
			
		||||
        final int trimemdLen = argArray.length - 1;
 | 
			
		||||
        Object[] trimmed = new Object[trimemdLen];
 | 
			
		||||
        System.arraycopy(argArray, 0, trimmed, 0, trimemdLen);
 | 
			
		||||
        return trimmed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final public static FormattingTuple arrayFormat(final String messagePattern, final Object[] argArray, Throwable throwable) {
 | 
			
		||||
 | 
			
		||||
        if (messagePattern == null) {
 | 
			
		||||
            return new FormattingTuple(null, argArray, throwable);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (argArray == null) {
 | 
			
		||||
            return new FormattingTuple(messagePattern);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        int j;
 | 
			
		||||
        // use string builder for better multicore performance
 | 
			
		||||
        StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
 | 
			
		||||
 | 
			
		||||
        int L;
 | 
			
		||||
        for (L = 0; L < argArray.length; L++) {
 | 
			
		||||
 | 
			
		||||
            j = messagePattern.indexOf(DELIM_STR, i);
 | 
			
		||||
 | 
			
		||||
            if (j == -1) {
 | 
			
		||||
                // no more variables
 | 
			
		||||
                if (i == 0) { // this is a simple string
 | 
			
		||||
                    return new FormattingTuple(messagePattern, argArray, throwable);
 | 
			
		||||
                } else { // add the tail string which contains no variables and return
 | 
			
		||||
                    // the result.
 | 
			
		||||
                    sbuf.append(messagePattern, i, messagePattern.length());
 | 
			
		||||
                    return new FormattingTuple(sbuf.toString(), argArray, throwable);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (isEscapedDelimeter(messagePattern, j)) {
 | 
			
		||||
                    if (!isDoubleEscaped(messagePattern, j)) {
 | 
			
		||||
                        L--; // DELIM_START was escaped, thus should not be incremented
 | 
			
		||||
                        sbuf.append(messagePattern, i, j - 1);
 | 
			
		||||
                        sbuf.append(DELIM_START);
 | 
			
		||||
                        i = j + 1;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // The escape character preceding the delimiter start is
 | 
			
		||||
                        // itself escaped: "abc x:\\{}"
 | 
			
		||||
                        // we have to consume one backward slash
 | 
			
		||||
                        sbuf.append(messagePattern, i, j - 1);
 | 
			
		||||
                        deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
 | 
			
		||||
                        i = j + 2;
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    // normal case
 | 
			
		||||
                    sbuf.append(messagePattern, i, j);
 | 
			
		||||
                    deeplyAppendParameter(sbuf, argArray[L], new HashMap<Object[], Object>());
 | 
			
		||||
                    i = j + 2;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // append the characters following the last {} pair.
 | 
			
		||||
        sbuf.append(messagePattern, i, messagePattern.length());
 | 
			
		||||
        return new FormattingTuple(sbuf.toString(), argArray, throwable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final static boolean isEscapedDelimeter(String messagePattern, int delimeterStartIndex) {
 | 
			
		||||
 | 
			
		||||
        if (delimeterStartIndex == 0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        char potentialEscape = messagePattern.charAt(delimeterStartIndex - 1);
 | 
			
		||||
        if (potentialEscape == ESCAPE_CHAR) {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final static boolean isDoubleEscaped(String messagePattern, int delimeterStartIndex) {
 | 
			
		||||
        if (delimeterStartIndex >= 2 && messagePattern.charAt(delimeterStartIndex - 2) == ESCAPE_CHAR) {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // special treatment of array values was suggested by 'lizongbo'
 | 
			
		||||
    private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Map<Object[], Object> seenMap) {
 | 
			
		||||
        if (o == null) {
 | 
			
		||||
            sbuf.append("null");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!o.getClass().isArray()) {
 | 
			
		||||
            safeObjectAppend(sbuf, o);
 | 
			
		||||
        } else {
 | 
			
		||||
            // check for primitive array types because they
 | 
			
		||||
            // unfortunately cannot be cast to Object[]
 | 
			
		||||
            if (o instanceof boolean[]) {
 | 
			
		||||
                booleanArrayAppend(sbuf, (boolean[]) o);
 | 
			
		||||
            } else if (o instanceof byte[]) {
 | 
			
		||||
                byteArrayAppend(sbuf, (byte[]) o);
 | 
			
		||||
            } else if (o instanceof char[]) {
 | 
			
		||||
                charArrayAppend(sbuf, (char[]) o);
 | 
			
		||||
            } else if (o instanceof short[]) {
 | 
			
		||||
                shortArrayAppend(sbuf, (short[]) o);
 | 
			
		||||
            } else if (o instanceof int[]) {
 | 
			
		||||
                intArrayAppend(sbuf, (int[]) o);
 | 
			
		||||
            } else if (o instanceof long[]) {
 | 
			
		||||
                longArrayAppend(sbuf, (long[]) o);
 | 
			
		||||
            } else if (o instanceof float[]) {
 | 
			
		||||
                floatArrayAppend(sbuf, (float[]) o);
 | 
			
		||||
            } else if (o instanceof double[]) {
 | 
			
		||||
                doubleArrayAppend(sbuf, (double[]) o);
 | 
			
		||||
            } else {
 | 
			
		||||
                objectArrayAppend(sbuf, (Object[]) o, seenMap);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void safeObjectAppend(StringBuilder sbuf, Object o) {
 | 
			
		||||
        try {
 | 
			
		||||
            String oAsString = o.toString();
 | 
			
		||||
            sbuf.append(oAsString);
 | 
			
		||||
        } catch (Throwable t) {
 | 
			
		||||
            sbuf.append("[FAILED toString()]");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Map<Object[], Object> seenMap) {
 | 
			
		||||
        sbuf.append('[');
 | 
			
		||||
        if (!seenMap.containsKey(a)) {
 | 
			
		||||
            seenMap.put(a, null);
 | 
			
		||||
            final int len = a.length;
 | 
			
		||||
            for (int i = 0; i < len; i++) {
 | 
			
		||||
                deeplyAppendParameter(sbuf, a[i], seenMap);
 | 
			
		||||
                if (i != len - 1) {
 | 
			
		||||
                    sbuf.append(", ");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // allow repeats in siblings
 | 
			
		||||
            seenMap.remove(a);
 | 
			
		||||
        } else {
 | 
			
		||||
            sbuf.append("...");
 | 
			
		||||
        }
 | 
			
		||||
        sbuf.append(']');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) {
 | 
			
		||||
        sbuf.append('[');
 | 
			
		||||
        final int len = a.length;
 | 
			
		||||
        for (int i = 0; i < len; i++) {
 | 
			
		||||
            sbuf.append(a[i]);
 | 
			
		||||
            if (i != len - 1) {
 | 
			
		||||
                sbuf.append(", ");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sbuf.append(']');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void byteArrayAppend(StringBuilder sbuf, byte[] a) {
 | 
			
		||||
        sbuf.append('[');
 | 
			
		||||
        final int len = a.length;
 | 
			
		||||
        for (int i = 0; i < len; i++) {
 | 
			
		||||
            sbuf.append(a[i]);
 | 
			
		||||
            if (i != len - 1) {
 | 
			
		||||
                sbuf.append(", ");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sbuf.append(']');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void charArrayAppend(StringBuilder sbuf, char[] a) {
 | 
			
		||||
        sbuf.append('[');
 | 
			
		||||
        final int len = a.length;
 | 
			
		||||
        for (int i = 0; i < len; i++) {
 | 
			
		||||
            sbuf.append(a[i]);
 | 
			
		||||
            if (i != len - 1) {
 | 
			
		||||
                sbuf.append(", ");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sbuf.append(']');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void shortArrayAppend(StringBuilder sbuf, short[] a) {
 | 
			
		||||
        sbuf.append('[');
 | 
			
		||||
        final int len = a.length;
 | 
			
		||||
        for (int i = 0; i < len; i++) {
 | 
			
		||||
            sbuf.append(a[i]);
 | 
			
		||||
            if (i != len - 1) {
 | 
			
		||||
                sbuf.append(", ");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sbuf.append(']');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void intArrayAppend(StringBuilder sbuf, int[] a) {
 | 
			
		||||
        sbuf.append('[');
 | 
			
		||||
        final int len = a.length;
 | 
			
		||||
        for (int i = 0; i < len; i++) {
 | 
			
		||||
            sbuf.append(a[i]);
 | 
			
		||||
            if (i != len - 1) {
 | 
			
		||||
                sbuf.append(", ");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sbuf.append(']');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void longArrayAppend(StringBuilder sbuf, long[] a) {
 | 
			
		||||
        sbuf.append('[');
 | 
			
		||||
        final int len = a.length;
 | 
			
		||||
        for (int i = 0; i < len; i++) {
 | 
			
		||||
            sbuf.append(a[i]);
 | 
			
		||||
            if (i != len - 1) {
 | 
			
		||||
                sbuf.append(", ");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sbuf.append(']');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void floatArrayAppend(StringBuilder sbuf, float[] a) {
 | 
			
		||||
        sbuf.append('[');
 | 
			
		||||
        final int len = a.length;
 | 
			
		||||
        for (int i = 0; i < len; i++) {
 | 
			
		||||
            sbuf.append(a[i]);
 | 
			
		||||
            if (i != len - 1) {
 | 
			
		||||
                sbuf.append(", ");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sbuf.append(']');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void doubleArrayAppend(StringBuilder sbuf, double[] a) {
 | 
			
		||||
        sbuf.append('[');
 | 
			
		||||
        final int len = a.length;
 | 
			
		||||
        for (int i = 0; i < len; i++) {
 | 
			
		||||
            sbuf.append(a[i]);
 | 
			
		||||
            if (i != len - 1) {
 | 
			
		||||
                sbuf.append(", ");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sbuf.append(']');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,74 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.metric.extension;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.EntryType;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Extended {@link MetricExtension} extending input parameters of each metric
 | 
			
		||||
 * collection method with {@link EntryType}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author bill_yip
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.8.0
 | 
			
		||||
 */
 | 
			
		||||
public interface AdvancedMetricExtension extends MetricExtension {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add current pass count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param rw          resource representation (including resource name, traffic type, etc.)
 | 
			
		||||
     * @param batchCount  count to add
 | 
			
		||||
     * @param args        additional arguments of the resource, eg. if the resource is a method name,
 | 
			
		||||
     *                    the args will be the parameters of the method.
 | 
			
		||||
     */
 | 
			
		||||
    void onPass(ResourceWrapper rw, int batchCount, Object[] args);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add current block count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param rw         resource representation (including resource name, traffic type, etc.)
 | 
			
		||||
     * @param batchCount count to add
 | 
			
		||||
     * @param origin     the origin of caller (if present)
 | 
			
		||||
     * @param e          the associated {@code BlockException}
 | 
			
		||||
     * @param args       additional arguments of the resource, eg. if the resource is a method name,
 | 
			
		||||
     *                   the args will be the parameters of the method.
 | 
			
		||||
     */
 | 
			
		||||
    void onBlocked(ResourceWrapper rw, int batchCount, String origin, BlockException e,
 | 
			
		||||
                   Object[] args);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add current completed count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param rw         resource representation (including resource name, traffic type, etc.)
 | 
			
		||||
     * @param batchCount count to add
 | 
			
		||||
     * @param rt         response time of current invocation
 | 
			
		||||
     * @param args       additional arguments of the resource
 | 
			
		||||
     */
 | 
			
		||||
    void onComplete(ResourceWrapper rw, long rt, int batchCount, Object[] args);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add current exception count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param rw         resource representation (including resource name, traffic type, etc.)
 | 
			
		||||
     * @param batchCount count to add
 | 
			
		||||
     * @param throwable  exception related.
 | 
			
		||||
     * @param args       additional arguments of the resource
 | 
			
		||||
     */
 | 
			
		||||
    void onError(ResourceWrapper rw, Throwable throwable, int batchCount, Object[] args);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,37 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.metric.extension;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.init.InitFunc;
 | 
			
		||||
import com.alibaba.csp.sentinel.metric.extension.callback.MetricEntryCallback;
 | 
			
		||||
import com.alibaba.csp.sentinel.metric.extension.callback.MetricExitCallback;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.statistic.StatisticSlotCallbackRegistry;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Register callbacks for metric extension.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 * @since 1.6.1
 | 
			
		||||
 */
 | 
			
		||||
public class MetricCallbackInit implements InitFunc {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void init() throws Exception {
 | 
			
		||||
        StatisticSlotCallbackRegistry.addEntryCallback(MetricEntryCallback.class.getCanonicalName(),
 | 
			
		||||
            new MetricEntryCallback());
 | 
			
		||||
        StatisticSlotCallbackRegistry.addExitCallback(MetricExitCallback.class.getCanonicalName(),
 | 
			
		||||
            new MetricExitCallback());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,101 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.metric.extension;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This interface provides extension to Sentinel internal statistics.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Please note that all method in this class will invoke in the same thread of biz logic.
 | 
			
		||||
 * It's necessary to not do time-consuming operation in any of the interface's method,
 | 
			
		||||
 * otherwise biz logic will be blocked.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 * @since 1.6.1
 | 
			
		||||
 */
 | 
			
		||||
public interface MetricExtension {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add current pass count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param n        count to add
 | 
			
		||||
     * @param resource resource name
 | 
			
		||||
     * @param args     additional arguments of the resource, eg. if the resource is a method name,
 | 
			
		||||
     *                 the args will be the parameters of the method.
 | 
			
		||||
     */
 | 
			
		||||
    void addPass(String resource, int n, Object... args);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add current block count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param n              count to add
 | 
			
		||||
     * @param resource       resource name
 | 
			
		||||
     * @param origin         the original invoker.
 | 
			
		||||
     * @param blockException block exception related.
 | 
			
		||||
     * @param args           additional arguments of the resource, eg. if the resource is a method name,
 | 
			
		||||
     *                       the args will be the parameters of the method.
 | 
			
		||||
     */
 | 
			
		||||
    void addBlock(String resource, int n, String origin, BlockException blockException, Object... args);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add current completed count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param n        count to add
 | 
			
		||||
     * @param resource resource name
 | 
			
		||||
     * @param args     additional arguments of the resource, eg. if the resource is a method name,
 | 
			
		||||
     *                 the args will be the parameters of the method.
 | 
			
		||||
     */
 | 
			
		||||
    void addSuccess(String resource, int n, Object... args);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add current exception count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param n         count to add
 | 
			
		||||
     * @param resource  resource name
 | 
			
		||||
     * @param throwable exception related.
 | 
			
		||||
     */
 | 
			
		||||
    void addException(String resource, int n, Throwable throwable);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add response time of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param rt       response time in millisecond
 | 
			
		||||
     * @param resource resource name
 | 
			
		||||
     * @param args     additional arguments of the resource, eg. if the resource is a method name,
 | 
			
		||||
     *                 the args will be the parameters of the method.
 | 
			
		||||
     */
 | 
			
		||||
    void addRt(String resource, long rt, Object... args);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Increase current thread count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param resource resource name
 | 
			
		||||
     * @param args     additional arguments of the resource, eg. if the resource is a method name,
 | 
			
		||||
     *                 the args will be the parameters of the method.
 | 
			
		||||
     */
 | 
			
		||||
    void increaseThreadNum(String resource, Object... args);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Decrease current thread count of the resource name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param resource resource name
 | 
			
		||||
     * @param args     additional arguments of the resource, eg. if the resource is a method name,
 | 
			
		||||
     *                 the args will be the parameters of the method.
 | 
			
		||||
     */
 | 
			
		||||
    void decreaseThreadNum(String resource, Object... args);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.metric.extension;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get all {@link MetricExtension} via SPI.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 * @since 1.6.1
 | 
			
		||||
 */
 | 
			
		||||
public class MetricExtensionProvider {
 | 
			
		||||
    private static List<MetricExtension> metricExtensions = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        resolveInstance();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void resolveInstance() {
 | 
			
		||||
        List<MetricExtension> extensions = SpiLoader.of(MetricExtension.class).loadInstanceList();
 | 
			
		||||
 | 
			
		||||
        if (extensions.isEmpty()) {
 | 
			
		||||
            RecordLog.info("[MetricExtensionProvider] No existing MetricExtension found");
 | 
			
		||||
        } else {
 | 
			
		||||
            metricExtensions.addAll(extensions);
 | 
			
		||||
            RecordLog.info("[MetricExtensionProvider] MetricExtension resolved, size={}", extensions.size());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>Get all registered metric extensions.</p>
 | 
			
		||||
     * <p>DO NOT MODIFY the returned list, use {@link #addMetricExtension(MetricExtension)}.</p>
 | 
			
		||||
     *
 | 
			
		||||
     * @return all registered metric extensions
 | 
			
		||||
     */
 | 
			
		||||
    public static List<MetricExtension> getMetricExtensions() {
 | 
			
		||||
        return metricExtensions;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add metric extension.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Note that this method is NOT thread safe.
 | 
			
		||||
     * </p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param metricExtension the metric extension to add.
 | 
			
		||||
     */
 | 
			
		||||
    public static void addMetricExtension(MetricExtension metricExtension) {
 | 
			
		||||
        metricExtensions.add(metricExtension);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,59 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.metric.extension.callback;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
import com.alibaba.csp.sentinel.metric.extension.AdvancedMetricExtension;
 | 
			
		||||
import com.alibaba.csp.sentinel.metric.extension.MetricExtension;
 | 
			
		||||
import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.DefaultNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotEntryCallback;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Metric extension entry callback.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 * @since 1.6.1
 | 
			
		||||
 */
 | 
			
		||||
public class MetricEntryCallback implements ProcessorSlotEntryCallback<DefaultNode> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onPass(Context context, ResourceWrapper rw, DefaultNode param, int count, Object... args)
 | 
			
		||||
        throws Exception {
 | 
			
		||||
        for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
 | 
			
		||||
            if (m instanceof AdvancedMetricExtension) {
 | 
			
		||||
                ((AdvancedMetricExtension) m).onPass(rw, count, args);
 | 
			
		||||
            } else {
 | 
			
		||||
                m.increaseThreadNum(rw.getName(), args);
 | 
			
		||||
                m.addPass(rw.getName(), count, args);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBlocked(BlockException ex, Context context, ResourceWrapper resourceWrapper, DefaultNode param,
 | 
			
		||||
                          int count, Object... args) {
 | 
			
		||||
        for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
 | 
			
		||||
            if (m instanceof AdvancedMetricExtension) {
 | 
			
		||||
                ((AdvancedMetricExtension) m).onBlocked(resourceWrapper, count, context.getOrigin(), ex, args);
 | 
			
		||||
            } else {
 | 
			
		||||
                m.addBlock(resourceWrapper.getName(), count, context.getOrigin(), ex, args);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.metric.extension.callback;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.Entry;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
import com.alibaba.csp.sentinel.metric.extension.AdvancedMetricExtension;
 | 
			
		||||
import com.alibaba.csp.sentinel.metric.extension.MetricExtension;
 | 
			
		||||
import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.TimeUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Metric extension exit callback.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.6.1
 | 
			
		||||
 */
 | 
			
		||||
public class MetricExitCallback implements ProcessorSlotExitCallback {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onExit(Context context, ResourceWrapper rw, int acquireCount, Object... args) {
 | 
			
		||||
        Entry curEntry = context.getCurEntry();
 | 
			
		||||
        if (curEntry == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
 | 
			
		||||
            if (curEntry.getBlockError() != null) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            String resource = rw.getName();
 | 
			
		||||
            Throwable ex = curEntry.getError();
 | 
			
		||||
            long completeTime = curEntry.getCompleteTimestamp();
 | 
			
		||||
            if (completeTime <= 0) {
 | 
			
		||||
                completeTime = TimeUtil.currentTimeMillis();
 | 
			
		||||
            }
 | 
			
		||||
            long rt = completeTime - curEntry.getCreateTimestamp();
 | 
			
		||||
 | 
			
		||||
            if (m instanceof AdvancedMetricExtension) {
 | 
			
		||||
                // Since 1.8.0 (as a temporary workaround for compatibility)
 | 
			
		||||
                ((AdvancedMetricExtension) m).onComplete(rw, rt, acquireCount, args);
 | 
			
		||||
                if (ex != null) {
 | 
			
		||||
                    ((AdvancedMetricExtension) m).onError(rw, ex, acquireCount, args);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                m.addRt(resource, rt, args);
 | 
			
		||||
                m.addSuccess(resource, acquireCount, args);
 | 
			
		||||
                m.decreaseThreadNum(resource, args);
 | 
			
		||||
                if (null != ex) {
 | 
			
		||||
                    m.addException(resource, acquireCount, ex);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,126 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.locks.ReentrantLock;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.ResourceTypeConstants;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.ContextUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.AssertUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * This class stores summary runtime statistics of the resource, including rt, thread count, qps
 | 
			
		||||
 * and so on. Same resource shares the same {@link ClusterNode} globally, no matter in which
 | 
			
		||||
 * {@link com.alibaba.csp.sentinel.context.Context}.
 | 
			
		||||
 * </p>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * To distinguish invocation from different origin (declared in
 | 
			
		||||
 * {@link ContextUtil#enter(String name, String origin)}),
 | 
			
		||||
 * one {@link ClusterNode} holds an {@link #originCountMap}, this map holds {@link StatisticNode}
 | 
			
		||||
 * of different origin. Use {@link #getOrCreateOriginNode(String)} to get {@link Node} of the specific
 | 
			
		||||
 * origin.<br/>
 | 
			
		||||
 * Note that 'origin' usually is Service Consumer's app name.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 */
 | 
			
		||||
public class ClusterNode extends StatisticNode {
 | 
			
		||||
 | 
			
		||||
    private final String name;
 | 
			
		||||
    private final int resourceType;
 | 
			
		||||
 | 
			
		||||
    public ClusterNode(String name) {
 | 
			
		||||
        this(name, ResourceTypeConstants.COMMON);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ClusterNode(String name, int resourceType) {
 | 
			
		||||
        AssertUtil.notEmpty(name, "name cannot be empty");
 | 
			
		||||
        this.name = name;
 | 
			
		||||
        this.resourceType = resourceType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>The origin map holds the pair: (origin, originNode) for one specific resource.</p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The longer the application runs, the more stable this mapping will become.
 | 
			
		||||
     * So we didn't use concurrent map here, but a lock, as this lock only happens
 | 
			
		||||
     * at the very beginning while concurrent map will hold the lock all the time.
 | 
			
		||||
     * </p>
 | 
			
		||||
     */
 | 
			
		||||
    private Map<String, StatisticNode> originCountMap = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
    private final ReentrantLock lock = new ReentrantLock();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get resource name of the resource node.
 | 
			
		||||
     *
 | 
			
		||||
     * @return resource name
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        return name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get classification (type) of the resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @return resource type
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    public int getResourceType() {
 | 
			
		||||
        return resourceType;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>Get {@link Node} of the specific origin. Usually the origin is the Service Consumer's app name.</p>
 | 
			
		||||
     * <p>If the origin node for given origin is absent, then a new {@link StatisticNode}
 | 
			
		||||
     * for the origin will be created and returned.</p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param origin The caller's name, which is designated in the {@code parameter} parameter
 | 
			
		||||
     *               {@link ContextUtil#enter(String name, String origin)}.
 | 
			
		||||
     * @return the {@link Node} of the specific origin
 | 
			
		||||
     */
 | 
			
		||||
    public Node getOrCreateOriginNode(String origin) {
 | 
			
		||||
        StatisticNode statisticNode = originCountMap.get(origin);
 | 
			
		||||
        if (statisticNode == null) {
 | 
			
		||||
            lock.lock();
 | 
			
		||||
            try {
 | 
			
		||||
                statisticNode = originCountMap.get(origin);
 | 
			
		||||
                if (statisticNode == null) {
 | 
			
		||||
                    // The node is absent, create a new node for the origin.
 | 
			
		||||
                    statisticNode = new StatisticNode();
 | 
			
		||||
                    HashMap<String, StatisticNode> newMap = new HashMap<>(originCountMap.size() + 1);
 | 
			
		||||
                    newMap.putAll(originCountMap);
 | 
			
		||||
                    newMap.put(origin, statisticNode);
 | 
			
		||||
                    originCountMap = newMap;
 | 
			
		||||
                }
 | 
			
		||||
            } finally {
 | 
			
		||||
                lock.unlock();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return statisticNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Map<String, StatisticNode> getOriginCountMap() {
 | 
			
		||||
        return originCountMap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,170 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.SphO;
 | 
			
		||||
import com.alibaba.csp.sentinel.SphU;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * A {@link Node} used to hold statistics for specific resource name in the specific context.
 | 
			
		||||
 * Each distinct resource in each distinct {@link Context} will corresponding to a {@link DefaultNode}.
 | 
			
		||||
 * </p>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * This class may have a list of sub {@link DefaultNode}s. Child nodes will be created when
 | 
			
		||||
 * calling {@link SphU}#entry() or {@link SphO}@entry() multiple times in the same {@link Context}.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @see NodeSelectorSlot
 | 
			
		||||
 */
 | 
			
		||||
public class DefaultNode extends StatisticNode {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The resource associated with the node.
 | 
			
		||||
     */
 | 
			
		||||
    private ResourceWrapper id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The list of all child nodes.
 | 
			
		||||
     */
 | 
			
		||||
    private volatile Set<Node> childList = new HashSet<>();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Associated cluster node.
 | 
			
		||||
     */
 | 
			
		||||
    private ClusterNode clusterNode;
 | 
			
		||||
 | 
			
		||||
    public DefaultNode(ResourceWrapper id, ClusterNode clusterNode) {
 | 
			
		||||
        this.id = id;
 | 
			
		||||
        this.clusterNode = clusterNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ResourceWrapper getId() {
 | 
			
		||||
        return id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ClusterNode getClusterNode() {
 | 
			
		||||
        return clusterNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setClusterNode(ClusterNode clusterNode) {
 | 
			
		||||
        this.clusterNode = clusterNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add child node to current node.
 | 
			
		||||
     *
 | 
			
		||||
     * @param node valid child node
 | 
			
		||||
     */
 | 
			
		||||
    public void addChild(Node node) {
 | 
			
		||||
        if (node == null) {
 | 
			
		||||
            RecordLog.warn("Trying to add null child to node <{}>, ignored", id.getName());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!childList.contains(node)) {
 | 
			
		||||
            synchronized (this) {
 | 
			
		||||
                if (!childList.contains(node)) {
 | 
			
		||||
                    Set<Node> newSet = new HashSet<>(childList.size() + 1);
 | 
			
		||||
                    newSet.addAll(childList);
 | 
			
		||||
                    newSet.add(node);
 | 
			
		||||
                    childList = newSet;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            RecordLog.info("Add child <{}> to node <{}>", ((DefaultNode)node).id.getName(), id.getName());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reset the child node list.
 | 
			
		||||
     */
 | 
			
		||||
    public void removeChildList() {
 | 
			
		||||
        this.childList = new HashSet<>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Set<Node> getChildList() {
 | 
			
		||||
        return childList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void increaseBlockQps(int count) {
 | 
			
		||||
        super.increaseBlockQps(count);
 | 
			
		||||
        this.clusterNode.increaseBlockQps(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void increaseExceptionQps(int count) {
 | 
			
		||||
        super.increaseExceptionQps(count);
 | 
			
		||||
        this.clusterNode.increaseExceptionQps(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addRtAndSuccess(long rt, int successCount) {
 | 
			
		||||
        super.addRtAndSuccess(rt, successCount);
 | 
			
		||||
        this.clusterNode.addRtAndSuccess(rt, successCount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void increaseThreadNum() {
 | 
			
		||||
        super.increaseThreadNum();
 | 
			
		||||
        this.clusterNode.increaseThreadNum();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void decreaseThreadNum() {
 | 
			
		||||
        super.decreaseThreadNum();
 | 
			
		||||
        this.clusterNode.decreaseThreadNum();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addPassRequest(int count) {
 | 
			
		||||
        super.addPassRequest(count);
 | 
			
		||||
        this.clusterNode.addPassRequest(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void printDefaultNode() {
 | 
			
		||||
        visitTree(0, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void visitTree(int level, DefaultNode node) {
 | 
			
		||||
        for (int i = 0; i < level; ++i) {
 | 
			
		||||
            System.out.print("-");
 | 
			
		||||
        }
 | 
			
		||||
        if (!(node instanceof EntranceNode)) {
 | 
			
		||||
            System.out.println(
 | 
			
		||||
                String.format("%s(thread:%s pq:%s bq:%s tq:%s rt:%s 1mp:%s 1mb:%s 1mt:%s)", node.id.getShowName(),
 | 
			
		||||
                    node.curThreadNum(), node.passQps(), node.blockQps(), node.totalQps(), node.avgRt(),
 | 
			
		||||
                    node.totalRequest() - node.blockRequest(), node.blockRequest(), node.totalRequest()));
 | 
			
		||||
        } else {
 | 
			
		||||
            System.out.println(
 | 
			
		||||
                String.format("Entry-%s(t:%s pq:%s bq:%s tq:%s rt:%s 1mp:%s 1mb:%s 1mt:%s)", node.id.getShowName(),
 | 
			
		||||
                    node.curThreadNum(), node.passQps(), node.blockQps(), node.totalQps(), node.avgRt(),
 | 
			
		||||
                    node.totalRequest() - node.blockRequest(), node.blockRequest(), node.totalRequest()));
 | 
			
		||||
        }
 | 
			
		||||
        for (Node n : node.getChildList()) {
 | 
			
		||||
            DefaultNode dn = (DefaultNode)n;
 | 
			
		||||
            visitTree(level + 1, dn);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,127 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
import com.alibaba.csp.sentinel.context.ContextUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * A {@link Node} represents the entrance of the invocation tree.
 | 
			
		||||
 * </p>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * One {@link Context} will related to a {@link EntranceNode},
 | 
			
		||||
 * which represents the entrance of the invocation tree. New {@link EntranceNode} will be created if
 | 
			
		||||
 * current context does't have one. Note that same context name will share same {@link EntranceNode}
 | 
			
		||||
 * globally.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @see ContextUtil
 | 
			
		||||
 * @see ContextUtil#enter(String, String)
 | 
			
		||||
 * @see NodeSelectorSlot
 | 
			
		||||
 */
 | 
			
		||||
public class EntranceNode extends DefaultNode {
 | 
			
		||||
 | 
			
		||||
    public EntranceNode(ResourceWrapper id, ClusterNode clusterNode) {
 | 
			
		||||
        super(id, clusterNode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double avgRt() {
 | 
			
		||||
        double total = 0;
 | 
			
		||||
        double totalQps = 0;
 | 
			
		||||
        for (Node node : getChildList()) {
 | 
			
		||||
            total += node.avgRt() * node.passQps();
 | 
			
		||||
            totalQps += node.passQps();
 | 
			
		||||
        }
 | 
			
		||||
        return total / (totalQps == 0 ? 1 : totalQps);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double blockQps() {
 | 
			
		||||
        double blockQps = 0;
 | 
			
		||||
        for (Node node : getChildList()) {
 | 
			
		||||
            blockQps += node.blockQps();
 | 
			
		||||
        }
 | 
			
		||||
        return blockQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long blockRequest() {
 | 
			
		||||
        long r = 0;
 | 
			
		||||
        for (Node node : getChildList()) {
 | 
			
		||||
            r += node.blockRequest();
 | 
			
		||||
        }
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int curThreadNum() {
 | 
			
		||||
        int r = 0;
 | 
			
		||||
        for (Node node : getChildList()) {
 | 
			
		||||
            r += node.curThreadNum();
 | 
			
		||||
        }
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double totalQps() {
 | 
			
		||||
        double r = 0;
 | 
			
		||||
        for (Node node : getChildList()) {
 | 
			
		||||
            r += node.totalQps();
 | 
			
		||||
        }
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double successQps() {
 | 
			
		||||
        double r = 0;
 | 
			
		||||
        for (Node node : getChildList()) {
 | 
			
		||||
            r += node.successQps();
 | 
			
		||||
        }
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double passQps() {
 | 
			
		||||
        double r = 0;
 | 
			
		||||
        for (Node node : getChildList()) {
 | 
			
		||||
            r += node.passQps();
 | 
			
		||||
        }
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long totalRequest() {
 | 
			
		||||
        long r = 0;
 | 
			
		||||
        for (Node node : getChildList()) {
 | 
			
		||||
            r += node.totalRequest();
 | 
			
		||||
        }
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long totalPass() {
 | 
			
		||||
        long r = 0;
 | 
			
		||||
        for (Node node : getChildList()) {
 | 
			
		||||
            r += node.totalPass();
 | 
			
		||||
        }
 | 
			
		||||
        return r;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,68 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
 | 
			
		||||
import com.alibaba.csp.sentinel.property.SimplePropertyListener;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * QPS statistics interval.
 | 
			
		||||
 *
 | 
			
		||||
 * @author youji.zj
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public class IntervalProperty {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>Interval in milliseconds. This variable determines sensitivity of the QPS calculation.</p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * DO NOT MODIFY this value directly, use {@link #updateInterval(int)}, otherwise the modification will not
 | 
			
		||||
     * take effect.
 | 
			
		||||
     * </p>
 | 
			
		||||
     */
 | 
			
		||||
    public static volatile int INTERVAL = RuleConstant.DEFAULT_WINDOW_INTERVAL_MS;
 | 
			
		||||
 | 
			
		||||
    public static void register2Property(SentinelProperty<Integer> property) {
 | 
			
		||||
        property.addListener(new SimplePropertyListener<Integer>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configUpdate(Integer value) {
 | 
			
		||||
                if (value != null) {
 | 
			
		||||
                    updateInterval(value);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update the {@link #INTERVAL}, All {@link ClusterNode}s will be reset if newInterval is
 | 
			
		||||
     * different from {@link #INTERVAL}
 | 
			
		||||
     *
 | 
			
		||||
     * @param newInterval New interval to set.
 | 
			
		||||
     */
 | 
			
		||||
    public static void updateInterval(int newInterval) {
 | 
			
		||||
        if (newInterval != INTERVAL) {
 | 
			
		||||
            INTERVAL = newInterval;
 | 
			
		||||
            ClusterBuilderSlot.resetClusterNodes();
 | 
			
		||||
        }
 | 
			
		||||
        RecordLog.info("[IntervalProperty] INTERVAL updated to: {}", INTERVAL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,204 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.Entry;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.metric.MetricNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.statistic.metric.DebugSupport;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.function.Predicate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Holds real-time statistics for resources.
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @author leyou
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public interface Node extends OccupySupport, DebugSupport {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get incoming request per minute ({@code pass + block}).
 | 
			
		||||
     *
 | 
			
		||||
     * @return total request count per minute
 | 
			
		||||
     */
 | 
			
		||||
    long totalRequest();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get pass count per minute.
 | 
			
		||||
     *
 | 
			
		||||
     * @return total passed request count per minute
 | 
			
		||||
     * @since 1.5.0
 | 
			
		||||
     */
 | 
			
		||||
    long totalPass();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get {@link Entry#exit()} count per minute.
 | 
			
		||||
     *
 | 
			
		||||
     * @return total completed request count per minute
 | 
			
		||||
     */
 | 
			
		||||
    long totalSuccess();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get blocked request count per minute (totalBlockRequest).
 | 
			
		||||
     *
 | 
			
		||||
     * @return total blocked request count per minute
 | 
			
		||||
     */
 | 
			
		||||
    long blockRequest();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get exception count per minute.
 | 
			
		||||
     *
 | 
			
		||||
     * @return total business exception count per minute
 | 
			
		||||
     */
 | 
			
		||||
    long totalException();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get pass request per second.
 | 
			
		||||
     *
 | 
			
		||||
     * @return QPS of passed requests
 | 
			
		||||
     */
 | 
			
		||||
    double passQps();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get block request per second.
 | 
			
		||||
     *
 | 
			
		||||
     * @return QPS of blocked requests
 | 
			
		||||
     */
 | 
			
		||||
    double blockQps();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get {@link #passQps()} + {@link #blockQps()} request per second.
 | 
			
		||||
     *
 | 
			
		||||
     * @return QPS of passed and blocked requests
 | 
			
		||||
     */
 | 
			
		||||
    double totalQps();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get {@link Entry#exit()} request per second.
 | 
			
		||||
     *
 | 
			
		||||
     * @return QPS of completed requests
 | 
			
		||||
     */
 | 
			
		||||
    double successQps();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get estimated max success QPS till now.
 | 
			
		||||
     *
 | 
			
		||||
     * @return max completed QPS
 | 
			
		||||
     */
 | 
			
		||||
    double maxSuccessQps();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get exception count per second.
 | 
			
		||||
     *
 | 
			
		||||
     * @return QPS of exception occurs
 | 
			
		||||
     */
 | 
			
		||||
    double exceptionQps();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get average rt per second.
 | 
			
		||||
     *
 | 
			
		||||
     * @return average response time per second
 | 
			
		||||
     */
 | 
			
		||||
    double avgRt();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get minimal response time.
 | 
			
		||||
     *
 | 
			
		||||
     * @return recorded minimal response time
 | 
			
		||||
     */
 | 
			
		||||
    double minRt();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get current active thread count.
 | 
			
		||||
     *
 | 
			
		||||
     * @return current active thread count
 | 
			
		||||
     */
 | 
			
		||||
    int curThreadNum();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get last second block QPS.
 | 
			
		||||
     */
 | 
			
		||||
    double previousBlockQps();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Last window QPS.
 | 
			
		||||
     */
 | 
			
		||||
    double previousPassQps();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch all valid metric nodes of resources.
 | 
			
		||||
     *
 | 
			
		||||
     * @return valid metric nodes of resources
 | 
			
		||||
     */
 | 
			
		||||
    Map<Long, MetricNode> metrics();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch all raw metric items that satisfies the time predicate.
 | 
			
		||||
     *
 | 
			
		||||
     * @param timePredicate time predicate
 | 
			
		||||
     * @return raw metric items that satisfies the time predicate
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    List<MetricNode> rawMetricsInMin(Predicate<Long> timePredicate);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add pass count.
 | 
			
		||||
     *
 | 
			
		||||
     * @param count count to add pass
 | 
			
		||||
     */
 | 
			
		||||
    void addPassRequest(int count);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add rt and success count.
 | 
			
		||||
     *
 | 
			
		||||
     * @param rt      response time
 | 
			
		||||
     * @param success success count to add
 | 
			
		||||
     */
 | 
			
		||||
    void addRtAndSuccess(long rt, int success);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Increase the block count.
 | 
			
		||||
     *
 | 
			
		||||
     * @param count count to add
 | 
			
		||||
     */
 | 
			
		||||
    void increaseBlockQps(int count);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add the biz exception count.
 | 
			
		||||
     *
 | 
			
		||||
     * @param count count to add
 | 
			
		||||
     */
 | 
			
		||||
    void increaseExceptionQps(int count);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Increase current thread count.
 | 
			
		||||
     */
 | 
			
		||||
    void increaseThreadNum();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Decrease current thread count.
 | 
			
		||||
     */
 | 
			
		||||
    void decreaseThreadNum();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reset the internal counter. Reset is needed when {@link IntervalProperty#INTERVAL} or
 | 
			
		||||
     * {@link SampleCountProperty#SAMPLE_COUNT} is changed.
 | 
			
		||||
     */
 | 
			
		||||
    void reset();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Builds new {@link DefaultNode} and {@link ClusterNode}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 */
 | 
			
		||||
@Deprecated
 | 
			
		||||
public interface NodeBuilder {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new {@link DefaultNode} as tree node.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id resource
 | 
			
		||||
     * @param clusterNode the cluster node of the provided resource
 | 
			
		||||
     * @return new created tree node
 | 
			
		||||
     */
 | 
			
		||||
    DefaultNode buildTreeNode(ResourceWrapper id, ClusterNode clusterNode);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new {@link ClusterNode} as universal statistic node for a single resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @return new created cluster node
 | 
			
		||||
     */
 | 
			
		||||
    ClusterNode buildClusterNode();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 1.5.0
 | 
			
		||||
 */
 | 
			
		||||
public interface OccupySupport {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Try to occupy latter time windows' tokens. If occupy success, a value less than
 | 
			
		||||
     * {@code occupyTimeout} in {@link OccupyTimeoutProperty} will be return.
 | 
			
		||||
     *
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Each time we occupy tokens of the future window, current thread should sleep for the
 | 
			
		||||
     * corresponding time for smoothing QPS. We can't occupy tokens of the future with unlimited,
 | 
			
		||||
     * the sleep time limit is {@code occupyTimeout} in {@link OccupyTimeoutProperty}.
 | 
			
		||||
     * </p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param currentTime  current time millis.
 | 
			
		||||
     * @param acquireCount tokens count to acquire.
 | 
			
		||||
     * @param threshold    qps threshold.
 | 
			
		||||
     * @return time should sleep. Time >= {@code occupyTimeout} in {@link OccupyTimeoutProperty} means
 | 
			
		||||
     * occupy fail, in this case, the request should be rejected immediately.
 | 
			
		||||
     */
 | 
			
		||||
    long tryOccupyNext(long currentTime, int acquireCount, double threshold);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get current waiting amount. Useful for debug.
 | 
			
		||||
     *
 | 
			
		||||
     * @return current waiting amount
 | 
			
		||||
     */
 | 
			
		||||
    long waiting();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add request that occupied.
 | 
			
		||||
     *
 | 
			
		||||
     * @param futureTime   future timestamp that the acquireCount should be added on.
 | 
			
		||||
     * @param acquireCount tokens count.
 | 
			
		||||
     */
 | 
			
		||||
    void addWaitingRequest(long futureTime, int acquireCount);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add occupied pass request, which represents pass requests that borrow the latter windows' token.
 | 
			
		||||
     *
 | 
			
		||||
     * @param acquireCount tokens count.
 | 
			
		||||
     */
 | 
			
		||||
    void addOccupiedPass(int acquireCount);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get current occupied pass QPS.
 | 
			
		||||
     *
 | 
			
		||||
     * @return current occupied pass QPS
 | 
			
		||||
     */
 | 
			
		||||
    double occupiedPassQps();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,79 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2019 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
 | 
			
		||||
import com.alibaba.csp.sentinel.property.SimplePropertyListener;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 * @since 1.5.0
 | 
			
		||||
 */
 | 
			
		||||
public class OccupyTimeoutProperty {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Max occupy timeout in milliseconds. Requests with priority can occupy tokens of the future statistic
 | 
			
		||||
     * window, and {@code occupyTimeout} limit the max time length that can be occupied.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Note that the timeout value should never be greeter than {@link IntervalProperty#INTERVAL}.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * DO NOT MODIFY this value directly, use {@link #updateTimeout(int)},
 | 
			
		||||
     * otherwise the modification will not take effect.
 | 
			
		||||
     */
 | 
			
		||||
    private static volatile int occupyTimeout = 500;
 | 
			
		||||
 | 
			
		||||
    public static void register2Property(SentinelProperty<Integer> property) {
 | 
			
		||||
        property.addListener(new SimplePropertyListener<Integer>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configUpdate(Integer value) {
 | 
			
		||||
                if (value != null) {
 | 
			
		||||
                    updateTimeout(value);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int getOccupyTimeout() {
 | 
			
		||||
        return occupyTimeout;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update the timeout value.</br>
 | 
			
		||||
     * Note that the time out should never greeter than {@link IntervalProperty#INTERVAL},
 | 
			
		||||
     * or it will be ignored.
 | 
			
		||||
     *
 | 
			
		||||
     * @param newInterval new value.
 | 
			
		||||
     */
 | 
			
		||||
    public static void updateTimeout(int newInterval) {
 | 
			
		||||
        if (newInterval < 0) {
 | 
			
		||||
            RecordLog.warn("[OccupyTimeoutProperty] Illegal timeout value will be ignored: " + occupyTimeout);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (newInterval > IntervalProperty.INTERVAL) {
 | 
			
		||||
            RecordLog.warn("[OccupyTimeoutProperty] Illegal timeout value will be ignored: {}, should <= {}",
 | 
			
		||||
                occupyTimeout, IntervalProperty.INTERVAL);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (newInterval != occupyTimeout) {
 | 
			
		||||
            occupyTimeout = newInterval;
 | 
			
		||||
        }
 | 
			
		||||
        RecordLog.info("[OccupyTimeoutProperty] occupyTimeout updated to: {}", occupyTimeout);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,65 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
 | 
			
		||||
import com.alibaba.csp.sentinel.property.SimplePropertyListener;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Holds statistic buckets count per second.
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author CarpenterLee
 | 
			
		||||
 */
 | 
			
		||||
public class SampleCountProperty {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Statistic buckets count per second. This variable determines sensitivity of the QPS calculation.
 | 
			
		||||
     * DO NOT MODIFY this value directly, use {@link #updateSampleCount(int)}, otherwise the modification will not
 | 
			
		||||
     * take effect.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * Node that this value must be divisor of 1000.
 | 
			
		||||
     */
 | 
			
		||||
    public static volatile int SAMPLE_COUNT = 2;
 | 
			
		||||
 | 
			
		||||
    public static void register2Property(SentinelProperty<Integer> property) {
 | 
			
		||||
        property.addListener(new SimplePropertyListener<Integer>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void configUpdate(Integer value) {
 | 
			
		||||
                if (value != null) {
 | 
			
		||||
                    updateSampleCount(value);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update the {@link #SAMPLE_COUNT}. All {@link ClusterNode}s will be reset if newSampleCount
 | 
			
		||||
     * is different from {@link #SAMPLE_COUNT}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param newSampleCount New sample count to set. This value must be divisor of 1000.
 | 
			
		||||
     */
 | 
			
		||||
    public static void updateSampleCount(int newSampleCount) {
 | 
			
		||||
        if (newSampleCount != SAMPLE_COUNT) {
 | 
			
		||||
            SAMPLE_COUNT = newSampleCount;
 | 
			
		||||
            ClusterBuilderSlot.resetClusterNodes();
 | 
			
		||||
        }
 | 
			
		||||
        RecordLog.info("SAMPLE_COUNT updated to: {}", SAMPLE_COUNT);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,337 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.atomic.LongAdder;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.node.metric.MetricNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.statistic.metric.ArrayMetric;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.statistic.metric.Metric;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.TimeUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.function.Predicate;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>The statistic node keep three kinds of real-time statistics metrics:</p>
 | 
			
		||||
 * <ol>
 | 
			
		||||
 * <li>metrics in second level ({@code rollingCounterInSecond})</li>
 | 
			
		||||
 * <li>metrics in minute level ({@code rollingCounterInMinute})</li>
 | 
			
		||||
 * <li>thread count</li>
 | 
			
		||||
 * </ol>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Sentinel use sliding window to record and count the resource statistics in real-time.
 | 
			
		||||
 * The sliding window infrastructure behind the {@link ArrayMetric} is {@code LeapArray}.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * case 1: When the first request comes in, Sentinel will create a new window bucket of
 | 
			
		||||
 * a specified time-span to store running statics, such as total response time(rt),
 | 
			
		||||
 * incoming request(QPS), block request(bq), etc. And the time-span is defined by sample count.
 | 
			
		||||
 * </p>
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * 	0      100ms
 | 
			
		||||
 *  +-------+--→ Sliding Windows
 | 
			
		||||
 * 	    ^
 | 
			
		||||
 * 	    |
 | 
			
		||||
 * 	  request
 | 
			
		||||
 * </pre>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Sentinel use the statics of the valid buckets to decide whether this request can be passed.
 | 
			
		||||
 * For example, if a rule defines that only 100 requests can be passed,
 | 
			
		||||
 * it will sum all qps in valid buckets, and compare it to the threshold defined in rule.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>case 2: continuous requests</p>
 | 
			
		||||
 * <pre>
 | 
			
		||||
 *  0    100ms    200ms    300ms
 | 
			
		||||
 *  +-------+-------+-------+-----→ Sliding Windows
 | 
			
		||||
 *                      ^
 | 
			
		||||
 *                      |
 | 
			
		||||
 *                   request
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>case 3: requests keeps coming, and previous buckets become invalid</p>
 | 
			
		||||
 * <pre>
 | 
			
		||||
 *  0    100ms    200ms	  800ms	   900ms  1000ms    1300ms
 | 
			
		||||
 *  +-------+-------+ ...... +-------+-------+ ...... +-------+-----→ Sliding Windows
 | 
			
		||||
 *                                                      ^
 | 
			
		||||
 *                                                      |
 | 
			
		||||
 *                                                    request
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * <p>The sliding window should become:</p>
 | 
			
		||||
 * <pre>
 | 
			
		||||
 * 300ms     800ms  900ms  1000ms  1300ms
 | 
			
		||||
 *  + ...... +-------+ ...... +-------+-----→ Sliding Windows
 | 
			
		||||
 *                                                      ^
 | 
			
		||||
 *                                                      |
 | 
			
		||||
 *                                                    request
 | 
			
		||||
 * </pre>
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 */
 | 
			
		||||
public class StatisticNode implements Node {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Holds statistics of the recent {@code INTERVAL} milliseconds. The {@code INTERVAL} is divided into time spans
 | 
			
		||||
     * by given {@code sampleCount}.
 | 
			
		||||
     */
 | 
			
		||||
    private transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,
 | 
			
		||||
        IntervalProperty.INTERVAL);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Holds statistics of the recent 60 seconds. The windowLengthInMs is deliberately set to 1000 milliseconds,
 | 
			
		||||
     * meaning each bucket per second, in this way we can get accurate statistics of each second.
 | 
			
		||||
     */
 | 
			
		||||
    private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The counter for thread count.
 | 
			
		||||
     */
 | 
			
		||||
    private LongAdder curThreadNum = new LongAdder();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The last timestamp when metrics were fetched.
 | 
			
		||||
     */
 | 
			
		||||
    private long lastFetchTime = -1;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Map<Long, MetricNode> metrics() {
 | 
			
		||||
        // The fetch operation is thread-safe under a single-thread scheduler pool.
 | 
			
		||||
        long currentTime = TimeUtil.currentTimeMillis();
 | 
			
		||||
        currentTime = currentTime - currentTime % 1000;
 | 
			
		||||
        Map<Long, MetricNode> metrics = new ConcurrentHashMap<>();
 | 
			
		||||
        List<MetricNode> nodesOfEverySecond = rollingCounterInMinute.details();
 | 
			
		||||
        long newLastFetchTime = lastFetchTime;
 | 
			
		||||
        // Iterate metrics of all resources, filter valid metrics (not-empty and up-to-date).
 | 
			
		||||
        for (MetricNode node : nodesOfEverySecond) {
 | 
			
		||||
            if (isNodeInTime(node, currentTime) && isValidMetricNode(node)) {
 | 
			
		||||
                metrics.put(node.getTimestamp(), node);
 | 
			
		||||
                newLastFetchTime = Math.max(newLastFetchTime, node.getTimestamp());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        lastFetchTime = newLastFetchTime;
 | 
			
		||||
 | 
			
		||||
        return metrics;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<MetricNode> rawMetricsInMin(Predicate<Long> timePredicate) {
 | 
			
		||||
        return rollingCounterInMinute.detailsOnCondition(timePredicate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isNodeInTime(MetricNode node, long currentTime) {
 | 
			
		||||
        return node.getTimestamp() > lastFetchTime && node.getTimestamp() < currentTime;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isValidMetricNode(MetricNode node) {
 | 
			
		||||
        return node.getPassQps() > 0 || node.getBlockQps() > 0 || node.getSuccessQps() > 0
 | 
			
		||||
            || node.getExceptionQps() > 0 || node.getRt() > 0 || node.getOccupiedPassQps() > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void reset() {
 | 
			
		||||
        rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long totalRequest() {
 | 
			
		||||
        return rollingCounterInMinute.pass() + rollingCounterInMinute.block();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long blockRequest() {
 | 
			
		||||
        return rollingCounterInMinute.block();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double blockQps() {
 | 
			
		||||
        return rollingCounterInSecond.block() / rollingCounterInSecond.getWindowIntervalInSec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double previousBlockQps() {
 | 
			
		||||
        return this.rollingCounterInMinute.previousWindowBlock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double previousPassQps() {
 | 
			
		||||
        return this.rollingCounterInMinute.previousWindowPass();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double totalQps() {
 | 
			
		||||
        return passQps() + blockQps();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long totalSuccess() {
 | 
			
		||||
        return rollingCounterInMinute.success();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double exceptionQps() {
 | 
			
		||||
        return rollingCounterInSecond.exception() / rollingCounterInSecond.getWindowIntervalInSec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long totalException() {
 | 
			
		||||
        return rollingCounterInMinute.exception();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double passQps() {
 | 
			
		||||
        return rollingCounterInSecond.pass() / rollingCounterInSecond.getWindowIntervalInSec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long totalPass() {
 | 
			
		||||
        return rollingCounterInMinute.pass();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double successQps() {
 | 
			
		||||
        return rollingCounterInSecond.success() / rollingCounterInSecond.getWindowIntervalInSec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double maxSuccessQps() {
 | 
			
		||||
        return (double) rollingCounterInSecond.maxSuccess() * rollingCounterInSecond.getSampleCount()
 | 
			
		||||
                / rollingCounterInSecond.getWindowIntervalInSec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double occupiedPassQps() {
 | 
			
		||||
        return rollingCounterInSecond.occupiedPass() / rollingCounterInSecond.getWindowIntervalInSec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double avgRt() {
 | 
			
		||||
        long successCount = rollingCounterInSecond.success();
 | 
			
		||||
        if (successCount == 0) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return rollingCounterInSecond.rt() * 1.0 / successCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public double minRt() {
 | 
			
		||||
        return rollingCounterInSecond.minRt();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int curThreadNum() {
 | 
			
		||||
        return (int)curThreadNum.sum();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addPassRequest(int count) {
 | 
			
		||||
        rollingCounterInSecond.addPass(count);
 | 
			
		||||
        rollingCounterInMinute.addPass(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addRtAndSuccess(long rt, int successCount) {
 | 
			
		||||
        rollingCounterInSecond.addSuccess(successCount);
 | 
			
		||||
        rollingCounterInSecond.addRT(rt);
 | 
			
		||||
 | 
			
		||||
        rollingCounterInMinute.addSuccess(successCount);
 | 
			
		||||
        rollingCounterInMinute.addRT(rt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void increaseBlockQps(int count) {
 | 
			
		||||
        rollingCounterInSecond.addBlock(count);
 | 
			
		||||
        rollingCounterInMinute.addBlock(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void increaseExceptionQps(int count) {
 | 
			
		||||
        rollingCounterInSecond.addException(count);
 | 
			
		||||
        rollingCounterInMinute.addException(count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void increaseThreadNum() {
 | 
			
		||||
        curThreadNum.increment();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void decreaseThreadNum() {
 | 
			
		||||
        curThreadNum.decrement();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void debug() {
 | 
			
		||||
        rollingCounterInSecond.debug();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long tryOccupyNext(long currentTime, int acquireCount, double threshold) {
 | 
			
		||||
        double maxCount = threshold * IntervalProperty.INTERVAL / 1000;
 | 
			
		||||
        long currentBorrow = rollingCounterInSecond.waiting();
 | 
			
		||||
        if (currentBorrow >= maxCount) {
 | 
			
		||||
            return OccupyTimeoutProperty.getOccupyTimeout();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int windowLength = IntervalProperty.INTERVAL / SampleCountProperty.SAMPLE_COUNT;
 | 
			
		||||
        long earliestTime = currentTime - currentTime % windowLength + windowLength - IntervalProperty.INTERVAL;
 | 
			
		||||
 | 
			
		||||
        int idx = 0;
 | 
			
		||||
        /*
 | 
			
		||||
         * Note: here {@code currentPass} may be less than it really is NOW, because time difference
 | 
			
		||||
         * since call rollingCounterInSecond.pass(). So in high concurrency, the following code may
 | 
			
		||||
         * lead more tokens be borrowed.
 | 
			
		||||
         */
 | 
			
		||||
        long currentPass = rollingCounterInSecond.pass();
 | 
			
		||||
        while (earliestTime < currentTime) {
 | 
			
		||||
            long waitInMs = idx * windowLength + windowLength - currentTime % windowLength;
 | 
			
		||||
            if (waitInMs >= OccupyTimeoutProperty.getOccupyTimeout()) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            long windowPass = rollingCounterInSecond.getWindowPass(earliestTime);
 | 
			
		||||
            if (currentPass + currentBorrow + acquireCount - windowPass <= maxCount) {
 | 
			
		||||
                return waitInMs;
 | 
			
		||||
            }
 | 
			
		||||
            earliestTime += windowLength;
 | 
			
		||||
            currentPass -= windowPass;
 | 
			
		||||
            idx++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return OccupyTimeoutProperty.getOccupyTimeout();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long waiting() {
 | 
			
		||||
        return rollingCounterInSecond.waiting();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addWaitingRequest(long futureTime, int acquireCount) {
 | 
			
		||||
        rollingCounterInSecond.addWaiting(futureTime, acquireCount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addOccupiedPass(int acquireCount) {
 | 
			
		||||
        rollingCounterInMinute.addOccupiedPass(acquireCount);
 | 
			
		||||
        rollingCounterInMinute.addPass(acquireCount);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,262 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node.metric;
 | 
			
		||||
 | 
			
		||||
import java.text.DateFormat;
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Metrics data for a specific resource at given {@code timestamp}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 */
 | 
			
		||||
public class MetricNode {
 | 
			
		||||
 | 
			
		||||
    private String resource;
 | 
			
		||||
    /**
 | 
			
		||||
     * Resource classification (e.g. SQL or RPC)
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    private int classification;
 | 
			
		||||
 | 
			
		||||
    private long timestamp;
 | 
			
		||||
    private long passQps;
 | 
			
		||||
    private long blockQps;
 | 
			
		||||
    private long successQps;
 | 
			
		||||
    private long exceptionQps;
 | 
			
		||||
    private long rt;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @since 1.5.0
 | 
			
		||||
     */
 | 
			
		||||
    private long occupiedPassQps;
 | 
			
		||||
    /**
 | 
			
		||||
     * @since 1.7.0
 | 
			
		||||
     */
 | 
			
		||||
    private int concurrency;
 | 
			
		||||
 | 
			
		||||
    public long getTimestamp() {
 | 
			
		||||
        return timestamp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getOccupiedPassQps() {
 | 
			
		||||
        return occupiedPassQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setOccupiedPassQps(long occupiedPassQps) {
 | 
			
		||||
        this.occupiedPassQps = occupiedPassQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setTimestamp(long timestamp) {
 | 
			
		||||
        this.timestamp = timestamp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getSuccessQps() {
 | 
			
		||||
        return successQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setSuccessQps(long successQps) {
 | 
			
		||||
        this.successQps = successQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getPassQps() {
 | 
			
		||||
        return passQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setPassQps(long passQps) {
 | 
			
		||||
        this.passQps = passQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getExceptionQps() {
 | 
			
		||||
        return exceptionQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setExceptionQps(long exceptionQps) {
 | 
			
		||||
        this.exceptionQps = exceptionQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getBlockQps() {
 | 
			
		||||
        return blockQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setBlockQps(long blockQps) {
 | 
			
		||||
        this.blockQps = blockQps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public long getRt() {
 | 
			
		||||
        return rt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setRt(long rt) {
 | 
			
		||||
        this.rt = rt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getResource() {
 | 
			
		||||
        return resource;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setResource(String resource) {
 | 
			
		||||
        this.resource = resource;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getClassification() {
 | 
			
		||||
        return classification;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MetricNode setClassification(int classification) {
 | 
			
		||||
        this.classification = classification;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getConcurrency() {
 | 
			
		||||
        return concurrency;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MetricNode setConcurrency(int concurrency) {
 | 
			
		||||
        this.concurrency = concurrency;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return "MetricNode{" +
 | 
			
		||||
            "resource='" + resource + '\'' +
 | 
			
		||||
            ", classification=" + classification +
 | 
			
		||||
            ", timestamp=" + timestamp +
 | 
			
		||||
            ", passQps=" + passQps +
 | 
			
		||||
            ", blockQps=" + blockQps +
 | 
			
		||||
            ", successQps=" + successQps +
 | 
			
		||||
            ", exceptionQps=" + exceptionQps +
 | 
			
		||||
            ", rt=" + rt +
 | 
			
		||||
            ", concurrency=" + concurrency +
 | 
			
		||||
            ", occupiedPassQps=" + occupiedPassQps +
 | 
			
		||||
            '}';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * To formatting string. All "|" in {@link #resource} will be replaced with
 | 
			
		||||
     * "_", format is: <br/>
 | 
			
		||||
     * <code>
 | 
			
		||||
     * timestamp|resource|passQps|blockQps|successQps|exceptionQps|rt|occupiedPassQps
 | 
			
		||||
     * </code>
 | 
			
		||||
     *
 | 
			
		||||
     * @return string format of this.
 | 
			
		||||
     */
 | 
			
		||||
    public String toThinString() {
 | 
			
		||||
        StringBuilder sb = new StringBuilder();
 | 
			
		||||
        sb.append(timestamp).append("|");
 | 
			
		||||
        String legalName = resource.replaceAll("\\|", "_");
 | 
			
		||||
        sb.append(legalName).append("|");
 | 
			
		||||
        sb.append(passQps).append("|");
 | 
			
		||||
        sb.append(blockQps).append("|");
 | 
			
		||||
        sb.append(successQps).append("|");
 | 
			
		||||
        sb.append(exceptionQps).append("|");
 | 
			
		||||
        sb.append(rt).append("|");
 | 
			
		||||
        sb.append(occupiedPassQps).append("|");
 | 
			
		||||
        sb.append(concurrency).append("|");
 | 
			
		||||
        sb.append(classification);
 | 
			
		||||
        return sb.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse {@link MetricNode} from thin string, see {@link #toThinString()}
 | 
			
		||||
     *
 | 
			
		||||
     * @param line
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    public static MetricNode fromThinString(String line) {
 | 
			
		||||
        MetricNode node = new MetricNode();
 | 
			
		||||
        String[] strs = line.split("\\|");
 | 
			
		||||
        node.setTimestamp(Long.parseLong(strs[0]));
 | 
			
		||||
        node.setResource(strs[1]);
 | 
			
		||||
        node.setPassQps(Long.parseLong(strs[2]));
 | 
			
		||||
        node.setBlockQps(Long.parseLong(strs[3]));
 | 
			
		||||
        node.setSuccessQps(Long.parseLong(strs[4]));
 | 
			
		||||
        node.setExceptionQps(Long.parseLong(strs[5]));
 | 
			
		||||
        node.setRt(Long.parseLong(strs[6]));
 | 
			
		||||
        if (strs.length >= 8) {
 | 
			
		||||
            node.setOccupiedPassQps(Long.parseLong(strs[7]));
 | 
			
		||||
        }
 | 
			
		||||
        if (strs.length >= 9) {
 | 
			
		||||
            node.setConcurrency(Integer.parseInt(strs[8]));
 | 
			
		||||
        }
 | 
			
		||||
        if (strs.length == 10) {
 | 
			
		||||
            node.setClassification(Integer.parseInt(strs[9]));
 | 
			
		||||
        }
 | 
			
		||||
        return node;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * To formatting string. All "|" in {@link MetricNode#resource} will be
 | 
			
		||||
     * replaced with "_", format is: <br/>
 | 
			
		||||
     * <code>
 | 
			
		||||
     * timestamp|yyyy-MM-dd HH:mm:ss|resource|passQps|blockQps|successQps|exceptionQps|rt|occupiedPassQps\n
 | 
			
		||||
     * </code>
 | 
			
		||||
     *
 | 
			
		||||
     * @return string format of this.
 | 
			
		||||
     */
 | 
			
		||||
    public String toFatString() {
 | 
			
		||||
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 | 
			
		||||
        StringBuilder sb = new StringBuilder(32);
 | 
			
		||||
        sb.delete(0, sb.length());
 | 
			
		||||
        sb.append(getTimestamp()).append("|");
 | 
			
		||||
        sb.append(df.format(new Date(getTimestamp()))).append("|");
 | 
			
		||||
        String legalName = getResource().replaceAll("\\|", "_");
 | 
			
		||||
        sb.append(legalName).append("|");
 | 
			
		||||
        sb.append(getPassQps()).append("|");
 | 
			
		||||
        sb.append(getBlockQps()).append("|");
 | 
			
		||||
        sb.append(getSuccessQps()).append("|");
 | 
			
		||||
        sb.append(getExceptionQps()).append("|");
 | 
			
		||||
        sb.append(getRt()).append("|");
 | 
			
		||||
        sb.append(getOccupiedPassQps()).append("|");
 | 
			
		||||
        sb.append(concurrency).append("|");
 | 
			
		||||
        sb.append(classification);
 | 
			
		||||
        sb.append('\n');
 | 
			
		||||
        return sb.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse {@link MetricNode} from fat string, see {@link #toFatString()}
 | 
			
		||||
     *
 | 
			
		||||
     * @param line
 | 
			
		||||
     * @return the {@link MetricNode} parsed.
 | 
			
		||||
     */
 | 
			
		||||
    public static MetricNode fromFatString(String line) {
 | 
			
		||||
        String[] strs = line.split("\\|");
 | 
			
		||||
        Long time = Long.parseLong(strs[0]);
 | 
			
		||||
        MetricNode node = new MetricNode();
 | 
			
		||||
        node.setTimestamp(time);
 | 
			
		||||
        node.setResource(strs[2]);
 | 
			
		||||
        node.setPassQps(Long.parseLong(strs[3]));
 | 
			
		||||
        node.setBlockQps(Long.parseLong(strs[4]));
 | 
			
		||||
        node.setSuccessQps(Long.parseLong(strs[5]));
 | 
			
		||||
        node.setExceptionQps(Long.parseLong(strs[6]));
 | 
			
		||||
        node.setRt(Long.parseLong(strs[7]));
 | 
			
		||||
        if (strs.length >= 9) {
 | 
			
		||||
            node.setOccupiedPassQps(Long.parseLong(strs[8]));
 | 
			
		||||
        }
 | 
			
		||||
        if (strs.length >= 10) {
 | 
			
		||||
            node.setConcurrency(Integer.parseInt(strs[9]));
 | 
			
		||||
        }
 | 
			
		||||
        if (strs.length == 11) {
 | 
			
		||||
            node.setClassification(Integer.parseInt(strs[10]));
 | 
			
		||||
        }
 | 
			
		||||
        return node;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,223 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node.metric;
 | 
			
		||||
 | 
			
		||||
import java.io.DataInputStream;
 | 
			
		||||
import java.io.EOFException;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.nio.charset.Charset;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 从指定目录下找出所有的metric文件,并按照指定时间戳进行检索,参考{@link MetricSearcher#find(long, int)}。
 | 
			
		||||
 * 会借助索引以提高检索效率,参考{@link MetricWriter};还会在内部缓存上一次检索的文件指针,以便下一次顺序检索时
 | 
			
		||||
 * 减少读盘次数。
 | 
			
		||||
 *
 | 
			
		||||
 * @author leyou
 | 
			
		||||
 */
 | 
			
		||||
public class MetricSearcher {
 | 
			
		||||
 | 
			
		||||
    private static final Charset defaultCharset = Charset.forName(SentinelConfig.charset());
 | 
			
		||||
    private final MetricsReader metricsReader;
 | 
			
		||||
 | 
			
		||||
    private String baseDir;
 | 
			
		||||
    private String baseFileName;
 | 
			
		||||
 | 
			
		||||
    private Position lastPosition = new Position();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param baseDir      metric文件所在目录
 | 
			
		||||
     * @param baseFileName metric文件名的关键字,比如 alihot-metrics.log
 | 
			
		||||
     */
 | 
			
		||||
    public MetricSearcher(String baseDir, String baseFileName) {
 | 
			
		||||
        this(baseDir, baseFileName, defaultCharset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param baseDir      metric文件所在目录
 | 
			
		||||
     * @param baseFileName metric文件名的关键字,比如 alihot-metrics.log
 | 
			
		||||
     * @param charset
 | 
			
		||||
     */
 | 
			
		||||
    public MetricSearcher(String baseDir, String baseFileName, Charset charset) {
 | 
			
		||||
        if (baseDir == null) {
 | 
			
		||||
            throw new IllegalArgumentException("baseDir can't be null");
 | 
			
		||||
        }
 | 
			
		||||
        if (baseFileName == null) {
 | 
			
		||||
            throw new IllegalArgumentException("baseFileName can't be null");
 | 
			
		||||
        }
 | 
			
		||||
        if (charset == null) {
 | 
			
		||||
            throw new IllegalArgumentException("charset can't be null");
 | 
			
		||||
        }
 | 
			
		||||
        this.baseDir = baseDir;
 | 
			
		||||
        if (!baseDir.endsWith(File.separator)) {
 | 
			
		||||
            this.baseDir += File.separator;
 | 
			
		||||
        }
 | 
			
		||||
        this.baseFileName = baseFileName;
 | 
			
		||||
        metricsReader = new MetricsReader(charset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 从beginTime开始,检索recommendLines条(大概)记录。同一秒中的数据是原子的,不能分割成多次查询。
 | 
			
		||||
     *
 | 
			
		||||
     * @param beginTimeMs    检索的最小时间戳
 | 
			
		||||
     * @param recommendLines 查询最多想得到的记录条数,返回条数会尽可能不超过这个数字。但是为保证每一秒的数据不被分割,有时候
 | 
			
		||||
     *                       返回的记录条数会大于该数字。
 | 
			
		||||
     * @return
 | 
			
		||||
     * @throws Exception
 | 
			
		||||
     */
 | 
			
		||||
    public synchronized List<MetricNode> find(long beginTimeMs, int recommendLines) throws Exception {
 | 
			
		||||
        List<String> fileNames = MetricWriter.listMetricFiles(baseDir, baseFileName);
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        long offsetInIndex = 0;
 | 
			
		||||
        if (validPosition(beginTimeMs)) {
 | 
			
		||||
            i = fileNames.indexOf(lastPosition.metricFileName);
 | 
			
		||||
            if (i == -1) {
 | 
			
		||||
                i = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                offsetInIndex = lastPosition.offsetInIndex;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        for (; i < fileNames.size(); i++) {
 | 
			
		||||
            String fileName = fileNames.get(i);
 | 
			
		||||
            long offset = findOffset(beginTimeMs, fileName,
 | 
			
		||||
                MetricWriter.formIndexFileName(fileName), offsetInIndex);
 | 
			
		||||
            offsetInIndex = 0;
 | 
			
		||||
            if (offset != -1) {
 | 
			
		||||
                return metricsReader.readMetrics(fileNames, i, offset, recommendLines);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find metric between [beginTimeMs, endTimeMs], both side inclusive.
 | 
			
		||||
     * When identity is null, all metric between the time intervalMs will be read, otherwise, only the specific
 | 
			
		||||
     * identity will be read.
 | 
			
		||||
     */
 | 
			
		||||
    public synchronized List<MetricNode> findByTimeAndResource(long beginTimeMs, long endTimeMs, String identity)
 | 
			
		||||
        throws Exception {
 | 
			
		||||
        List<String> fileNames = MetricWriter.listMetricFiles(baseDir, baseFileName);
 | 
			
		||||
        //RecordLog.info("pid=" + pid + ", findByTimeAndResource([" + beginTimeMs + ", " + endTimeMs
 | 
			
		||||
        //    + "], " + identity + ")");
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        long offsetInIndex = 0;
 | 
			
		||||
        if (validPosition(beginTimeMs)) {
 | 
			
		||||
            i = fileNames.indexOf(lastPosition.metricFileName);
 | 
			
		||||
            if (i == -1) {
 | 
			
		||||
                i = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                offsetInIndex = lastPosition.offsetInIndex;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            //RecordLog.info("lastPosition is invalidate, will re iterate all files, pid = " + pid);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (; i < fileNames.size(); i++) {
 | 
			
		||||
            String fileName = fileNames.get(i);
 | 
			
		||||
            long offset = findOffset(beginTimeMs, fileName,
 | 
			
		||||
                    MetricWriter.formIndexFileName(fileName), offsetInIndex);
 | 
			
		||||
            offsetInIndex = 0;
 | 
			
		||||
            if (offset != -1) {
 | 
			
		||||
                return metricsReader.readMetricsByEndTime(fileNames, i, offset, beginTimeMs, endTimeMs, identity);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 记录上一次读取的index文件位置和数值
 | 
			
		||||
     */
 | 
			
		||||
    private static final class Position {
 | 
			
		||||
        String metricFileName;
 | 
			
		||||
        String indexFileName;
 | 
			
		||||
        /**
 | 
			
		||||
         * 索引文件内的偏移
 | 
			
		||||
         */
 | 
			
		||||
        long offsetInIndex;
 | 
			
		||||
        /**
 | 
			
		||||
         * 索引文件中offsetInIndex位置上的数字,秒数。
 | 
			
		||||
         */
 | 
			
		||||
        long second;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The position we cached is useful only when {@code beginTimeMs} is >= {@code lastPosition.second}
 | 
			
		||||
     * and the index file exists and the second we cached is same as in the index file.
 | 
			
		||||
     */
 | 
			
		||||
    private boolean validPosition(long beginTimeMs) {
 | 
			
		||||
        if (beginTimeMs / 1000 < lastPosition.second) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (lastPosition.indexFileName == null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        // index file dose not exits
 | 
			
		||||
        if (!new File(lastPosition.indexFileName).exists()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        FileInputStream in = null;
 | 
			
		||||
        try {
 | 
			
		||||
            in = new FileInputStream(lastPosition.indexFileName);
 | 
			
		||||
            in.getChannel().position(lastPosition.offsetInIndex);
 | 
			
		||||
            DataInputStream indexIn = new DataInputStream(in);
 | 
			
		||||
            // timestamp(second) in the specific position == that we cached
 | 
			
		||||
            return indexIn.readLong() == lastPosition.second;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            return false;
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (in != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    in.close();
 | 
			
		||||
                } catch (Exception ignore) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private long findOffset(long beginTime, String metricFileName,
 | 
			
		||||
                            String idxFileName, long offsetInIndex) throws Exception {
 | 
			
		||||
        lastPosition.metricFileName = null;
 | 
			
		||||
        lastPosition.indexFileName = null;
 | 
			
		||||
        if (!new File(idxFileName).exists()) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        long beginSecond = beginTime / 1000;
 | 
			
		||||
        FileInputStream in = new FileInputStream(idxFileName);
 | 
			
		||||
        in.getChannel().position(offsetInIndex);
 | 
			
		||||
        DataInputStream indexIn = new DataInputStream(in);
 | 
			
		||||
        long offset;
 | 
			
		||||
        try {
 | 
			
		||||
            long second;
 | 
			
		||||
            lastPosition.offsetInIndex = in.getChannel().position();
 | 
			
		||||
            while ((second = indexIn.readLong()) < beginSecond) {
 | 
			
		||||
                offset = indexIn.readLong();
 | 
			
		||||
                lastPosition.offsetInIndex = in.getChannel().position();
 | 
			
		||||
            }
 | 
			
		||||
            offset = indexIn.readLong();
 | 
			
		||||
            lastPosition.metricFileName = metricFileName;
 | 
			
		||||
            lastPosition.indexFileName = idxFileName;
 | 
			
		||||
            lastPosition.second = second;
 | 
			
		||||
            return offset;
 | 
			
		||||
        } catch (EOFException ignore) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        } finally {
 | 
			
		||||
            indexIn.close();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,71 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node.metric;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Map.Entry;
 | 
			
		||||
import java.util.TreeMap;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.Constants;
 | 
			
		||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
import com.alibaba.csp.sentinel.node.ClusterNode;
 | 
			
		||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 */
 | 
			
		||||
public class MetricTimerListener implements Runnable {
 | 
			
		||||
 | 
			
		||||
    private static final MetricWriter metricWriter = new MetricWriter(SentinelConfig.singleMetricFileSize(),
 | 
			
		||||
        SentinelConfig.totalMetricFileCount());
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void run() {
 | 
			
		||||
        Map<Long, List<MetricNode>> maps = new TreeMap<>();
 | 
			
		||||
        for (Entry<ResourceWrapper, ClusterNode> e : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
 | 
			
		||||
            ClusterNode node = e.getValue();
 | 
			
		||||
            Map<Long, MetricNode> metrics = node.metrics();
 | 
			
		||||
            aggregate(maps, metrics, node);
 | 
			
		||||
        }
 | 
			
		||||
        aggregate(maps, Constants.ENTRY_NODE.metrics(), Constants.ENTRY_NODE);
 | 
			
		||||
        if (!maps.isEmpty()) {
 | 
			
		||||
            for (Entry<Long, List<MetricNode>> entry : maps.entrySet()) {
 | 
			
		||||
                try {
 | 
			
		||||
                    metricWriter.write(entry.getKey(), entry.getValue());
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    RecordLog.warn("[MetricTimerListener] Write metric error", e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void aggregate(Map<Long, List<MetricNode>> maps, Map<Long, MetricNode> metrics, ClusterNode node) {
 | 
			
		||||
        for (Entry<Long, MetricNode> entry : metrics.entrySet()) {
 | 
			
		||||
            long time = entry.getKey();
 | 
			
		||||
            MetricNode metricNode = entry.getValue();
 | 
			
		||||
            metricNode.setResource(node.getName());
 | 
			
		||||
            metricNode.setClassification(node.getResourceType());
 | 
			
		||||
            maps.computeIfAbsent(time, k -> new ArrayList<MetricNode>());
 | 
			
		||||
            List<MetricNode> nodes = maps.get(time);
 | 
			
		||||
            nodes.add(entry.getValue());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,402 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node.metric;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedOutputStream;
 | 
			
		||||
import java.io.DataOutputStream;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.text.DateFormat;
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.LogBase;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.PidUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class is responsible for writing {@link MetricNode} to disk:
 | 
			
		||||
 * <ol>
 | 
			
		||||
 * <li>metric with the same second should write to the same file;</li>
 | 
			
		||||
 * <li>single file size must be controlled;</li>
 | 
			
		||||
 * <li>file name is like: {@code ${appName}-metrics.log.pid${pid}.yyyy-MM-dd.[number]}</li>
 | 
			
		||||
 * <li>metric of different day should in different file;</li>
 | 
			
		||||
 * <li>every metric file is accompanied with an index file, which file name is {@code ${metricFileName}.idx}</li>
 | 
			
		||||
 * </ol>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 */
 | 
			
		||||
public class MetricWriter {
 | 
			
		||||
 | 
			
		||||
    private static final String CHARSET = SentinelConfig.charset();
 | 
			
		||||
    public static final String METRIC_BASE_DIR = LogBase.getLogBaseDir();
 | 
			
		||||
    /**
 | 
			
		||||
     * Note: {@link MetricFileNameComparator}'s implementation relies on the metric file name,
 | 
			
		||||
     * so we should be careful when changing the metric file name.
 | 
			
		||||
     *
 | 
			
		||||
     * @see #formMetricFileName(String, int)
 | 
			
		||||
     */
 | 
			
		||||
    public static final String METRIC_FILE = "metrics.log";
 | 
			
		||||
    public static final String METRIC_FILE_INDEX_SUFFIX = ".idx";
 | 
			
		||||
    public static final Comparator<String> METRIC_FILE_NAME_CMP = new MetricFileNameComparator();
 | 
			
		||||
 | 
			
		||||
    private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 | 
			
		||||
    /**
 | 
			
		||||
     * 排除时差干扰
 | 
			
		||||
     */
 | 
			
		||||
    private long timeSecondBase;
 | 
			
		||||
    private String baseDir;
 | 
			
		||||
    private String baseFileName;
 | 
			
		||||
    /**
 | 
			
		||||
     * file must exist when writing
 | 
			
		||||
     */
 | 
			
		||||
    private File curMetricFile;
 | 
			
		||||
    private File curMetricIndexFile;
 | 
			
		||||
 | 
			
		||||
    private FileOutputStream outMetric;
 | 
			
		||||
    private DataOutputStream outIndex;
 | 
			
		||||
    private BufferedOutputStream outMetricBuf;
 | 
			
		||||
    private long singleFileSize;
 | 
			
		||||
    private int totalFileCount;
 | 
			
		||||
    private boolean append = false;
 | 
			
		||||
    private final int pid = PidUtil.getPid();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 秒级统计,忽略毫秒数。
 | 
			
		||||
     */
 | 
			
		||||
    private long lastSecond = -1;
 | 
			
		||||
 | 
			
		||||
    public MetricWriter(long singleFileSize) {
 | 
			
		||||
        this(singleFileSize, 6);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MetricWriter(long singleFileSize, int totalFileCount) {
 | 
			
		||||
        if (singleFileSize <= 0 || totalFileCount <= 0) {
 | 
			
		||||
            throw new IllegalArgumentException();
 | 
			
		||||
        }
 | 
			
		||||
        RecordLog.info("[MetricWriter] Creating new MetricWriter, singleFileSize={}, totalFileCount={}",
 | 
			
		||||
            singleFileSize, totalFileCount);
 | 
			
		||||
        this.baseDir = METRIC_BASE_DIR;
 | 
			
		||||
        File dir = new File(baseDir);
 | 
			
		||||
        if (!dir.exists()) {
 | 
			
		||||
            dir.mkdirs();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        long time = System.currentTimeMillis();
 | 
			
		||||
        this.lastSecond = time / 1000;
 | 
			
		||||
        this.singleFileSize = singleFileSize;
 | 
			
		||||
        this.totalFileCount = totalFileCount;
 | 
			
		||||
        try {
 | 
			
		||||
            this.timeSecondBase = df.parse("1970-01-01 00:00:00").getTime() / 1000;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            RecordLog.warn("[MetricWriter] Create new MetricWriter error", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 如果传入了time,就认为nodes中所有的时间时间戳都是time.
 | 
			
		||||
     *
 | 
			
		||||
     * @param time
 | 
			
		||||
     * @param nodes
 | 
			
		||||
     */
 | 
			
		||||
    public synchronized void write(long time, List<MetricNode> nodes) throws Exception {
 | 
			
		||||
        if (nodes == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        for (MetricNode node : nodes) {
 | 
			
		||||
            node.setTimestamp(time);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String appName = SentinelConfig.getAppName();
 | 
			
		||||
        if (appName == null) {
 | 
			
		||||
            appName = "";
 | 
			
		||||
        }
 | 
			
		||||
        // first write, should create file
 | 
			
		||||
        if (curMetricFile == null) {
 | 
			
		||||
            baseFileName = formMetricFileName(appName, pid);
 | 
			
		||||
            closeAndNewFile(nextFileNameOfDay(time));
 | 
			
		||||
        }
 | 
			
		||||
        if (!(curMetricFile.exists() && curMetricIndexFile.exists())) {
 | 
			
		||||
            closeAndNewFile(nextFileNameOfDay(time));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        long second = time / 1000;
 | 
			
		||||
        if (second < lastSecond) {
 | 
			
		||||
            // 时间靠前的直接忽略,不应该发生。
 | 
			
		||||
        } else if (second == lastSecond) {
 | 
			
		||||
            for (MetricNode node : nodes) {
 | 
			
		||||
                outMetricBuf.write(node.toFatString().getBytes(CHARSET));
 | 
			
		||||
            }
 | 
			
		||||
            outMetricBuf.flush();
 | 
			
		||||
            if (!validSize()) {
 | 
			
		||||
                closeAndNewFile(nextFileNameOfDay(time));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            writeIndex(second, outMetric.getChannel().position());
 | 
			
		||||
            if (isNewDay(lastSecond, second)) {
 | 
			
		||||
                closeAndNewFile(nextFileNameOfDay(time));
 | 
			
		||||
                for (MetricNode node : nodes) {
 | 
			
		||||
                    outMetricBuf.write(node.toFatString().getBytes(CHARSET));
 | 
			
		||||
                }
 | 
			
		||||
                outMetricBuf.flush();
 | 
			
		||||
                if (!validSize()) {
 | 
			
		||||
                    closeAndNewFile(nextFileNameOfDay(time));
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                for (MetricNode node : nodes) {
 | 
			
		||||
                    outMetricBuf.write(node.toFatString().getBytes(CHARSET));
 | 
			
		||||
                }
 | 
			
		||||
                outMetricBuf.flush();
 | 
			
		||||
                if (!validSize()) {
 | 
			
		||||
                    closeAndNewFile(nextFileNameOfDay(time));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            lastSecond = second;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public synchronized void close() throws Exception {
 | 
			
		||||
        if (outMetricBuf != null) {
 | 
			
		||||
            outMetricBuf.close();
 | 
			
		||||
        }
 | 
			
		||||
        if (outIndex != null) {
 | 
			
		||||
            outIndex.close();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void writeIndex(long time, long offset) throws Exception {
 | 
			
		||||
        outIndex.writeLong(time);
 | 
			
		||||
        outIndex.writeLong(offset);
 | 
			
		||||
        outIndex.flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String nextFileNameOfDay(long time) {
 | 
			
		||||
        List<String> list = new ArrayList<String>();
 | 
			
		||||
        File baseFile = new File(baseDir);
 | 
			
		||||
        DateFormat fileNameDf = new SimpleDateFormat("yyyy-MM-dd");
 | 
			
		||||
        String dateStr = fileNameDf.format(new Date(time));
 | 
			
		||||
        String fileNameModel = baseFileName + "." + dateStr;
 | 
			
		||||
        for (File file : baseFile.listFiles()) {
 | 
			
		||||
            String fileName = file.getName();
 | 
			
		||||
            if (fileName.contains(fileNameModel)
 | 
			
		||||
                && !fileName.endsWith(METRIC_FILE_INDEX_SUFFIX)
 | 
			
		||||
                && !fileName.endsWith(".lck")) {
 | 
			
		||||
                list.add(file.getAbsolutePath());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Collections.sort(list, METRIC_FILE_NAME_CMP);
 | 
			
		||||
        if (list.isEmpty()) {
 | 
			
		||||
            return baseDir + fileNameModel;
 | 
			
		||||
        }
 | 
			
		||||
        String last = list.get(list.size() - 1);
 | 
			
		||||
        int n = 0;
 | 
			
		||||
        String[] strs = last.split("\\.");
 | 
			
		||||
        if (strs.length > 0 && strs[strs.length - 1].matches("[0-9]{1,10}")) {
 | 
			
		||||
            n = Integer.parseInt(strs[strs.length - 1]);
 | 
			
		||||
        }
 | 
			
		||||
        return baseDir + fileNameModel + "." + (n + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A comparator for metric file name. Metric file name is like: <br/>
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * metrics.log.2018-03-06
 | 
			
		||||
     * metrics.log.2018-03-07
 | 
			
		||||
     * metrics.log.2018-03-07.10
 | 
			
		||||
     * metrics.log.2018-03-06.100
 | 
			
		||||
     * </pre>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * File name with the early date is smaller, if date is same, the one with the small file number is smaller.
 | 
			
		||||
     * Note that if the name is an absolute path, only the fileName({@link File#getName()}) part will be considered.
 | 
			
		||||
     * So the above file names should be sorted as: <br/>
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * metrics.log.2018-03-06
 | 
			
		||||
     * metrics.log.2018-03-06.100
 | 
			
		||||
     * metrics.log.2018-03-07
 | 
			
		||||
     * metrics.log.2018-03-07.10
 | 
			
		||||
     *
 | 
			
		||||
     * </pre>
 | 
			
		||||
     * </p>
 | 
			
		||||
     */
 | 
			
		||||
    private static final class MetricFileNameComparator implements Comparator<String> {
 | 
			
		||||
        private final String pid = "pid";
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int compare(String o1, String o2) {
 | 
			
		||||
            String name1 = new File(o1).getName();
 | 
			
		||||
            String name2 = new File(o2).getName();
 | 
			
		||||
            String dateStr1 = name1.split("\\.")[2];
 | 
			
		||||
            String dateStr2 = name2.split("\\.")[2];
 | 
			
		||||
            // in case of file name contains pid, skip it, like Sentinel-Admin-metrics.log.pid22568.2018-12-24
 | 
			
		||||
            if (dateStr1.startsWith(pid)) {
 | 
			
		||||
                dateStr1 = name1.split("\\.")[3];
 | 
			
		||||
                dateStr2 = name2.split("\\.")[3];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // compare date first
 | 
			
		||||
            int t = dateStr1.compareTo(dateStr2);
 | 
			
		||||
            if (t != 0) {
 | 
			
		||||
                return t;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // same date, compare file number
 | 
			
		||||
            t = name1.length() - name2.length();
 | 
			
		||||
            if (t != 0) {
 | 
			
		||||
                return t;
 | 
			
		||||
            }
 | 
			
		||||
            return name1.compareTo(name2);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all metric files' name in {@code baseDir}. The file name must like
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * baseFileName + ".yyyy-MM-dd.number"
 | 
			
		||||
     * </pre>
 | 
			
		||||
     * and not endsWith {@link #METRIC_FILE_INDEX_SUFFIX} or ".lck".
 | 
			
		||||
     *
 | 
			
		||||
     * @param baseDir      the directory to search.
 | 
			
		||||
     * @param baseFileName the file name pattern.
 | 
			
		||||
     * @return the metric files' absolute path({@link File#getAbsolutePath()})
 | 
			
		||||
     * @throws Exception
 | 
			
		||||
     */
 | 
			
		||||
    static List<String> listMetricFiles(String baseDir, String baseFileName) throws Exception {
 | 
			
		||||
        List<String> list = new ArrayList<String>();
 | 
			
		||||
        File baseFile = new File(baseDir);
 | 
			
		||||
        File[] files = baseFile.listFiles();
 | 
			
		||||
        if (files == null) {
 | 
			
		||||
            return list;
 | 
			
		||||
        }
 | 
			
		||||
        for (File file : files) {
 | 
			
		||||
            String fileName = file.getName();
 | 
			
		||||
            if (file.isFile()
 | 
			
		||||
                && fileNameMatches(fileName, baseFileName)
 | 
			
		||||
                && !fileName.endsWith(MetricWriter.METRIC_FILE_INDEX_SUFFIX)
 | 
			
		||||
                && !fileName.endsWith(".lck")) {
 | 
			
		||||
                list.add(file.getAbsolutePath());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Collections.sort(list, MetricWriter.METRIC_FILE_NAME_CMP);
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test whether fileName matches baseFileName. fileName matches baseFileName when
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * fileName = baseFileName + ".yyyy-MM-dd.number"
 | 
			
		||||
     * </pre>
 | 
			
		||||
     *
 | 
			
		||||
     * @param fileName     file name
 | 
			
		||||
     * @param baseFileName base file name.
 | 
			
		||||
     * @return if fileName matches baseFileName return true, else return false.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean fileNameMatches(String fileName, String baseFileName) {
 | 
			
		||||
        if (fileName.startsWith(baseFileName)) {
 | 
			
		||||
            String part = fileName.substring(baseFileName.length());
 | 
			
		||||
            // part is like: ".yyyy-MM-dd.number", eg. ".2018-12-24.11"
 | 
			
		||||
            return part.matches("\\.[0-9]{4}-[0-9]{2}-[0-9]{2}(\\.[0-9]*)?");
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void removeMoreFiles() throws Exception {
 | 
			
		||||
        List<String> list = listMetricFiles(baseDir, baseFileName);
 | 
			
		||||
        if (list == null || list.isEmpty()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 0; i < list.size() - totalFileCount + 1; i++) {
 | 
			
		||||
            String fileName = list.get(i);
 | 
			
		||||
            String indexFile = formIndexFileName(fileName);
 | 
			
		||||
            new File(fileName).delete();
 | 
			
		||||
            RecordLog.info("[MetricWriter] Removing metric file: {}", fileName);
 | 
			
		||||
            new File(indexFile).delete();
 | 
			
		||||
            RecordLog.info("[MetricWriter] Removing metric index file: {}", indexFile);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void closeAndNewFile(String fileName) throws Exception {
 | 
			
		||||
        removeMoreFiles();
 | 
			
		||||
        if (outMetricBuf != null) {
 | 
			
		||||
            outMetricBuf.close();
 | 
			
		||||
        }
 | 
			
		||||
        if (outIndex != null) {
 | 
			
		||||
            outIndex.close();
 | 
			
		||||
        }
 | 
			
		||||
        outMetric = new FileOutputStream(fileName, append);
 | 
			
		||||
        outMetricBuf = new BufferedOutputStream(outMetric);
 | 
			
		||||
        curMetricFile = new File(fileName);
 | 
			
		||||
        String idxFile = formIndexFileName(fileName);
 | 
			
		||||
        curMetricIndexFile = new File(idxFile);
 | 
			
		||||
        outIndex = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile, append)));
 | 
			
		||||
        RecordLog.info("[MetricWriter] New metric file created: {}", fileName);
 | 
			
		||||
        RecordLog.info("[MetricWriter] New metric index file created: {}", idxFile);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean validSize() throws Exception {
 | 
			
		||||
        long size = outMetric.getChannel().size();
 | 
			
		||||
        return size < singleFileSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isNewDay(long lastSecond, long second) {
 | 
			
		||||
        long lastDay = (lastSecond - timeSecondBase) / 86400;
 | 
			
		||||
        long newDay = (second - timeSecondBase) / 86400;
 | 
			
		||||
        return newDay > lastDay;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Form metric file name use the specific appName and pid. Note that only
 | 
			
		||||
     * form the file name, not include path.
 | 
			
		||||
     *
 | 
			
		||||
     * Note: {@link MetricFileNameComparator}'s implementation relays on the metric file name,
 | 
			
		||||
     * we should be careful when changing the metric file name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param appName
 | 
			
		||||
     * @param pid
 | 
			
		||||
     * @return metric file name.
 | 
			
		||||
     */
 | 
			
		||||
    public static String formMetricFileName(String appName, int pid) {
 | 
			
		||||
        if (appName == null) {
 | 
			
		||||
            appName = "";
 | 
			
		||||
        }
 | 
			
		||||
        // dot is special char that should be replaced.
 | 
			
		||||
        final String dot = ".";
 | 
			
		||||
        final String separator = "-";
 | 
			
		||||
        if (appName.contains(dot)) {
 | 
			
		||||
            appName = appName.replace(dot, separator);
 | 
			
		||||
        }
 | 
			
		||||
        String name = appName + separator + METRIC_FILE;
 | 
			
		||||
        if (LogBase.isLogNameUsePid()) {
 | 
			
		||||
            name += ".pid" + pid;
 | 
			
		||||
        }
 | 
			
		||||
        return name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Form index file name of the {@code metricFileName}
 | 
			
		||||
     *
 | 
			
		||||
     * @param metricFileName
 | 
			
		||||
     * @return the index file name of the metricFileName
 | 
			
		||||
     */
 | 
			
		||||
    public static String formIndexFileName(String metricFileName) {
 | 
			
		||||
        return metricFileName + METRIC_FILE_INDEX_SUFFIX;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,142 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.node.metric;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.InputStreamReader;
 | 
			
		||||
import java.nio.charset.Charset;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Reads metrics data from log file.
 | 
			
		||||
 */
 | 
			
		||||
class MetricsReader {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Avoid OOM in any cases.
 | 
			
		||||
     */
 | 
			
		||||
    private static final int MAX_LINES_RETURN = 100000;
 | 
			
		||||
 | 
			
		||||
    private final Charset charset;
 | 
			
		||||
 | 
			
		||||
    public MetricsReader(Charset charset) {
 | 
			
		||||
        this.charset = charset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return if should continue read, return true, else false.
 | 
			
		||||
     */
 | 
			
		||||
    boolean readMetricsInOneFileByEndTime(List<MetricNode> list, String fileName, long offset,
 | 
			
		||||
                                          long beginTimeMs, long endTimeMs, String identity) throws Exception {
 | 
			
		||||
        FileInputStream in = null;
 | 
			
		||||
        long beginSecond = beginTimeMs / 1000;
 | 
			
		||||
        long endSecond = endTimeMs / 1000;
 | 
			
		||||
        try {
 | 
			
		||||
            in = new FileInputStream(fileName);
 | 
			
		||||
            in.getChannel().position(offset);
 | 
			
		||||
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
 | 
			
		||||
            String line;
 | 
			
		||||
            while ((line = reader.readLine()) != null) {
 | 
			
		||||
                MetricNode node = MetricNode.fromFatString(line);
 | 
			
		||||
                long currentSecond = node.getTimestamp() / 1000;
 | 
			
		||||
                // currentSecond should >= beginSecond, otherwise a wrong metric file must occur
 | 
			
		||||
                if (currentSecond < beginSecond) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                if (currentSecond <= endSecond) {
 | 
			
		||||
                    // read all
 | 
			
		||||
                    if (identity == null) {
 | 
			
		||||
                        list.add(node);
 | 
			
		||||
                    } else if (node.getResource().equals(identity)) {
 | 
			
		||||
                        list.add(node);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                if (list.size() >= MAX_LINES_RETURN) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (in != null) {
 | 
			
		||||
                in.close();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void readMetricsInOneFile(List<MetricNode> list, String fileName,
 | 
			
		||||
                              long offset, int recommendLines) throws Exception {
 | 
			
		||||
        //if(list.size() >= recommendLines){
 | 
			
		||||
        //    return;
 | 
			
		||||
        //}
 | 
			
		||||
        long lastSecond = -1;
 | 
			
		||||
        if (list.size() > 0) {
 | 
			
		||||
            lastSecond = list.get(list.size() - 1).getTimestamp() / 1000;
 | 
			
		||||
        }
 | 
			
		||||
        FileInputStream in = null;
 | 
			
		||||
        try {
 | 
			
		||||
            in = new FileInputStream(fileName);
 | 
			
		||||
            in.getChannel().position(offset);
 | 
			
		||||
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
 | 
			
		||||
            String line;
 | 
			
		||||
            while ((line = reader.readLine()) != null) {
 | 
			
		||||
                MetricNode node = MetricNode.fromFatString(line);
 | 
			
		||||
                long currentSecond = node.getTimestamp() / 1000;
 | 
			
		||||
 | 
			
		||||
                if (list.size() < recommendLines) {
 | 
			
		||||
                    list.add(node);
 | 
			
		||||
                } else if (currentSecond == lastSecond) {
 | 
			
		||||
                    list.add(node);
 | 
			
		||||
                } else {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                lastSecond = currentSecond;
 | 
			
		||||
            }
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (in != null) {
 | 
			
		||||
                in.close();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When identity is null, all metric between the time intervalMs will be read, otherwise, only the specific
 | 
			
		||||
     * identity will be read.
 | 
			
		||||
     */
 | 
			
		||||
    List<MetricNode> readMetricsByEndTime(List<String> fileNames, int pos, long offset,
 | 
			
		||||
                                          long beginTimeMs, long endTimeMs, String identity) throws Exception {
 | 
			
		||||
        List<MetricNode> list = new ArrayList<MetricNode>(1024);
 | 
			
		||||
        if (readMetricsInOneFileByEndTime(list, fileNames.get(pos++), offset, beginTimeMs, endTimeMs, identity)) {
 | 
			
		||||
            while (pos < fileNames.size()
 | 
			
		||||
                && readMetricsInOneFileByEndTime(list, fileNames.get(pos++), 0, beginTimeMs, endTimeMs, identity)) {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    List<MetricNode> readMetrics(List<String> fileNames, int pos,
 | 
			
		||||
                                 long offset, int recommendLines) throws Exception {
 | 
			
		||||
        List<MetricNode> list = new ArrayList<MetricNode>(recommendLines);
 | 
			
		||||
        readMetricsInOneFile(list, fileNames.get(pos++), offset, recommendLines);
 | 
			
		||||
        while (list.size() < recommendLines && pos < fileNames.size()) {
 | 
			
		||||
            readMetricsInOneFile(list, fileNames.get(pos++), 0, recommendLines);
 | 
			
		||||
        }
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,77 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.property;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.log.RecordLog;
 | 
			
		||||
 | 
			
		||||
public class DynamicSentinelProperty<T> implements SentinelProperty<T> {
 | 
			
		||||
 | 
			
		||||
    protected Set<PropertyListener<T>> listeners = Collections.synchronizedSet(new HashSet<PropertyListener<T>>());
 | 
			
		||||
    private T value = null;
 | 
			
		||||
 | 
			
		||||
    public DynamicSentinelProperty() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DynamicSentinelProperty(T value) {
 | 
			
		||||
        super();
 | 
			
		||||
        this.value = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addListener(PropertyListener<T> listener) {
 | 
			
		||||
        listeners.add(listener);
 | 
			
		||||
        listener.configLoad(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void removeListener(PropertyListener<T> listener) {
 | 
			
		||||
        listeners.remove(listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean updateValue(T newValue) {
 | 
			
		||||
        if (isEqual(value, newValue)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        RecordLog.info("[DynamicSentinelProperty] Config will be updated to: {}", newValue);
 | 
			
		||||
 | 
			
		||||
        value = newValue;
 | 
			
		||||
        for (PropertyListener<T> listener : listeners) {
 | 
			
		||||
            listener.configUpdate(newValue);
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isEqual(T oldValue, T newValue) {
 | 
			
		||||
        if (oldValue == null && newValue == null) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (oldValue == null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return oldValue.equals(newValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void close() {
 | 
			
		||||
        listeners.clear();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.property;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A {@link SentinelProperty} that will never inform the {@link PropertyListener} on it.
 | 
			
		||||
 *
 | 
			
		||||
 * @author leyou
 | 
			
		||||
 */
 | 
			
		||||
public final class NoOpSentinelProperty implements SentinelProperty<Object> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addListener(PropertyListener<Object> listener) { }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void removeListener(PropertyListener<Object> listener) { }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean updateValue(Object newValue) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.property;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class holds callback method when {@link SentinelProperty#updateValue(Object)} need inform the listener
 | 
			
		||||
 *
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 */
 | 
			
		||||
public interface PropertyListener<T> {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Callback method when {@link SentinelProperty#updateValue(Object)} need inform the listener.
 | 
			
		||||
     *
 | 
			
		||||
     * @param value updated value.
 | 
			
		||||
     */
 | 
			
		||||
    void configUpdate(T value);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The first time of the {@code value}'s load.
 | 
			
		||||
     *
 | 
			
		||||
     * @param value the value loaded.
 | 
			
		||||
     */
 | 
			
		||||
    void configLoad(T value);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,62 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.property;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * <p>
 | 
			
		||||
 * This class holds current value of the config, and is responsible for informing all {@link PropertyListener}s
 | 
			
		||||
 * added on this when the config is updated.
 | 
			
		||||
 * </p>
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Note that not every {@link #updateValue(Object newValue)} invocation should inform the listeners, only when
 | 
			
		||||
 * {@code newValue} is not Equals to the old value, informing is needed.
 | 
			
		||||
 * </p>
 | 
			
		||||
 *
 | 
			
		||||
 * @param <T> the target type.
 | 
			
		||||
 * @author Carpenter Lee
 | 
			
		||||
 */
 | 
			
		||||
public interface SentinelProperty<T> {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Add a {@link PropertyListener} to this {@link SentinelProperty}. After the listener is added,
 | 
			
		||||
     * {@link #updateValue(Object)} will inform the listener if needed.
 | 
			
		||||
     * </p>
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This method can invoke multi times to add more than one listeners.
 | 
			
		||||
     * </p>
 | 
			
		||||
     *
 | 
			
		||||
     * @param listener listener to add.
 | 
			
		||||
     */
 | 
			
		||||
    void addListener(PropertyListener<T> listener);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Remove the {@link PropertyListener} on this. After removing, {@link #updateValue(Object)}
 | 
			
		||||
     * will not inform the listener.
 | 
			
		||||
     *
 | 
			
		||||
     * @param listener the listener to remove.
 | 
			
		||||
     */
 | 
			
		||||
    void removeListener(PropertyListener<T> listener);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update the {@code newValue} as the current value of this property and inform all {@link PropertyListener}s
 | 
			
		||||
     * added on this only when new {@code newValue} is not Equals to the old value.
 | 
			
		||||
     *
 | 
			
		||||
     * @param newValue the new value.
 | 
			
		||||
     * @return true if the value in property has been updated, otherwise false
 | 
			
		||||
     */
 | 
			
		||||
    boolean updateValue(T newValue);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.property;
 | 
			
		||||
 | 
			
		||||
public abstract class SimplePropertyListener<T> implements PropertyListener<T> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void configLoad(T value) {
 | 
			
		||||
        configUpdate(value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.slotchain;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 */
 | 
			
		||||
public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {
 | 
			
		||||
 | 
			
		||||
    private AbstractLinkedProcessorSlot<?> next = null;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
 | 
			
		||||
        throws Throwable {
 | 
			
		||||
        if (next != null) {
 | 
			
		||||
            next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)
 | 
			
		||||
        throws Throwable {
 | 
			
		||||
        T t = (T)o;
 | 
			
		||||
        entry(context, resourceWrapper, t, count, prioritized, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
 | 
			
		||||
        if (next != null) {
 | 
			
		||||
            next.exit(context, resourceWrapper, count, args);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AbstractLinkedProcessorSlot<?> getNext() {
 | 
			
		||||
        return next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setNext(AbstractLinkedProcessorSlot<?> next) {
 | 
			
		||||
        this.next = next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,83 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.slotchain;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 */
 | 
			
		||||
public class DefaultProcessorSlotChain extends ProcessorSlotChain {
 | 
			
		||||
 | 
			
		||||
    AbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)
 | 
			
		||||
            throws Throwable {
 | 
			
		||||
            super.fireEntry(context, resourceWrapper, t, count, prioritized, args);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
 | 
			
		||||
            super.fireExit(context, resourceWrapper, count, args);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
    AbstractLinkedProcessorSlot<?> end = first;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addFirst(AbstractLinkedProcessorSlot<?> protocolProcessor) {
 | 
			
		||||
        protocolProcessor.setNext(first.getNext());
 | 
			
		||||
        first.setNext(protocolProcessor);
 | 
			
		||||
        if (end == first) {
 | 
			
		||||
            end = protocolProcessor;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {
 | 
			
		||||
        end.setNext(protocolProcessor);
 | 
			
		||||
        end = protocolProcessor;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Same as {@link #addLast(AbstractLinkedProcessorSlot)}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param next processor to be added.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setNext(AbstractLinkedProcessorSlot<?> next) {
 | 
			
		||||
        addLast(next);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public AbstractLinkedProcessorSlot<?> getNext() {
 | 
			
		||||
        return first.getNext();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)
 | 
			
		||||
        throws Throwable {
 | 
			
		||||
        first.transformEntry(context, resourceWrapper, t, count, prioritized, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
 | 
			
		||||
        first.exit(context, resourceWrapper, count, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,60 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.slotchain;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.EntryType;
 | 
			
		||||
import com.alibaba.csp.sentinel.ResourceTypeConstants;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.IdUtil;
 | 
			
		||||
import com.alibaba.csp.sentinel.util.MethodUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Resource wrapper for method invocation.
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 */
 | 
			
		||||
public class MethodResourceWrapper extends ResourceWrapper {
 | 
			
		||||
 | 
			
		||||
    private final transient Method method;
 | 
			
		||||
 | 
			
		||||
    public MethodResourceWrapper(Method method, EntryType e) {
 | 
			
		||||
        this(method, e, ResourceTypeConstants.COMMON);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MethodResourceWrapper(Method method, EntryType e, int resType) {
 | 
			
		||||
        super(MethodUtil.resolveMethodName(method), e, resType);
 | 
			
		||||
        this.method = method;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Method getMethod() {
 | 
			
		||||
        return method;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getShowName() {
 | 
			
		||||
        return name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString() {
 | 
			
		||||
        return "MethodResourceWrapper{" +
 | 
			
		||||
            "name='" + name + '\'' +
 | 
			
		||||
            ", entryType=" + entryType +
 | 
			
		||||
            ", resourceType=" + resourceType +
 | 
			
		||||
            '}';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,77 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.slotchain;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A container of some process and ways of notification when the process is finished.
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 * @author jialiang.linjl
 | 
			
		||||
 * @author leyou(lihao)
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 */
 | 
			
		||||
public interface ProcessorSlot<T> {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Entrance of this slot.
 | 
			
		||||
     *
 | 
			
		||||
     * @param context         current {@link Context}
 | 
			
		||||
     * @param resourceWrapper current resource
 | 
			
		||||
     * @param param           generics parameter, usually is a {@link com.alibaba.csp.sentinel.node.Node}
 | 
			
		||||
     * @param count           tokens needed
 | 
			
		||||
     * @param prioritized     whether the entry is prioritized
 | 
			
		||||
     * @param args            parameters of the original call
 | 
			
		||||
     * @throws Throwable blocked exception or unexpected error
 | 
			
		||||
     */
 | 
			
		||||
    void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, boolean prioritized,
 | 
			
		||||
               Object... args) throws Throwable;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Means finish of {@link #entry(Context, ResourceWrapper, Object, int, boolean, Object...)}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param context         current {@link Context}
 | 
			
		||||
     * @param resourceWrapper current resource
 | 
			
		||||
     * @param obj             relevant object (e.g. Node)
 | 
			
		||||
     * @param count           tokens needed
 | 
			
		||||
     * @param prioritized     whether the entry is prioritized
 | 
			
		||||
     * @param args            parameters of the original call
 | 
			
		||||
     * @throws Throwable blocked exception or unexpected error
 | 
			
		||||
     */
 | 
			
		||||
    void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized,
 | 
			
		||||
                   Object... args) throws Throwable;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Exit of this slot.
 | 
			
		||||
     *
 | 
			
		||||
     * @param context         current {@link Context}
 | 
			
		||||
     * @param resourceWrapper current resource
 | 
			
		||||
     * @param count           tokens needed
 | 
			
		||||
     * @param args            parameters of the original call
 | 
			
		||||
     */
 | 
			
		||||
    void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Means finish of {@link #exit(Context, ResourceWrapper, int, Object...)}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param context         current {@link Context}
 | 
			
		||||
     * @param resourceWrapper current resource
 | 
			
		||||
     * @param count           tokens needed
 | 
			
		||||
     * @param args            parameters of the original call
 | 
			
		||||
     */
 | 
			
		||||
    void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.slotchain;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Link all processor slots as a chain.
 | 
			
		||||
 *
 | 
			
		||||
 * @author qinan.qn
 | 
			
		||||
 */
 | 
			
		||||
public abstract class ProcessorSlotChain extends AbstractLinkedProcessorSlot<Object> {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add a processor to the head of this slot chain.
 | 
			
		||||
     *
 | 
			
		||||
     * @param protocolProcessor processor to be added.
 | 
			
		||||
     */
 | 
			
		||||
    public abstract void addFirst(AbstractLinkedProcessorSlot<?> protocolProcessor);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add a processor to the tail of this slot chain.
 | 
			
		||||
     *
 | 
			
		||||
     * @param protocolProcessor processor to be added.
 | 
			
		||||
     */
 | 
			
		||||
    public abstract void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.slotchain;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Callback for entering {@link com.alibaba.csp.sentinel.slots.statistic.StatisticSlot} (passed and blocked).
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 0.2.0
 | 
			
		||||
 */
 | 
			
		||||
public interface ProcessorSlotEntryCallback<T> {
 | 
			
		||||
 | 
			
		||||
    void onPass(Context context, ResourceWrapper resourceWrapper, T param, int count, Object... args) throws Exception;
 | 
			
		||||
 | 
			
		||||
    void onBlocked(BlockException ex, Context context, ResourceWrapper resourceWrapper, T param, int count, Object... args);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 com.alibaba.csp.sentinel.slotchain;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.csp.sentinel.context.Context;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Callback for exiting {@link com.alibaba.csp.sentinel.slots.statistic.StatisticSlot} (passed and blocked).
 | 
			
		||||
 *
 | 
			
		||||
 * @author Eric Zhao
 | 
			
		||||
 * @since 0.2.0
 | 
			
		||||
 */
 | 
			
		||||
public interface ProcessorSlotExitCallback {
 | 
			
		||||
 | 
			
		||||
    void onExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user