// Path: core/src/main/java/com/github/phantomthief/tuple/Tuple.java
// public static <A, B> TwoTuple<A, B> tuple(final A a, final B b) {
//     return new TwoTuple<>(a, b);
// }
// 
// Path: core/src/main/java/com/github/phantomthief/tuple/ThreeTuple.java
// public class ThreeTuple<A, B, C> extends TwoTuple<A, B> {
// 
//     public final C third;
// 
//     /**
//      * use {@link Tuple#tuple(Object, Object, Object)} instead
//      */
//     @Deprecated
//     public ThreeTuple(final A a, final B b, final C c) {
//         super(a, b);
//         third = c;
//     }
// 
//     public C getThird() {
//         return third;
//     }
// 
//     @Override
//     public String toString() {
//         return "(" + first + ", " + second + ", " + third + ")";
//     }
// 
//     @Override
//     public int hashCode() {
//         final int prime = 31;
//         int result = super.hashCode();
//         result = prime * result + (third == null ? 0 : third.hashCode());
//         return result;
//     }
// 
//     @Override
//     public boolean equals(final Object obj) {
//         if (this == obj) {
//             return true;
//         }
//         if (!super.equals(obj)) {
//             return false;
//         }
//         if (getClass() != obj.getClass()) {
//             return false;
//         }
//         final ThreeTuple<?, ?, ?> other = (ThreeTuple<?, ?, ?>) obj;
//         if (third == null) {
//             if (other.third != null) {
//                 return false;
//             }
//         } else if (!third.equals(other.third)) {
//             return false;
//         }
//         return true;
//     }
// 
// }


import static com.github.phantomthief.tuple.Tuple.tuple;
import static org.slf4j.spi.LocationAwareLogger.DEBUG_INT;
import static org.slf4j.spi.LocationAwareLogger.ERROR_INT;
import static org.slf4j.spi.LocationAwareLogger.INFO_INT;
import static org.slf4j.spi.LocationAwareLogger.TRACE_INT;
import static org.slf4j.spi.LocationAwareLogger.WARN_INT;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nullable;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.spi.LocationAwareLogger;
import com.github.phantomthief.tuple.ThreeTuple;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

    /**
     * 工厂方法
     *
     * @param logger 要封装的logger实例
     * @param permitsPer 打log的每秒允许个数，例如传入0.2，就意味着五秒打一条log
     */
    public static RateLogger rateLogger(Logger logger, double permitsPer) {
        return rateLogger(logger, permitsPer, false);
    }

    /**
     * 工厂方法，和 {@link #rateLogger} 的区别是，会按照不同的 msg 分别采样计算
     *
     * @param logger 要封装的logger实例
     * @param permitsPer 打log的每秒允许个数，例如传入0.2，就意味着五秒打一条log
     */
    public static RateLogger perMessageRateLogger(Logger logger, double permitsPer) {
        return rateLogger(logger, permitsPer, true);
    }

    /**
     * 工厂方法
     *
     * @param logger 要封装的logger实例
     * @param permitsPer 打log的每秒允许个数，例如传入0.2，就意味着五秒打一条log
     * @param perFormatString 如果为 {@code true}，则按照每个 formatString 为单位而不是整个 logger 为单位执行采样
     */
    private static RateLogger rateLogger(Logger logger, double permitsPer, boolean perFormatString) {
        String name = logger.getName();
// Path: core/src/main/java/com/github/phantomthief/tuple/Tuple.java
// public static <A, B> TwoTuple<A, B> tuple(final A a, final B b) {
//     return new TwoTuple<>(a, b);
// }
// 
// Path: core/src/main/java/com/github/phantomthief/tuple/ThreeTuple.java
// public class ThreeTuple<A, B, C> extends TwoTuple<A, B> {
// 
//     public final C third;
// 
//     /**
//      * use {@link Tuple#tuple(Object, Object, Object)} instead
//      */
//     @Deprecated
//     public ThreeTuple(final A a, final B b, final C c) {
//         super(a, b);
//         third = c;
//     }
// 
//     public C getThird() {
//         return third;
//     }
// 
//     @Override
//     public String toString() {
//         return "(" + first + ", " + second + ", " + third + ")";
//     }
// 
//     @Override
//     public int hashCode() {
//         final int prime = 31;
//         int result = super.hashCode();
//         result = prime * result + (third == null ? 0 : third.hashCode());
//         return result;
//     }
// 
//     @Override
//     public boolean equals(final Object obj) {
//         if (this == obj) {
//             return true;
//         }
//         if (!super.equals(obj)) {
//             return false;
//         }
//         if (getClass() != obj.getClass()) {
//             return false;
//         }
//         final ThreeTuple<?, ?, ?> other = (ThreeTuple<?, ?, ?>) obj;
//         if (third == null) {
//             if (other.third != null) {
//                 return false;
//             }
//         } else if (!third.equals(other.third)) {
//             return false;
//         }
//         return true;
//     }
// 
// }


// Path: core/src/main/java/com/github/phantomthief/util/RateLogger.java
import static com.github.phantomthief.tuple.Tuple.tuple;
import static org.slf4j.spi.LocationAwareLogger.DEBUG_INT;
import static org.slf4j.spi.LocationAwareLogger.ERROR_INT;
import static org.slf4j.spi.LocationAwareLogger.INFO_INT;
import static org.slf4j.spi.LocationAwareLogger.TRACE_INT;
import static org.slf4j.spi.LocationAwareLogger.WARN_INT;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nullable;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.spi.LocationAwareLogger;
import com.github.phantomthief.tuple.ThreeTuple;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;


    /**
     * 工厂方法
     *
     * @param logger 要封装的logger实例
     * @param permitsPer 打log的每秒允许个数，例如传入0.2，就意味着五秒打一条log
     */
    public static RateLogger rateLogger(Logger logger, double permitsPer) {
        return rateLogger(logger, permitsPer, false);
    }

    /**
     * 工厂方法，和 {@link #rateLogger} 的区别是，会按照不同的 msg 分别采样计算
     *
     * @param logger 要封装的logger实例
     * @param permitsPer 打log的每秒允许个数，例如传入0.2，就意味着五秒打一条log
     */
    public static RateLogger perMessageRateLogger(Logger logger, double permitsPer) {
        return rateLogger(logger, permitsPer, true);
    }

    /**
     * 工厂方法
     *
     * @param logger 要封装的logger实例
     * @param permitsPer 打log的每秒允许个数，例如传入0.2，就意味着五秒打一条log
     * @param perFormatString 如果为 {@code true}，则按照每个 formatString 为单位而不是整个 logger 为单位执行采样
     */
    private static RateLogger rateLogger(Logger logger, double permitsPer, boolean perFormatString) {
        String name = logger.getName();
        ThreeTuple<String, Double, Boolean> key = tuple(name, permitsPer, perFormatString);