com.indeed.status.core.CheckResult Maven / Gradle / Ivy
package com.indeed.status.core;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.google.common.base.Functions;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.primitives.Longs;
import com.indeed.status.core.DependencyChecker.CheckException;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* The {@link CheckResult} represents the outcome of a single dependency check. This
* object is immutable and may be persisted indefinitely to communicate the snapshot
* status of the dependency to interested parties.
*/
@SuppressWarnings({"UnusedDeclaration"}) // Suppress unused declarations since most are there for serialization
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class CheckResult {
/// The final status resulting from the dependency evaluation
@Nonnull
private final CheckStatus status;
/// More detailed description of the dependency, for use by operators reading the report.
@Nonnull
private final String description;
/// More detailed status message describing the final result of the dependency check
@Nonnull
private final String errorMessage;
/// The time this result was generated
@Nullable
private final Date timestamp;
/// The duration of the check
@Nonnegative
private final long duration;
/// The last known time this result was OK'd.
@Nonnegative
private final long lastKnownGoodTimestamp;
/// The periodicity of this result
@Nonnegative
private final long period;
/// The id of the dependency that generated this result;
@Nonnull
private final String id;
/// The urgency of this dependency
@Nonnull
private final Urgency urgency;
/// The documentation URL giving additional info about the result
@Nonnull
private final String documentationUrl;
/// The exception thrown during execution, if any.
@Nonnull
private final DependencyType type;
@Nonnull
private final String servicePool;
@JsonIgnore
private final Throwable throwable;
public static final ThreadLocal DATE_FORMAT = new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
}
};
/**
* @deprecated Use {@link CheckResult.Builder} instead
*/
@Deprecated
public CheckResult(
@Nonnull final Dependency dependency,
@Nonnull final CheckStatus status,
@Nonnull final String errorMessage,
@Nonnegative final long timestamp,
@Nonnegative final long duration,
@Nonnegative final long period,
@Nullable final Throwable t
) {
this(
dependency,
status,
errorMessage,
timestamp,
duration,
0L, /* lastKnownGoodTimestamp */
period,
t
);
}
private CheckResult(
@Nonnull final Dependency dependency,
@Nonnull final CheckStatus status,
@Nonnull final String errorMessage,
@Nonnegative final long timestamp,
@Nonnegative final long duration,
@Nonnegative final long lastKnownGoodTimestamp,
@Nonnegative final long period,
@Nullable final Throwable t
) {
this.id = dependency.getId();
this.status = status;
this.description = dependency.getDescription();
this.errorMessage = errorMessage;
this.documentationUrl = dependency.getDocumentationUrl();
this.urgency = dependency.getUrgency();
this.type = dependency.getType();
this.servicePool = dependency.getServicePool();
this.timestamp = 0 == timestamp ? null : new Date(timestamp);
this.duration = duration;
this.lastKnownGoodTimestamp = lastKnownGoodTimestamp;
this.period = period;
this.throwable = t;
}
@Nonnull
public String getId () {
return id;
}
@Nonnull
public CheckStatus getStatus () {
return status;
}
@Nonnull
public String getDescription () {
return description;
}
@Nonnull
public String getErrorMessage () {
return errorMessage;
}
@Nonnull
public String getDocumentationUrl() {
return documentationUrl;
}
@Nonnegative
public long getDuration () {
return duration;
}
@Nonnegative
public long getLastKnownGoodTimestamp() {
return lastKnownGoodTimestamp;
}
@Nonnegative
public String getUrgency() {
return String.valueOf(urgency);
}
@JsonSerialize(using = ToStringSerializer.class)
@Nonnull
public DependencyType getType() {
return type;
}
@Nonnull
public String getServicePool() {
return servicePool;
}
@Nonnegative
public long getTimestamp() {
return null == timestamp ? 0L : timestamp.getTime();
}
@Nullable
public String getDate () {
return null == timestamp ? null : DATE_FORMAT.get().format(timestamp);
}
@Nonnegative
public long getPeriod () {
return period;
}
@Nullable
public Thrown getThrown() {
return null == throwable ? null : new Thrown(throwable);
}
@JsonIgnore
public Throwable getThrowable() {
return throwable;
}
@Nonnull
public String toString () {
return "{'id':'" + getId() + "';'status':'" + status + "';}";
}
@SuppressWarnings ( { "UnusedDeclaration" })
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public static class Thrown {
private static final int MAX_DEPTH = 20;
private static final int MAX_TRACE = 20;
public Thrown(@Nonnull final Throwable throwable) {
this(throwable instanceof CheckException ? throwable.getCause() : throwable, new HashSet(), 0);
}
private Thrown(@Nonnull final Throwable throwable, final Set seen, final int depth) {
this.message = throwable.getMessage();
this.exception = throwable.getClass().getSimpleName();
final StackTraceElement[] trace = throwable.getStackTrace();
if (null != trace) {
this.stack = trace.length > MAX_TRACE ?
Lists.newArrayList(Arrays.asList(trace).subList(0, MAX_TRACE)) :
Lists.newArrayList(trace);
}
if (depth <= MAX_DEPTH) {
final Throwable cause = throwable.getCause();
if (null != cause && !seen.contains(cause)) {
seen.add(cause);
this.thrown = new Thrown(cause, seen, depth+1);
} else {
this.thrown = null;
}
} else {
this.thrown = null;
}
}
public String getException () {
return exception;
}
public String getMessage () {
return message;
}
public List getStack () {
// TODO - ketan's right that collapsing the rich data on the stack trace isn't right, but
// as long it's mostly humans reading the output, the terser format is more usable. We
// could bridge the gap with a nondefault pretty-printer if we wanted to, but for now
// let's stick with the well-recognized format.
return Lists.transform(this.stack, Functions.toStringFunction());
}
public Thrown getThrown() {
return thrown;
}
private final String exception;
private final String message;
private final Thrown thrown;
private volatile List stack;
}
@Nonnull
public static Builder newBuilder(
@Nonnull final Dependency dependency,
@Nonnull final CheckStatus status,
@Nonnull final String errorMessage
) {
return new Builder(dependency, status, errorMessage);
}
@Nonnull
public static Builder newBuilder(@Nonnull final Dependency dependency, @Nonnull final CheckResult source) {
return newBuilder(dependency, source.getStatus(), source.getErrorMessage())
.setTimestamp(source.getTimestamp())
.setDuration(source.getDuration())
.setLastKnownGoodTimestamp(source.getLastKnownGoodTimestamp())
.setPeriod(source.getPeriod())
.setThrowable(source.getThrowable());
}
public static class Builder {
@Nonnull
private Dependency dependency;
@Nonnull
private CheckStatus status;
@Nonnull
private String errorMessage;
@Nonnegative
private long timestamp = 0L;
@Nonnegative
private long duration = 0L;
@Nonnegative
private long lastKnownGoodTimestamp = 0L;
@Nonnegative
private long period = 0L;
@Nullable
private Throwable t;
private Builder(
@Nonnull final Dependency dependency,
@Nonnull final CheckStatus status,
@Nonnull final String errorMessage
) {
this.dependency = dependency;
this.status = status;
this.errorMessage = errorMessage;
}
public Builder setDependency(@Nonnull final Dependency dependency) {
this.dependency = Preconditions.checkNotNull(dependency, "Missing dependency reference");
return this;
}
public Builder setStatus(@Nonnull final CheckStatus status) {
this.status = Preconditions.checkNotNull(status, "Missing status");
return this;
}
public Builder setErrorMessage(@Nonnull final String errorMessage) {
this.errorMessage = Preconditions.checkNotNull(errorMessage, "Missing error message");
return this;
}
public Builder setTimestamp(@Nonnegative final long timestamp) {
this.timestamp = Longs.max(0, timestamp);
return this;
}
public Builder setDuration(@Nonnegative final long duration) {
this.duration = Longs.max(0, duration);
return this;
}
public Builder setLastKnownGoodTimestamp(@Nonnegative final long lastKnownGoodTimestamp) {
this.lastKnownGoodTimestamp = Longs.max(0, lastKnownGoodTimestamp);
return this;
}
public Builder setPeriod(@Nonnegative final long period) {
this.period = Longs.max(0, period);
return this;
}
public Builder setThrowable(@Nullable final Throwable t) {
this.t = t;
return this;
}
public CheckResult build() {
return new CheckResult(
dependency,
status,
errorMessage,
timestamp,
duration,
lastKnownGoodTimestamp,
period,
t);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy