blob: 0ae7b19c2f2a47ce1f69964533fefc3e0e91cfba [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.internal.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.internal.A;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_EXCEPTION_REGISTRY_MAX_SIZE;
/**
* Utility to collect suppressed errors within internal code.
*/
public class IgniteExceptionRegistry {
/** */
public static final int DEFAULT_QUEUE_SIZE = 1000;
/** */
private static final IgniteExceptionRegistry instance = new IgniteExceptionRegistry();
/** */
private int maxSize = IgniteSystemProperties.getInteger(IGNITE_EXCEPTION_REGISTRY_MAX_SIZE, DEFAULT_QUEUE_SIZE);
/** */
private AtomicLong errCnt = new AtomicLong();
/** */
private final ConcurrentLinkedDeque<ExceptionInfo> q = new ConcurrentLinkedDeque<>();
/**
* @return Registry instance.
*/
public static IgniteExceptionRegistry get() {
return instance;
}
/**
*
*/
private IgniteExceptionRegistry() {
// No-op.
}
/**
* Puts exception into queue.
* Thread-safe.
*
* @param msg Message that describe reason why error was suppressed.
* @param e Exception.
*/
public void onException(String msg, Throwable e) {
q.offerFirst(
new ExceptionInfo(
errCnt.incrementAndGet(),
e,
msg,
Thread.currentThread().getId(),
Thread.currentThread().getName(),
U.currentTimeMillis()));
// Remove extra entries.
int delta = q.size() - maxSize;
for (int i = 0; i < delta && q.size() > maxSize; i++)
q.pollLast();
}
/**
* Gets suppressed errors.
*
* @param order Order number to filter errors.
* @return List of exceptions that happened after specified order.
*/
public List<ExceptionInfo> getErrors(long order) {
List<ExceptionInfo> errors = new ArrayList<>();
for (ExceptionInfo error : q) {
if (error.order > order)
errors.add(error);
}
return errors;
}
/**
* Sets max size. Default value {@link #DEFAULT_QUEUE_SIZE}
*
* @param maxSize Max size.
*/
public void setMaxSize(int maxSize) {
A.ensure(maxSize > 0, "Max queue size must be greater than 0.");
this.maxSize = maxSize;
}
/**
* Prints errors.
*
* @param log Logger.
*/
public void printErrors(IgniteLogger log) {
int size = q.size();
Iterator<ExceptionInfo> descIter = q.descendingIterator();
for (int i = 0; i < size && descIter.hasNext(); i++) {
ExceptionInfo error = descIter.next();
U.error(
log,
"Error: " + (i + 1) + U.nl() +
" Time: " + new Date(error.time()) + U.nl() +
" Error: " + error.message() + U.nl() +
" Thread ID: " + error.threadId() + U.nl() +
" Thread name: " + error.threadName(),
error.error()
);
}
}
/**
* Errors count.
*
* @return Errors count.
*/
public long errorCount() {
return errCnt.get();
}
/**
* Detailed info about suppressed error.
*/
@SuppressWarnings("PublicInnerClass")
public static class ExceptionInfo implements Serializable {
/** */
private static final long serialVersionUID = 0L;
/** */
private final long order;
/** */
@GridToStringExclude
private final Throwable error;
/** */
private final long threadId;
/** */
private final String threadName;
/** */
private final long time;
/** */
private String msg;
/**
* Constructor.
*
* @param order Locally unique ID that is atomically incremented for each new error.
* @param error Suppressed error.
* @param msg Message that describe reason why error was suppressed.
* @param threadId Thread ID.
* @param threadName Thread name.
* @param time Occurrence time.
*/
public ExceptionInfo(long order, Throwable error, String msg, long threadId, String threadName, long time) {
this.order = order;
this.error = error;
this.threadId = threadId;
this.threadName = threadName;
this.time = time;
this.msg = msg;
}
/**
* @return Locally unique ID that is atomically incremented for each new error.
*/
public long order() {
return order;
}
/**
* @return Gets message that describe reason why error was suppressed.
*/
public String message() {
return msg;
}
/**
* @return Suppressed error.
*/
public Throwable error() {
return error;
}
/**
* @return Gets thread ID.
*/
public long threadId() {
return threadId;
}
/**
* @return Gets thread name.
*/
public String threadName() {
return threadName;
}
/**
* @return Gets time.
*/
public long time() {
return time;
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(ExceptionInfo.class, this);
}
}
}