All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rx.exception.ExceptionHandler Maven / Gradle / Ivy

package org.rx.exception;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.rx.annotation.DbColumn;
import org.rx.bean.DateTime;
import org.rx.core.*;
import org.rx.io.EntityDatabase;
import org.rx.io.EntityQueryLambda;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;

import java.io.Serializable;
import java.sql.Time;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledFuture;

import static org.rx.core.Extends.as;

@Slf4j
public final class ExceptionHandler implements Thread.UncaughtExceptionHandler {
    @Data
    public static class ErrorEntity implements Serializable {
        private static final long serialVersionUID = 8387064071982888474L;
        @DbColumn(primaryKey = true)
        int id;
        ExceptionLevel level;
        String message;
        String stackTrace;
        int occurCount;

        String appName;
        String threadName;
        Date modifyTime;
    }

    @Data
    public static class MetricsEntity implements Serializable {
        private static final long serialVersionUID = 2049476730423563051L;
        @DbColumn(primaryKey = true)
        String name;
        String message;
        Date createTime;
    }

    public static final ExceptionHandler INSTANCE = new ExceptionHandler();

    public static Object[] getMessageCandidate(Object... args) {
        if (args != null && args.length != 0) {
            int lastIndex = args.length - 1;
            Object last = args[lastIndex];
            if (last instanceof Throwable) {
                if (lastIndex == 0) {
                    return Arrays.EMPTY_OBJECT_ARRAY;
                }
                return Linq.from(args).take(lastIndex).toArray();
            }
        }
        return args;
    }

    ScheduledFuture future;

    public int getKeepDays() {
        return RxConfig.INSTANCE.getTraceKeepDays();
    }

    public synchronized void setKeepDays(int keepDays) {
        if (keepDays > 0) {
            EntityDatabase db = EntityDatabase.DEFAULT;
            db.createMapping(ErrorEntity.class, MetricsEntity.class);
            if (future == null) {
                future = Tasks.scheduleDaily(() -> {
                    db.delete(new EntityQueryLambda<>(ErrorEntity.class)
                            .lt(ErrorEntity::getModifyTime, DateTime.now().addDays(-keepDays - 1)));
                    db.compact();
                }, Time.valueOf("3:00:00"));
            }
        } else {
            if (future != null) {
                future.cancel(true);
                future = null;
            }
        }
        RxConfig.INSTANCE.setTraceKeepDays(keepDays);
    }

    private ExceptionHandler() {
        try {
            Thread.setDefaultUncaughtExceptionHandler(this);
            setKeepDays(RxConfig.INSTANCE.getTraceKeepDays());
        } catch (Throwable e) {
            log.error("rx init error", e);
        }
    }

    public void log(String format, Object... args) {
        try {
            FormattingTuple tuple = MessageFormatter.arrayFormat(format, args);
            if (tuple.getThrowable() == null) {
                log.warn(format + "[NoThrowableCandidate]", args);
                return;
            }

            log.error(format, args);
            saveTrace(Thread.currentThread(), tuple.getMessage(), tuple.getThrowable());
        } catch (Throwable ie) {
            ie.printStackTrace();
        }
    }

    public void log(Throwable e) {
        uncaughtException(Thread.currentThread(), e);
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        try {
            log.error("Thread[{}] uncaught", t.getId(), e);
            saveTrace(t, null, e);
        } catch (Throwable ie) {
            ie.printStackTrace();
        }
    }

    public List queryTraces(Boolean newest, ExceptionLevel level, Integer limit) {
        if (getKeepDays() <= 0) {
            return Collections.emptyList();
        }
        if (newest == null) {
            newest = Boolean.FALSE;
        }
        if (limit == null) {
            limit = 20;
        }

        EntityQueryLambda q = new EntityQueryLambda<>(ErrorEntity.class).limit(limit);
        if (newest) {
            q.orderByDescending(ErrorEntity::getModifyTime);
        } else {
            q.orderByDescending(ErrorEntity::getOccurCount);
        }
        if (level != null) {
            q.eq(ErrorEntity::getLevel, level);
        }
        EntityDatabase db = EntityDatabase.DEFAULT;
        return db.findBy(q);
    }

    public void saveTrace(Thread t, String msg, Throwable e) {
        if (Strings.contains(e.getMessage(), "Duplicate entry ")) {
            saveMetrics(Constants.DUPLICATE_KEY, e.getMessage());
        }

        if (getKeepDays() <= 0) {
            return;
        }

        String stackTrace = ExceptionUtils.getStackTrace(e);
        int pk = msg != null ? java.util.Arrays.hashCode(new Object[]{msg, stackTrace}) : stackTrace.hashCode();
        EntityDatabase db = EntityDatabase.DEFAULT;
        db.begin();
        try {
            ErrorEntity entity = db.findById(ErrorEntity.class, pk);
            boolean doInsert = entity == null;
            if (doInsert) {
                entity = new ErrorEntity();
                entity.setId(pk);
                InvalidException invalidException = as(e, InvalidException.class);
                ExceptionLevel level = invalidException != null && invalidException.getLevel() != null ? invalidException.getLevel()
                        : ExceptionLevel.SYSTEM;
                entity.setLevel(level);
                entity.setMessage(msg);
                entity.setStackTrace(stackTrace);
            }
            entity.occurCount++;
            entity.setAppName(RxConfig.INSTANCE.getId());
            entity.setThreadName(t.getName());
            entity.setModifyTime(DateTime.now());
            db.save(entity, doInsert);
            db.commit();
        } catch (Throwable ex) {
            log.error("dbTrace", ex);
            db.rollback();
        }
    }

    public void saveMetrics(String name, String message) {
        MetricsEntity entity = new MetricsEntity();
        entity.setName(name);
        entity.setMessage(message);
        entity.setCreateTime(DateTime.now());
        EntityDatabase db = EntityDatabase.DEFAULT;
        db.save(entity, true);
    }

    public List queryMetrics(String name, Integer limit) {
        EntityQueryLambda q = new EntityQueryLambda<>(MetricsEntity.class);
        if (name != null) {
            q.eq(MetricsEntity::getName, name);
        }
        if (limit == null) {
            limit = 20;
        }

        EntityDatabase db = EntityDatabase.DEFAULT;
        return db.findBy(q.orderByDescending(MetricsEntity::getCreateTime).limit(limit));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy