Add initial OpenCensus API.

This commit is contained in:
Bogdan Drutu 2019-03-19 19:14:39 -07:00
parent 505ef89acf
commit 9cb3ad5eb7
130 changed files with 16072 additions and 18 deletions

View File

@ -1,5 +1,6 @@
# OpenConsensus API
OpenCensus API
======================================================
* Java 7 and Android 14 compatible.
* Java 6 and Android compatible.
* The abstract classes in this directory can be subclassed to create alternative
implementations of the OpenCensus library.

View File

@ -1,8 +1,15 @@
description = 'OpenConsensus API'
description = 'OpenCensus API'
dependencies {
compile project(':openconsensus-context')
compile libraries.grpc_context
compileOnly libraries.auto_value
signature "org.codehaus.mojo.signature:java17:1.0@signature"
signature "net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature"
}
javadoc {
exclude 'io/opencensus/internal/**'
exclude 'io/opencensus/trace/internal/**'
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
/**
* Interface for getting the current time.
*
* @since 0.1.0
*/
public abstract class Clock {
/**
* Obtains the current instant from this clock.
*
* @return the current instant.
* @since 0.1.0
*/
public abstract Timestamp now();
/**
* Returns a time measurement with nanosecond precision that can only be used to calculate elapsed
* time.
*
* @return a time measurement with nanosecond precision that can only be used to calculate elapsed
* time.
* @since 0.1.0
*/
public abstract long nowNanos();
}

View File

@ -0,0 +1,136 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
import static openconsensus.common.TimeUtils.MAX_NANOS;
import static openconsensus.common.TimeUtils.MAX_SECONDS;
import static openconsensus.common.TimeUtils.MILLIS_PER_SECOND;
import static openconsensus.common.TimeUtils.NANOS_PER_MILLI;
import com.google.auto.value.AutoValue;
import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.Immutable;
/**
* Represents a signed, fixed-length span of time represented as a count of seconds and fractions of
* seconds at nanosecond resolution. It is independent of any calendar and concepts like "day" or
* "month". Range is approximately +-10,000 years.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class Duration implements Comparable<Duration> {
/**
* Creates a new time duration from given seconds and nanoseconds.
*
* @param seconds Signed seconds of the span of time. Must be from -315,576,000,000 to
* +315,576,000,000 inclusive.
* @param nanos Signed fractions of a second at nanosecond resolution of the span of time.
* Durations less than one second are represented with a 0 `seconds` field and a positive or
* negative `nanos` field. For durations of one second or more, a non-zero value for the
* `nanos` field must be of the same sign as the `seconds` field. Must be from -999,999,999 to
* +999,999,999 inclusive.
* @return new {@code Duration} with specified fields.
* @throws IllegalArgumentException if the arguments are out of range or have inconsistent sign.
* @since 0.1.0
*/
public static Duration create(long seconds, int nanos) {
if (seconds < -MAX_SECONDS) {
throw new IllegalArgumentException(
"'seconds' is less than minimum (" + -MAX_SECONDS + "): " + seconds);
}
if (seconds > MAX_SECONDS) {
throw new IllegalArgumentException(
"'seconds' is greater than maximum (" + MAX_SECONDS + "): " + seconds);
}
if (nanos < -MAX_NANOS) {
throw new IllegalArgumentException(
"'nanos' is less than minimum (" + -MAX_NANOS + "): " + nanos);
}
if (nanos > MAX_NANOS) {
throw new IllegalArgumentException(
"'nanos' is greater than maximum (" + MAX_NANOS + "): " + nanos);
}
if ((seconds < 0 && nanos > 0) || (seconds > 0 && nanos < 0)) {
throw new IllegalArgumentException(
"'seconds' and 'nanos' have inconsistent sign: seconds=" + seconds + ", nanos=" + nanos);
}
return new AutoValue_Duration(seconds, nanos);
}
/**
* Creates a new {@code Duration} from given milliseconds.
*
* @param millis the duration in milliseconds.
* @return a new {@code Duration} from given milliseconds.
* @throws IllegalArgumentException if the number of milliseconds is out of the range that can be
* represented by {@code Duration}.
* @since 0.1.0
*/
public static Duration fromMillis(long millis) {
long seconds = millis / MILLIS_PER_SECOND;
int nanos = (int) (millis % MILLIS_PER_SECOND * NANOS_PER_MILLI);
return Duration.create(seconds, nanos);
}
/**
* Converts a {@link Duration} to milliseconds.
*
* @return the milliseconds representation of this {@code Duration}.
* @since 0.1.0
*/
public long toMillis() {
return TimeUnit.SECONDS.toMillis(getSeconds()) + TimeUnit.NANOSECONDS.toMillis(getNanos());
}
/**
* Returns the number of seconds in the {@code Duration}.
*
* @return the number of seconds in the {@code Duration}.
* @since 0.1.0
*/
public abstract long getSeconds();
/**
* Returns the number of nanoseconds in the {@code Duration}.
*
* @return the number of nanoseconds in the {@code Duration}.
* @since 0.1.0
*/
public abstract int getNanos();
/**
* Compares this {@code Duration} to the specified {@code Duration}.
*
* @param otherDuration the other {@code Duration} to compare to, not {@code null}.
* @return the comparator value: zero if equal, negative if this duration is smaller than
* otherDuration, positive if larger.
* @throws NullPointerException if otherDuration is {@code null}.
*/
@Override
public int compareTo(Duration otherDuration) {
int cmp = TimeUtils.compareLongs(getSeconds(), otherDuration.getSeconds());
if (cmp != 0) {
return cmp;
}
return TimeUtils.compareLongs(getNanos(), otherDuration.getNanos());
}
Duration() {}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
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;
/**
* Indicates a public API that can change at any time, and has no guarantee of API stability and
* backward-compatibility.
*
* <p>Usage guidelines:
*
* <ol>
* <li>This annotation is used only on public API. Internal interfaces should not use it.
* <li>After OpenCensus has gained API stability, this annotation can only be added to new API.
* Adding it to an existing API is considered API-breaking.
* <li>Removing this annotation from an API gives it stable status.
* </ol>
*
* @since 0.1.0
*/
@Internal
@Retention(RetentionPolicy.SOURCE)
@Target({
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.FIELD,
ElementType.METHOD,
ElementType.PACKAGE,
ElementType.TYPE
})
@Documented
public @interface ExperimentalApi {
/**
* Context information such as links to discussion thread, tracking issue etc.
*
* @since 0.1.0
*/
String value() default "";
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
import openconsensus.trace.AttributeValue;
/**
* Used to specify matching functions for use encoding tagged unions (i.e. sum types) in Java. See
* {@link AttributeValue#match} for an example of its use.
*
* <p>Note: This class is based on the java.util.Function class added in Java 1.8. We cannot use the
* Function from Java 1.8 because this library is Java 1.6 compatible.
*
* @since 0.1.0
*/
public interface Function<A, B> {
/**
* Applies the function to the given argument.
*
* @param arg the argument to the function.
* @return the result of the function.
* @since 0.1.0
*/
B apply(A arg);
}

View File

@ -0,0 +1,130 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
/**
* Commonly used {@link Function} instances.
*
* @since 0.1.0
*/
public final class Functions {
private Functions() {}
private static final Function<Object, /*@Nullable*/ Void> RETURN_NULL =
new Function<Object, /*@Nullable*/ Void>() {
@Override
@javax.annotation.Nullable
public Void apply(Object ignored) {
return null;
}
};
private static final Function<Object, Void> THROW_ILLEGAL_ARGUMENT_EXCEPTION =
new Function<Object, Void>() {
@Override
public Void apply(Object ignored) {
throw new IllegalArgumentException();
}
};
private static final Function<Object, Void> THROW_ASSERTION_ERROR =
new Function<Object, Void>() {
@Override
public Void apply(Object ignored) {
throw new AssertionError();
}
};
private static final Function<Object, /*@Nullable*/ String> RETURN_TO_STRING =
new Function<Object, /*@Nullable*/ String>() {
@Override
public /*@Nullable*/ String apply(Object input) {
return input == null ? null : input.toString();
}
};
/**
* A {@code Function} that always ignores its argument and returns {@code null}.
*
* @return a {@code Function} that always ignores its argument and returns {@code null}.
* @since 0.1.0
*/
public static <T> Function<Object, /*@Nullable*/ T> returnNull() {
// It is safe to cast a producer of Void to anything, because Void is always null.
@SuppressWarnings("unchecked")
Function<Object, /*@Nullable*/ T> function = (Function<Object, /*@Nullable*/ T>) RETURN_NULL;
return function;
}
/**
* A {@code Function} that always ignores its argument and returns a constant value.
*
* @return a {@code Function} that always ignores its argument and returns a constant value.
* @since 0.1.0
*/
public static <T> Function<Object, T> returnConstant(final T constant) {
return new Function<Object, T>() {
@Override
public T apply(Object ignored) {
return constant;
}
};
}
/**
* A {@code Function} that always returns the {@link #toString()} value of the input.
*
* @return a {@code Function} that always returns the {@link #toString()} value of the input.
* @since 0.1.0
*/
public static Function<Object, /*@Nullable*/ String> returnToString() {
return RETURN_TO_STRING;
}
/**
* A {@code Function} that always ignores its argument and throws an {@link
* IllegalArgumentException}.
*
* @return a {@code Function} that always ignores its argument and throws an {@link
* IllegalArgumentException}.
* @since 0.1.0
*/
public static <T> Function<Object, T> throwIllegalArgumentException() {
// It is safe to cast this function to have any return type, since it never returns a result.
@SuppressWarnings("unchecked")
Function<Object, T> function = (Function<Object, T>) THROW_ILLEGAL_ARGUMENT_EXCEPTION;
return function;
}
/**
* A {@code Function} that always ignores its argument and throws an {@link AssertionError}.
*
* @return a {@code Function} that always ignores its argument and throws an {@code
* AssertionError}.
* @since 0.1.0
*/
public static <T> Function<Object, T> throwAssertionError() {
// It is safe to cast this function to have any return type, since it never returns a result.
@SuppressWarnings("unchecked")
Function<Object, T> function = (Function<Object, T>) THROW_ASSERTION_ERROR;
return function;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
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;
/**
* Annotates a program element (class, method, package etc) which is internal to OpenCensus, not
* part of the public API, and should not be used by users of the OpenCensus library.
*
* @since 0.1.0
*/
@Internal
@Retention(RetentionPolicy.SOURCE)
@Target({
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.FIELD,
ElementType.METHOD,
ElementType.PACKAGE,
ElementType.TYPE
})
@Documented
public @interface Internal {}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
import java.io.Closeable;
/**
* An {@link Closeable} which cannot throw a checked exception.
*
* <p>This is useful because such a reversion otherwise requires the caller to catch the
* (impossible) Exception in the try-with-resources.
*
* <p>Example of usage:
*
* <pre>
* try (NonThrowingAutoCloseable ctx = tryEnter()) {
* ...
* }
* </pre>
*
* @deprecated {@link Scope} is a better match for operations involving the current context.
* @since 0.1.0
*/
@Deprecated
public interface NonThrowingCloseable extends Closeable {
@Override
void close();
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
/**
* Class holder for all common constants (such as the version) for the OpenCensus Java library.
*
* @since 0.1.0
*/
@ExperimentalApi
public final class OpenCensusLibraryInformation {
/**
* The current version of the OpenCensus Java library.
*
* @since 0.1.0
*/
public static final String VERSION = "0.20.0-SNAPSHOT"; // CURRENT_OPENCENSUS_VERSION
private OpenCensusLibraryInformation() {}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
/**
* A {@link java.io.Closeable} that represents a change to the current context over a scope of code.
* {@link Scope#close} cannot throw a checked exception.
*
* <p>Example of usage:
*
* <pre>
* try (Scope ctx = tryEnter()) {
* ...
* }
* </pre>
*
* @since 0.1.0
*/
@SuppressWarnings("deprecation")
public interface Scope extends NonThrowingCloseable {
@Override
void close();
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
import com.google.auto.value.AutoValue;
import javax.annotation.concurrent.Immutable;
/**
* A representation of stats measured on the server side.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class ServerStats {
ServerStats() {}
/**
* Returns Load Balancer latency, a latency observed at Load Balancer.
*
* @return Load Balancer latency in nanoseconds.
* @since 0.1.0
*/
public abstract long getLbLatencyNs();
/**
* Returns Service latency, a latency observed at Server.
*
* @return Service latency in nanoseconds.
* @since 0.1.0
*/
public abstract long getServiceLatencyNs();
/**
* Returns Trace options, a set of bits indicating properties of trace.
*
* @return Trace options a set of bits indicating properties of trace.
* @since 0.1.0
*/
public abstract byte getTraceOption();
/**
* Creates new {@link ServerStats} from specified parameters.
*
* @param lbLatencyNs Represents request processing latency observed on Load Balancer. It is
* measured in nanoseconds. Must not be less than 0. Value of 0 represents that the latency is
* not measured.
* @param serviceLatencyNs Represents request processing latency observed on Server. It is
* measured in nanoseconds. Must not be less than 0. Value of 0 represents that the latency is
* not measured.
* @param traceOption Represents set of bits to indicate properties of trace. Currently it used
* only the least signification bit to represent sampling of the request on the server side.
* Other bits are ignored.
* @return new {@code ServerStats} with specified fields.
* @throws IllegalArgumentException if the arguments are out of range.
* @since 0.1.0
*/
public static ServerStats create(long lbLatencyNs, long serviceLatencyNs, byte traceOption) {
if (lbLatencyNs < 0) {
throw new IllegalArgumentException("'getLbLatencyNs' is less than zero: " + lbLatencyNs);
}
if (serviceLatencyNs < 0) {
throw new IllegalArgumentException(
"'getServiceLatencyNs' is less than zero: " + serviceLatencyNs);
}
return new AutoValue_ServerStats(lbLatencyNs, serviceLatencyNs, traceOption);
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
/**
* Exception thrown when a {@link ServerStats} cannot be parsed.
*
* @since 0.1.0
*/
public final class ServerStatsDeserializationException extends Exception {
private static final long serialVersionUID = 0L;
/**
* Constructs a new {@code ServerStatsDeserializationException} with the given message.
*
* @param message a message describing the error.
* @since 0.1.0
*/
public ServerStatsDeserializationException(String message) {
super(message);
}
/**
* Constructs a new {@code ServerStatsDeserializationException} with the given message and cause.
*
* @param message a message describing the error.
* @param cause the cause of the error.
* @since 0.1.0
*/
public ServerStatsDeserializationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* A service class to encode/decode {@link ServerStats} as defined by the spec.
*
* <p>See <a
* href="https://github.com/census-instrumentation/opencensus-specs/blob/master/encodings/CensusServerStatsEncoding.md">opencensus-server-stats-specs</a>
* for encoding {@code ServerStats}
*
* <p>Use {@code ServerStatsEncoding.toBytes(ServerStats stats)} to encode.
*
* <p>Use {@code ServerStatsEncoding.parseBytes(byte[] serialized)} to decode.
*
* @since 0.1.0
*/
public final class ServerStatsEncoding {
private ServerStatsEncoding() {}
/**
* The current encoding version. The value is {@value #CURRENT_VERSION}
*
* @since 0.1.0
*/
public static final byte CURRENT_VERSION = (byte) 0;
/**
* Encodes the {@link ServerStats} as per the Opencensus Summary Span specification.
*
* @param stats {@code ServerStats} to encode.
* @return encoded byte array.
* @since 0.1.0
*/
public static byte[] toBytes(ServerStats stats) {
// Should this be optimized to not include invalid values?
ByteBuffer bb = ByteBuffer.allocate(ServerStatsFieldEnums.getTotalSize() + 1);
bb.order(ByteOrder.LITTLE_ENDIAN);
// put version
bb.put(CURRENT_VERSION);
bb.put((byte) ServerStatsFieldEnums.Id.SERVER_STATS_LB_LATENCY_ID.value());
bb.putLong(stats.getLbLatencyNs());
bb.put((byte) ServerStatsFieldEnums.Id.SERVER_STATS_SERVICE_LATENCY_ID.value());
bb.putLong(stats.getServiceLatencyNs());
bb.put((byte) ServerStatsFieldEnums.Id.SERVER_STATS_TRACE_OPTION_ID.value());
bb.put(stats.getTraceOption());
return bb.array();
}
/**
* Decodes serialized byte array to create {@link ServerStats} as per Opencensus Summary Span
* specification.
*
* @param serialized encoded {@code ServerStats} in byte array.
* @return decoded {@code ServerStats}. null if decoding fails.
* @since 0.1.0
*/
public static ServerStats parseBytes(byte[] serialized)
throws ServerStatsDeserializationException {
final ByteBuffer bb = ByteBuffer.wrap(serialized);
bb.order(ByteOrder.LITTLE_ENDIAN);
long serviceLatencyNs = 0L;
long lbLatencyNs = 0L;
byte traceOption = (byte) 0;
// Check the version first.
if (!bb.hasRemaining()) {
throw new ServerStatsDeserializationException("Serialized ServerStats buffer is empty");
}
byte version = bb.get();
if (version > CURRENT_VERSION || version < 0) {
throw new ServerStatsDeserializationException("Invalid ServerStats version: " + version);
}
while (bb.hasRemaining()) {
ServerStatsFieldEnums.Id id = ServerStatsFieldEnums.Id.valueOf((int) bb.get() & 0xFF);
if (id == null) {
// Skip remaining;
bb.position(bb.limit());
} else {
switch (id) {
case SERVER_STATS_LB_LATENCY_ID:
lbLatencyNs = bb.getLong();
break;
case SERVER_STATS_SERVICE_LATENCY_ID:
serviceLatencyNs = bb.getLong();
break;
case SERVER_STATS_TRACE_OPTION_ID:
traceOption = bb.get();
break;
}
}
}
try {
return ServerStats.create(lbLatencyNs, serviceLatencyNs, traceOption);
} catch (IllegalArgumentException e) {
throw new ServerStatsDeserializationException(
"Serialized ServiceStats contains invalid values: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,159 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
import java.util.TreeMap;
import javax.annotation.Nullable;
/**
* A Enum representation for Ids and Size for attributes of {@code ServerStats}.
*
* <p>See <a
* href="https://github.com/census-instrumentation/opencensus-specs/blob/master/encodings/CensusServerStatsEncoding.md">opencensus-server-stats-specs</a>
* for the field ids and their length defined for Server Stats
*
* @since 0.1.0
*/
public final class ServerStatsFieldEnums {
/**
* Available Ids for {@code ServerStats} attributes.
*
* @since 0.1.0
*/
public enum Id {
/**
* Id for Latency observed at Load Balancer.
*
* @since 0.1.0
*/
SERVER_STATS_LB_LATENCY_ID(0),
/**
* Id for Latency observed at Server.
*
* @since 0.1.0
*/
SERVER_STATS_SERVICE_LATENCY_ID(1),
/**
* Id for Trace options.
*
* @since 0.1.0
*/
SERVER_STATS_TRACE_OPTION_ID(2);
private final int value;
private Id(int value) {
this.value = value;
}
/**
* Returns the numerical value of the {@link Id}.
*
* @return the numerical value of the {@code Id}.
* @since 0.1.0
*/
public int value() {
return value;
}
private static final TreeMap<Integer, Id> map = new TreeMap<Integer, Id>();
static {
for (Id id : Id.values()) {
map.put(id.value, id);
}
}
/**
* Returns the {@link Id} representing the value value of the id.
*
* @param value integer value for which {@code Id} is being requested.
* @return the numerical value of the id. null if the id is not valid
* @since 0.1.0
*/
@Nullable
public static Id valueOf(int value) {
return map.get(value);
}
}
/**
* Size for each attributes in {@code ServerStats}.
*
* @since 0.1.0
*/
public enum Size {
/**
* Number of bytes used to represent latency observed at Load Balancer.
*
* @since 0.1.0
*/
SERVER_STATS_LB_LATENCY_SIZE(8),
/**
* Number of bytes used to represent latency observed at Server.
*
* @since 0.1.0
*/
SERVER_STATS_SERVICE_LATENCY_SIZE(8),
/**
* Number of bytes used to represent Trace option.
*
* @since 0.1.0
*/
SERVER_STATS_TRACE_OPTION_SIZE(1);
private final int value;
private Size(int value) {
this.value = value;
}
/**
* Returns the numerical value of the {@link Size}.
*
* @return the numerical value of the {@code Size}.
* @since 0.1.0
*/
public int value() {
return value;
}
}
private static final int TOTALSIZE = computeTotalSize();
private ServerStatsFieldEnums() {}
private static int computeTotalSize() {
int sum = 0;
for (Size sizeValue : Size.values()) {
sum += sizeValue.value();
sum += 1; // For Id
}
return sum;
}
/**
* Returns the total size required to encode the {@code ServerStats}.
*
* @return the total size required to encode all fields in {@code ServerStats}.
* @since 0.1.0
*/
public static int getTotalSize() {
return TOTALSIZE;
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
import java.math.BigInteger;
/** Util class for {@link Timestamp} and {@link Duration}. */
final class TimeUtils {
static final long MAX_SECONDS = 315576000000L;
static final int MAX_NANOS = 999999999;
static final long MILLIS_PER_SECOND = 1000L;
static final long NANOS_PER_MILLI = 1000 * 1000;
static final long NANOS_PER_SECOND = NANOS_PER_MILLI * MILLIS_PER_SECOND;
private TimeUtils() {}
/**
* Compares two longs. This functionality is provided by {@code Long.compare(long, long)} in Java
* 7.
*/
static int compareLongs(long x, long y) {
if (x < y) {
return -1;
} else if (x == y) {
return 0;
} else {
return 1;
}
}
private static final BigInteger MAX_LONG_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
private static final BigInteger MIN_LONG_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
/**
* Adds two longs and throws an {@link ArithmeticException} if the result overflows. This
* functionality is provided by {@code Math.addExact(long, long)} in Java 8.
*/
static long checkedAdd(long x, long y) {
BigInteger sum = BigInteger.valueOf(x).add(BigInteger.valueOf(y));
if (sum.compareTo(MAX_LONG_VALUE) > 0 || sum.compareTo(MIN_LONG_VALUE) < 0) {
throw new ArithmeticException("Long sum overflow: x=" + x + ", y=" + y);
}
return x + y;
}
}

View File

@ -0,0 +1,200 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
import static openconsensus.common.TimeUtils.MAX_NANOS;
import static openconsensus.common.TimeUtils.MAX_SECONDS;
import static openconsensus.common.TimeUtils.MILLIS_PER_SECOND;
import static openconsensus.common.TimeUtils.NANOS_PER_MILLI;
import static openconsensus.common.TimeUtils.NANOS_PER_SECOND;
import com.google.auto.value.AutoValue;
import java.math.BigDecimal;
import java.math.RoundingMode;
import javax.annotation.concurrent.Immutable;
/**
* A representation of an instant in time. The instant is the number of nanoseconds after the number
* of seconds since the Unix Epoch.
*
* <p>Use {@code Tracing.getClock().now()} to get the current timestamp since epoch
* (1970-01-01T00:00:00Z).
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class Timestamp implements Comparable<Timestamp> {
Timestamp() {}
/**
* Creates a new timestamp from given seconds and nanoseconds.
*
* @param seconds Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must be
* from from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.
* @param nanos Non-negative fractions of a second at nanosecond resolution. Negative second
* values with fractions must still have non-negative nanos values that count forward in time.
* Must be from 0 to 999,999,999 inclusive.
* @return new {@code Timestamp} with specified fields.
* @throws IllegalArgumentException if the arguments are out of range.
* @since 0.1.0
*/
public static Timestamp create(long seconds, int nanos) {
if (seconds < -MAX_SECONDS) {
throw new IllegalArgumentException(
"'seconds' is less than minimum (" + -MAX_SECONDS + "): " + seconds);
}
if (seconds > MAX_SECONDS) {
throw new IllegalArgumentException(
"'seconds' is greater than maximum (" + MAX_SECONDS + "): " + seconds);
}
if (nanos < 0) {
throw new IllegalArgumentException("'nanos' is less than zero: " + nanos);
}
if (nanos > MAX_NANOS) {
throw new IllegalArgumentException(
"'nanos' is greater than maximum (" + MAX_NANOS + "): " + nanos);
}
return new AutoValue_Timestamp(seconds, nanos);
}
/**
* Creates a new timestamp from the given milliseconds.
*
* @param epochMilli the timestamp represented in milliseconds since epoch.
* @return new {@code Timestamp} with specified fields.
* @throws IllegalArgumentException if the number of milliseconds is out of the range that can be
* represented by {@code Timestamp}.
* @since 0.1.0
*/
public static Timestamp fromMillis(long epochMilli) {
long secs = floorDiv(epochMilli, MILLIS_PER_SECOND);
int mos = (int) floorMod(epochMilli, MILLIS_PER_SECOND);
return create(secs, (int) (mos * NANOS_PER_MILLI)); // Safe int * NANOS_PER_MILLI
}
/**
* Returns the number of seconds since the Unix Epoch represented by this timestamp.
*
* @return the number of seconds since the Unix Epoch.
* @since 0.1.0
*/
public abstract long getSeconds();
/**
* Returns the number of nanoseconds after the number of seconds since the Unix Epoch represented
* by this timestamp.
*
* @return the number of nanoseconds after the number of seconds since the Unix Epoch.
* @since 0.1.0
*/
public abstract int getNanos();
/**
* Returns a {@code Timestamp} calculated as this {@code Timestamp} plus some number of
* nanoseconds.
*
* @param nanosToAdd the nanos to add, positive or negative.
* @return the calculated {@code Timestamp}. For invalid inputs, a {@code Timestamp} of zero is
* returned.
* @throws ArithmeticException if numeric overflow occurs.
* @since 0.1.0
*/
public Timestamp addNanos(long nanosToAdd) {
return plus(0, nanosToAdd);
}
/**
* Returns a {@code Timestamp} calculated as this {@code Timestamp} plus some {@code Duration}.
*
* @param duration the {@code Duration} to add.
* @return a {@code Timestamp} with the specified {@code Duration} added.
* @since 0.1.0
*/
public Timestamp addDuration(Duration duration) {
return plus(duration.getSeconds(), duration.getNanos());
}
/**
* Returns a {@link Duration} calculated as: {@code this - timestamp}.
*
* @param timestamp the {@code Timestamp} to subtract.
* @return the calculated {@code Duration}. For invalid inputs, a {@code Duration} of zero is
* returned.
* @since 0.1.0
*/
public Duration subtractTimestamp(Timestamp timestamp) {
long durationSeconds = getSeconds() - timestamp.getSeconds();
int durationNanos = getNanos() - timestamp.getNanos();
if (durationSeconds < 0 && durationNanos > 0) {
durationSeconds += 1;
durationNanos = (int) (durationNanos - NANOS_PER_SECOND);
} else if (durationSeconds > 0 && durationNanos < 0) {
durationSeconds -= 1;
durationNanos = (int) (durationNanos + NANOS_PER_SECOND);
}
return Duration.create(durationSeconds, durationNanos);
}
/**
* Compares this {@code Timestamp} to the specified {@code Timestamp}.
*
* @param otherTimestamp the other {@code Timestamp} to compare to, not {@code null}.
* @return the comparator value: zero if equal, negative if this timestamp happens before
* otherTimestamp, positive if after.
* @throws NullPointerException if otherTimestamp is {@code null}.
*/
@Override
public int compareTo(Timestamp otherTimestamp) {
int cmp = TimeUtils.compareLongs(getSeconds(), otherTimestamp.getSeconds());
if (cmp != 0) {
return cmp;
}
return TimeUtils.compareLongs(getNanos(), otherTimestamp.getNanos());
}
// Returns a Timestamp with the specified duration added.
private Timestamp plus(long secondsToAdd, long nanosToAdd) {
if ((secondsToAdd | nanosToAdd) == 0) {
return this;
}
long epochSec = TimeUtils.checkedAdd(getSeconds(), secondsToAdd);
epochSec = TimeUtils.checkedAdd(epochSec, nanosToAdd / NANOS_PER_SECOND);
nanosToAdd = nanosToAdd % NANOS_PER_SECOND;
long nanoAdjustment = getNanos() + nanosToAdd; // safe int + NANOS_PER_SECOND
return ofEpochSecond(epochSec, nanoAdjustment);
}
// Returns a Timestamp calculated using seconds from the epoch and nanosecond fraction of
// second (arbitrary number of nanoseconds).
private static Timestamp ofEpochSecond(long epochSecond, long nanoAdjustment) {
long secs = TimeUtils.checkedAdd(epochSecond, floorDiv(nanoAdjustment, NANOS_PER_SECOND));
int nos = (int) floorMod(nanoAdjustment, NANOS_PER_SECOND);
return create(secs, nos);
}
// Returns the result of dividing x by y rounded using floor.
private static long floorDiv(long x, long y) {
return BigDecimal.valueOf(x).divide(BigDecimal.valueOf(y), 0, RoundingMode.FLOOR).longValue();
}
// Returns the floor modulus "x - (floorDiv(x, y) * y)"
private static long floorMod(long x, long y) {
return x - floorDiv(x, y) * y;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
import openconsensus.metrics.MetricRegistry;
/**
* Represents a function that produces a double-valued result. See {@link
* MetricRegistry} for an example of its use.
*
* <p>Note: This class is based on the java.util.ToDoubleFunction class added in Java 1.8. We cannot
* use the Function from Java 1.8 because this library is Java 1.6 compatible.
*
* @since 0.1.0
*/
public interface ToDoubleFunction</*@Nullable*/ T> {
/**
* Applies this function to the given argument.
*
* @param value the function argument.
* @return the function result.
*/
double applyAsDouble(/*@Nullable*/ T value);
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.common;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
import openconsensus.metrics.MetricRegistry;
/**
* Represents a function that produces a long-valued result. See {@link
* MetricRegistry} for an example of its use.
*
* <p>Note: This class is based on the java.util.ToLongFunction class added in Java 1.8. We cannot
* use the Function from Java 1.8 because this library is Java 1.6 compatible.
*
* @since 0.1.0
*/
public interface ToLongFunction</*@Nullable*/ T> {
/**
* Applies this function to the given argument.
*
* @param value the function argument.
* @return the function result.
*/
long applyAsLong(/*@Nullable*/ T value);
}

View File

@ -0,0 +1,18 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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.
*/
/** Common API between different packages in this artifact. */
package openconsensus.common;

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.internal;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that an element is package-private instead of private only for the purpose of testing.
* This annotation is only meant to be used as documentation in the source code.
*/
@Retention(RetentionPolicy.SOURCE)
@Target({
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.FIELD,
ElementType.METHOD,
ElementType.PACKAGE,
ElementType.TYPE
})
public @interface DefaultVisibilityForTesting {}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.internal;
import openconsensus.common.Scope;
/** A {@link Scope} that does nothing when it is created or closed. */
public final class NoopScope implements Scope {
private static final Scope INSTANCE = new NoopScope();
private NoopScope() {}
/**
* Returns a {@code NoopScope}.
*
* @return a {@code NoopScope}.
*/
public static Scope getInstance() {
return INSTANCE;
}
@Override
public void close() {}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.internal;
import java.util.ServiceConfigurationError;
/**
* OpenCensus service provider mechanism.
*
* <pre>{@code
* // Initialize a variable using reflection.
* foo = Provider.createInstance(
* Class.forName("FooImpl", true, classLoader), Foo.class);
* }</pre>
*/
public final class Provider {
private Provider() {}
/**
* Tries to create an instance of the given rawClass as a subclass of the given superclass.
*
* @param rawClass The class that is initialized.
* @param superclass The initialized class must be a subclass of this.
* @return an instance of the class given rawClass which is a subclass of the given superclass.
* @throws ServiceConfigurationError if any error happens.
*/
public static <T> T createInstance(Class<?> rawClass, Class<T> superclass) {
try {
return rawClass.asSubclass(superclass).getConstructor().newInstance();
} catch (Exception e) {
throw new ServiceConfigurationError(
"Provider " + rawClass.getName() + " could not be instantiated.", e);
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.internal;
/** Internal utility methods for working with tag keys, tag values, and metric names. */
public final class StringUtils {
/**
* Determines whether the {@code String} contains only printable characters.
*
* @param str the {@code String} to be validated.
* @return whether the {@code String} contains only printable characters.
*/
public static boolean isPrintableString(String str) {
for (int i = 0; i < str.length(); i++) {
if (!isPrintableChar(str.charAt(i))) {
return false;
}
}
return true;
}
private static boolean isPrintableChar(char ch) {
return ch >= ' ' && ch <= '~';
}
private StringUtils() {}
}

View File

@ -0,0 +1,191 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.internal;
import java.util.List;
/*>>>
import org.checkerframework.checker.nullness.qual.NonNull;
*/
/** General internal utility methods. */
public final class Utils {
private Utils() {}
/**
* Throws an {@link IllegalArgumentException} if the argument is false. This method is similar to
* {@code Preconditions.checkArgument(boolean, Object)} from Guava.
*
* @param isValid whether the argument check passed.
* @param errorMessage the message to use for the exception. Will be converted to a string using
* {@link String#valueOf(Object)}.
*/
public static void checkArgument(
boolean isValid, @javax.annotation.Nullable Object errorMessage) {
if (!isValid) {
throw new IllegalArgumentException(String.valueOf(errorMessage));
}
}
/**
* Throws an {@link IllegalArgumentException} if the argument is false. This method is similar to
* {@code Preconditions.checkArgument(boolean, Object)} from Guava.
*
* @param expression a boolean expression
* @param errorMessageTemplate a template for the exception message should the check fail. The
* message is formed by replacing each {@code %s} placeholder in the template with an
* argument. These are matched by position - the first {@code %s} gets {@code
* errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in
* square braces. Unmatched placeholders will be left as-is.
* @param errorMessageArgs the arguments to be substituted into the message template. Arguments
* are converted to strings using {@link String#valueOf(Object)}.
* @throws IllegalArgumentException if {@code expression} is false
* @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or
* {@code errorMessageArgs} is null (don't let this happen)
*/
public static void checkArgument(
boolean expression,
String errorMessageTemplate,
@javax.annotation.Nullable Object... errorMessageArgs) {
if (!expression) {
throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs));
}
}
/**
* Throws an {@link IllegalStateException} if the argument is false. This method is similar to
* {@code Preconditions.checkState(boolean, Object)} from Guava.
*
* @param isValid whether the state check passed.
* @param errorMessage the message to use for the exception. Will be converted to a string using
* {@link String#valueOf(Object)}.
*/
public static void checkState(boolean isValid, @javax.annotation.Nullable Object errorMessage) {
if (!isValid) {
throw new IllegalStateException(String.valueOf(errorMessage));
}
}
/**
* Validates an index in an array or other container. This method throws an {@link
* IllegalArgumentException} if the size is negative and throws an {@link
* IndexOutOfBoundsException} if the index is negative or greater than or equal to the size. This
* method is similar to {@code Preconditions.checkElementIndex(int, int)} from Guava.
*
* @param index the index to validate.
* @param size the size of the array or container.
*/
public static void checkIndex(int index, int size) {
if (size < 0) {
throw new IllegalArgumentException("Negative size: " + size);
}
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index out of bounds: size=" + size + ", index=" + index);
}
}
/**
* Throws a {@link NullPointerException} if the argument is null. This method is similar to {@code
* Preconditions.checkNotNull(Object, Object)} from Guava.
*
* @param arg the argument to check for null.
* @param errorMessage the message to use for the exception. Will be converted to a string using
* {@link String#valueOf(Object)}.
* @return the argument, if it passes the null check.
*/
public static <T /*>>> extends @NonNull Object*/> T checkNotNull(
T arg, @javax.annotation.Nullable Object errorMessage) {
if (arg == null) {
throw new NullPointerException(String.valueOf(errorMessage));
}
return arg;
}
/**
* Throws a {@link NullPointerException} if any of the list elements is null.
*
* @param list the argument list to check for null.
* @param errorMessage the message to use for the exception. Will be converted to a string using
* {@link String#valueOf(Object)}.
*/
public static <T /*>>> extends @NonNull Object*/> void checkListElementNotNull(
List<T> list, @javax.annotation.Nullable Object errorMessage) {
for (T element : list) {
if (element == null) {
throw new NullPointerException(String.valueOf(errorMessage));
}
}
}
/**
* Compares two Objects for equality. This functionality is provided by {@code
* Objects.equal(Object, Object)} in Java 7.
*/
public static boolean equalsObjects(
@javax.annotation.Nullable Object x, @javax.annotation.Nullable Object y) {
return x == null ? y == null : x.equals(y);
}
/**
* Substitutes each {@code %s} in {@code template} with an argument. These are matched by
* position: the first {@code %s} gets {@code args[0]}, etc. If there are more arguments than
* placeholders, the unmatched arguments will be appended to the end of the formatted message in
* square braces.
*
* <p>Copied from {@code Preconditions.format(String, Object...)} from Guava
*
* @param template a non-null string containing 0 or more {@code %s} placeholders.
* @param args the arguments to be substituted into the message template. Arguments are converted
* to strings using {@link String#valueOf(Object)}. Arguments can be null.
*/
// Note that this is somewhat-improperly used from Verify.java as well.
private static String format(String template, @javax.annotation.Nullable Object... args) {
// If no arguments return the template.
if (args == null) {
return template;
}
// start substituting the arguments into the '%s' placeholders
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
int templateStart = 0;
int i = 0;
while (i < args.length) {
int placeholderStart = template.indexOf("%s", templateStart);
if (placeholderStart == -1) {
break;
}
builder.append(template, templateStart, placeholderStart);
builder.append(args[i++]);
templateStart = placeholderStart + 2;
}
builder.append(template, templateStart, template.length());
// if we run out of placeholders, append the extra args in square braces
if (i < args.length) {
builder.append(" [");
builder.append(args[i++]);
while (i < args.length) {
builder.append(", ");
builder.append(args[i++]);
}
builder.append(']');
}
return builder.toString();
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.internal;
import openconsensus.common.Clock;
import openconsensus.common.Timestamp;
import javax.annotation.concurrent.Immutable;
/** A {@link Clock} that always returns 0. */
@Immutable
public final class ZeroTimeClock extends Clock {
private static final ZeroTimeClock INSTANCE = new ZeroTimeClock();
private static final Timestamp ZERO_TIMESTAMP = Timestamp.create(0, 0);
private ZeroTimeClock() {}
/**
* Returns a {@code ZeroTimeClock}.
*
* @return a {@code ZeroTimeClock}.
*/
public static ZeroTimeClock getInstance() {
return INSTANCE;
}
@Override
public Timestamp now() {
return ZERO_TIMESTAMP;
}
@Override
public long nowNanos() {
return 0;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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.
*/
/**
* Interfaces and implementations that are internal to OpenCensus.
*
* <p>All the content under this package and its subpackages are considered annotated with {@link
* openconsensus.common.Internal}.
*/
@Internal
package openconsensus.internal;
import openconsensus.common.Internal;

View File

@ -0,0 +1,151 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics;
import openconsensus.common.ToDoubleFunction;
import openconsensus.internal.Utils;
import java.lang.ref.WeakReference;
import java.util.List;
import javax.annotation.concurrent.ThreadSafe;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
/**
* Derived Double Gauge metric, to report instantaneous measurement of a double value. Gauges can go
* both up and down. The gauges values can be negative.
*
* <p>Example: Create a Gauge with an object and a callback function.
*
* <pre>{@code
* class YourClass {
*
* private static final MetricRegistry metricRegistry = Metrics.getMetricRegistry();
*
* List<LabelKey> labelKeys = Arrays.asList(LabelKey.create("Name", "desc"));
* List<LabelValue> labelValues = Arrays.asList(LabelValue.create("Inbound"));
*
* DerivedDoubleGauge gauge = metricRegistry.addDerivedDoubleGauge(
* "queue_size", "Pending jobs in a queue", "1", labelKeys);
*
* QueueManager queueManager = new QueueManager();
* gauge.createTimeSeries(labelValues, queueManager,
* new ToDoubleFunction<QueueManager>() {
* {@literal @}Override
* public double applyAsDouble(QueueManager queue) {
* return queue.size();
* }
* });
*
* void doWork() {
* // Your code here.
* }
* }
*
* }</pre>
*
* @since 0.1.0
*/
@ThreadSafe
public abstract class DerivedDoubleGauge {
/**
* Creates a {@code TimeSeries}. The value of a single point in the TimeSeries is observed from a
* callback function. This function is invoked whenever metrics are collected, meaning the
* reported value is up-to-date. It keeps a {@link WeakReference} to the object and it is the
* user's responsibility to manage the lifetime of the object.
*
* @param labelValues the list of label values.
* @param obj the state object from which the function derives a measurement.
* @param function the function to be called.
* @param <T> the type of the object upon which the function derives a measurement.
* @throws NullPointerException if {@code labelValues} is null OR any element of {@code
* labelValues} is null OR {@code function} is null.
* @throws IllegalArgumentException if different time series with the same labels already exists
* OR if number of {@code labelValues}s are not equal to the label keys.
* @since 0.1.0
*/
public abstract <T> void createTimeSeries(
List<LabelValue> labelValues,
/*@Nullable*/ T obj,
ToDoubleFunction</*@Nullable*/ T> function);
/**
* Removes the {@code TimeSeries} from the gauge metric, if it is present.
*
* @param labelValues the list of label values.
* @throws NullPointerException if {@code labelValues} is null.
* @since 0.1.0
*/
public abstract void removeTimeSeries(List<LabelValue> labelValues);
/**
* Removes all {@code TimeSeries} from the gauge metric.
*
* @since 0.1.0
*/
public abstract void clear();
/**
* Returns the no-op implementation of the {@code DerivedDoubleGauge}.
*
* @return the no-op implementation of the {@code DerivedDoubleGauge}.
* @since 0.1.0
*/
static DerivedDoubleGauge newNoopDerivedDoubleGauge(
String name, String description, String unit, List<LabelKey> labelKeys) {
return NoopDerivedDoubleGauge.create(name, description, unit, labelKeys);
}
/** No-op implementations of DerivedDoubleGauge class. */
private static final class NoopDerivedDoubleGauge extends DerivedDoubleGauge {
private final int labelKeysSize;
static NoopDerivedDoubleGauge create(
String name, String description, String unit, List<LabelKey> labelKeys) {
return new NoopDerivedDoubleGauge(name, description, unit, labelKeys);
}
/** Creates a new {@code NoopDerivedDoubleGauge}. */
NoopDerivedDoubleGauge(String name, String description, String unit, List<LabelKey> labelKeys) {
Utils.checkNotNull(name, "name");
Utils.checkNotNull(description, "description");
Utils.checkNotNull(unit, "unit");
Utils.checkListElementNotNull(Utils.checkNotNull(labelKeys, "labelKeys"), "labelKey");
labelKeysSize = labelKeys.size();
}
@Override
public <T> void createTimeSeries(
List<LabelValue> labelValues,
/*@Nullable*/ T obj,
ToDoubleFunction</*@Nullable*/ T> function) {
Utils.checkListElementNotNull(Utils.checkNotNull(labelValues, "labelValues"), "labelValue");
Utils.checkArgument(
labelKeysSize == labelValues.size(), "Label Keys and Label Values don't have same size.");
Utils.checkNotNull(function, "function");
}
@Override
public void removeTimeSeries(List<LabelValue> labelValues) {
Utils.checkNotNull(labelValues, "labelValues");
}
@Override
public void clear() {}
}
}

View File

@ -0,0 +1,149 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics;
import openconsensus.common.ToLongFunction;
import openconsensus.internal.Utils;
import java.lang.ref.WeakReference;
import java.util.List;
import javax.annotation.concurrent.ThreadSafe;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
/**
* Derived Long Gauge metric, to report instantaneous measurement of an int64 value. Gauges can go
* both up and down. The gauges values can be negative.
*
* <p>Example: Create a Gauge with an object and a callback function.
*
* <pre>{@code
* class YourClass {
*
* private static final MetricRegistry metricRegistry = Metrics.getMetricRegistry();
*
* List<LabelKey> labelKeys = Arrays.asList(LabelKey.create("Name", "desc"));
* List<LabelValue> labelValues = Arrays.asList(LabelValue.create("Inbound"));
*
* DerivedLongGauge gauge = metricRegistry.addDerivedLongGauge(
* "queue_size", "Pending jobs in a queue", "1", labelKeys);
*
* QueueManager queueManager = new QueueManager();
* gauge.createTimeSeries(labelValues, queueManager,
* new ToLongFunction<QueueManager>() {
* {@literal @}Override
* public long applyAsLong(QueueManager queue) {
* return queue.size();
* }
* });
*
* void doWork() {
* // Your code here.
* }
* }
*
* }</pre>
*
* @since 0.1.0
*/
@ThreadSafe
public abstract class DerivedLongGauge {
/**
* Creates a {@code TimeSeries}. The value of a single point in the TimeSeries is observed from a
* callback function. This function is invoked whenever metrics are collected, meaning the
* reported value is up-to-date. It keeps a {@link WeakReference} to the object and it is the
* user's responsibility to manage the lifetime of the object.
*
* @param labelValues the list of label values.
* @param obj the state object from which the function derives a measurement.
* @param function the function to be called.
* @param <T> the type of the object upon which the function derives a measurement.
* @throws NullPointerException if {@code labelValues} is null OR any element of {@code
* labelValues} is null OR {@code function} is null.
* @throws IllegalArgumentException if different time series with the same labels already exists
* OR if number of {@code labelValues}s are not equal to the label keys.
* @since 0.1.0
*/
public abstract <T> void createTimeSeries(
List<LabelValue> labelValues, /*@Nullable*/ T obj, ToLongFunction</*@Nullable*/ T> function);
/**
* Removes the {@code TimeSeries} from the gauge metric, if it is present.
*
* @param labelValues the list of label values.
* @throws NullPointerException if {@code labelValues} is null.
* @since 0.1.0
*/
public abstract void removeTimeSeries(List<LabelValue> labelValues);
/**
* Removes all {@code TimeSeries} from the gauge metric.
*
* @since 0.1.0
*/
public abstract void clear();
/**
* Returns the no-op implementation of the {@code DerivedLongGauge}.
*
* @return the no-op implementation of the {@code DerivedLongGauge}.
* @since 0.1.0
*/
static DerivedLongGauge newNoopDerivedLongGauge(
String name, String description, String unit, List<LabelKey> labelKeys) {
return NoopDerivedLongGauge.create(name, description, unit, labelKeys);
}
/** No-op implementations of DerivedLongGauge class. */
private static final class NoopDerivedLongGauge extends DerivedLongGauge {
private final int labelKeysSize;
static NoopDerivedLongGauge create(
String name, String description, String unit, List<LabelKey> labelKeys) {
return new NoopDerivedLongGauge(name, description, unit, labelKeys);
}
/** Creates a new {@code NoopDerivedLongGauge}. */
NoopDerivedLongGauge(String name, String description, String unit, List<LabelKey> labelKeys) {
Utils.checkNotNull(name, "name");
Utils.checkNotNull(description, "description");
Utils.checkNotNull(unit, "unit");
Utils.checkListElementNotNull(Utils.checkNotNull(labelKeys, "labelKeys"), "labelKey");
labelKeysSize = labelKeys.size();
}
@Override
public <T> void createTimeSeries(
List<LabelValue> labelValues,
/*@Nullable*/ T obj,
ToLongFunction</*@Nullable*/ T> function) {
Utils.checkListElementNotNull(Utils.checkNotNull(labelValues, "labelValues"), "labelValue");
Utils.checkArgument(
labelKeysSize == labelValues.size(), "Label Keys and Label Values don't have same size.");
Utils.checkNotNull(function, "function");
}
@Override
public void removeTimeSeries(List<LabelValue> labelValues) {
Utils.checkNotNull(labelValues, "labelValues");
}
@Override
public void clear() {}
}
}

View File

@ -0,0 +1,212 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics;
import openconsensus.internal.Utils;
import java.util.List;
import javax.annotation.concurrent.ThreadSafe;
/**
* Double Gauge metric, to report instantaneous measurement of a double value. Gauges can go both up
* and down. The gauges values can be negative.
*
* <p>Example 1: Create a Gauge with default labels.
*
* <pre>{@code
* class YourClass {
*
* private static final MetricRegistry metricRegistry = Metrics.getMetricRegistry();
*
* List<LabelKey> labelKeys = Arrays.asList(LabelKey.create("Name", "desc"));
*
* DoubleGauge gauge = metricRegistry.addDoubleGauge("queue_size",
* "Pending jobs", "1", labelKeys);
*
* // It is recommended to keep a reference of a point for manual operations.
* DoublePoint defaultPoint = gauge.getDefaultTimeSeries();
*
* void doWork() {
* // Your code here.
* defaultPoint.add(10);
* }
*
* }
* }</pre>
*
* <p>Example 2: You can also use labels(keys and values) to track different types of metric.
*
* <pre>{@code
* class YourClass {
*
* private static final MetricRegistry metricRegistry = Metrics.getMetricRegistry();
*
* List<LabelKey> labelKeys = Arrays.asList(LabelKey.create("Name", "desc"));
* List<LabelValue> labelValues = Arrays.asList(LabelValue.create("Inbound"));
*
* DoubleGauge gauge = metricRegistry.addDoubleGauge("queue_size",
* "Pending jobs", "1", labelKeys);
*
* // It is recommended to keep a reference of a point for manual operations.
* DoublePoint inboundPoint = gauge.getOrCreateTimeSeries(labelValues);
*
* void doSomeWork() {
* // Your code here.
* inboundPoint.set(15);
* }
*
* }
* }</pre>
*
* @since 0.1.0
*/
@ThreadSafe
public abstract class DoubleGauge {
/**
* Creates a {@code TimeSeries} and returns a {@code DoublePoint} if the specified {@code
* labelValues} is not already associated with this gauge, else returns an existing {@code
* DoublePoint}.
*
* <p>It is recommended to keep a reference to the DoublePoint instead of always calling this
* method for manual operations.
*
* @param labelValues the list of label values. The number of label values must be the same to
* that of the label keys passed to {@link MetricRegistry#addDoubleGauge}.
* @return a {@code DoublePoint} the value of single gauge.
* @throws NullPointerException if {@code labelValues} is null OR any element of {@code
* labelValues} is null.
* @throws IllegalArgumentException if number of {@code labelValues}s are not equal to the label
* keys.
* @since 0.1.0
*/
public abstract DoublePoint getOrCreateTimeSeries(List<LabelValue> labelValues);
/**
* Returns a {@code DoublePoint} for a gauge with all labels not set, or default labels.
*
* @return a {@code DoublePoint} for a gauge with all labels not set, or default labels.
* @since 0.1.0
*/
public abstract DoublePoint getDefaultTimeSeries();
/**
* Removes the {@code TimeSeries} from the gauge metric, if it is present. i.e. references to
* previous {@code DoublePoint} objects are invalid (not part of the metric).
*
* @param labelValues the list of label values.
* @throws NullPointerException if {@code labelValues} is null or any element of {@code
* labelValues} is null.
* @since 0.1.0
*/
public abstract void removeTimeSeries(List<LabelValue> labelValues);
/**
* Removes all {@code TimeSeries} from the gauge metric. i.e. references to all previous {@code
* DoublePoint} objects are invalid (not part of the metric).
*
* @since 0.1.0
*/
public abstract void clear();
/**
* Returns the no-op implementation of the {@code DoubleGauge}.
*
* @return the no-op implementation of the {@code DoubleGauge}.
* @since 0.1.0
*/
static DoubleGauge newNoopDoubleGauge(
String name, String description, String unit, List<LabelKey> labelKeys) {
return NoopDoubleGauge.create(name, description, unit, labelKeys);
}
/**
* The value of a single point in the Gauge.TimeSeries.
*
* @since 0.1.0
*/
public abstract static class DoublePoint {
/**
* Adds the given value to the current value. The values can be negative.
*
* @param amt the value to add
* @since 0.1.0
*/
public abstract void add(double amt);
/**
* Sets the given value.
*
* @param val the new value.
* @since 0.1.0
*/
public abstract void set(double val);
}
/** No-op implementations of DoubleGauge class. */
private static final class NoopDoubleGauge extends DoubleGauge {
private final int labelKeysSize;
static NoopDoubleGauge create(
String name, String description, String unit, List<LabelKey> labelKeys) {
return new NoopDoubleGauge(name, description, unit, labelKeys);
}
/** Creates a new {@code NoopDoublePoint}. */
NoopDoubleGauge(String name, String description, String unit, List<LabelKey> labelKeys) {
Utils.checkNotNull(name, "name");
Utils.checkNotNull(description, "description");
Utils.checkNotNull(unit, "unit");
Utils.checkListElementNotNull(Utils.checkNotNull(labelKeys, "labelKeys"), "labelKey");
labelKeysSize = labelKeys.size();
}
@Override
public NoopDoublePoint getOrCreateTimeSeries(List<LabelValue> labelValues) {
Utils.checkListElementNotNull(Utils.checkNotNull(labelValues, "labelValues"), "labelValue");
Utils.checkArgument(
labelKeysSize == labelValues.size(), "Label Keys and Label Values don't have same size.");
return NoopDoublePoint.INSTANCE;
}
@Override
public NoopDoublePoint getDefaultTimeSeries() {
return NoopDoublePoint.INSTANCE;
}
@Override
public void removeTimeSeries(List<LabelValue> labelValues) {
Utils.checkNotNull(labelValues, "labelValues");
}
@Override
public void clear() {}
/** No-op implementations of DoublePoint class. */
private static final class NoopDoublePoint extends DoublePoint {
private static final NoopDoublePoint INSTANCE = new NoopDoublePoint();
private NoopDoublePoint() {}
@Override
public void add(double amt) {}
@Override
public void set(double val) {}
}
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import javax.annotation.concurrent.Immutable;
/**
* The key of a {@code Label} associated with a {@code MetricDescriptor}.
*
* @since 0.1.0
*/
@ExperimentalApi
@Immutable
@AutoValue
public abstract class LabelKey {
LabelKey() {}
/**
* Creates a {@link LabelKey}.
*
* @param key the key of a {@code Label}.
* @param description a human-readable description of what this label key represents.
* @return a {@code LabelKey}.
* @since 0.1.0
*/
public static LabelKey create(String key, String description) {
return new AutoValue_LabelKey(key, description);
}
/**
* Returns the key of this {@link LabelKey}.
*
* @return the key.
* @since 0.1.0
*/
public abstract String getKey();
/**
* Returns the description of this {@link LabelKey}.
*
* @return the description.
* @since 0.1.0
*/
public abstract String getDescription();
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* The value of a {@code Label} associated with a {@code TimeSeries}.
*
* @since 0.1.0
*/
@ExperimentalApi
@Immutable
@AutoValue
public abstract class LabelValue {
LabelValue() {}
/**
* Creates a {@link LabelValue}.
*
* @param value the value of a {@code Label}. {@code null} value indicates an unset {@code
* LabelValue}.
* @return a {@code LabelValue}.
* @since 0.1.0
*/
public static LabelValue create(@Nullable String value) {
return new AutoValue_LabelValue(value);
}
/**
* Returns the value of this {@link LabelValue}. Returns {@code null} if the value is unset and
* supposed to be ignored.
*
* @return the value.
* @since 0.1.0
*/
@Nullable
public abstract String getValue();
}

View File

@ -0,0 +1,205 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics;
import openconsensus.internal.Utils;
import java.util.List;
import javax.annotation.concurrent.ThreadSafe;
/**
* Long Gauge metric, to report instantaneous measurement of an int64 value. Gauges can go both up
* and down. The gauges values can be negative.
*
* <p>Example 1: Create a Gauge with default labels.
*
* <pre>{@code
* class YourClass {
*
* private static final MetricRegistry metricRegistry = Metrics.getMetricRegistry();
*
* List<LabelKey> labelKeys = Arrays.asList(LabelKey.create("Name", "desc"));
*
* LongGauge gauge = metricRegistry.addLongGauge("queue_size", "Pending jobs", "1", labelKeys);
*
* // It is recommended to keep a reference of a point for manual operations.
* LongPoint defaultPoint = gauge.getDefaultTimeSeries();
*
* void doWork() {
* // Your code here.
* defaultPoint.add(10);
* }
*
* }
* }</pre>
*
* <p>Example 2: You can also use labels(keys and values) to track different types of metric.
*
* <pre>{@code
* class YourClass {
*
* private static final MetricRegistry metricRegistry = Metrics.getMetricRegistry();
*
* List<LabelKey> labelKeys = Arrays.asList(LabelKey.create("Name", "desc"));
* List<LabelValue> labelValues = Arrays.asList(LabelValue.create("Inbound"));
*
* LongGauge gauge = metricRegistry.addLongGauge("queue_size", "Pending jobs", "1", labelKeys);
*
* // It is recommended to keep a reference of a point for manual operations.
* LongPoint inboundPoint = gauge.getOrCreateTimeSeries(labelValues);
*
* void doSomeWork() {
* // Your code here.
* inboundPoint.set(15);
* }
*
* }
* }</pre>
*
* @since 0.1.0
*/
@ThreadSafe
public abstract class LongGauge {
/**
* Creates a {@code TimeSeries} and returns a {@code LongPoint} if the specified {@code
* labelValues} is not already associated with this gauge, else returns an existing {@code
* LongPoint}.
*
* <p>It is recommended to keep a reference to the LongPoint instead of always calling this method
* for manual operations.
*
* @param labelValues the list of label values. The number of label values must be the same to
* that of the label keys passed to {@link MetricRegistry#addLongGauge}.
* @return a {@code LongPoint} the value of single gauge.
* @throws NullPointerException if {@code labelValues} is null OR any element of {@code
* labelValues} is null.
* @throws IllegalArgumentException if number of {@code labelValues}s are not equal to the label
* keys passed to {@link MetricRegistry#addLongGauge}.
* @since 0.1.0
*/
public abstract LongPoint getOrCreateTimeSeries(List<LabelValue> labelValues);
/**
* Returns a {@code LongPoint} for a gauge with all labels not set, or default labels.
*
* @return a {@code LongPoint} for a gauge with all labels not set, or default labels.
* @since 0.1.0
*/
public abstract LongPoint getDefaultTimeSeries();
/**
* Removes the {@code TimeSeries} from the gauge metric, if it is present. i.e. references to
* previous {@code LongPoint} objects are invalid (not part of the metric).
*
* @param labelValues the list of label values.
* @throws NullPointerException if {@code labelValues} is null.
* @since 0.1.0
*/
public abstract void removeTimeSeries(List<LabelValue> labelValues);
/**
* Removes all {@code TimeSeries} from the gauge metric. i.e. references to all previous {@code
* LongPoint} objects are invalid (not part of the metric).
*
* @since 0.1.0
*/
public abstract void clear();
/**
* Returns the no-op implementation of the {@code LongGauge}.
*
* @return the no-op implementation of the {@code LongGauge}.
* @since 0.1.0
*/
static LongGauge newNoopLongGauge(
String name, String description, String unit, List<LabelKey> labelKeys) {
return NoopLongGauge.create(name, description, unit, labelKeys);
}
/**
* The value of a single point in the Gauge.TimeSeries.
*
* @since 0.1.0
*/
public abstract static class LongPoint {
/**
* Adds the given value to the current value. The values can be negative.
*
* @param amt the value to add
* @since 0.1.0
*/
public abstract void add(long amt);
/**
* Sets the given value.
*
* @param val the new value.
* @since 0.1.0
*/
public abstract void set(long val);
}
/** No-op implementations of LongGauge class. */
private static final class NoopLongGauge extends LongGauge {
private final int labelKeysSize;
static NoopLongGauge create(
String name, String description, String unit, List<LabelKey> labelKeys) {
return new NoopLongGauge(name, description, unit, labelKeys);
}
/** Creates a new {@code NoopLongPoint}. */
NoopLongGauge(String name, String description, String unit, List<LabelKey> labelKeys) {
labelKeysSize = labelKeys.size();
}
@Override
public NoopLongPoint getOrCreateTimeSeries(List<LabelValue> labelValues) {
Utils.checkListElementNotNull(Utils.checkNotNull(labelValues, "labelValues"), "labelValue");
Utils.checkArgument(
labelKeysSize == labelValues.size(), "Label Keys and Label Values don't have same size.");
return NoopLongPoint.INSTANCE;
}
@Override
public NoopLongPoint getDefaultTimeSeries() {
return NoopLongPoint.INSTANCE;
}
@Override
public void removeTimeSeries(List<LabelValue> labelValues) {
Utils.checkNotNull(labelValues, "labelValues");
}
@Override
public void clear() {}
/** No-op implementations of LongPoint class. */
private static final class NoopLongPoint extends LongPoint {
private static final NoopLongPoint INSTANCE = new NoopLongPoint();
private NoopLongPoint() {}
@Override
public void add(long amt) {}
@Override
public void set(long val) {}
}
}
}

View File

@ -0,0 +1,158 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics;
import openconsensus.common.ExperimentalApi;
import openconsensus.common.ToDoubleFunction;
import openconsensus.common.ToLongFunction;
import openconsensus.internal.Utils;
import java.util.List;
import openconsensus.metrics.export.MetricProducer;
import openconsensus.metrics.export.MetricProducerManager;
/**
* Creates and manages your application's set of metrics. The default implementation of this creates
* a {@link MetricProducer} and registers it to the global {@link
* MetricProducerManager}.
*
* @since 0.1.0
*/
@ExperimentalApi
public abstract class MetricRegistry {
/**
* Builds a new long gauge to be added to the registry. This is more convenient form when you want
* to manually increase and decrease values as per your service requirements.
*
* @param name the name of the metric.
* @param description the description of the metric.
* @param unit the unit of the metric.
* @param labelKeys the list of the label keys.
* @return a {@code LongGauge}.
* @throws NullPointerException if {@code labelKeys} is null OR any element of {@code labelKeys}
* is null OR {@code name}, {@code description}, {@code unit} is null.
* @throws IllegalArgumentException if different metric with the same name already registered.
* @since 0.1.0
*/
@ExperimentalApi
public abstract LongGauge addLongGauge(
String name, String description, String unit, List<LabelKey> labelKeys);
/**
* Builds a new double gauge to be added to the registry. This is more convenient form when you
* want to manually increase and decrease values as per your service requirements.
*
* @param name the name of the metric.
* @param description the description of the metric.
* @param unit the unit of the metric.
* @param labelKeys the list of the label keys.
* @return a {@code DoubleGauge}.
* @throws NullPointerException if {@code labelKeys} is null OR any element of {@code labelKeys}
* is null OR {@code name}, {@code description}, {@code unit} is null.
* @throws IllegalArgumentException if different metric with the same name already registered.
* @since 0.1.0
*/
@ExperimentalApi
public abstract DoubleGauge addDoubleGauge(
String name, String description, String unit, List<LabelKey> labelKeys);
/**
* Builds a new derived long gauge to be added to the registry. This is more convenient form when
* you want to define a gauge by executing a {@link ToLongFunction} on an object.
*
* @param name the name of the metric.
* @param description the description of the metric.
* @param unit the unit of the metric.
* @param labelKeys the list of the label keys.
* @return a {@code DerivedLongGauge}.
* @throws NullPointerException if {@code labelKeys} is null OR any element of {@code labelKeys}
* is null OR {@code name}, {@code description}, {@code unit} is null.
* @throws IllegalArgumentException if different metric with the same name already registered.
* @since 0.1.0
*/
@ExperimentalApi
public abstract DerivedLongGauge addDerivedLongGauge(
String name, String description, String unit, List<LabelKey> labelKeys);
/**
* Builds a new derived double gauge to be added to the registry. This is more convenient form
* when you want to define a gauge by executing a {@link ToDoubleFunction} on an object.
*
* @param name the name of the metric.
* @param description the description of the metric.
* @param unit the unit of the metric.
* @param labelKeys the list of the label keys.
* @return a {@code DerivedDoubleGauge}.
* @throws NullPointerException if {@code labelKeys} is null OR any element of {@code labelKeys}
* is null OR {@code name}, {@code description}, {@code unit} is null.
* @throws IllegalArgumentException if different metric with the same name already registered.
* @since 0.1.0
*/
@ExperimentalApi
public abstract DerivedDoubleGauge addDerivedDoubleGauge(
String name, String description, String unit, List<LabelKey> labelKeys);
static MetricRegistry newNoopMetricRegistry() {
return new NoopMetricRegistry();
}
private static final class NoopMetricRegistry extends MetricRegistry {
@Override
public LongGauge addLongGauge(
String name, String description, String unit, List<LabelKey> labelKeys) {
Utils.checkListElementNotNull(Utils.checkNotNull(labelKeys, "labelKeys"), "labelKey");
return LongGauge.newNoopLongGauge(
Utils.checkNotNull(name, "name"),
Utils.checkNotNull(description, "description"),
Utils.checkNotNull(unit, "unit"),
labelKeys);
}
@Override
public DoubleGauge addDoubleGauge(
String name, String description, String unit, List<LabelKey> labelKeys) {
Utils.checkListElementNotNull(Utils.checkNotNull(labelKeys, "labelKeys"), "labelKey");
return DoubleGauge.newNoopDoubleGauge(
Utils.checkNotNull(name, "name"),
Utils.checkNotNull(description, "description"),
Utils.checkNotNull(unit, "unit"),
labelKeys);
}
@Override
public DerivedLongGauge addDerivedLongGauge(
String name, String description, String unit, List<LabelKey> labelKeys) {
Utils.checkListElementNotNull(Utils.checkNotNull(labelKeys, "labelKeys"), "labelKey");
return DerivedLongGauge.newNoopDerivedLongGauge(
Utils.checkNotNull(name, "name"),
Utils.checkNotNull(description, "description"),
Utils.checkNotNull(unit, "unit"),
labelKeys);
}
@Override
public DerivedDoubleGauge addDerivedDoubleGauge(
String name, String description, String unit, List<LabelKey> labelKeys) {
Utils.checkListElementNotNull(Utils.checkNotNull(labelKeys, "labelKeys"), "labelKey");
return DerivedDoubleGauge.newNoopDerivedDoubleGauge(
Utils.checkNotNull(name, "name"),
Utils.checkNotNull(description, "description"),
Utils.checkNotNull(unit, "unit"),
labelKeys);
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics;
import openconsensus.common.ExperimentalApi;
import openconsensus.internal.DefaultVisibilityForTesting;
import openconsensus.internal.Provider;
import openconsensus.metrics.export.ExportComponent;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import openconsensus.metrics.export.MetricProducerManager;
/**
* Class for accessing the default {@link MetricsComponent}.
*
* @since 0.1.0
*/
@ExperimentalApi
public final class Metrics {
private static final Logger logger = Logger.getLogger(Metrics.class.getName());
private static final MetricsComponent metricsComponent =
loadMetricsComponent(MetricsComponent.class.getClassLoader());
/**
* Returns the global {@link ExportComponent}.
*
* @return the global {@code ExportComponent}.
* @since 0.1.0
*/
public static ExportComponent getExportComponent() {
return metricsComponent.getExportComponent();
}
/**
* Returns the global {@link MetricRegistry}.
*
* <p>This {@code MetricRegistry} is already added to the global {@link
* MetricProducerManager}.
*
* @return the global {@code MetricRegistry}.
* @since 0.1.0
*/
public static MetricRegistry getMetricRegistry() {
return metricsComponent.getMetricRegistry();
}
// Any provider that may be used for MetricsComponent can be added here.
@DefaultVisibilityForTesting
static MetricsComponent loadMetricsComponent(@Nullable ClassLoader classLoader) {
try {
// Call Class.forName with literal string name of the class to help shading tools.
return Provider.createInstance(
Class.forName(
"io.opencensus.impl.metrics.MetricsComponentImpl", /*initialize=*/ true, classLoader),
MetricsComponent.class);
} catch (ClassNotFoundException e) {
logger.log(
Level.FINE,
"Couldn't load full implementation for MetricsComponent, now trying to load lite "
+ "implementation.",
e);
}
try {
// Call Class.forName with literal string name of the class to help shading tools.
return Provider.createInstance(
Class.forName(
"io.opencensus.impllite.metrics.MetricsComponentImplLite",
/*initialize=*/ true,
classLoader),
MetricsComponent.class);
} catch (ClassNotFoundException e) {
logger.log(
Level.FINE,
"Couldn't load lite implementation for MetricsComponent, now using default "
+ "implementation for MetricsComponent.",
e);
}
return MetricsComponent.newNoopMetricsComponent();
}
private Metrics() {}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics;
import openconsensus.common.ExperimentalApi;
import openconsensus.metrics.export.ExportComponent;
/**
* Class that holds the implementation instance for {@link ExportComponent}.
*
* @since 0.1.0
*/
@ExperimentalApi
public abstract class MetricsComponent {
/**
* Returns the {@link ExportComponent} with the provided implementation. If no implementation is
* provided then no-op implementations will be used.
*
* @return the {@link ExportComponent} implementation.
* @since 0.1.0
*/
public abstract ExportComponent getExportComponent();
/**
* Returns the {@link MetricRegistry} with the provided implementation.
*
* @return the {@link MetricRegistry} implementation.
* @since 0.1.0
*/
public abstract MetricRegistry getMetricRegistry();
/**
* Returns an instance that contains no-op implementations for all the instances.
*
* @return an instance that contains no-op implementations for all the instances.
*/
static MetricsComponent newNoopMetricsComponent() {
return new NoopMetricsComponent();
}
private static final class NoopMetricsComponent extends MetricsComponent {
private static final ExportComponent EXPORT_COMPONENT =
ExportComponent.newNoopExportComponent();
private static final MetricRegistry METRIC_REGISTRY = MetricRegistry.newNoopMetricRegistry();
@Override
public ExportComponent getExportComponent() {
return EXPORT_COMPONENT;
}
@Override
public MetricRegistry getMetricRegistry() {
return METRIC_REGISTRY;
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.data;
import com.google.auto.value.AutoValue;
import javax.annotation.concurrent.Immutable;
/**
* The value of {@link Exemplar} attachment.
*
* <p>In Stats API we only provide one subclass {@link AttachmentValueString}. No other subclasses
* are added because we don't want to introduce dependencies on other libraries, for example Tracing
* APIs.
*
* <p>Other packages are free to extend this class to hold specific information. As an example, see
* {@code io.opencensus.contrib.exemplar.util.AttachmentValueSpanContext}.
*
* @since 0.1.0
*/
public abstract class AttachmentValue {
/**
* Returns the string attachment value.
*
* @return the string attachment value.
* @since 0.1.0
*/
public abstract String getValue();
/**
* String {@link AttachmentValue}.
*
* @since 0.1.0
*/
@AutoValue
@Immutable
public abstract static class AttachmentValueString extends AttachmentValue {
AttachmentValueString() {}
/**
* Creates an {@link AttachmentValueString}.
*
* @param value the string value.
* @return an {@code AttachmentValueString}.
* @since 0.1.0
*/
public static AttachmentValueString create(String value) {
return new AutoValue_AttachmentValue_AttachmentValueString(value);
}
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.data;
import com.google.auto.value.AutoValue;
import openconsensus.common.Timestamp;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.concurrent.Immutable;
/*>>>
import org.checkerframework.checker.nullness.qual.NonNull;
*/
/**
* An example point that may be used to annotate aggregated distribution values, associated with a
* histogram bucket.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class Exemplar {
Exemplar() {}
/**
* Returns value of the {@link Exemplar} point.
*
* @return value of the {@code Exemplar} point.
* @since 0.1.0
*/
public abstract double getValue();
/**
* Returns the time that this {@link Exemplar}'s value was recorded.
*
* @return the time that this {@code Exemplar}'s value was recorded.
* @since 0.1.0
*/
public abstract Timestamp getTimestamp();
/**
* Returns the contextual information about the example value.
*
* @return the contextual information about the example value.
* @since 0.1.0
*/
public abstract Map<String, AttachmentValue> getAttachments();
/**
* Creates an {@link Exemplar}.
*
* @param value value of the {@link Exemplar} point.
* @param timestamp the time that this {@code Exemplar}'s value was recorded.
* @param attachments the contextual information about the example value.
* @return an {@code Exemplar}.
* @since 0.1.0
*/
public static Exemplar create(
double value, Timestamp timestamp, Map<String, AttachmentValue> attachments) {
checkNotNull(attachments, "attachments");
Map<String, AttachmentValue> attachmentsCopy =
Collections.unmodifiableMap(new HashMap<String, AttachmentValue>(attachments));
for (Entry<String, AttachmentValue> entry : attachmentsCopy.entrySet()) {
checkNotNull(entry.getKey(), "key of attachments");
checkNotNull(entry.getValue(), "value of attachments");
}
return new AutoValue_Exemplar(value, timestamp, attachmentsCopy);
}
// TODO(songy23): shade the internal Utils jar and remove this duplicated method.
private static <T /*>>> extends @NonNull Object*/> T checkNotNull(
T arg, @javax.annotation.Nullable Object errorMessage) {
if (arg == null) {
throw new NullPointerException(String.valueOf(errorMessage));
}
return arg;
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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.
*/
/**
* This package describes common data models in Metrics that are shared across multiple packages.
*
* <p>WARNING: Currently all the public classes under this package are marked as {@link
* openconsensus.common.ExperimentalApi}. The classes and APIs under {@link io.opencensus.metrics}
* are likely to get backwards-incompatible updates in the future. DO NOT USE except for
* experimental purposes.
*/
@ExperimentalApi
package openconsensus.metrics.data;
import openconsensus.common.ExperimentalApi;

View File

@ -0,0 +1,285 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import openconsensus.common.Function;
import openconsensus.internal.Utils;
import openconsensus.metrics.data.Exemplar;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import openconsensus.common.Functions;
/**
* {@link Distribution} contains summary statistics for a population of values. It optionally
* contains a histogram representing the distribution of those values across a set of buckets.
*
* @since 0.1.0
*/
@ExperimentalApi
@AutoValue
@Immutable
public abstract class Distribution {
Distribution() {}
/**
* Creates a {@link Distribution}.
*
* @param count the count of the population values.
* @param sum the sum of the population values.
* @param sumOfSquaredDeviations the sum of squared deviations of the population values.
* @param bucketOptions the bucket options used to create a histogram for the distribution.
* @param buckets {@link Bucket}s of a histogram.
* @return a {@code Distribution}.
* @since 0.1.0
*/
public static Distribution create(
long count,
double sum,
double sumOfSquaredDeviations,
BucketOptions bucketOptions,
List<Bucket> buckets) {
Utils.checkArgument(count >= 0, "count should be non-negative.");
Utils.checkArgument(
sumOfSquaredDeviations >= 0, "sum of squared deviations should be non-negative.");
if (count == 0) {
Utils.checkArgument(sum == 0, "sum should be 0 if count is 0.");
Utils.checkArgument(
sumOfSquaredDeviations == 0, "sum of squared deviations should be 0 if count is 0.");
}
Utils.checkNotNull(bucketOptions, "bucketOptions");
List<Bucket> bucketsCopy =
Collections.unmodifiableList(new ArrayList<Bucket>(Utils.checkNotNull(buckets, "buckets")));
Utils.checkListElementNotNull(bucketsCopy, "bucket");
return new AutoValue_Distribution(
count, sum, sumOfSquaredDeviations, bucketOptions, bucketsCopy);
}
/**
* Returns the aggregated count.
*
* @return the aggregated count.
* @since 0.1.0
*/
public abstract long getCount();
/**
* Returns the aggregated sum.
*
* @return the aggregated sum.
* @since 0.1.0
*/
public abstract double getSum();
/**
* Returns the aggregated sum of squared deviations.
*
* <p>The sum of squared deviations from the mean of the values in the population. For values x_i
* this is:
*
* <p>Sum[i=1..n]((x_i - mean)^2)
*
* <p>If count is zero then this field must be zero.
*
* @return the aggregated sum of squared deviations.
* @since 0.1.0
*/
public abstract double getSumOfSquaredDeviations();
/**
* Returns bucket options used to create a histogram for the distribution.
*
* @return the {@code BucketOptions} associated with the {@code Distribution}, or {@code null} if
* there isn't one.
* @since 0.1.0
*/
@Nullable
public abstract BucketOptions getBucketOptions();
/**
* Returns the aggregated histogram {@link Bucket}s.
*
* @return the aggregated histogram buckets.
* @since 0.1.0
*/
public abstract List<Bucket> getBuckets();
/**
* The bucket options used to create a histogram for the distribution.
*
* @since 0.1.0
*/
@Immutable
public abstract static class BucketOptions {
private BucketOptions() {}
/**
* Returns a {@link ExplicitOptions}.
*
* <p>The bucket boundaries for that histogram are described by bucket_bounds. This defines
* size(bucket_bounds) + 1 (= N) buckets. The boundaries for bucket index i are:
*
* <ul>
* <li>{@code [0, bucket_bounds[i]) for i == 0}
* <li>{@code [bucket_bounds[i-1], bucket_bounds[i]) for 0 < i < N-1}
* <li>{@code [bucket_bounds[i-1], +infinity) for i == N-1}
* </ul>
*
* <p>If bucket_bounds has no elements (zero size), then there is no histogram associated with
* the Distribution. If bucket_bounds has only one element, there are no finite buckets, and
* that single element is the common boundary of the overflow and underflow buckets. The values
* must be monotonically increasing.
*
* @param bucketBoundaries the bucket boundaries of a distribution (given explicitly). The
* values must be strictly increasing and should be positive values.
* @return a {@code ExplicitOptions} {@code BucketOptions}.
* @since 0.1.0
*/
public static BucketOptions explicitOptions(List<Double> bucketBoundaries) {
return ExplicitOptions.create(bucketBoundaries);
}
/**
* Applies the given match function to the underlying BucketOptions.
*
* @param explicitFunction the function that should be applied if the BucketOptions has type
* {@code ExplicitOptions}.
* @param defaultFunction the function that should be applied if the BucketOptions has a type
* that was added after this {@code match} method was added to the API. See {@link
* Functions} for some common functions for handling unknown types.
* @return the result of the function applied to the underlying BucketOptions.
* @since 0.1.0
*/
public abstract <T> T match(
Function<? super ExplicitOptions, T> explicitFunction,
Function<? super BucketOptions, T> defaultFunction);
/** A Bucket with explicit bounds {@link BucketOptions}. */
@AutoValue
@Immutable
public abstract static class ExplicitOptions extends BucketOptions {
ExplicitOptions() {}
@Override
public final <T> T match(
Function<? super ExplicitOptions, T> explicitFunction,
Function<? super BucketOptions, T> defaultFunction) {
return explicitFunction.apply(this);
}
/**
* Creates a {@link ExplicitOptions}.
*
* @param bucketBoundaries the bucket boundaries of a distribution (given explicitly). The
* values must be strictly increasing and should be positive.
* @return a {@code ExplicitOptions}.
* @since 0.1.0
*/
private static ExplicitOptions create(List<Double> bucketBoundaries) {
Utils.checkNotNull(bucketBoundaries, "bucketBoundaries");
List<Double> bucketBoundariesCopy =
Collections.unmodifiableList(new ArrayList<Double>(bucketBoundaries));
checkBucketBoundsAreSorted(bucketBoundariesCopy);
return new AutoValue_Distribution_BucketOptions_ExplicitOptions(bucketBoundariesCopy);
}
private static void checkBucketBoundsAreSorted(List<Double> bucketBoundaries) {
if (bucketBoundaries.size() >= 1) {
double previous = Utils.checkNotNull(bucketBoundaries.get(0), "bucketBoundary");
Utils.checkArgument(previous > 0, "bucket boundary should be > 0");
for (int i = 1; i < bucketBoundaries.size(); i++) {
double next = Utils.checkNotNull(bucketBoundaries.get(i), "bucketBoundary");
Utils.checkArgument(previous < next, "bucket boundaries not sorted.");
previous = next;
}
}
}
/**
* Returns the bucket boundaries of this distribution.
*
* @return the bucket boundaries of this distribution.
* @since 0.1.0
*/
public abstract List<Double> getBucketBoundaries();
}
}
/**
* The histogram bucket of the population values.
*
* @since 0.1.0
*/
@AutoValue
@Immutable
public abstract static class Bucket {
Bucket() {}
/**
* Creates a {@link Bucket}.
*
* @param count the number of values in each bucket of the histogram.
* @return a {@code Bucket}.
* @since 0.1.0
*/
public static Bucket create(long count) {
Utils.checkArgument(count >= 0, "bucket count should be non-negative.");
return new AutoValue_Distribution_Bucket(count, null);
}
/**
* Creates a {@link Bucket} with an {@link Exemplar}.
*
* @param count the number of values in each bucket of the histogram.
* @param exemplar the {@code Exemplar} of this {@code Bucket}.
* @return a {@code Bucket}.
* @since 0.1.0
*/
public static Bucket create(long count, Exemplar exemplar) {
Utils.checkArgument(count >= 0, "bucket count should be non-negative.");
Utils.checkNotNull(exemplar, "exemplar");
return new AutoValue_Distribution_Bucket(count, exemplar);
}
/**
* Returns the number of values in each bucket of the histogram.
*
* @return the number of values in each bucket of the histogram.
* @since 0.1.0
*/
public abstract long getCount();
/**
* Returns the {@link Exemplar} associated with the {@link Bucket}, or {@code null} if there
* isn't one.
*
* @return the {@code Exemplar} associated with the {@code Bucket}, or {@code null} if there
* isn't one.
* @since 0.1.0
*/
@Nullable
public abstract Exemplar getExemplar();
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import openconsensus.common.ExperimentalApi;
/**
* Class that holds the implementation instance for {@link MetricProducerManager}.
*
* <p>Unless otherwise noted all methods (on component) results are cacheable.
*
* @since 0.1.0
*/
@ExperimentalApi
public abstract class ExportComponent {
/**
* Returns the no-op implementation of the {@code ExportComponent}.
*
* @return the no-op implementation of the {@code ExportComponent}.
* @since 0.1.0
*/
public static ExportComponent newNoopExportComponent() {
return new NoopExportComponent();
}
/**
* Returns the global {@link MetricProducerManager} which can be used to register handlers to
* export all the recorded metrics.
*
* @return the implementation of the {@code MetricExporter} or no-op if no implementation linked
* in the binary.
* @since 0.1.0
*/
public abstract MetricProducerManager getMetricProducerManager();
private static final class NoopExportComponent extends ExportComponent {
private static final MetricProducerManager METRIC_PRODUCER_MANAGER =
MetricProducerManager.newNoopMetricProducerManager();
@Override
public MetricProducerManager getMetricProducerManager() {
return METRIC_PRODUCER_MANAGER;
}
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import openconsensus.internal.Utils;
import openconsensus.metrics.export.Value.ValueDistribution;
import openconsensus.metrics.export.Value.ValueDouble;
import openconsensus.metrics.export.Value.ValueLong;
import openconsensus.metrics.export.Value.ValueSummary;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.concurrent.Immutable;
/**
* A {@link Metric} with one or more {@link TimeSeries}.
*
* @since 0.1.0
*/
@ExperimentalApi
@Immutable
@AutoValue
public abstract class Metric {
Metric() {}
/**
* Creates a {@link Metric}.
*
* @param metricDescriptor the {@link MetricDescriptor}.
* @param timeSeriesList the {@link TimeSeries} list for this metric.
* @return a {@code Metric}.
* @since 0.1.0
*/
public static Metric create(MetricDescriptor metricDescriptor, List<TimeSeries> timeSeriesList) {
Utils.checkListElementNotNull(
Utils.checkNotNull(timeSeriesList, "timeSeriesList"), "timeSeries");
return createInternal(
metricDescriptor, Collections.unmodifiableList(new ArrayList<TimeSeries>(timeSeriesList)));
}
/**
* Creates a {@link Metric}.
*
* @param metricDescriptor the {@link MetricDescriptor}.
* @param timeSeries the single {@link TimeSeries} for this metric.
* @return a {@code Metric}.
* @since 0.1.0
*/
public static Metric createWithOneTimeSeries(
MetricDescriptor metricDescriptor, TimeSeries timeSeries) {
return createInternal(
metricDescriptor, Collections.singletonList(Utils.checkNotNull(timeSeries, "timeSeries")));
}
/**
* Creates a {@link Metric}.
*
* @param metricDescriptor the {@link MetricDescriptor}.
* @param timeSeriesList the {@link TimeSeries} list for this metric.
* @return a {@code Metric}.
* @since 0.1.0
*/
private static Metric createInternal(
MetricDescriptor metricDescriptor, List<TimeSeries> timeSeriesList) {
Utils.checkNotNull(metricDescriptor, "metricDescriptor");
checkTypeMatch(metricDescriptor.getType(), timeSeriesList);
return new AutoValue_Metric(metricDescriptor, timeSeriesList);
}
/**
* Returns the {@link MetricDescriptor} of this metric.
*
* @return the {@code MetricDescriptor} of this metric.
* @since 0.1.0
*/
public abstract MetricDescriptor getMetricDescriptor();
/**
* Returns the {@link TimeSeries} list for this metric.
*
* <p>The type of the {@link TimeSeries#getPoints()} must match {@link MetricDescriptor.Type}.
*
* @return the {@code TimeSeriesList} for this metric.
* @since 0.1.0
*/
public abstract List<TimeSeries> getTimeSeriesList();
private static void checkTypeMatch(MetricDescriptor.Type type, List<TimeSeries> timeSeriesList) {
for (TimeSeries timeSeries : timeSeriesList) {
for (Point point : timeSeries.getPoints()) {
Value value = point.getValue();
String valueClassName = "";
if (value.getClass().getSuperclass() != null) { // work around nullness check
// AutoValue classes should always have a super class.
valueClassName = value.getClass().getSuperclass().getSimpleName();
}
switch (type) {
case GAUGE_INT64:
case CUMULATIVE_INT64:
Utils.checkArgument(
value instanceof ValueLong, "Type mismatch: %s, %s.", type, valueClassName);
break;
case CUMULATIVE_DOUBLE:
case GAUGE_DOUBLE:
Utils.checkArgument(
value instanceof ValueDouble, "Type mismatch: %s, %s.", type, valueClassName);
break;
case GAUGE_DISTRIBUTION:
case CUMULATIVE_DISTRIBUTION:
Utils.checkArgument(
value instanceof ValueDistribution, "Type mismatch: %s, %s.", type, valueClassName);
break;
case SUMMARY:
Utils.checkArgument(
value instanceof ValueSummary, "Type mismatch: %s, %s.", type, valueClassName);
}
}
}
}
}

View File

@ -0,0 +1,172 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import openconsensus.internal.Utils;
import openconsensus.metrics.LabelKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.concurrent.Immutable;
/**
* {@link MetricDescriptor} defines a {@code Metric} type and its schema.
*
* @since 0.1.0
*/
@ExperimentalApi
@Immutable
@AutoValue
public abstract class MetricDescriptor {
MetricDescriptor() {}
/**
* Creates a {@link MetricDescriptor}.
*
* @param name name of {@code MetricDescriptor}.
* @param description description of {@code MetricDescriptor}.
* @param unit the metric unit.
* @param type type of {@code MetricDescriptor}.
* @param labelKeys the label keys associated with the {@code MetricDescriptor}.
* @return a {@code MetricDescriptor}.
* @since 0.1.0
*/
public static MetricDescriptor create(
String name, String description, String unit, Type type, List<LabelKey> labelKeys) {
Utils.checkListElementNotNull(Utils.checkNotNull(labelKeys, "labelKeys"), "labelKey");
return new AutoValue_MetricDescriptor(
name,
description,
unit,
type,
Collections.unmodifiableList(new ArrayList<LabelKey>(labelKeys)));
}
/**
* Returns the metric descriptor name.
*
* @return the metric descriptor name.
* @since 0.1.0
*/
public abstract String getName();
/**
* Returns the description of this metric descriptor.
*
* @return the description of this metric descriptor.
* @since 0.1.0
*/
public abstract String getDescription();
/**
* Returns the unit of this metric descriptor.
*
* @return the unit of this metric descriptor.
* @since 0.1.0
*/
public abstract String getUnit();
/**
* Returns the type of this metric descriptor.
*
* @return the type of this metric descriptor.
* @since 0.1.0
*/
public abstract Type getType();
/**
* Returns the label keys associated with this metric descriptor.
*
* @return the label keys associated with this metric descriptor.
* @since 0.1.0
*/
public abstract List<LabelKey> getLabelKeys();
/**
* The kind of metric. It describes how the data is reported.
*
* <p>A gauge is an instantaneous measurement of a value.
*
* <p>A cumulative measurement is a value accumulated over a time interval. In a time series,
* cumulative measurements should have the same start time and increasing end times, until an
* event resets the cumulative value to zero and sets a new start time for the following points.
*
* @since 0.1.0
*/
public enum Type {
/**
* An instantaneous measurement of an int64 value.
*
* @since 0.1.0
*/
GAUGE_INT64,
/**
* An instantaneous measurement of a double value.
*
* @since 0.1.0
*/
GAUGE_DOUBLE,
/**
* An instantaneous measurement of a distribution value. The count and sum can go both up and
* down. Used in scenarios like a snapshot of time the current items in a queue have spent
* there.
*
* @since 0.1.0
*/
GAUGE_DISTRIBUTION,
/**
* An cumulative measurement of an int64 value.
*
* @since 0.1.0
*/
CUMULATIVE_INT64,
/**
* An cumulative measurement of a double value.
*
* @since 0.1.0
*/
CUMULATIVE_DOUBLE,
/**
* An cumulative measurement of a distribution value. The count and sum can only go up, if
* resets then the start_time should also be reset.
*
* @since 0.1.0
*/
CUMULATIVE_DISTRIBUTION,
/**
* Some frameworks implemented DISTRIBUTION as a summary of observations (usually things like
* request durations and response sizes). While it also provides a total count of observations
* and a sum of all observed values, it calculates configurable quantiles over a sliding time
* window.
*
* <p>This is not recommended, since it cannot be aggregated.
*
* @since 0.1.0
*/
SUMMARY,
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import openconsensus.common.ExperimentalApi;
import java.util.Collection;
/**
* A {@link Metric} producer that can be registered for exporting using {@link
* MetricProducerManager}.
*
* <p>All implementation MUST be thread-safe.
*
* @since 0.1.0
*/
@ExperimentalApi
public abstract class MetricProducer {
/**
* Returns a collection of produced {@link Metric}s to be exported.
*
* @return a collection of produced {@link Metric}s to be exported.
* @since 0.1.0
*/
public abstract Collection<Metric> getMetrics();
}

View File

@ -0,0 +1,88 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import openconsensus.common.ExperimentalApi;
import openconsensus.internal.Utils;
import java.util.Collections;
import java.util.Set;
import javax.annotation.concurrent.ThreadSafe;
/**
* Keeps a set of {@link MetricProducer} that is used by exporters to determine the metrics that
* need to be exported.
*
* @since 0.1.0
*/
@ExperimentalApi
@ThreadSafe
public abstract class MetricProducerManager {
/**
* Adds the {@link MetricProducer} to the manager if it is not already present.
*
* @param metricProducer the {@code MetricProducer} to be added to the manager.
* @since 0.1.0
*/
public abstract void add(MetricProducer metricProducer);
/**
* Removes the {@link MetricProducer} to the manager if it is present.
*
* @param metricProducer the {@code MetricProducer} to be removed from the manager.
* @since 0.1.0
*/
public abstract void remove(MetricProducer metricProducer);
/**
* Returns all registered {@link MetricProducer}s that should be exported.
*
* <p>This method should be used by any metrics exporter that automatically exports data for
* {@code MetricProducer} registered with the {@code MetricProducerManager}.
*
* @return all registered {@code MetricProducer}s that should be exported.
* @since 0.1.0
*/
public abstract Set<MetricProducer> getAllMetricProducer();
/**
* Returns a no-op implementation for {@link MetricProducerManager}.
*
* @return a no-op implementation for {@code MetricProducerManager}.
*/
static MetricProducerManager newNoopMetricProducerManager() {
return new NoopMetricProducerManager();
}
private static final class NoopMetricProducerManager extends MetricProducerManager {
@Override
public void add(MetricProducer metricProducer) {
Utils.checkNotNull(metricProducer, "metricProducer");
}
@Override
public void remove(MetricProducer metricProducer) {
Utils.checkNotNull(metricProducer, "metricProducer");
}
@Override
public Set<MetricProducer> getAllMetricProducer() {
return Collections.emptySet();
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import openconsensus.common.Timestamp;
import javax.annotation.concurrent.Immutable;
/**
* A timestamped measurement of a {@code TimeSeries}.
*
* @since 0.1.0
*/
@ExperimentalApi
@AutoValue
@Immutable
public abstract class Point {
Point() {}
/**
* Creates a {@link Point}.
*
* @param value the {@link Value} of this {@link Point}.
* @param timestamp the {@link Timestamp} when this {@link Point} was recorded.
* @return a {@code Point}.
* @since 0.1.0
*/
public static Point create(Value value, Timestamp timestamp) {
return new AutoValue_Point(value, timestamp);
}
/**
* Returns the {@link Value}.
*
* @return the {@code Value}.
* @since 0.1.0
*/
public abstract Value getValue();
/**
* Returns the {@link Timestamp} when this {@link Point} was recorded.
*
* @return the {@code Timestamp}.
* @since 0.1.0
*/
public abstract Timestamp getTimestamp();
}

View File

@ -0,0 +1,187 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import openconsensus.internal.Utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* Implementation of the {@link Distribution} as a summary of observations.
*
* <p>This is not recommended, since it cannot be aggregated.
*
* @since 0.1.0
*/
@ExperimentalApi
@AutoValue
@Immutable
public abstract class Summary {
Summary() {}
/**
* Creates a {@link Summary}.
*
* @param count the count of the population values.
* @param sum the sum of the population values.
* @param snapshot bucket boundaries of a histogram.
* @return a {@code Summary} with the given values.
* @since 0.1.0
*/
public static Summary create(@Nullable Long count, @Nullable Double sum, Snapshot snapshot) {
checkCountAndSum(count, sum);
Utils.checkNotNull(snapshot, "snapshot");
return new AutoValue_Summary(count, sum, snapshot);
}
/**
* Returns the aggregated count. If not available returns {@code null}.
*
* @return the aggregated count.
* @since 0.1.0
*/
@Nullable
public abstract Long getCount();
/**
* Returns the aggregated sum. If not available returns {@code null}.
*
* @return the aggregated sum.
* @since 0.1.0
*/
@Nullable
public abstract Double getSum();
/**
* Returns the {@link Snapshot}.
*
* @return the {@code Snapshot}.
* @since 0.1.0
*/
public abstract Snapshot getSnapshot();
/**
* Represents the summary observation of the recorded events over a sliding time window.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class Snapshot {
/**
* Returns the number of values in this {@code Snapshot}. If not available returns {@code null}.
*
* @return the number of values in this {@code Snapshot}.
* @since 0.1.0
*/
@Nullable
public abstract Long getCount();
/**
* Returns the sum of values in this {@code Snapshot}. If not available returns {@code null}.
*
* @return the sum of values in this {@code Snapshot}.
* @since 0.1.0
*/
@Nullable
public abstract Double getSum();
/**
* Returns the list of {@code ValueAtPercentile}s in this {@code Snapshot}.
*
* @return the list of {@code ValueAtPercentile}s in this {@code Snapshot}.
* @since 0.1.0
*/
public abstract List<ValueAtPercentile> getValueAtPercentiles();
/**
* Creates a {@link Snapshot}.
*
* @param count the number of values in this {@code Snapshot}.
* @param sum the number of values in this {@code Snapshot}.
* @param valueAtPercentiles the list of {@code ValueAtPercentile}.
* @return a {@code Snapshot} with the given values.
* @since 0.1.0
*/
public static Snapshot create(
@Nullable Long count, @Nullable Double sum, List<ValueAtPercentile> valueAtPercentiles) {
checkCountAndSum(count, sum);
Utils.checkListElementNotNull(
Utils.checkNotNull(valueAtPercentiles, "valueAtPercentiles"), "valueAtPercentile");
return new AutoValue_Summary_Snapshot(
count,
sum,
Collections.unmodifiableList(new ArrayList<ValueAtPercentile>(valueAtPercentiles)));
}
/**
* Represents the value at a given percentile of a distribution.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class ValueAtPercentile {
/**
* Returns the percentile in this {@code ValueAtPercentile}.
*
* <p>Must be in the interval (0.0, 100.0].
*
* @return the percentile in this {@code ValueAtPercentile}.
* @since 0.1.0
*/
public abstract double getPercentile();
/**
* Returns the value in this {@code ValueAtPercentile}.
*
* @return the value in this {@code ValueAtPercentile}.
* @since 0.1.0
*/
public abstract double getValue();
/**
* Creates a {@link ValueAtPercentile}.
*
* @param percentile the percentile in this {@code ValueAtPercentile}.
* @param value the value in this {@code ValueAtPercentile}.
* @return a {@code ValueAtPercentile} with the given values.
* @since 0.1.0
*/
public static ValueAtPercentile create(double percentile, double value) {
Utils.checkArgument(
0 < percentile && percentile <= 100.0,
"percentile must be in the interval (0.0, 100.0]");
Utils.checkArgument(value >= 0, "value must be non-negative");
return new AutoValue_Summary_Snapshot_ValueAtPercentile(percentile, value);
}
}
}
private static void checkCountAndSum(@Nullable Long count, @Nullable Double sum) {
Utils.checkArgument(count == null || count >= 0, "count must be non-negative.");
Utils.checkArgument(sum == null || sum >= 0, "sum must be non-negative.");
if (count != null && count == 0) {
Utils.checkArgument(sum == null || sum == 0, "sum must be 0 if count is 0.");
}
}
}

View File

@ -0,0 +1,148 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import openconsensus.common.Timestamp;
import openconsensus.internal.Utils;
import openconsensus.metrics.LabelKey;
import openconsensus.metrics.LabelValue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A collection of data points that describes the time-varying values of a {@code Metric}.
*
* @since 0.1.0
*/
@ExperimentalApi
@Immutable
@AutoValue
public abstract class TimeSeries {
TimeSeries() {}
/**
* Creates a {@link TimeSeries}.
*
* @param labelValues the {@code LabelValue}s that uniquely identify this {@code TimeSeries}.
* @param points the data {@code Point}s of this {@code TimeSeries}.
* @param startTimestamp the start {@code Timestamp} of this {@code TimeSeries}. Must be non-null
* for cumulative {@code Point}s.
* @return a {@code TimeSeries}.
* @since 0.1.0
*/
public static TimeSeries create(
List<LabelValue> labelValues, List<Point> points, @Nullable Timestamp startTimestamp) {
Utils.checkListElementNotNull(Utils.checkNotNull(points, "points"), "point");
return createInternal(
labelValues, Collections.unmodifiableList(new ArrayList<Point>(points)), startTimestamp);
}
/**
* Creates a {@link TimeSeries} with empty(or no) points.
*
* @param labelValues the {@code LabelValue}s that uniquely identify this {@code TimeSeries}.
* @return a {@code TimeSeries}.
* @since 0.1.0
*/
public static TimeSeries create(List<LabelValue> labelValues) {
return createInternal(labelValues, Collections.<Point>emptyList(), null);
}
/**
* Creates a {@link TimeSeries}.
*
* @param labelValues the {@code LabelValue}s that uniquely identify this {@code TimeSeries}.
* @param point the single data {@code Point} of this {@code TimeSeries}.
* @param startTimestamp the start {@code Timestamp} of this {@code TimeSeries}. Must be non-null
* for cumulative {@code Point}s.
* @return a {@code TimeSeries}.
* @since 0.1.0
*/
public static TimeSeries createWithOnePoint(
List<LabelValue> labelValues, Point point, @Nullable Timestamp startTimestamp) {
Utils.checkNotNull(point, "point");
return createInternal(labelValues, Collections.singletonList(point), startTimestamp);
}
/**
* Sets the {@code Point} of the {@link TimeSeries}.
*
* @param point the single data {@code Point} of this {@code TimeSeries}.
* @return a {@code TimeSeries}.
* @since 0.1.0
*/
public TimeSeries setPoint(Point point) {
Utils.checkNotNull(point, "point");
return new AutoValue_TimeSeries(getLabelValues(), Collections.singletonList(point), null);
}
/**
* Creates a {@link TimeSeries}.
*
* @param labelValues the {@code LabelValue}s that uniquely identify this {@code TimeSeries}.
* @param points the data {@code Point}s of this {@code TimeSeries}.
* @param startTimestamp the start {@code Timestamp} of this {@code TimeSeries}. Must be non-null
* for cumulative {@code Point}s.
* @return a {@code TimeSeries}.
*/
private static TimeSeries createInternal(
List<LabelValue> labelValues, List<Point> points, @Nullable Timestamp startTimestamp) {
// Fail fast on null lists to prevent NullPointerException when copying the lists.
Utils.checkListElementNotNull(Utils.checkNotNull(labelValues, "labelValues"), "labelValue");
return new AutoValue_TimeSeries(
Collections.unmodifiableList(new ArrayList<LabelValue>(labelValues)),
points,
startTimestamp);
}
/**
* Returns the set of {@link LabelValue}s that uniquely identify this {@link TimeSeries}.
*
* <p>Apply to all {@link Point}s.
*
* <p>The order of {@link LabelValue}s must match that of {@link LabelKey}s in the {@code
* MetricDescriptor}.
*
* @return the {@code LabelValue}s.
* @since 0.1.0
*/
public abstract List<LabelValue> getLabelValues();
/**
* Returns the data {@link Point}s of this {@link TimeSeries}.
*
* @return the data {@code Point}s.
* @since 0.1.0
*/
public abstract List<Point> getPoints();
/**
* Returns the start {@link Timestamp} of this {@link TimeSeries} if the {@link Point}s are
* cumulative, or {@code null} if the {@link Point}s are gauge.
*
* @return the start {@code Timestamp} or {@code null}.
* @since 0.1.0
*/
@Nullable
public abstract Timestamp getStartTimestamp();
}

View File

@ -0,0 +1,246 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.metrics.export;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import openconsensus.common.Function;
import javax.annotation.concurrent.Immutable;
/**
* The actual point value for a {@link Point}.
*
* <p>Currently there are three types of {@link Value}:
*
* <ul>
* <li>{@code double}
* <li>{@code long}
* <li>{@link Distribution}
* </ul>
*
* <p>Each {@link Point} contains exactly one of the three {@link Value} types.
*
* @since 0.1.0
*/
@ExperimentalApi
@Immutable
public abstract class Value {
Value() {}
/**
* Returns a double {@link Value}.
*
* @param value value in double.
* @return a double {@code Value}.
* @since 0.1.0
*/
public static Value doubleValue(double value) {
return ValueDouble.create(value);
}
/**
* Returns a long {@link Value}.
*
* @param value value in long.
* @return a long {@code Value}.
* @since 0.1.0
*/
public static Value longValue(long value) {
return ValueLong.create(value);
}
/**
* Returns a {@link Distribution} {@link Value}.
*
* @param value value in {@link Distribution}.
* @return a {@code Distribution} {@code Value}.
* @since 0.1.0
*/
public static Value distributionValue(Distribution value) {
return ValueDistribution.create(value);
}
/**
* Returns a {@link Summary} {@link Value}.
*
* @param value value in {@link Summary}.
* @return a {@code Summary} {@code Value}.
* @since 0.1.0
*/
public static Value summaryValue(Summary value) {
return ValueSummary.create(value);
}
/**
* Applies the given match function to the underlying data type.
*
* @since 0.1.0
*/
public abstract <T> T match(
Function<? super Double, T> doubleFunction,
Function<? super Long, T> longFunction,
Function<? super Distribution, T> distributionFunction,
Function<? super Summary, T> summaryFunction,
Function<? super Value, T> defaultFunction);
/** A 64-bit double-precision floating-point {@link Value}. */
@AutoValue
@Immutable
abstract static class ValueDouble extends Value {
ValueDouble() {}
@Override
public final <T> T match(
Function<? super Double, T> doubleFunction,
Function<? super Long, T> longFunction,
Function<? super Distribution, T> distributionFunction,
Function<? super Summary, T> summaryFunction,
Function<? super Value, T> defaultFunction) {
return doubleFunction.apply(getValue());
}
/**
* Creates a {@link ValueDouble}.
*
* @param value the value in double.
* @return a {@code ValueDouble}.
*/
static ValueDouble create(double value) {
return new AutoValue_Value_ValueDouble(value);
}
/**
* Returns the double value.
*
* @return the double value.
*/
abstract double getValue();
}
/** A 64-bit integer {@link Value}. */
@AutoValue
@Immutable
abstract static class ValueLong extends Value {
ValueLong() {}
@Override
public final <T> T match(
Function<? super Double, T> doubleFunction,
Function<? super Long, T> longFunction,
Function<? super Distribution, T> distributionFunction,
Function<? super Summary, T> summaryFunction,
Function<? super Value, T> defaultFunction) {
return longFunction.apply(getValue());
}
/**
* Creates a {@link ValueLong}.
*
* @param value the value in long.
* @return a {@code ValueLong}.
*/
static ValueLong create(long value) {
return new AutoValue_Value_ValueLong(value);
}
/**
* Returns the long value.
*
* @return the long value.
*/
abstract long getValue();
}
/**
* {@link ValueDistribution} contains summary statistics for a population of values. It optionally
* contains a histogram representing the distribution of those values across a set of buckets.
*/
@AutoValue
@Immutable
abstract static class ValueDistribution extends Value {
ValueDistribution() {}
@Override
public final <T> T match(
Function<? super Double, T> doubleFunction,
Function<? super Long, T> longFunction,
Function<? super Distribution, T> distributionFunction,
Function<? super Summary, T> summaryFunction,
Function<? super Value, T> defaultFunction) {
return distributionFunction.apply(getValue());
}
/**
* Creates a {@link ValueDistribution}.
*
* @param value the {@link Distribution} value.
* @return a {@code ValueDistribution}.
*/
static ValueDistribution create(Distribution value) {
return new AutoValue_Value_ValueDistribution(value);
}
/**
* Returns the {@link Distribution} value.
*
* @return the {@code Distribution} value.
*/
abstract Distribution getValue();
}
/**
* {@link ValueSummary} contains a snapshot representing values calculated over an arbitrary time
* window.
*/
@AutoValue
@Immutable
abstract static class ValueSummary extends Value {
ValueSummary() {}
@Override
public final <T> T match(
Function<? super Double, T> doubleFunction,
Function<? super Long, T> longFunction,
Function<? super Distribution, T> distributionFunction,
Function<? super Summary, T> summaryFunction,
Function<? super Value, T> defaultFunction) {
return summaryFunction.apply(getValue());
}
/**
* Creates a {@link ValueSummary}.
*
* @param value the {@link Summary} value.
* @return a {@code ValueSummary}.
*/
static ValueSummary create(Summary value) {
return new AutoValue_Value_ValueSummary(value);
}
/**
* Returns the {@link Summary} value.
*
* @return the {@code Summary} value.
*/
abstract Summary getValue();
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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.
*/
/**
* This package describes the Metrics data model. Metrics are a data model for what stats exporters
* take as input. This data model may eventually become the wire format for metrics.
*
* <p>WARNING: Currently all the public classes under this package are marked as {@link
* openconsensus.common.ExperimentalApi}. The classes and APIs under {@link io.opencensus.metrics}
* are likely to get backwards-incompatible updates in the future. DO NOT USE except for
* experimental purposes.
*
* <p>Please see
* https://github.com/census-instrumentation/opencensus-specs/blob/master/stats/Metrics.md and
* https://github.com/census-instrumentation/opencensus-proto/blob/master/opencensus/proto/stats/metrics/metrics.proto
* for more details.
*/
@ExperimentalApi
package openconsensus.metrics;
import openconsensus.common.ExperimentalApi;

View File

@ -0,0 +1,229 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.resource;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import openconsensus.internal.DefaultVisibilityForTesting;
import openconsensus.internal.StringUtils;
import openconsensus.internal.Utils;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.concurrent.Immutable;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
/**
* {@link Resource} represents a resource, which capture identifying information about the entities
* for which signals (stats or traces) are reported. It further provides a framework for detection
* of resource information from the environment and progressive population as signals propagate from
* the core instrumentation library to a backend's exporter.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
@ExperimentalApi
public abstract class Resource {
@DefaultVisibilityForTesting
static final int MAX_LENGTH = 255;
private static final String OC_RESOURCE_TYPE_ENV = "OC_RESOURCE_TYPE";
private static final String OC_RESOURCE_LABELS_ENV = "OC_RESOURCE_LABELS";
private static final String LABEL_LIST_SPLITTER = ",";
private static final String LABEL_KEY_VALUE_SPLITTER = "=";
private static final String ERROR_MESSAGE_INVALID_CHARS =
" should be a ASCII string with a length greater than 0 and not exceed "
+ MAX_LENGTH
+ " characters.";
private static final String ERROR_MESSAGE_INVALID_VALUE =
" should be a ASCII string with a length not exceed " + MAX_LENGTH + " characters.";
@javax.annotation.Nullable
private static final String ENV_TYPE = parseResourceType(System.getenv(OC_RESOURCE_TYPE_ENV));
private static final Map<String, String> ENV_LABEL_MAP =
parseResourceLabels(System.getenv(OC_RESOURCE_LABELS_ENV));
Resource() {}
/**
* Returns the type identifier for the resource.
*
* @return the type identifier for the resource.
* @since 0.1.0
*/
@javax.annotation.Nullable
public abstract String getType();
/**
* Returns a map of labels that describe the resource.
*
* @return a map of labels.
* @since 0.1.0
*/
public abstract Map<String, String> getLabels();
/**
* Returns a {@link Resource}. This resource information is loaded from the OC_RESOURCE_TYPE and
* OC_RESOURCE_LABELS environment variables.
*
* @return a {@code Resource}.
* @since 0.1.0
*/
public static Resource createFromEnvironmentVariables() {
return createInternal(ENV_TYPE, ENV_LABEL_MAP);
}
/**
* Returns a {@link Resource}.
*
* @param type the type identifier for the resource.
* @param labels a map of labels that describe the resource.
* @return a {@code Resource}.
* @throws NullPointerException if {@code labels} is null.
* @throws IllegalArgumentException if type or label key or label value is not a valid printable
* ASCII string or exceed {@link #MAX_LENGTH} characters.
* @since 0.1.0
*/
public static Resource create(
@javax.annotation.Nullable String type, Map<String, String> labels) {
return createInternal(
type,
Collections.unmodifiableMap(
new LinkedHashMap<String, String>(Utils.checkNotNull(labels, "labels"))));
}
/**
* Returns a {@link Resource} that runs all input resources sequentially and merges their results.
* In case a type of label key is already set, the first set value takes precedence.
*
* @param resources a list of resources.
* @return a {@code Resource}.
* @since 0.1.0
*/
@javax.annotation.Nullable
public static Resource mergeResources(List<Resource> resources) {
Resource currentResource = null;
for (Resource resource : resources) {
currentResource = merge(currentResource, resource);
}
return currentResource;
}
private static Resource createInternal(
@javax.annotation.Nullable String type, Map<String, String> labels) {
return new AutoValue_Resource(type, labels);
}
/**
* Creates a resource type from the OC_RESOURCE_TYPE environment variable.
*
* <p>OC_RESOURCE_TYPE: A string that describes the type of the resource prefixed by a domain
* namespace, e.g. kubernetes.io/container.
*/
@javax.annotation.Nullable
static String parseResourceType(@javax.annotation.Nullable String rawEnvType) {
if (rawEnvType != null && !rawEnvType.isEmpty()) {
Utils.checkArgument(isValidAndNotEmpty(rawEnvType), "Type" + ERROR_MESSAGE_INVALID_CHARS);
return rawEnvType.trim();
}
return rawEnvType;
}
/*
* Creates a label map from the OC_RESOURCE_LABELS environment variable.
*
* <p>OC_RESOURCE_LABELS: A comma-separated list of labels describing the source in more detail,
* e.g. key1=val1,key2=val2. Domain names and paths are accepted as label keys. Values may be
* quoted or unquoted in general. If a value contains whitespaces, =, or " characters, it must
* always be quoted.
*/
static Map<String, String> parseResourceLabels(@javax.annotation.Nullable String rawEnvLabels) {
if (rawEnvLabels == null) {
return Collections.<String, String>emptyMap();
} else {
Map<String, String> labels = new HashMap<String, String>();
String[] rawLabels = rawEnvLabels.split(LABEL_LIST_SPLITTER, -1);
for (String rawLabel : rawLabels) {
String[] keyValuePair = rawLabel.split(LABEL_KEY_VALUE_SPLITTER, -1);
if (keyValuePair.length != 2) {
continue;
}
String key = keyValuePair[0].trim();
String value = keyValuePair[1].trim().replaceAll("^\"|\"$", "");
Utils.checkArgument(isValidAndNotEmpty(key), "Label key" + ERROR_MESSAGE_INVALID_CHARS);
Utils.checkArgument(isValid(value), "Label value" + ERROR_MESSAGE_INVALID_VALUE);
labels.put(key, value);
}
return Collections.unmodifiableMap(labels);
}
}
/**
* Returns a new, merged {@link Resource} by merging two resources. In case of a collision, first
* resource takes precedence.
*/
@javax.annotation.Nullable
private static Resource merge(
@javax.annotation.Nullable Resource resource,
@javax.annotation.Nullable Resource otherResource) {
if (otherResource == null) {
return resource;
}
if (resource == null) {
return otherResource;
}
String mergedType = resource.getType() != null ? resource.getType() : otherResource.getType();
Map<String, String> mergedLabelMap =
new LinkedHashMap<String, String>(otherResource.getLabels());
// Labels from resource overwrite labels from otherResource.
for (Entry<String, String> entry : resource.getLabels().entrySet()) {
mergedLabelMap.put(entry.getKey(), entry.getValue());
}
return createInternal(mergedType, Collections.unmodifiableMap(mergedLabelMap));
}
/**
* Determines whether the given {@code String} is a valid printable ASCII string with a length not
* exceed {@link #MAX_LENGTH} characters.
*
* @param name the name to be validated.
* @return whether the name is valid.
*/
private static boolean isValid(String name) {
return name.length() <= MAX_LENGTH && StringUtils.isPrintableString(name);
}
/**
* Determines whether the given {@code String} is a valid printable ASCII string with a length
* greater than 0 and not exceed {@link #MAX_LENGTH} characters.
*
* @param name the name to be validated.
* @return whether the name is valid.
*/
private static boolean isValidAndNotEmpty(String name) {
return !name.isEmpty() && isValid(name);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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.
*/
/**
* API for resource information population.
*
* <p>The resource library primarily defines a type "Resource" that captures information about the
* entity for which stats or traces are recorded. For example, metrics exposed by a Kubernetes
* container can be linked to a resource that specifies the cluster, namespace, pod, and container
* name.
*
* <p>Two environment variables are used to populate resource information:
*
* <ul>
* <li>OC_RESOURCE_TYPE: A string that describes the type of the resource prefixed by a domain
* namespace. Leading and trailing whitespaces are trimmed. e.g. kubernetes.io/container.
* <li>OC_RESOURCE_LABELS: A comma-separated list of labels describing the source in more detail,
* e.g. key1=val1,key2=val2. The allowed character set is appropriately constrained.
* </ul>
*
* <p>Type, label keys, and label values MUST contain only printable ASCII (codes between 32 and
* 126, inclusive) and less than 256 characters. Type and label keys MUST have a length greater than
* zero. They SHOULD start with a domain name and separate hierarchies with / characters, e.g.
* k8s.io/namespace/name.
*
* <p>WARNING: Currently all the public classes under this package are marked as {@link
* openconsensus.common.ExperimentalApi}. DO NOT USE except for experimental purposes.
*
* <p>Please see
* https://github.com/census-instrumentation/opencensus-specs/blob/master/resource/Resource.md for
* more details.
*/
@ExperimentalApi
package openconsensus.resource;
import openconsensus.common.ExperimentalApi;

View File

@ -0,0 +1,239 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import com.google.auto.value.AutoValue;
import openconsensus.common.Function;
import openconsensus.internal.Utils;
import javax.annotation.concurrent.Immutable;
/**
* {@link Aggregation} is the process of combining a certain set of {@code MeasureValue}s for a
* given {@code Measure} into an {@link AggregationData}.
*
* <p>{@link Aggregation} currently supports 4 types of basic aggregation:
*
* <ul>
* <li>Sum
* <li>Count
* <li>Distribution
* <li>LastValue
* </ul>
*
* <p>When creating a {@link View}, one {@link Aggregation} needs to be specified as how to
* aggregate {@code MeasureValue}s.
*
* @since 0.1.0
*/
@Immutable
public abstract class Aggregation {
private Aggregation() {}
/**
* Applies the given match function to the underlying data type.
*
* @since 0.1.0
*/
public abstract <T> T match(
Function<? super Sum, T> p0,
Function<? super Count, T> p1,
Function<? super Distribution, T> p2,
Function<? super LastValue, T> p3,
Function<? super Aggregation, T> defaultFunction);
/**
* Calculate sum on aggregated {@code MeasureValue}s.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class Sum extends Aggregation {
Sum() {}
private static final Sum INSTANCE = new AutoValue_Aggregation_Sum();
/**
* Construct a {@code Sum}.
*
* @return a new {@code Sum}.
* @since 0.1.0
*/
public static Sum create() {
return INSTANCE;
}
@Override
public final <T> T match(
Function<? super Sum, T> p0,
Function<? super Count, T> p1,
Function<? super Distribution, T> p2,
Function<? super LastValue, T> p3,
Function<? super Aggregation, T> defaultFunction) {
return p0.apply(this);
}
}
/**
* Calculate count on aggregated {@code MeasureValue}s.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class Count extends Aggregation {
Count() {}
private static final Count INSTANCE = new AutoValue_Aggregation_Count();
/**
* Construct a {@code Count}.
*
* @return a new {@code Count}.
* @since 0.1.0
*/
public static Count create() {
return INSTANCE;
}
@Override
public final <T> T match(
Function<? super Sum, T> p0,
Function<? super Count, T> p1,
Function<? super Distribution, T> p2,
Function<? super LastValue, T> p3,
Function<? super Aggregation, T> defaultFunction) {
return p1.apply(this);
}
}
/**
* Calculate mean on aggregated {@code MeasureValue}s.
*
* @since 0.1.0
* @deprecated since 0.13, use {@link Distribution} instead.
*/
@Immutable
@AutoValue
@Deprecated
@AutoValue.CopyAnnotations
public abstract static class Mean extends Aggregation {
Mean() {}
private static final Mean INSTANCE = new AutoValue_Aggregation_Mean();
/**
* Construct a {@code Mean}.
*
* @return a new {@code Mean}.
* @since 0.1.0
*/
public static Mean create() {
return INSTANCE;
}
@Override
public final <T> T match(
Function<? super Sum, T> p0,
Function<? super Count, T> p1,
Function<? super Distribution, T> p2,
Function<? super LastValue, T> p3,
Function<? super Aggregation, T> defaultFunction) {
return defaultFunction.apply(this);
}
}
/**
* Calculate distribution stats on aggregated {@code MeasureValue}s. Distribution includes mean,
* count, histogram, min, max and sum of squared deviations.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class Distribution extends Aggregation {
Distribution() {}
/**
* Construct a {@code Distribution}.
*
* @return a new {@code Distribution}.
* @since 0.1.0
*/
public static Distribution create(BucketBoundaries bucketBoundaries) {
Utils.checkNotNull(bucketBoundaries, "bucketBoundaries");
return new AutoValue_Aggregation_Distribution(bucketBoundaries);
}
/**
* Returns the {@code Distribution}'s bucket boundaries.
*
* @return the {@code Distribution}'s bucket boundaries.
* @since 0.1.0
*/
public abstract BucketBoundaries getBucketBoundaries();
@Override
public final <T> T match(
Function<? super Sum, T> p0,
Function<? super Count, T> p1,
Function<? super Distribution, T> p2,
Function<? super LastValue, T> p3,
Function<? super Aggregation, T> defaultFunction) {
return p2.apply(this);
}
}
/**
* Calculate the last value of aggregated {@code MeasureValue}s.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class LastValue extends Aggregation {
LastValue() {}
private static final LastValue INSTANCE = new AutoValue_Aggregation_LastValue();
/**
* Construct a {@code LastValue}.
*
* @return a new {@code LastValue}.
* @since 0.1.0
*/
public static LastValue create() {
return INSTANCE;
}
@Override
public final <T> T match(
Function<? super Sum, T> p0,
Function<? super Count, T> p1,
Function<? super Distribution, T> p2,
Function<? super LastValue, T> p3,
Function<? super Aggregation, T> defaultFunction) {
return p3.apply(this);
}
}
}

View File

@ -0,0 +1,532 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import com.google.auto.value.AutoValue;
import openconsensus.common.Function;
import openconsensus.internal.Utils;
import openconsensus.metrics.data.Exemplar;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.concurrent.Immutable;
/**
* {@link AggregationData} is the result of applying a given {@link Aggregation} to a set of {@code
* MeasureValue}s.
*
* <p>{@link AggregationData} currently supports 6 types of basic aggregation values:
*
* <ul>
* <li>SumDataDouble
* <li>SumDataLong
* <li>CountData
* <li>DistributionData
* <li>LastValueDataDouble
* <li>LastValueDataLong
* </ul>
*
* <p>{@link ViewData} will contain one {@link AggregationData}, corresponding to its {@link
* Aggregation} definition in {@link View}.
*
* @since 0.1.0
*/
@Immutable
public abstract class AggregationData {
private AggregationData() {}
/**
* Applies the given match function to the underlying data type.
*
* @since 0.1.0
*/
public abstract <T> T match(
Function<? super SumDataDouble, T> p0,
Function<? super SumDataLong, T> p1,
Function<? super CountData, T> p2,
Function<? super DistributionData, T> p3,
Function<? super LastValueDataDouble, T> p4,
Function<? super LastValueDataLong, T> p5,
Function<? super AggregationData, T> defaultFunction);
/**
* The sum value of aggregated {@code MeasureValueDouble}s.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class SumDataDouble extends AggregationData {
SumDataDouble() {}
/**
* Creates a {@code SumDataDouble}.
*
* @param sum the aggregated sum.
* @return a {@code SumDataDouble}.
* @since 0.1.0
*/
public static SumDataDouble create(double sum) {
return new AutoValue_AggregationData_SumDataDouble(sum);
}
/**
* Returns the aggregated sum.
*
* @return the aggregated sum.
* @since 0.1.0
*/
public abstract double getSum();
@Override
public final <T> T match(
Function<? super SumDataDouble, T> p0,
Function<? super SumDataLong, T> p1,
Function<? super CountData, T> p2,
Function<? super DistributionData, T> p3,
Function<? super LastValueDataDouble, T> p4,
Function<? super LastValueDataLong, T> p5,
Function<? super AggregationData, T> defaultFunction) {
return p0.apply(this);
}
}
/**
* The sum value of aggregated {@code MeasureValueLong}s.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class SumDataLong extends AggregationData {
SumDataLong() {}
/**
* Creates a {@code SumDataLong}.
*
* @param sum the aggregated sum.
* @return a {@code SumDataLong}.
* @since 0.1.0
*/
public static SumDataLong create(long sum) {
return new AutoValue_AggregationData_SumDataLong(sum);
}
/**
* Returns the aggregated sum.
*
* @return the aggregated sum.
* @since 0.1.0
*/
public abstract long getSum();
@Override
public final <T> T match(
Function<? super SumDataDouble, T> p0,
Function<? super SumDataLong, T> p1,
Function<? super CountData, T> p2,
Function<? super DistributionData, T> p3,
Function<? super LastValueDataDouble, T> p4,
Function<? super LastValueDataLong, T> p5,
Function<? super AggregationData, T> defaultFunction) {
return p1.apply(this);
}
}
/**
* The count value of aggregated {@code MeasureValue}s.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class CountData extends AggregationData {
CountData() {}
/**
* Creates a {@code CountData}.
*
* @param count the aggregated count.
* @return a {@code CountData}.
* @since 0.1.0
*/
public static CountData create(long count) {
return new AutoValue_AggregationData_CountData(count);
}
/**
* Returns the aggregated count.
*
* @return the aggregated count.
* @since 0.1.0
*/
public abstract long getCount();
@Override
public final <T> T match(
Function<? super SumDataDouble, T> p0,
Function<? super SumDataLong, T> p1,
Function<? super CountData, T> p2,
Function<? super DistributionData, T> p3,
Function<? super LastValueDataDouble, T> p4,
Function<? super LastValueDataLong, T> p5,
Function<? super AggregationData, T> defaultFunction) {
return p2.apply(this);
}
}
/**
* The mean value of aggregated {@code MeasureValue}s.
*
* @since 0.1.0
* @deprecated since 0.13, use {@link DistributionData} instead.
*/
@Immutable
@AutoValue
@Deprecated
@AutoValue.CopyAnnotations
public abstract static class MeanData extends AggregationData {
MeanData() {}
/**
* Creates a {@code MeanData}.
*
* @param mean the aggregated mean.
* @param count the aggregated count.
* @return a {@code MeanData}.
* @since 0.1.0
*/
public static MeanData create(double mean, long count) {
return new AutoValue_AggregationData_MeanData(mean, count);
}
/**
* Returns the aggregated mean.
*
* @return the aggregated mean.
* @since 0.1.0
*/
public abstract double getMean();
/**
* Returns the aggregated count.
*
* @return the aggregated count.
* @since 0.1.0
*/
public abstract long getCount();
@Override
public final <T> T match(
Function<? super SumDataDouble, T> p0,
Function<? super SumDataLong, T> p1,
Function<? super CountData, T> p2,
Function<? super DistributionData, T> p3,
Function<? super LastValueDataDouble, T> p4,
Function<? super LastValueDataLong, T> p5,
Function<? super AggregationData, T> defaultFunction) {
return defaultFunction.apply(this);
}
}
/**
* The distribution stats of aggregated {@code MeasureValue}s. Distribution stats include mean,
* count, histogram, min, max and sum of squared deviations.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class DistributionData extends AggregationData {
DistributionData() {}
/**
* Creates a {@code DistributionData}.
*
* @param mean mean value.
* @param count count value.
* @param min min value.
* @param max max value.
* @param sumOfSquaredDeviations sum of squared deviations.
* @param bucketCounts histogram bucket counts.
* @param exemplars the exemplars associated with histogram buckets.
* @return a {@code DistributionData}.
* @since 0.1.0
* @deprecated since 0.17. Use {@link #create(double, long, double, List, List)}
*/
@Deprecated
@SuppressWarnings("InconsistentOverloads")
public static DistributionData create(
double mean,
long count,
double min,
double max,
double sumOfSquaredDeviations,
List<Long> bucketCounts,
List<Exemplar> exemplars) {
return create(mean, count, sumOfSquaredDeviations, bucketCounts, exemplars);
}
/**
* Creates a {@code DistributionData}.
*
* @param mean mean value.
* @param count count value.
* @param sumOfSquaredDeviations sum of squared deviations.
* @param bucketCounts histogram bucket counts.
* @param exemplars the exemplars associated with histogram buckets.
* @return a {@code DistributionData}.
* @since 0.1.0
*/
public static DistributionData create(
double mean,
long count,
double sumOfSquaredDeviations,
List<Long> bucketCounts,
List<Exemplar> exemplars) {
List<Long> bucketCountsCopy =
Collections.unmodifiableList(
new ArrayList<Long>(Utils.checkNotNull(bucketCounts, "bucketCounts")));
for (Long bucketCount : bucketCountsCopy) {
Utils.checkNotNull(bucketCount, "bucketCount");
}
Utils.checkNotNull(exemplars, "exemplars");
for (Exemplar exemplar : exemplars) {
Utils.checkNotNull(exemplar, "exemplar");
}
return new AutoValue_AggregationData_DistributionData(
mean,
count,
sumOfSquaredDeviations,
bucketCountsCopy,
Collections.<Exemplar>unmodifiableList(new ArrayList<Exemplar>(exemplars)));
}
/**
* Creates a {@code DistributionData}.
*
* @param mean mean value.
* @param count count value.
* @param min min value.
* @param max max value.
* @param sumOfSquaredDeviations sum of squared deviations.
* @param bucketCounts histogram bucket counts.
* @return a {@code DistributionData}.
* @since 0.1.0
* @deprecated since 0.17. Use {@link #create(double, long, double, List)}.
*/
@Deprecated
@SuppressWarnings("InconsistentOverloads")
public static DistributionData create(
double mean,
long count,
double min,
double max,
double sumOfSquaredDeviations,
List<Long> bucketCounts) {
return create(
mean, count, sumOfSquaredDeviations, bucketCounts, Collections.<Exemplar>emptyList());
}
/**
* Creates a {@code DistributionData}.
*
* @param mean mean value.
* @param count count value.
* @param sumOfSquaredDeviations sum of squared deviations.
* @param bucketCounts histogram bucket counts.
* @return a {@code DistributionData}.
* @since 0.1.0
*/
public static DistributionData create(
double mean, long count, double sumOfSquaredDeviations, List<Long> bucketCounts) {
return create(
mean, count, sumOfSquaredDeviations, bucketCounts, Collections.<Exemplar>emptyList());
}
/**
* Returns the aggregated mean.
*
* @return the aggregated mean.
* @since 0.1.0
*/
public abstract double getMean();
/**
* Returns the aggregated count.
*
* @return the aggregated count.
* @since 0.1.0
*/
public abstract long getCount();
/**
* Returns the minimum of the population values.
*
* @return the minimum of the population values.
* @since 0.1.0
* @deprecated since 0.17. Returns {@code 0}.
*/
@Deprecated
public double getMin() {
return 0;
}
/**
* Returns the maximum of the population values.
*
* @return the maximum of the population values.
* @since 0.1.0
* @deprecated since 0.17. Returns {@code 0}.
*/
@Deprecated
public double getMax() {
return 0;
}
/**
* Returns the aggregated sum of squared deviations.
*
* @return the aggregated sum of squared deviations.
* @since 0.1.0
*/
public abstract double getSumOfSquaredDeviations();
/**
* Returns the aggregated bucket counts. The returned list is immutable, trying to update it
* will throw an {@code UnsupportedOperationException}.
*
* @return the aggregated bucket counts.
* @since 0.1.0
*/
public abstract List<Long> getBucketCounts();
/**
* Returns the {@link Exemplar}s associated with histogram buckets.
*
* @return the {@code Exemplar}s associated with histogram buckets.
* @since 0.1.0
*/
public abstract List<Exemplar> getExemplars();
@Override
public final <T> T match(
Function<? super SumDataDouble, T> p0,
Function<? super SumDataLong, T> p1,
Function<? super CountData, T> p2,
Function<? super DistributionData, T> p3,
Function<? super LastValueDataDouble, T> p4,
Function<? super LastValueDataLong, T> p5,
Function<? super AggregationData, T> defaultFunction) {
return p3.apply(this);
}
}
/**
* The last value of aggregated {@code MeasureValueDouble}s.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class LastValueDataDouble extends AggregationData {
LastValueDataDouble() {}
/**
* Creates a {@code LastValueDataDouble}.
*
* @param lastValue the last value.
* @return a {@code LastValueDataDouble}.
* @since 0.1.0
*/
public static LastValueDataDouble create(double lastValue) {
return new AutoValue_AggregationData_LastValueDataDouble(lastValue);
}
/**
* Returns the last value.
*
* @return the last value.
* @since 0.1.0
*/
public abstract double getLastValue();
@Override
public final <T> T match(
Function<? super SumDataDouble, T> p0,
Function<? super SumDataLong, T> p1,
Function<? super CountData, T> p2,
Function<? super DistributionData, T> p3,
Function<? super LastValueDataDouble, T> p4,
Function<? super LastValueDataLong, T> p5,
Function<? super AggregationData, T> defaultFunction) {
return p4.apply(this);
}
}
/**
* The last value of aggregated {@code MeasureValueLong}s.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class LastValueDataLong extends AggregationData {
LastValueDataLong() {}
/**
* Creates a {@code LastValueDataLong}.
*
* @param lastValue the last value.
* @return a {@code LastValueDataLong}.
* @since 0.1.0
*/
public static LastValueDataLong create(long lastValue) {
return new AutoValue_AggregationData_LastValueDataLong(lastValue);
}
/**
* Returns the last value.
*
* @return the last value.
* @since 0.1.0
*/
public abstract long getLastValue();
@Override
public final <T> T match(
Function<? super SumDataDouble, T> p0,
Function<? super SumDataLong, T> p1,
Function<? super CountData, T> p2,
Function<? super DistributionData, T> p3,
Function<? super LastValueDataDouble, T> p4,
Function<? super LastValueDataLong, T> p5,
Function<? super AggregationData, T> defaultFunction) {
return p5.apply(this);
}
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import com.google.auto.value.AutoValue;
import openconsensus.internal.Utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
/**
* The bucket boundaries for a histogram.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class BucketBoundaries {
private static final Logger logger = Logger.getLogger(BucketBoundaries.class.getName());
/**
* Returns a {@code BucketBoundaries} with the given buckets.
*
* @param bucketBoundaries the boundaries for the buckets in the underlying histogram.
* @return a new {@code BucketBoundaries} with the specified boundaries.
* @throws NullPointerException if {@code bucketBoundaries} is null.
* @throws IllegalArgumentException if {@code bucketBoundaries} is not sorted.
* @since 0.1.0
*/
public static final BucketBoundaries create(List<Double> bucketBoundaries) {
Utils.checkNotNull(bucketBoundaries, "bucketBoundaries");
List<Double> bucketBoundariesCopy = new ArrayList<Double>(bucketBoundaries); // Deep copy.
// Check if sorted.
if (bucketBoundariesCopy.size() > 1) {
double previous = bucketBoundariesCopy.get(0);
for (int i = 1; i < bucketBoundariesCopy.size(); i++) {
double next = bucketBoundariesCopy.get(i);
Utils.checkArgument(previous < next, "Bucket boundaries not sorted.");
previous = next;
}
}
return new AutoValue_BucketBoundaries(
Collections.unmodifiableList(dropNegativeBucketBounds(bucketBoundariesCopy)));
}
private static List<Double> dropNegativeBucketBounds(List<Double> bucketBoundaries) {
// Negative values (BucketBounds) are currently not supported by any of the backends
// that OC supports.
int negativeBucketBounds = 0;
int zeroBucketBounds = 0;
for (Double value : bucketBoundaries) {
if (value <= 0) {
if (value == 0) {
zeroBucketBounds++;
} else {
negativeBucketBounds++;
}
} else {
break;
}
}
if (negativeBucketBounds > 0) {
logger.log(
Level.WARNING,
"Dropping "
+ negativeBucketBounds
+ " negative bucket boundaries, the values must be strictly > 0.");
}
return bucketBoundaries.subList(
negativeBucketBounds + zeroBucketBounds, bucketBoundaries.size());
}
/**
* Returns a list of histogram bucket boundaries.
*
* @return a list of histogram bucket boundaries.
* @since 0.1.0
*/
public abstract List<Double> getBoundaries();
}

View File

@ -0,0 +1,177 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import com.google.auto.value.AutoValue;
import openconsensus.common.Function;
import openconsensus.internal.DefaultVisibilityForTesting;
import openconsensus.internal.StringUtils;
import openconsensus.internal.Utils;
import javax.annotation.concurrent.Immutable;
/**
* The definition of the {@link Measurement} that is taken by OpenCensus library.
*
* @since 0.1.0
*/
@Immutable
public abstract class Measure {
@DefaultVisibilityForTesting static final int NAME_MAX_LENGTH = 255;
private static final String ERROR_MESSAGE_INVALID_NAME =
"Name should be a ASCII string with a length no greater than "
+ NAME_MAX_LENGTH
+ " characters.";
/**
* Applies the given match function to the underlying data type.
*
* @since 0.1.0
*/
public abstract <T> T match(
Function<? super MeasureDouble, T> p0,
Function<? super MeasureLong, T> p1,
Function<? super Measure, T> defaultFunction);
/**
* Name of measure, as a {@code String}. Should be a ASCII string with a length no greater than
* 255 characters.
*
* <p>Suggested format for name: {@code <web_host>/<path>}.
*
* @since 0.1.0
*/
public abstract String getName();
/**
* Detailed description of the measure, used in documentation.
*
* @since 0.1.0
*/
public abstract String getDescription();
/**
* The units in which {@link Measure} values are measured.
*
* <p>The suggested grammar for a unit is as follows:
*
* <ul>
* <li>Expression = Component { "." Component } {"/" Component };
* <li>Component = [ PREFIX ] UNIT [ Annotation ] | Annotation | "1";
* <li>Annotation = "{" NAME "}" ;
* </ul>
*
* <p>For example, string MBy{transmitted}/ms stands for megabytes per milliseconds, and the
* annotation transmitted inside {} is just a comment of the unit.
*
* @since 0.1.0
*/
// TODO(songya): determine whether we want to check the grammar on string unit.
public abstract String getUnit();
// Prevents this class from being subclassed anywhere else.
private Measure() {}
/**
* {@link Measure} with {@code Double} typed values.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class MeasureDouble extends Measure {
MeasureDouble() {}
/**
* Constructs a new {@link MeasureDouble}.
*
* @param name name of {@code Measure}. Suggested format: {@code <web_host>/<path>}.
* @param description description of {@code Measure}.
* @param unit unit of {@code Measure}.
* @return a {@code MeasureDouble}.
* @since 0.1.0
*/
public static MeasureDouble create(String name, String description, String unit) {
Utils.checkArgument(
StringUtils.isPrintableString(name) && name.length() <= NAME_MAX_LENGTH,
ERROR_MESSAGE_INVALID_NAME);
return new AutoValue_Measure_MeasureDouble(name, description, unit);
}
@Override
public <T> T match(
Function<? super MeasureDouble, T> p0,
Function<? super MeasureLong, T> p1,
Function<? super Measure, T> defaultFunction) {
return p0.apply(this);
}
@Override
public abstract String getName();
@Override
public abstract String getDescription();
@Override
public abstract String getUnit();
}
/**
* {@link Measure} with {@code Long} typed values.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class MeasureLong extends Measure {
MeasureLong() {}
/**
* Constructs a new {@link MeasureLong}.
*
* @param name name of {@code Measure}. Suggested format: {@code <web_host>/<path>}.
* @param description description of {@code Measure}.
* @param unit unit of {@code Measure}.
* @return a {@code MeasureLong}.
* @since 0.1.0
*/
public static MeasureLong create(String name, String description, String unit) {
Utils.checkArgument(
StringUtils.isPrintableString(name) && name.length() <= NAME_MAX_LENGTH,
ERROR_MESSAGE_INVALID_NAME);
return new AutoValue_Measure_MeasureLong(name, description, unit);
}
@Override
public <T> T match(
Function<? super MeasureDouble, T> p0,
Function<? super MeasureLong, T> p1,
Function<? super Measure, T> defaultFunction) {
return p1.apply(this);
}
@Override
public abstract String getName();
@Override
public abstract String getDescription();
@Override
public abstract String getUnit();
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import openconsensus.internal.Utils;
import openconsensus.metrics.data.AttachmentValue;
import openconsensus.metrics.data.AttachmentValue.AttachmentValueString;
import openconsensus.stats.Measure.MeasureDouble;
import openconsensus.stats.Measure.MeasureLong;
import openconsensus.tags.TagContext;
import javax.annotation.concurrent.NotThreadSafe;
/**
* A map from {@link Measure}s to measured values to be recorded at the same time.
*
* @since 0.1.0
*/
@NotThreadSafe
public abstract class MeasureMap {
/**
* Associates the {@link MeasureDouble} with the given value. Subsequent updates to the same
* {@link MeasureDouble} will overwrite the previous value.
*
* @param measure the {@link MeasureDouble}
* @param value the value to be associated with {@code measure}
* @return this
* @since 0.1.0
*/
public abstract MeasureMap put(MeasureDouble measure, double value);
/**
* Associates the {@link MeasureLong} with the given value. Subsequent updates to the same {@link
* MeasureLong} will overwrite the previous value.
*
* @param measure the {@link MeasureLong}
* @param value the value to be associated with {@code measure}
* @return this
* @since 0.1.0
*/
public abstract MeasureMap put(MeasureLong measure, long value);
/**
* Associate the contextual information of an {@code Exemplar} to this {@link MeasureMap}.
* Contextual information is represented as {@code String} key-value pairs.
*
* <p>If this method is called multiple times with the same key, only the last value will be kept.
*
* @param key the key of contextual information of an {@code Exemplar}.
* @param value the string representation of contextual information of an {@code Exemplar}.
* @return this
* @since 0.1.0
* @deprecated in favor of {@link #putAttachment(String, AttachmentValue)}.
*/
@Deprecated
public MeasureMap putAttachment(String key, String value) {
return putAttachment(key, AttachmentValueString.create(value));
}
/**
* Associate the contextual information of an {@code Exemplar} to this {@link MeasureMap}.
* Contextual information is represented as a {@code String} key and an {@link AttachmentValue}.
*
* <p>If this method is called multiple times with the same key, only the last value will be kept.
*
* @param key the key of contextual information of an {@code Exemplar}.
* @param value the value of contextual information of an {@code Exemplar}.
* @return this
* @since 0.1.0
*/
public MeasureMap putAttachment(String key, AttachmentValue value) {
// Provides a default no-op implementation to avoid breaking other existing sub-classes.
Utils.checkNotNull(key, "key");
Utils.checkNotNull(value, "value");
return this;
}
/**
* Records all of the measures at the same time, with the current {@link TagContext}.
*
* <p>This method records all of the stats in the {@code MeasureMap} every time it is called.
*
* @since 0.1.0
*/
public abstract void record();
/**
* Records all of the measures at the same time, with an explicit {@link TagContext}.
*
* <p>This method records all of the stats in the {@code MeasureMap} every time it is called.
*
* @param tags the tags associated with the measurements.
* @since 0.1.0
*/
public abstract void record(TagContext tags);
}

View File

@ -0,0 +1,130 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import com.google.auto.value.AutoValue;
import openconsensus.common.Function;
import openconsensus.stats.Measure.MeasureDouble;
import openconsensus.stats.Measure.MeasureLong;
import javax.annotation.concurrent.Immutable;
/**
* Immutable representation of a Measurement.
*
* @since 0.1.0
*/
@Immutable
public abstract class Measurement {
/**
* Applies the given match function to the underlying data type.
*
* @since 0.1.0
*/
public abstract <T> T match(
Function<? super MeasurementDouble, T> p0,
Function<? super MeasurementLong, T> p1,
Function<? super Measurement, T> defaultFunction);
/**
* Extracts the measured {@link Measure}.
*
* @since 0.1.0
*/
public abstract Measure getMeasure();
// Prevents this class from being subclassed anywhere else.
private Measurement() {}
/**
* {@code Double} typed {@link Measurement}.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class MeasurementDouble extends Measurement {
MeasurementDouble() {}
/**
* Constructs a new {@link MeasurementDouble}.
*
* @since 0.1.0
*/
public static MeasurementDouble create(MeasureDouble measure, double value) {
return new AutoValue_Measurement_MeasurementDouble(measure, value);
}
@Override
public abstract MeasureDouble getMeasure();
/**
* Returns the value for the measure.
*
* @return the value for the measure.
* @since 0.1.0
*/
public abstract double getValue();
@Override
public <T> T match(
Function<? super MeasurementDouble, T> p0,
Function<? super MeasurementLong, T> p1,
Function<? super Measurement, T> defaultFunction) {
return p0.apply(this);
}
}
/**
* {@code Long} typed {@link Measurement}.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract static class MeasurementLong extends Measurement {
MeasurementLong() {}
/**
* Constructs a new {@link MeasurementLong}.
*
* @since 0.1.0
*/
public static MeasurementLong create(MeasureLong measure, long value) {
return new AutoValue_Measurement_MeasurementLong(measure, value);
}
@Override
public abstract MeasureLong getMeasure();
/**
* Returns the value for the measure.
*
* @return the value for the measure.
* @since 0.1.0
*/
public abstract long getValue();
@Override
public <T> T match(
Function<? super MeasurementDouble, T> p0,
Function<? super MeasurementLong, T> p1,
Function<? super Measurement, T> defaultFunction) {
return p1.apply(this);
}
}
}

View File

@ -0,0 +1,234 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import openconsensus.common.Functions;
import openconsensus.common.Timestamp;
import openconsensus.internal.Utils;
import openconsensus.stats.Measure.MeasureDouble;
import openconsensus.stats.Measure.MeasureLong;
import openconsensus.tags.TagContext;
import openconsensus.tags.TagValue;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
/** No-op implementations of stats classes. */
final class NoopStats {
private NoopStats() {}
/**
* Returns a {@code StatsComponent} that has a no-op implementation for {@link StatsRecorder}.
*
* @return a {@code StatsComponent} that has a no-op implementation for {@code StatsRecorder}.
*/
static StatsComponent newNoopStatsComponent() {
return new NoopStatsComponent();
}
/**
* Returns a {@code StatsRecorder} that does not record any data.
*
* @return a {@code StatsRecorder} that does not record any data.
*/
static StatsRecorder getNoopStatsRecorder() {
return NoopStatsRecorder.INSTANCE;
}
/**
* Returns a {@code MeasureMap} that ignores all calls to {@link MeasureMap#put}.
*
* @return a {@code MeasureMap} that ignores all calls to {@code MeasureMap#put}.
*/
static MeasureMap newNoopMeasureMap() {
return new NoopMeasureMap();
}
/**
* Returns a {@code ViewManager} that maintains a map of views, but always returns empty {@link
* ViewData}s.
*
* @return a {@code ViewManager} that maintains a map of views, but always returns empty {@code
* ViewData}s.
*/
static ViewManager newNoopViewManager() {
return new NoopViewManager();
}
@ThreadSafe
private static final class NoopStatsComponent extends StatsComponent {
private final ViewManager viewManager = newNoopViewManager();
private volatile boolean isRead;
@Override
public ViewManager getViewManager() {
return viewManager;
}
@Override
public StatsRecorder getStatsRecorder() {
return getNoopStatsRecorder();
}
@Override
public StatsCollectionState getState() {
isRead = true;
return StatsCollectionState.DISABLED;
}
@Override
@Deprecated
public void setState(StatsCollectionState state) {
Utils.checkNotNull(state, "state");
Utils.checkState(!isRead, "State was already read, cannot set state.");
}
}
@Immutable
private static final class NoopStatsRecorder extends StatsRecorder {
static final StatsRecorder INSTANCE = new NoopStatsRecorder();
@Override
public MeasureMap newMeasureMap() {
return newNoopMeasureMap();
}
}
private static final class NoopMeasureMap extends MeasureMap {
private static final Logger logger = Logger.getLogger(NoopMeasureMap.class.getName());
private boolean hasUnsupportedValues;
@Override
public MeasureMap put(MeasureDouble measure, double value) {
if (value < 0) {
hasUnsupportedValues = true;
}
return this;
}
@Override
public MeasureMap put(MeasureLong measure, long value) {
if (value < 0) {
hasUnsupportedValues = true;
}
return this;
}
@Override
public void record() {}
@Override
public void record(TagContext tags) {
Utils.checkNotNull(tags, "tags");
if (hasUnsupportedValues) {
// drop all the recorded values
logger.log(Level.WARNING, "Dropping values, value to record must be non-negative.");
}
}
}
@ThreadSafe
private static final class NoopViewManager extends ViewManager {
private static final Timestamp ZERO_TIMESTAMP = Timestamp.create(0, 0);
@GuardedBy("registeredViews")
private final Map<View.Name, View> registeredViews = new HashMap<View.Name, View>();
// Cached set of exported views. It must be set to null whenever a view is registered or
// unregistered.
@javax.annotation.Nullable private volatile Set<View> exportedViews;
@Override
public void registerView(View newView) {
Utils.checkNotNull(newView, "newView");
synchronized (registeredViews) {
exportedViews = null;
View existing = registeredViews.get(newView.getName());
Utils.checkArgument(
existing == null || newView.equals(existing),
"A different view with the same name already exists.");
if (existing == null) {
registeredViews.put(newView.getName(), newView);
}
}
}
@Override
@javax.annotation.Nullable
@SuppressWarnings("deprecation")
public ViewData getView(View.Name name) {
Utils.checkNotNull(name, "name");
synchronized (registeredViews) {
View view = registeredViews.get(name);
if (view == null) {
return null;
} else {
return ViewData.create(
view,
Collections.<List</*@Nullable*/ TagValue>, AggregationData>emptyMap(),
view.getWindow()
.match(
Functions.<ViewData.AggregationWindowData>returnConstant(
ViewData.AggregationWindowData.CumulativeData.create(
ZERO_TIMESTAMP, ZERO_TIMESTAMP)),
Functions.<ViewData.AggregationWindowData>returnConstant(
ViewData.AggregationWindowData.IntervalData.create(ZERO_TIMESTAMP)),
Functions.<ViewData.AggregationWindowData>throwAssertionError()));
}
}
}
@Override
public Set<View> getAllExportedViews() {
Set<View> views = exportedViews;
if (views == null) {
synchronized (registeredViews) {
exportedViews = views = filterExportedViews(registeredViews.values());
}
}
return views;
}
// Returns the subset of the given views that should be exported
@SuppressWarnings("deprecation")
private static Set<View> filterExportedViews(Collection<View> allViews) {
Set<View> views = new HashSet<View>();
for (View view : allViews) {
if (view.getWindow() instanceof View.AggregationWindow.Interval) {
continue;
}
views.add(view);
}
return Collections.unmodifiableSet(views);
}
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import openconsensus.internal.DefaultVisibilityForTesting;
import openconsensus.internal.Provider;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* Class for accessing the default {@link StatsComponent}.
*
* @since 0.1.0
*/
public final class Stats {
private static final Logger logger = Logger.getLogger(Stats.class.getName());
private static final StatsComponent statsComponent =
loadStatsComponent(StatsComponent.class.getClassLoader());
/**
* Returns the default {@link StatsRecorder}.
*
* @since 0.1.0
*/
public static StatsRecorder getStatsRecorder() {
return statsComponent.getStatsRecorder();
}
/**
* Returns the default {@link ViewManager}.
*
* @since 0.1.0
*/
public static ViewManager getViewManager() {
return statsComponent.getViewManager();
}
/**
* Returns the current {@code StatsCollectionState}.
*
* <p>When no implementation is available, {@code getState} always returns {@link
* StatsCollectionState#DISABLED}.
*
* <p>Once {@link #getState()} is called, subsequent calls to {@link
* #setState(StatsCollectionState)} will throw an {@code IllegalStateException}.
*
* @return the current {@code StatsCollectionState}.
* @since 0.1.0
*/
public static StatsCollectionState getState() {
return statsComponent.getState();
}
/**
* Sets the current {@code StatsCollectionState}.
*
* <p>When no implementation is available, {@code setState} does not change the state.
*
* <p>If state is set to {@link StatsCollectionState#DISABLED}, all stats that are previously
* recorded will be cleared.
*
* @param state the new {@code StatsCollectionState}.
* @throws IllegalStateException if {@link #getState()} was previously called.
* @deprecated This method is deprecated because other libraries could cache the result of {@link
* #getState()}, use a stale value, and behave incorrectly. It is only safe to call early in
* initialization. This method throws {@link IllegalStateException} after {@code getState()}
* has been called, in order to limit changes to the result of {@code getState()}.
* @since 0.1.0
*/
@Deprecated
public static void setState(StatsCollectionState state) {
statsComponent.setState(state);
}
// Any provider that may be used for StatsComponent can be added here.
@DefaultVisibilityForTesting
static StatsComponent loadStatsComponent(@Nullable ClassLoader classLoader) {
try {
// Call Class.forName with literal string name of the class to help shading tools.
return Provider.createInstance(
Class.forName(
"io.opencensus.impl.stats.StatsComponentImpl", /*initialize=*/ true, classLoader),
StatsComponent.class);
} catch (ClassNotFoundException e) {
logger.log(
Level.FINE,
"Couldn't load full implementation for StatsComponent, now trying to load lite "
+ "implementation.",
e);
}
try {
// Call Class.forName with literal string name of the class to help shading tools.
return Provider.createInstance(
Class.forName(
"io.opencensus.impllite.stats.StatsComponentImplLite",
/*initialize=*/ true,
classLoader),
StatsComponent.class);
} catch (ClassNotFoundException e) {
logger.log(
Level.FINE,
"Couldn't load lite implementation for StatsComponent, now using "
+ "default implementation for StatsComponent.",
e);
}
return NoopStats.newNoopStatsComponent();
}
private Stats() {}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
/**
* State of the {@link StatsComponent}.
*
* @since 0.1.0
*/
public enum StatsCollectionState {
/**
* State that fully enables stats collection.
*
* <p>The {@link StatsComponent} collects stats for registered views.
*
* @since 0.1.0
*/
ENABLED,
/**
* State that disables stats collection.
*
* <p>The {@link StatsComponent} does not need to collect stats for registered views and may
* return empty {@link ViewData}s from {@link ViewManager#getView(View.Name)}.
*
* @since 0.1.0
*/
DISABLED
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
/**
* Class that holds the implementations for {@link ViewManager} and {@link StatsRecorder}.
*
* <p>All objects returned by methods on {@code StatsComponent} are cacheable.
*
* @since 0.1.0
*/
public abstract class StatsComponent {
/**
* Returns the default {@link ViewManager}.
*
* @since 0.1.0
*/
public abstract ViewManager getViewManager();
/**
* Returns the default {@link StatsRecorder}.
*
* @since 0.1.0
*/
public abstract StatsRecorder getStatsRecorder();
/**
* Returns the current {@code StatsCollectionState}.
*
* <p>When no implementation is available, {@code getState} always returns {@link
* StatsCollectionState#DISABLED}.
*
* <p>Once {@link #getState()} is called, subsequent calls to {@link
* #setState(StatsCollectionState)} will throw an {@code IllegalStateException}.
*
* @return the current {@code StatsCollectionState}.
* @since 0.1.0
*/
public abstract StatsCollectionState getState();
/**
* Sets the current {@code StatsCollectionState}.
*
* <p>When no implementation is available, {@code setState} does not change the state.
*
* <p>If state is set to {@link StatsCollectionState#DISABLED}, all stats that are previously
* recorded will be cleared.
*
* @param state the new {@code StatsCollectionState}.
* @throws IllegalStateException if {@link #getState()} was previously called.
* @deprecated This method is deprecated because other libraries could cache the result of {@link
* #getState()}, use a stale value, and behave incorrectly. It is only safe to call early in
* initialization. This method throws {@link IllegalStateException} after {@code getState()}
* has been called, in order to limit changes to the result of {@code getState()}.
* @since 0.1.0
*/
@Deprecated
public abstract void setState(StatsCollectionState state);
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
/**
* Provides methods to record stats against tags.
*
* @since 0.1.0
*/
public abstract class StatsRecorder {
// TODO(sebright): Should we provide convenience methods for only recording one measure?
/**
* Returns an object for recording multiple measurements.
*
* @return an object for recording multiple measurements.
* @since 0.1.0
*/
public abstract MeasureMap newMeasureMap();
}

View File

@ -0,0 +1,306 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import com.google.auto.value.AutoValue;
import openconsensus.common.Duration;
import openconsensus.common.Function;
import openconsensus.internal.DefaultVisibilityForTesting;
import openconsensus.internal.StringUtils;
import openconsensus.internal.Utils;
import openconsensus.tags.TagKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import javax.annotation.concurrent.Immutable;
/**
* A View specifies an aggregation and a set of tag keys. The aggregation will be broken down by the
* unique set of matching tag values for each measure.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
@AutoValue.CopyAnnotations
@SuppressWarnings("deprecation")
public abstract class View {
@DefaultVisibilityForTesting static final int NAME_MAX_LENGTH = 255;
private static final Comparator<TagKey> TAG_KEY_COMPARATOR =
new Comparator<TagKey>() {
@Override
public int compare(TagKey key1, TagKey key2) {
return key1.getName().compareToIgnoreCase(key2.getName());
}
};
View() {}
/**
* Name of view. Must be unique.
*
* @since 0.1.0
*/
public abstract Name getName();
/**
* More detailed description, for documentation purposes.
*
* @since 0.1.0
*/
public abstract String getDescription();
/**
* Measure type of this view.
*
* @since 0.1.0
*/
public abstract Measure getMeasure();
/**
* The {@link Aggregation} associated with this {@link View}.
*
* @since 0.1.0
*/
public abstract Aggregation getAggregation();
/**
* Columns (a.k.a Tag Keys) to match with the associated {@link Measure}.
*
* <p>{@link Measure} will be recorded in a "greedy" way. That is, every view aggregates every
* measure. This is similar to doing a GROUPBY on views columns. Columns must be unique.
*
* @since 0.1.0
*/
public abstract List<TagKey> getColumns();
/**
* Returns the time {@link AggregationWindow} for this {@code View}.
*
* @return the time {@link AggregationWindow}.
* @since 0.1.0
* @deprecated since 0.13. In the future all {@link View}s will be cumulative.
*/
@Deprecated
public abstract AggregationWindow getWindow();
/**
* Constructs a new {@link View}.
*
* @param name the {@link Name} of view. Must be unique.
* @param description the description of view.
* @param measure the {@link Measure} to be aggregated by this view.
* @param aggregation the basic {@link Aggregation} that this view will support.
* @param columns the {@link TagKey}s that this view will aggregate on. Columns should not contain
* duplicates.
* @param window the {@link AggregationWindow} of view.
* @return a new {@link View}.
* @since 0.1.0
* @deprecated in favor of {@link #create(Name, String, Measure, Aggregation, List)}.
*/
@Deprecated
public static View create(
Name name,
String description,
Measure measure,
Aggregation aggregation,
List<TagKey> columns,
AggregationWindow window) {
Utils.checkArgument(
new HashSet<TagKey>(columns).size() == columns.size(), "Columns have duplicate.");
List<TagKey> tagKeys = new ArrayList<TagKey>(columns);
Collections.sort(tagKeys, TAG_KEY_COMPARATOR);
return new AutoValue_View(
name, description, measure, aggregation, Collections.unmodifiableList(tagKeys), window);
}
/**
* Constructs a new {@link View}.
*
* @param name the {@link Name} of view. Must be unique.
* @param description the description of view.
* @param measure the {@link Measure} to be aggregated by this view.
* @param aggregation the basic {@link Aggregation} that this view will support.
* @param columns the {@link TagKey}s that this view will aggregate on. Columns should not contain
* duplicates.
* @return a new {@link View}.
* @since 0.1.0
*/
public static View create(
Name name,
String description,
Measure measure,
Aggregation aggregation,
List<TagKey> columns) {
Utils.checkArgument(
new HashSet<TagKey>(columns).size() == columns.size(), "Columns have duplicate.");
return create(
name, description, measure, aggregation, columns, AggregationWindow.Cumulative.create());
}
/**
* The name of a {@code View}.
*
* @since 0.1.0
*/
// This type should be used as the key when associating data with Views.
@Immutable
@AutoValue
public abstract static class Name {
Name() {}
/**
* Returns the name as a {@code String}.
*
* @return the name as a {@code String}.
* @since 0.1.0
*/
public abstract String asString();
/**
* Creates a {@code View.Name} from a {@code String}. Should be a ASCII string with a length no
* greater than 255 characters.
*
* <p>Suggested format for name: {@code <web_host>/<path>}.
*
* @param name the name {@code String}.
* @return a {@code View.Name} with the given name {@code String}.
* @since 0.1.0
*/
public static Name create(String name) {
Utils.checkArgument(
StringUtils.isPrintableString(name) && name.length() <= NAME_MAX_LENGTH,
"Name should be a ASCII string with a length no greater than 255 characters.");
return new AutoValue_View_Name(name);
}
}
/**
* The time window for a {@code View}.
*
* @since 0.1.0
* @deprecated since 0.13. In the future all {@link View}s will be cumulative.
*/
@Deprecated
@Immutable
public abstract static class AggregationWindow {
private AggregationWindow() {}
/**
* Applies the given match function to the underlying data type.
*
* @since 0.1.0
*/
public abstract <T> T match(
Function<? super Cumulative, T> p0,
Function<? super Interval, T> p1,
Function<? super AggregationWindow, T> defaultFunction);
/**
* Cumulative (infinite interval) time {@code AggregationWindow}.
*
* @since 0.1.0
* @deprecated since 0.13. In the future all {@link View}s will be cumulative.
*/
@Deprecated
@Immutable
@AutoValue
@AutoValue.CopyAnnotations
public abstract static class Cumulative extends AggregationWindow {
private static final Cumulative CUMULATIVE =
new AutoValue_View_AggregationWindow_Cumulative();
Cumulative() {}
/**
* Constructs a cumulative {@code AggregationWindow} that does not have an explicit {@code
* Duration}. Instead, cumulative {@code AggregationWindow} always has an interval of infinite
* {@code Duration}.
*
* @return a cumulative {@code AggregationWindow}.
* @since 0.1.0
*/
public static Cumulative create() {
return CUMULATIVE;
}
@Override
public final <T> T match(
Function<? super Cumulative, T> p0,
Function<? super Interval, T> p1,
Function<? super AggregationWindow, T> defaultFunction) {
return p0.apply(this);
}
}
/**
* Interval (finite interval) time {@code AggregationWindow}.
*
* @since 0.1.0
* @deprecated since 0.13. In the future all {@link View}s will be cumulative.
*/
@Deprecated
@Immutable
@AutoValue
@AutoValue.CopyAnnotations
public abstract static class Interval extends AggregationWindow {
private static final Duration ZERO = Duration.create(0, 0);
Interval() {}
/**
* Returns the {@code Duration} associated with this {@code Interval}.
*
* @return a {@code Duration}.
* @since 0.1.0
*/
public abstract Duration getDuration();
/**
* Constructs an interval {@code AggregationWindow} that has a finite explicit {@code
* Duration}.
*
* <p>The {@code Duration} should be able to round to milliseconds. Currently interval window
* cannot have smaller {@code Duration} such as microseconds or nanoseconds.
*
* @return an interval {@code AggregationWindow}.
* @since 0.1.0
*/
public static Interval create(Duration duration) {
Utils.checkArgument(duration.compareTo(ZERO) > 0, "Duration must be positive");
return new AutoValue_View_AggregationWindow_Interval(duration);
}
@Override
public final <T> T match(
Function<? super Cumulative, T> p0,
Function<? super Interval, T> p1,
Function<? super AggregationWindow, T> defaultFunction) {
return p1.apply(this);
}
}
}
}

View File

@ -0,0 +1,461 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import com.google.auto.value.AutoValue;
import openconsensus.common.Duration;
import openconsensus.common.Function;
import openconsensus.common.Functions;
import openconsensus.common.Timestamp;
import openconsensus.stats.Aggregation.Count;
import openconsensus.stats.Aggregation.Distribution;
import openconsensus.stats.Aggregation.LastValue;
import openconsensus.stats.Aggregation.Sum;
import openconsensus.stats.AggregationData.CountData;
import openconsensus.stats.AggregationData.DistributionData;
import openconsensus.stats.AggregationData.LastValueDataDouble;
import openconsensus.stats.AggregationData.LastValueDataLong;
import openconsensus.stats.AggregationData.SumDataDouble;
import openconsensus.stats.AggregationData.SumDataLong;
import openconsensus.stats.Measure.MeasureDouble;
import openconsensus.stats.Measure.MeasureLong;
import openconsensus.tags.TagValue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.concurrent.Immutable;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
/**
* The aggregated data for a particular {@link View}.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
@AutoValue.CopyAnnotations
@SuppressWarnings("deprecation")
public abstract class ViewData {
// Prevents this class from being subclassed anywhere else.
ViewData() {}
/**
* The {@link View} associated with this {@link ViewData}.
*
* @since 0.1.0
*/
public abstract View getView();
/**
* The {@link AggregationData} grouped by combination of tag values, associated with this {@link
* ViewData}.
*
* @since 0.1.0
*/
public abstract Map<List</*@Nullable*/ TagValue>, AggregationData> getAggregationMap();
/**
* Returns the {@link AggregationWindowData} associated with this {@link ViewData}.
*
* <p>{@link AggregationWindowData} is deprecated since 0.13, please avoid using this method. Use
* {@link #getStart()} and {@link #getEnd()} instead.
*
* @return the {@code AggregationWindowData}.
* @since 0.1.0
* @deprecated in favor of {@link #getStart()} and {@link #getEnd()}.
*/
@Deprecated
public abstract AggregationWindowData getWindowData();
/**
* Returns the start {@code Timestamp} for a {@link ViewData}.
*
* @return the start {@code Timestamp}.
* @since 0.1.0
*/
public abstract Timestamp getStart();
/**
* Returns the end {@code Timestamp} for a {@link ViewData}.
*
* @return the end {@code Timestamp}.
* @since 0.1.0
*/
public abstract Timestamp getEnd();
/**
* Constructs a new {@link ViewData}.
*
* @param view the {@link View} associated with this {@link ViewData}.
* @param map the mapping from {@link TagValue} list to {@link AggregationData}.
* @param windowData the {@link AggregationWindowData}.
* @return a {@code ViewData}.
* @throws IllegalArgumentException if the types of {@code Aggregation} and {@code
* AggregationData} don't match, or the types of {@code Window} and {@code WindowData} don't
* match.
* @since 0.1.0
* @deprecated in favor of {@link #create(View, Map, Timestamp, Timestamp)}.
*/
@Deprecated
public static ViewData create(
final View view,
Map<? extends List</*@Nullable*/ TagValue>, ? extends AggregationData> map,
final AggregationWindowData windowData) {
checkWindow(view.getWindow(), windowData);
final Map<List</*@Nullable*/ TagValue>, AggregationData> deepCopy =
new HashMap<List</*@Nullable*/ TagValue>, AggregationData>();
for (Entry<? extends List</*@Nullable*/ TagValue>, ? extends AggregationData> entry :
map.entrySet()) {
checkAggregation(view.getAggregation(), entry.getValue(), view.getMeasure());
deepCopy.put(
Collections.unmodifiableList(new ArrayList</*@Nullable*/ TagValue>(entry.getKey())),
entry.getValue());
}
return windowData.match(
new Function<ViewData.AggregationWindowData.CumulativeData, ViewData>() {
@Override
public ViewData apply(ViewData.AggregationWindowData.CumulativeData arg) {
return createInternal(
view, Collections.unmodifiableMap(deepCopy), arg, arg.getStart(), arg.getEnd());
}
},
new Function<ViewData.AggregationWindowData.IntervalData, ViewData>() {
@Override
public ViewData apply(ViewData.AggregationWindowData.IntervalData arg) {
Duration duration = ((View.AggregationWindow.Interval) view.getWindow()).getDuration();
return createInternal(
view,
Collections.unmodifiableMap(deepCopy),
arg,
arg.getEnd()
.addDuration(Duration.create(-duration.getSeconds(), -duration.getNanos())),
arg.getEnd());
}
},
Functions.<ViewData>throwAssertionError());
}
/**
* Constructs a new {@link ViewData}.
*
* @param view the {@link View} associated with this {@link ViewData}.
* @param map the mapping from {@link TagValue} list to {@link AggregationData}.
* @param start the start {@link Timestamp} for this {@link ViewData}.
* @param end the end {@link Timestamp} for this {@link ViewData}.
* @return a {@code ViewData}.
* @throws IllegalArgumentException if the types of {@code Aggregation} and {@code
* AggregationData} don't match.
* @since 0.1.0
*/
public static ViewData create(
View view,
Map<? extends List</*@Nullable*/ TagValue>, ? extends AggregationData> map,
Timestamp start,
Timestamp end) {
Map<List</*@Nullable*/ TagValue>, AggregationData> deepCopy =
new HashMap<List</*@Nullable*/ TagValue>, AggregationData>();
for (Entry<? extends List</*@Nullable*/ TagValue>, ? extends AggregationData> entry :
map.entrySet()) {
checkAggregation(view.getAggregation(), entry.getValue(), view.getMeasure());
deepCopy.put(
Collections.unmodifiableList(new ArrayList</*@Nullable*/ TagValue>(entry.getKey())),
entry.getValue());
}
return createInternal(
view,
Collections.unmodifiableMap(deepCopy),
AggregationWindowData.CumulativeData.create(start, end),
start,
end);
}
// Suppresses a nullness warning about calls to the AutoValue_ViewData constructor. The generated
// constructor does not have the @Nullable annotation on TagValue.
private static ViewData createInternal(
View view,
Map<List</*@Nullable*/ TagValue>, AggregationData> aggregationMap,
AggregationWindowData window,
Timestamp start,
Timestamp end) {
@SuppressWarnings("nullness")
Map<List<TagValue>, AggregationData> map = aggregationMap;
return new AutoValue_ViewData(view, map, window, start, end);
}
private static void checkWindow(
View.AggregationWindow window, final AggregationWindowData windowData) {
window.match(
new Function<View.AggregationWindow.Cumulative, Void>() {
@Override
public Void apply(View.AggregationWindow.Cumulative arg) {
throwIfWindowMismatch(
windowData instanceof AggregationWindowData.CumulativeData, arg, windowData);
return null;
}
},
new Function<View.AggregationWindow.Interval, Void>() {
@Override
public Void apply(View.AggregationWindow.Interval arg) {
throwIfWindowMismatch(
windowData instanceof AggregationWindowData.IntervalData, arg, windowData);
return null;
}
},
Functions.</*@Nullable*/ Void>throwAssertionError());
}
private static void throwIfWindowMismatch(
boolean isValid, View.AggregationWindow window, AggregationWindowData windowData) {
if (!isValid) {
throw new IllegalArgumentException(createErrorMessageForWindow(window, windowData));
}
}
private static String createErrorMessageForWindow(
View.AggregationWindow window, AggregationWindowData windowData) {
return "AggregationWindow and AggregationWindowData types mismatch. "
+ "AggregationWindow: "
+ window.getClass().getSimpleName()
+ " AggregationWindowData: "
+ windowData.getClass().getSimpleName();
}
private static void checkAggregation(
final Aggregation aggregation, final AggregationData aggregationData, final Measure measure) {
aggregation.match(
new Function<Sum, Void>() {
@Override
public Void apply(Sum arg) {
measure.match(
new Function<MeasureDouble, Void>() {
@Override
public Void apply(MeasureDouble arg) {
throwIfAggregationMismatch(
aggregationData instanceof SumDataDouble, aggregation, aggregationData);
return null;
}
},
new Function<MeasureLong, Void>() {
@Override
public Void apply(MeasureLong arg) {
throwIfAggregationMismatch(
aggregationData instanceof SumDataLong, aggregation, aggregationData);
return null;
}
},
Functions.</*@Nullable*/ Void>throwAssertionError());
return null;
}
},
new Function<Count, Void>() {
@Override
public Void apply(Count arg) {
throwIfAggregationMismatch(
aggregationData instanceof CountData, aggregation, aggregationData);
return null;
}
},
new Function<Distribution, Void>() {
@Override
public Void apply(Distribution arg) {
throwIfAggregationMismatch(
aggregationData instanceof DistributionData, aggregation, aggregationData);
return null;
}
},
new Function<LastValue, Void>() {
@Override
public Void apply(LastValue arg) {
measure.match(
new Function<MeasureDouble, Void>() {
@Override
public Void apply(MeasureDouble arg) {
throwIfAggregationMismatch(
aggregationData instanceof LastValueDataDouble,
aggregation,
aggregationData);
return null;
}
},
new Function<MeasureLong, Void>() {
@Override
public Void apply(MeasureLong arg) {
throwIfAggregationMismatch(
aggregationData instanceof LastValueDataLong, aggregation, aggregationData);
return null;
}
},
Functions.</*@Nullable*/ Void>throwAssertionError());
return null;
}
},
new Function<Aggregation, Void>() {
@Override
public Void apply(Aggregation arg) {
// TODO(songya): remove this once Mean aggregation is completely removed. Before that
// we need to continue supporting Mean, since it could still be used by users and some
// deprecated RPC views.
if (arg instanceof Aggregation.Mean) {
throwIfAggregationMismatch(
aggregationData instanceof AggregationData.MeanData,
aggregation,
aggregationData);
return null;
}
throw new AssertionError();
}
});
}
private static void throwIfAggregationMismatch(
boolean isValid, Aggregation aggregation, AggregationData aggregationData) {
if (!isValid) {
throw new IllegalArgumentException(
createErrorMessageForAggregation(aggregation, aggregationData));
}
}
private static String createErrorMessageForAggregation(
Aggregation aggregation, AggregationData aggregationData) {
return "Aggregation and AggregationData types mismatch. "
+ "Aggregation: "
+ aggregation.getClass().getSimpleName()
+ " AggregationData: "
+ aggregationData.getClass().getSimpleName();
}
/**
* The {@code AggregationWindowData} for a {@link ViewData}.
*
* @since 0.1.0
* @deprecated since 0.13, please use start and end {@link Timestamp} instead.
*/
@Deprecated
@Immutable
public abstract static class AggregationWindowData {
private AggregationWindowData() {}
/**
* Applies the given match function to the underlying data type.
*
* @since 0.1.0
*/
public abstract <T> T match(
Function<? super CumulativeData, T> p0,
Function<? super IntervalData, T> p1,
Function<? super AggregationWindowData, T> defaultFunction);
/**
* Cumulative {@code AggregationWindowData}.
*
* @since 0.1.0
* @deprecated since 0.13, please use start and end {@link Timestamp} instead.
*/
@Deprecated
@Immutable
@AutoValue
@AutoValue.CopyAnnotations
public abstract static class CumulativeData extends AggregationWindowData {
CumulativeData() {}
/**
* Returns the start {@code Timestamp} for a {@link CumulativeData}.
*
* @return the start {@code Timestamp}.
* @since 0.1.0
*/
public abstract Timestamp getStart();
/**
* Returns the end {@code Timestamp} for a {@link CumulativeData}.
*
* @return the end {@code Timestamp}.
* @since 0.1.0
*/
public abstract Timestamp getEnd();
@Override
public final <T> T match(
Function<? super CumulativeData, T> p0,
Function<? super IntervalData, T> p1,
Function<? super AggregationWindowData, T> defaultFunction) {
return p0.apply(this);
}
/**
* Constructs a new {@link CumulativeData}.
*
* @since 0.1.0
*/
public static CumulativeData create(Timestamp start, Timestamp end) {
if (start.compareTo(end) > 0) {
throw new IllegalArgumentException("Start time is later than end time.");
}
return new AutoValue_ViewData_AggregationWindowData_CumulativeData(start, end);
}
}
/**
* Interval {@code AggregationWindowData}.
*
* @since 0.1.0
* @deprecated since 0.13, please use start and end {@link Timestamp} instead.
*/
@Deprecated
@Immutable
@AutoValue
@AutoValue.CopyAnnotations
public abstract static class IntervalData extends AggregationWindowData {
IntervalData() {}
/**
* Returns the end {@code Timestamp} for an {@link IntervalData}.
*
* @return the end {@code Timestamp}.
* @since 0.1.0
*/
public abstract Timestamp getEnd();
@Override
public final <T> T match(
Function<? super CumulativeData, T> p0,
Function<? super IntervalData, T> p1,
Function<? super AggregationWindowData, T> defaultFunction) {
return p1.apply(this);
}
/**
* Constructs a new {@link IntervalData}.
*
* @since 0.1.0
*/
public static IntervalData create(Timestamp end) {
return new AutoValue_ViewData_AggregationWindowData_IntervalData(end);
}
}
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.stats;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Provides facilities to register {@link View}s for collecting stats and retrieving stats data as a
* {@link ViewData}.
*
* @since 0.1.0
*/
public abstract class ViewManager {
/**
* Pull model for stats. Registers a {@link View} that will collect data to be accessed via {@link
* #getView(View.Name)}.
*
* @param view the {@code View} to be registered.
* @since 0.1.0
*/
public abstract void registerView(View view);
/**
* Returns the current stats data, {@link ViewData}, associated with the given view name.
*
* <p>Returns {@code null} if the {@code View} is not registered.
*
* @param view the name of {@code View} for the current stats.
* @return {@code ViewData} for the {@code View}, or {@code null} if the {@code View} is not
* registered.
* @since 0.1.0
*/
@Nullable
public abstract ViewData getView(View.Name view);
/**
* Returns all registered views that should be exported.
*
* <p>This method should be used by any stats exporter that automatically exports data for views
* registered with the {@link ViewManager}.
*
* @return all registered views that should be exported.
* @since 0.1.0
*/
public abstract Set<View> getAllExportedViews();
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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.
*/
/** API for stats recording. */
// TODO: Add more details.
// TODO: Add code examples.
package openconsensus.stats;

View File

@ -0,0 +1,39 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import java.util.Iterator;
import openconsensus.common.Internal;
/**
* Internal tagging utilities.
*
* @since 0.1.0
*/
@Internal
public final class InternalUtils {
private InternalUtils() {}
/**
* Internal tag accessor.
*
* @since 0.1.0
*/
public static Iterator<Tag> getTags(TagContext tags) {
return tags.getIterator();
}
}

View File

@ -0,0 +1,223 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import openconsensus.common.Scope;
import openconsensus.internal.NoopScope;
import openconsensus.internal.Utils;
import openconsensus.tags.propagation.TagContextBinarySerializer;
import openconsensus.tags.propagation.TagPropagationComponent;
import java.util.Collections;
import java.util.Iterator;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;
/** No-op implementations of tagging classes. */
final class NoopTags {
private NoopTags() {}
/**
* Returns a {@code TagsComponent} that has a no-op implementation for {@link Tagger}.
*
* @return a {@code TagsComponent} that has a no-op implementation for {@code Tagger}.
*/
static TagsComponent newNoopTagsComponent() {
return new NoopTagsComponent();
}
/**
* Returns a {@code Tagger} that only produces {@link TagContext}s with no tags.
*
* @return a {@code Tagger} that only produces {@code TagContext}s with no tags.
*/
static Tagger getNoopTagger() {
return NoopTagger.INSTANCE;
}
/**
* Returns a {@code TagContextBuilder} that ignores all calls to {@link TagContextBuilder#put}.
*
* @return a {@code TagContextBuilder} that ignores all calls to {@link TagContextBuilder#put}.
*/
static TagContextBuilder getNoopTagContextBuilder() {
return NoopTagContextBuilder.INSTANCE;
}
/**
* Returns a {@code TagContext} that does not contain any tags.
*
* @return a {@code TagContext} that does not contain any tags.
*/
static TagContext getNoopTagContext() {
return NoopTagContext.INSTANCE;
}
/** Returns a {@code TagPropagationComponent} that contains no-op serializers. */
static TagPropagationComponent getNoopTagPropagationComponent() {
return NoopTagPropagationComponent.INSTANCE;
}
/**
* Returns a {@code TagContextBinarySerializer} that serializes all {@code TagContext}s to zero
* bytes and deserializes all inputs to empty {@code TagContext}s.
*/
static TagContextBinarySerializer getNoopTagContextBinarySerializer() {
return NoopTagContextBinarySerializer.INSTANCE;
}
@ThreadSafe
private static final class NoopTagsComponent extends TagsComponent {
private volatile boolean isRead;
@Override
public Tagger getTagger() {
return getNoopTagger();
}
@Override
public TagPropagationComponent getTagPropagationComponent() {
return getNoopTagPropagationComponent();
}
@Override
public TaggingState getState() {
isRead = true;
return TaggingState.DISABLED;
}
@Override
@Deprecated
public void setState(TaggingState state) {
Utils.checkNotNull(state, "state");
Utils.checkState(!isRead, "State was already read, cannot set state.");
}
}
@Immutable
private static final class NoopTagger extends Tagger {
static final Tagger INSTANCE = new NoopTagger();
@Override
public TagContext empty() {
return getNoopTagContext();
}
@Override
public TagContext getCurrentTagContext() {
return getNoopTagContext();
}
@Override
public TagContextBuilder emptyBuilder() {
return getNoopTagContextBuilder();
}
@Override
public TagContextBuilder toBuilder(TagContext tags) {
Utils.checkNotNull(tags, "tags");
return getNoopTagContextBuilder();
}
@Override
public TagContextBuilder currentBuilder() {
return getNoopTagContextBuilder();
}
@Override
public Scope withTagContext(TagContext tags) {
Utils.checkNotNull(tags, "tags");
return NoopScope.getInstance();
}
}
@Immutable
private static final class NoopTagContextBuilder extends TagContextBuilder {
static final TagContextBuilder INSTANCE = new NoopTagContextBuilder();
@Override
@SuppressWarnings("deprecation")
public TagContextBuilder put(TagKey key, TagValue value) {
Utils.checkNotNull(key, "key");
Utils.checkNotNull(value, "value");
return this;
}
@Override
public TagContextBuilder put(TagKey key, TagValue value, TagMetadata tagMetadata) {
Utils.checkNotNull(key, "key");
Utils.checkNotNull(value, "value");
Utils.checkNotNull(tagMetadata, "tagMetadata");
return this;
}
@Override
public TagContextBuilder remove(TagKey key) {
Utils.checkNotNull(key, "key");
return this;
}
@Override
public TagContext build() {
return getNoopTagContext();
}
@Override
public Scope buildScoped() {
return NoopScope.getInstance();
}
}
@Immutable
private static final class NoopTagContext extends TagContext {
static final TagContext INSTANCE = new NoopTagContext();
// TODO(sebright): Is there any way to let the user know that their tags were ignored?
@Override
protected Iterator<Tag> getIterator() {
return Collections.<Tag>emptySet().iterator();
}
}
@Immutable
private static final class NoopTagPropagationComponent extends TagPropagationComponent {
static final TagPropagationComponent INSTANCE = new NoopTagPropagationComponent();
@Override
public TagContextBinarySerializer getBinarySerializer() {
return getNoopTagContextBinarySerializer();
}
}
@Immutable
private static final class NoopTagContextBinarySerializer extends TagContextBinarySerializer {
static final TagContextBinarySerializer INSTANCE = new NoopTagContextBinarySerializer();
static final byte[] EMPTY_BYTE_ARRAY = {};
@Override
public byte[] toByteArray(TagContext tags) {
Utils.checkNotNull(tags, "tags");
return EMPTY_BYTE_ARRAY;
}
@Override
public TagContext fromByteArray(byte[] bytes) {
Utils.checkNotNull(bytes, "bytes");
return getNoopTagContext();
}
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import com.google.auto.value.AutoValue;
import openconsensus.tags.TagMetadata.TagTtl;
import javax.annotation.concurrent.Immutable;
/**
* {@link TagKey} paired with a {@link TagValue}.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class Tag {
private static final TagMetadata METADATA_UNLIMITED_PROPAGATION =
TagMetadata.create(TagTtl.UNLIMITED_PROPAGATION);
Tag() {}
/**
* Creates a {@code Tag} from the given key and value.
*
* <p>For backwards-compatibility this method still produces propagating {@link Tag}s.
*
* <p>This is equivalent to calling {@code create(key, value,
* TagMetadata.create(TagTtl.UNLIMITED_PROPAGATION))}.
*
* @param key the tag key.
* @param value the tag value.
* @return a {@code Tag} with the given key and value.
* @since 0.1.0
* @deprecated in favor of {@link #create(TagKey, TagValue, TagMetadata)}.
*/
@Deprecated
public static Tag create(TagKey key, TagValue value) {
return create(key, value, METADATA_UNLIMITED_PROPAGATION);
}
/**
* Creates a {@code Tag} from the given key, value and metadata.
*
* @param key the tag key.
* @param value the tag value.
* @param tagMetadata the tag metadata.
* @return a {@code Tag}.
* @since 0.1.0
*/
public static Tag create(TagKey key, TagValue value, TagMetadata tagMetadata) {
return new AutoValue_Tag(key, value, tagMetadata);
}
/**
* Returns the tag's key.
*
* @return the tag's key.
* @since 0.1.0
*/
public abstract TagKey getKey();
/**
* Returns the tag's value.
*
* @return the tag's value.
* @since 0.1.0
*/
public abstract TagValue getValue();
/**
* Returns the {@link TagMetadata} associated with this {@link Tag}.
*
* @return the {@code TagMetadata}.
* @since 0.1.0
*/
public abstract TagMetadata getTagMetadata();
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import java.util.HashMap;
import java.util.Iterator;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A map from {@link TagKey} to {@link TagValue} that can be used to label anything that is
* associated with a specific operation.
*
* <p>For example, {@code TagContext}s can be used to label stats, log messages, or debugging
* information.
*
* @since 0.1.0
*/
@Immutable
public abstract class TagContext {
/**
* Returns an iterator over the tags in this {@code TagContext}.
*
* @return an iterator over the tags in this {@code TagContext}.
* @since 0.1.0
*/
// This method is protected to prevent client code from accessing the tags of any TagContext. We
// don't currently support efficient access to tags. However, every TagContext subclass needs to
// provide access to its tags to the stats and tagging implementations by implementing this
// method. If we decide to support access to tags in the future, we can add a public iterator()
// method and implement it for all subclasses by calling getIterator().
//
// The stats and tagging implementations can access any TagContext's tags through
// InternalUtils.getTags, which calls this method.
protected abstract Iterator<Tag> getIterator();
@Override
public String toString() {
return "TagContext";
}
/**
* Returns true iff the other object is an instance of {@code TagContext} and contains the same
* key-value pairs. Implementations are free to override this method to provide better
* performance.
*/
@Override
public boolean equals(@Nullable Object other) {
if (!(other instanceof TagContext)) {
return false;
}
TagContext otherTags = (TagContext) other;
Iterator<Tag> iter1 = getIterator();
Iterator<Tag> iter2 = otherTags.getIterator();
HashMap<Tag, Integer> tags = new HashMap<Tag, Integer>();
while (iter1 != null && iter1.hasNext()) {
Tag tag = iter1.next();
if (tags.containsKey(tag)) {
tags.put(tag, tags.get(tag) + 1);
} else {
tags.put(tag, 1);
}
}
while (iter2 != null && iter2.hasNext()) {
Tag tag = iter2.next();
if (!tags.containsKey(tag)) {
return false;
}
int count = tags.get(tag);
if (count > 1) {
tags.put(tag, count - 1);
} else {
tags.remove(tag);
}
}
return tags.isEmpty();
}
@Override
public final int hashCode() {
int hashCode = 0;
Iterator<Tag> i = getIterator();
if (i == null) {
return hashCode;
}
while (i.hasNext()) {
Tag tag = i.next();
if (tag != null) {
hashCode += tag.hashCode();
}
}
return hashCode;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import openconsensus.common.Scope;
/**
* Builder for the {@link TagContext} class.
*
* @since 0.1.0
*/
public abstract class TagContextBuilder {
/**
* Adds the key/value pair regardless of whether the key is present.
*
* <p>For backwards-compatibility this method still produces propagating {@link Tag}s.
*
* <p>Equivalent to calling {@code put(key, value,
* TagMetadata.create(TagTtl.UNLIMITED_PROPAGATION))}.
*
* @param key the {@code TagKey} which will be set.
* @param value the {@code TagValue} to set for the given key.
* @return this
* @since 0.1.0
* @deprecated in favor of {@link #put(TagKey, TagValue, TagMetadata)}.
*/
@Deprecated
public abstract TagContextBuilder put(TagKey key, TagValue value);
/**
* Adds the key/value pair and metadata regardless of whether the key is present.
*
* @param key the {@code TagKey} which will be set.
* @param value the {@code TagValue} to set for the given key.
* @param tagMetadata the {@code TagMetadata} associated with this {@link Tag}.
* @return this
* @since 0.1.0
*/
public abstract TagContextBuilder put(TagKey key, TagValue value, TagMetadata tagMetadata);
/**
* Removes the key if it exists.
*
* @param key the {@code TagKey} which will be removed.
* @return this
* @since 0.1.0
*/
public abstract TagContextBuilder remove(TagKey key);
/**
* Creates a {@code TagContext} from this builder.
*
* @return a {@code TagContext} with the same tags as this builder.
* @since 0.1.0
*/
public abstract TagContext build();
/**
* Enters the scope of code where the {@link TagContext} created from this builder is in the
* current context and returns an object that represents that scope. The scope is exited when the
* returned object is closed.
*
* @return an object that defines a scope where the {@code TagContext} created from this builder
* is set to the current context.
* @since 0.1.0
*/
public abstract Scope buildScoped();
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import com.google.auto.value.AutoValue;
import openconsensus.internal.StringUtils;
import openconsensus.internal.Utils;
import javax.annotation.concurrent.Immutable;
/**
* A key to a value stored in a {@link TagContext}.
*
* <p>Each {@code TagKey} has a {@code String} name. Names have a maximum length of {@link
* #MAX_LENGTH} and contain only printable ASCII characters.
*
* <p>{@code TagKey}s are designed to be used as constants. Declaring each key as a constant
* prevents key names from being validated multiple times.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class TagKey {
/**
* The maximum length for a tag key name. The value is {@value #MAX_LENGTH}.
*
* @since 0.1.0
*/
public static final int MAX_LENGTH = 255;
TagKey() {}
/**
* Constructs a {@code TagKey} with the given name.
*
* <p>The name must meet the following requirements:
*
* <ol>
* <li>It cannot be longer than {@link #MAX_LENGTH}.
* <li>It can only contain printable ASCII characters.
* </ol>
*
* @param name the name of the key.
* @return a {@code TagKey} with the given name.
* @throws IllegalArgumentException if the name is not valid.
* @since 0.1.0
*/
public static TagKey create(String name) {
Utils.checkArgument(isValid(name), "Invalid TagKey name: %s", name);
return new AutoValue_TagKey(name);
}
/**
* Returns the name of the key.
*
* @return the name of the key.
* @since 0.1.0
*/
public abstract String getName();
/**
* Determines whether the given {@code String} is a valid tag key.
*
* @param name the tag key name to be validated.
* @return whether the name is valid.
*/
private static boolean isValid(String name) {
return !name.isEmpty() && name.length() <= MAX_LENGTH && StringUtils.isPrintableString(name);
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import com.google.auto.value.AutoValue;
import javax.annotation.concurrent.Immutable;
/**
* {@link TagMetadata} contains properties associated with a {@link Tag}.
*
* <p>For now only the property {@link TagTtl} is defined. In future, additional properties may be
* added to address specific situations.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class TagMetadata {
TagMetadata() {}
/**
* Creates a {@link TagMetadata} with the given {@link TagTtl}.
*
* @param tagTtl TTL of a {@code Tag}.
* @return a {@code TagMetadata}.
* @since 0.1.0
*/
public static TagMetadata create(TagTtl tagTtl) {
return new AutoValue_TagMetadata(tagTtl);
}
/**
* Returns the {@link TagTtl} of this {@link TagMetadata}.
*
* @return the {@code TagTtl}.
* @since 0.1.0
*/
public abstract TagTtl getTagTtl();
/**
* {@link TagTtl} is an integer that represents number of hops a tag can propagate.
*
* <p>Anytime a sender serializes a tag, sends it over the wire and receiver deserializes the tag
* then the tag is considered to have travelled one hop.
*
* <p>There could be one or more proxy(ies) between sender and receiver. Proxies are treated as
* transparent entities and they are not counted as hops.
*
* <p>For now, only special values of {@link TagTtl} are supported.
*
* @since 0.1.0
*/
public enum TagTtl {
/**
* A {@link Tag} with {@link TagTtl#NO_PROPAGATION} is considered to have local scope and is
* used within the process where it's created.
*
* @since 0.1.0
*/
NO_PROPAGATION(0),
/**
* A {@link Tag} with {@link TagTtl#UNLIMITED_PROPAGATION} can propagate unlimited hops.
*
* <p>However, it is still subject to outgoing and incoming (on remote side) filter criteria.
*
* <p>{@link TagTtl#UNLIMITED_PROPAGATION} is typical used to track a request, which may be
* processed across multiple entities.
*
* @since 0.1.0
*/
UNLIMITED_PROPAGATION(-1);
private final int hops;
private TagTtl(int hops) {
this.hops = hops;
}
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import com.google.auto.value.AutoValue;
import openconsensus.internal.StringUtils;
import openconsensus.internal.Utils;
import javax.annotation.concurrent.Immutable;
/**
* A validated tag value.
*
* <p>Validation ensures that the {@code String} has a maximum length of {@link #MAX_LENGTH} and
* contains only printable ASCII characters.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class TagValue {
/**
* The maximum length for a tag value. The value is {@value #MAX_LENGTH}.
*
* @since 0.1.0
*/
public static final int MAX_LENGTH = 255;
TagValue() {}
/**
* Constructs a {@code TagValue} from the given string. The string must meet the following
* requirements:
*
* <ol>
* <li>It cannot be longer than {@link #MAX_LENGTH}.
* <li>It can only contain printable ASCII characters.
* </ol>
*
* @param value the tag value.
* @throws IllegalArgumentException if the {@code String} is not valid.
* @since 0.1.0
*/
public static TagValue create(String value) {
Utils.checkArgument(isValid(value), "Invalid TagValue: %s", value);
return new AutoValue_TagValue(value);
}
/**
* Returns the tag value as a {@code String}.
*
* @return the tag value as a {@code String}.
* @since 0.1.0
*/
public abstract String asString();
/**
* Determines whether the given {@code String} is a valid tag value.
*
* @param value the tag value to be validated.
* @return whether the value is valid.
*/
private static boolean isValid(String value) {
return value.length() <= MAX_LENGTH && StringUtils.isPrintableString(value);
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import openconsensus.common.Scope;
/**
* Object for creating new {@link TagContext}s and {@code TagContext}s based on the current context.
*
* <p>This class returns {@link TagContextBuilder builders} that can be used to create the
* implementation-dependent {@link TagContext}s.
*
* <p>Implementations may have different constraints and are free to convert tag contexts to their
* own subtypes. This means callers cannot assume the {@link #getCurrentTagContext() current
* context} is the same instance as the one {@link #withTagContext(TagContext) placed into scope}.
*
* @since 0.1.0
*/
public abstract class Tagger {
/**
* Returns an empty {@code TagContext}.
*
* @return an empty {@code TagContext}.
* @since 0.1.0
*/
public abstract TagContext empty();
/**
* Returns the current {@code TagContext}.
*
* @return the current {@code TagContext}.
* @since 0.1.0
*/
public abstract TagContext getCurrentTagContext();
/**
* Returns a new empty {@code Builder}.
*
* @return a new empty {@code Builder}.
* @since 0.1.0
*/
public abstract TagContextBuilder emptyBuilder();
/**
* Returns a builder based on this {@code TagContext}.
*
* @return a builder based on this {@code TagContext}.
* @since 0.1.0
*/
public abstract TagContextBuilder toBuilder(TagContext tags);
/**
* Returns a new builder created from the current {@code TagContext}.
*
* @return a new builder created from the current {@code TagContext}.
* @since 0.1.0
*/
public abstract TagContextBuilder currentBuilder();
/**
* Enters the scope of code where the given {@code TagContext} is in the current context
* (replacing the previous {@code TagContext}) and returns an object that represents that scope.
* The scope is exited when the returned object is closed.
*
* @param tags the {@code TagContext} to be set to the current context.
* @return an object that defines a scope where the given {@code TagContext} is set to the current
* context.
* @since 0.1.0
*/
public abstract Scope withTagContext(TagContext tags);
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
/**
* State of the {@link TagsComponent}.
*
* @since 0.1.0
*/
public enum TaggingState {
// TODO(sebright): Should we add a state that propagates the tags, but doesn't allow
// modifications?
/**
* State that fully enables tagging.
*
* <p>The {@link TagsComponent} can add tags to {@link TagContext}s, propagate {@code TagContext}s
* in the current context, and serialize {@code TagContext}s.
*
* @since 0.1.0
*/
ENABLED,
/**
* State that disables tagging.
*
* <p>The {@link TagsComponent} may not add tags to {@link TagContext}s, propagate {@code
* TagContext}s in the current context, or serialize {@code TagContext}s.
*
* @since 0.1.0
*/
// TODO(sebright): Document how this interacts with stats collection.
DISABLED
}

View File

@ -0,0 +1,126 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import openconsensus.internal.DefaultVisibilityForTesting;
import openconsensus.internal.Provider;
import openconsensus.tags.propagation.TagPropagationComponent;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* Class for accessing the default {@link TagsComponent}.
*
* @since 0.1.0
*/
public final class Tags {
private static final Logger logger = Logger.getLogger(Tags.class.getName());
private static final TagsComponent tagsComponent =
loadTagsComponent(TagsComponent.class.getClassLoader());
private Tags() {}
/**
* Returns the default {@code Tagger}.
*
* @return the default {@code Tagger}.
* @since 0.1.0
*/
public static Tagger getTagger() {
return tagsComponent.getTagger();
}
/**
* Returns the default {@code TagPropagationComponent}.
*
* @return the default {@code TagPropagationComponent}.
* @since 0.1.0
*/
public static TagPropagationComponent getTagPropagationComponent() {
return tagsComponent.getTagPropagationComponent();
}
/**
* Returns the current {@code TaggingState}.
*
* <p>When no implementation is available, {@code getState} always returns {@link
* TaggingState#DISABLED}.
*
* <p>Once {@link #getState()} is called, subsequent calls to {@link #setState(TaggingState)} will
* throw an {@code IllegalStateException}.
*
* @return the current {@code TaggingState}.
* @since 0.1.0
*/
public static TaggingState getState() {
return tagsComponent.getState();
}
/**
* Sets the current {@code TaggingState}.
*
* <p>When no implementation is available, {@code setState} does not change the state.
*
* @param state the new {@code TaggingState}.
* @throws IllegalStateException if {@link #getState()} was previously called.
* @deprecated This method is deprecated because other libraries could cache the result of {@link
* #getState()}, use a stale value, and behave incorrectly. It is only safe to call early in
* initialization. This method throws {@link IllegalStateException} after {@link #getState()}
* has been called, in order to limit changes to the result of {@code getState()}.
* @since 0.1.0
*/
@Deprecated
public static void setState(TaggingState state) {
tagsComponent.setState(state);
}
// Any provider that may be used for TagsComponent can be added here.
@DefaultVisibilityForTesting
static TagsComponent loadTagsComponent(@Nullable ClassLoader classLoader) {
try {
// Call Class.forName with literal string name of the class to help shading tools.
return Provider.createInstance(
Class.forName(
"io.opencensus.impl.tags.TagsComponentImpl", /*initialize=*/ true, classLoader),
TagsComponent.class);
} catch (ClassNotFoundException e) {
logger.log(
Level.FINE,
"Couldn't load full implementation for TagsComponent, now trying to load lite "
+ "implementation.",
e);
}
try {
// Call Class.forName with literal string name of the class to help shading tools.
return Provider.createInstance(
Class.forName(
"io.opencensus.impllite.tags.TagsComponentImplLite",
/*initialize=*/ true,
classLoader),
TagsComponent.class);
} catch (ClassNotFoundException e) {
logger.log(
Level.FINE,
"Couldn't load lite implementation for TagsComponent, now using "
+ "default implementation for TagsComponent.",
e);
}
return NoopTags.newNoopTagsComponent();
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags;
import openconsensus.tags.propagation.TagPropagationComponent;
/**
* Class that holds the implementation for {@link Tagger} and {@link TagPropagationComponent}.
*
* <p>All objects returned by methods on {@code TagsComponent} are cacheable.
*
* @since 0.1.0
*/
public abstract class TagsComponent {
/**
* Returns the {@link Tagger} for this implementation.
*
* @since 0.1.0
*/
public abstract Tagger getTagger();
/**
* Returns the {@link TagPropagationComponent} for this implementation.
*
* @since 0.1.0
*/
public abstract TagPropagationComponent getTagPropagationComponent();
/**
* Returns the current {@code TaggingState}.
*
* <p>When no implementation is available, {@code getState} always returns {@link
* TaggingState#DISABLED}.
*
* <p>Once {@link #getState()} is called, subsequent calls to {@link #setState(TaggingState)} will
* throw an {@code IllegalStateException}.
*
* @return the current {@code TaggingState}.
* @since 0.1.0
*/
public abstract TaggingState getState();
/**
* Sets the current {@code TaggingState}.
*
* <p>When no implementation is available, {@code setState} does not change the state.
*
* @param state the new {@code TaggingState}.
* @throws IllegalStateException if {@link #getState()} was previously called.
* @deprecated This method is deprecated because other libraries could cache the result of {@link
* #getState()}, use a stale value, and behave incorrectly. It is only safe to call early in
* initialization. This method throws {@link IllegalStateException} after {@code getState()}
* has been called, in order to limit changes to the result of {@code getState()}.
* @since 0.1.0
*/
@Deprecated
public abstract void setState(TaggingState state);
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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.
*/
/**
* API for associating tags with scoped operations.
*
* <p>This package manages a set of tags in the {@code io.grpc.Context}. The tags can be used to
* label anything that is associated with a specific operation. For example, the {@code
* io.opencensus.stats} package labels all stats with the current tags.
*
* <p>{@link openconsensus.tags.Tag Tags} are key-value pairs. The {@link openconsensus.tags.TagKey
* keys} and {@link openconsensus.tags.TagValue values} are wrapped {@code String}s. They are stored
* as a map in a {@link openconsensus.tags.TagContext}.
*
* <p>Note that tags are independent of the tracing data that is propagated in the {@code
* io.grpc.Context}, such as trace ID.
*/
// TODO(sebright): Add code examples.
package openconsensus.tags;

View File

@ -0,0 +1,63 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags.propagation;
import openconsensus.tags.Tag;
import openconsensus.tags.TagContext;
import openconsensus.tags.TagMetadata;
import openconsensus.tags.TagMetadata.TagTtl;
/**
* Object for serializing and deserializing {@link TagContext}s with the binary format.
*
* <p>See <a
* href="https://github.com/census-instrumentation/opencensus-specs/blob/master/encodings/BinaryEncoding.md#tag-context">opencensus-specs</a>
* for the specification of the cross-language binary serialization format.
*
* @since 0.1.0
*/
public abstract class TagContextBinarySerializer {
/**
* Serializes the {@code TagContext} into the on-the-wire representation.
*
* <p>This method should be the inverse of {@link #fromByteArray}.
*
* <p>{@link Tag}s that have a {@link TagMetadata} with {@link TagTtl#NO_PROPAGATION} will not be
* serialized.
*
* @param tags the {@code TagContext} to serialize.
* @return the on-the-wire representation of a {@code TagContext}.
* @throws TagContextSerializationException if the result would be larger than the maximum allowed
* serialized size.
* @since 0.1.0
*/
public abstract byte[] toByteArray(TagContext tags) throws TagContextSerializationException;
/**
* Creates a {@code TagContext} from the given on-the-wire encoded representation.
*
* <p>This method should be the inverse of {@link #toByteArray}.
*
* @param bytes on-the-wire representation of a {@code TagContext}.
* @return a {@code TagContext} deserialized from {@code bytes}.
* @throws TagContextDeserializationException if there is a parse error, the input contains
* invalid tags, or the input is larger than the maximum allowed serialized size.
* @since 0.1.0
*/
public abstract TagContext fromByteArray(byte[] bytes) throws TagContextDeserializationException;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags.propagation;
import openconsensus.tags.TagContext;
/**
* Exception thrown when a {@link TagContext} cannot be parsed.
*
* @since 0.1.0
*/
public final class TagContextDeserializationException extends Exception {
private static final long serialVersionUID = 0L;
/**
* Constructs a new {@code TagContextParseException} with the given message.
*
* @param message a message describing the error.
* @since 0.1.0
*/
public TagContextDeserializationException(String message) {
super(message);
}
/**
* Constructs a new {@code TagContextParseException} with the given message and cause.
*
* @param message a message describing the error.
* @param cause the cause of the error.
* @since 0.1.0
*/
public TagContextDeserializationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags.propagation;
import openconsensus.tags.TagContext;
/**
* Exception thrown when a {@link TagContext} cannot be serialized.
*
* @since 0.1.0
*/
public final class TagContextSerializationException extends Exception {
private static final long serialVersionUID = 0L;
/**
* Constructs a new {@code TagContextSerializationException} with the given message.
*
* @param message a message describing the error.
* @since 0.1.0
*/
public TagContextSerializationException(String message) {
super(message);
}
/**
* Constructs a new {@code TagContextSerializationException} with the given message and cause.
*
* @param message a message describing the error.
* @param cause the cause of the error.
* @since 0.1.0
*/
public TagContextSerializationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags.propagation;
import openconsensus.tags.TagContext;
/**
* Object containing all supported {@link TagContext} propagation formats.
*
* @since 0.1.0
*/
// TODO(sebright): Add an HTTP serializer.
public abstract class TagPropagationComponent {
/**
* Returns the {@link TagContextBinarySerializer} for this implementation.
*
* @return the {@code TagContextBinarySerializer} for this implementation.
* @since 0.1.0
*/
public abstract TagContextBinarySerializer getBinarySerializer();
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.tags.unsafe;
import io.grpc.Context;
import openconsensus.tags.Tag;
import openconsensus.tags.TagContext;
import java.util.Collections;
import java.util.Iterator;
import javax.annotation.concurrent.Immutable;
/**
* Utility methods for accessing the {@link TagContext} contained in the {@link io.grpc.Context}.
*
* <p>Most code should interact with the current context via the public APIs in {@link
* TagContext} and avoid accessing {@link #TAG_CONTEXT_KEY} directly.
*
* @since 0.1.0
*/
public final class ContextUtils {
private static final TagContext EMPTY_TAG_CONTEXT = new EmptyTagContext();
private ContextUtils() {}
/**
* The {@link io.grpc.Context.Key} used to interact with the {@code TagContext} contained in the
* {@link io.grpc.Context}.
*
* @since 0.1.0
*/
public static final Context.Key<TagContext> TAG_CONTEXT_KEY =
Context.keyWithDefault("opencensus-tag-context-key", EMPTY_TAG_CONTEXT);
@Immutable
private static final class EmptyTagContext extends TagContext {
@Override
protected Iterator<Tag> getIterator() {
return Collections.<Tag>emptySet().iterator();
}
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import com.google.auto.value.AutoValue;
import openconsensus.internal.Utils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.concurrent.Immutable;
/**
* A text annotation with a set of attributes.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class Annotation {
private static final Map<String, AttributeValue> EMPTY_ATTRIBUTES =
Collections.unmodifiableMap(Collections.<String, AttributeValue>emptyMap());
/**
* Returns a new {@code Annotation} with the given description.
*
* @param description the text description of the {@code Annotation}.
* @return a new {@code Annotation} with the given description.
* @throws NullPointerException if {@code description} is {@code null}.
* @since 0.1.0
*/
public static Annotation fromDescription(String description) {
return new AutoValue_Annotation(description, EMPTY_ATTRIBUTES);
}
/**
* Returns a new {@code Annotation} with the given description and set of attributes.
*
* @param description the text description of the {@code Annotation}.
* @param attributes the attributes of the {@code Annotation}.
* @return a new {@code Annotation} with the given description and set of attributes.
* @throws NullPointerException if {@code description} or {@code attributes} are {@code null}.
* @since 0.1.0
*/
public static Annotation fromDescriptionAndAttributes(
String description, Map<String, AttributeValue> attributes) {
return new AutoValue_Annotation(
description,
Collections.unmodifiableMap(
new HashMap<String, AttributeValue>(Utils.checkNotNull(attributes, "attributes"))));
}
/**
* Return the description of the {@code Annotation}.
*
* @return the description of the {@code Annotation}.
* @since 0.1.0
*/
public abstract String getDescription();
/**
* Return the attributes of the {@code Annotation}.
*
* @return the attributes of the {@code Annotation}.
* @since 0.1.0
*/
public abstract Map<String, AttributeValue> getAttributes();
Annotation() {}
}

View File

@ -0,0 +1,256 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import com.google.auto.value.AutoValue;
import openconsensus.common.Function;
import openconsensus.common.Functions;
import openconsensus.internal.Utils;
import javax.annotation.concurrent.Immutable;
/**
* A class that represents all the possible values for an attribute. An attribute can have 3 types
* of values: {@code String}, {@code Boolean} or {@code Long}.
*
* @since 0.1.0
*/
@Immutable
public abstract class AttributeValue {
/**
* Returns an {@code AttributeValue} with a string value.
*
* @param stringValue The new value.
* @return an {@code AttributeValue} with a string value.
* @throws NullPointerException if {@code stringValue} is {@code null}.
* @since 0.1.0
*/
public static AttributeValue stringAttributeValue(String stringValue) {
return AttributeValueString.create(stringValue);
}
/**
* Returns an {@code AttributeValue} with a boolean value.
*
* @param booleanValue The new value.
* @return an {@code AttributeValue} with a boolean value.
* @since 0.1.0
*/
public static AttributeValue booleanAttributeValue(boolean booleanValue) {
return AttributeValueBoolean.create(booleanValue);
}
/**
* Returns an {@code AttributeValue} with a long value.
*
* @param longValue The new value.
* @return an {@code AttributeValue} with a long value.
* @since 0.1.0
*/
public static AttributeValue longAttributeValue(long longValue) {
return AttributeValueLong.create(longValue);
}
/**
* Returns an {@code AttributeValue} with a double value.
*
* @param doubleValue The new value.
* @return an {@code AttributeValue} with a double value.
* @since 0.1.0
*/
public static AttributeValue doubleAttributeValue(double doubleValue) {
return AttributeValueDouble.create(doubleValue);
}
AttributeValue() {}
/**
* Applies a function to the underlying value. The function that is called depends on the value's
* type, which can be {@code String}, {@code Long}, or {@code Boolean}.
*
* @param stringFunction the function that should be applied if the value has type {@code String}.
* @param longFunction the function that should be applied if the value has type {@code Long}.
* @param booleanFunction the function that should be applied if the value has type {@code
* Boolean}.
* @param defaultFunction the function that should be applied if the value has a type that was
* added after this {@code match} method was added to the API. See {@link
* Functions} for some common functions for handling unknown types.
* @return the result of the function applied to the underlying value.
* @since 0.1.0
* @deprecated in favor of {@link #match(Function, Function, Function, Function, Function)}.
*/
@Deprecated
public abstract <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<Object, T> defaultFunction);
/**
* Applies a function to the underlying value. The function that is called depends on the value's
* type, which can be {@code String}, {@code Long}, or {@code Boolean}.
*
* @param stringFunction the function that should be applied if the value has type {@code String}.
* @param longFunction the function that should be applied if the value has type {@code Long}.
* @param booleanFunction the function that should be applied if the value has type {@code
* Boolean}.
* @param doubleFunction the function that should be applied if the value has type {@code Double}.
* @param defaultFunction the function that should be applied if the value has a type that was
* added after this {@code match} method was added to the API. See {@link
* Functions} for some common functions for handling unknown types.
* @return the result of the function applied to the underlying value.
* @since 0.1.0
*/
@SuppressWarnings("InconsistentOverloads")
public abstract <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<? super Double, T> doubleFunction,
Function<Object, T> defaultFunction);
@Immutable
@AutoValue
abstract static class AttributeValueString extends AttributeValue {
AttributeValueString() {}
static AttributeValue create(String stringValue) {
return new AutoValue_AttributeValue_AttributeValueString(
Utils.checkNotNull(stringValue, "stringValue"));
}
@Override
public final <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<Object, T> defaultFunction) {
return stringFunction.apply(getStringValue());
}
@Override
public final <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<? super Double, T> doubleFunction,
Function<Object, T> defaultFunction) {
return stringFunction.apply(getStringValue());
}
abstract String getStringValue();
}
@Immutable
@AutoValue
abstract static class AttributeValueBoolean extends AttributeValue {
AttributeValueBoolean() {}
static AttributeValue create(Boolean booleanValue) {
return new AutoValue_AttributeValue_AttributeValueBoolean(
Utils.checkNotNull(booleanValue, "booleanValue"));
}
@Override
public final <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<Object, T> defaultFunction) {
return booleanFunction.apply(getBooleanValue());
}
@Override
public final <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<? super Double, T> doubleFunction,
Function<Object, T> defaultFunction) {
return booleanFunction.apply(getBooleanValue());
}
abstract Boolean getBooleanValue();
}
@Immutable
@AutoValue
abstract static class AttributeValueLong extends AttributeValue {
AttributeValueLong() {}
static AttributeValue create(Long longValue) {
return new AutoValue_AttributeValue_AttributeValueLong(
Utils.checkNotNull(longValue, "longValue"));
}
@Override
public final <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<Object, T> defaultFunction) {
return longFunction.apply(getLongValue());
}
@Override
public final <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<? super Double, T> doubleFunction,
Function<Object, T> defaultFunction) {
return longFunction.apply(getLongValue());
}
abstract Long getLongValue();
}
@Immutable
@AutoValue
abstract static class AttributeValueDouble extends AttributeValue {
AttributeValueDouble() {}
static AttributeValue create(Double doubleValue) {
return new AutoValue_AttributeValue_AttributeValueDouble(
Utils.checkNotNull(doubleValue, "doubleValue"));
}
@Override
public final <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<Object, T> defaultFunction) {
return defaultFunction.apply(getDoubleValue());
}
@Override
public final <T> T match(
Function<? super String, T> stringFunction,
Function<? super Boolean, T> booleanFunction,
Function<? super Long, T> longFunction,
Function<? super Double, T> doubleFunction,
Function<Object, T> defaultFunction) {
return doubleFunction.apply(getDoubleValue());
}
abstract Double getDoubleValue();
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
/**
* Superclass for {@link MessageEvent} and {@link NetworkEvent} to resolve API backward
* compatibility issue.
*
* <p>{@code SpanData.create} can't be overloaded with parameter types that differ only in the type
* of the TimedEvent, because the signatures are the same after generic type erasure. {@code
* BaseMessageEvent} allows the same method to accept both {@code TimedEvents<NetworkEvent>} and
* {@code TimedEvents<MessageEvent>}.
*
* <p>This class should only be extended by {@code NetworkEvent} and {@code MessageEvent}.
*
* @deprecated This class is for internal use only.
* @since 0.1.0
*/
@Deprecated
public abstract class BaseMessageEvent {
// package protected to avoid users to extend it.
BaseMessageEvent() {}
}

View File

@ -0,0 +1,167 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import openconsensus.internal.Utils;
import java.util.Arrays;
final class BigendianEncoding {
static final int LONG_BYTES = Long.SIZE / Byte.SIZE;
static final int BYTE_BASE16 = 2;
static final int LONG_BASE16 = BYTE_BASE16 * LONG_BYTES;
private static final String ALPHABET = "0123456789abcdef";
private static final int ASCII_CHARACTERS = 128;
private static final char[] ENCODING = buildEncodingArray();
private static final byte[] DECODING = buildDecodingArray();
private static char[] buildEncodingArray() {
char[] encoding = new char[512];
for (int i = 0; i < 256; ++i) {
encoding[i] = ALPHABET.charAt(i >>> 4);
encoding[i | 0x100] = ALPHABET.charAt(i & 0xF);
}
return encoding;
}
private static byte[] buildDecodingArray() {
byte[] decoding = new byte[ASCII_CHARACTERS];
Arrays.fill(decoding, (byte) -1);
for (int i = 0; i < ALPHABET.length(); i++) {
char c = ALPHABET.charAt(i);
decoding[c] = (byte) i;
}
return decoding;
}
/**
* Returns the {@code long} value whose big-endian representation is stored in the first 8 bytes
* of {@code bytes} starting from the {@code offset}.
*
* @param bytes the byte array representation of the {@code long}.
* @param offset the starting offset in the byte array.
* @return the {@code long} value whose big-endian representation is given.
* @throws IllegalArgumentException if {@code bytes} has fewer than 8 elements.
*/
static long longFromByteArray(byte[] bytes, int offset) {
Utils.checkArgument(bytes.length >= offset + LONG_BYTES, "array too small");
return (bytes[offset] & 0xFFL) << 56
| (bytes[offset + 1] & 0xFFL) << 48
| (bytes[offset + 2] & 0xFFL) << 40
| (bytes[offset + 3] & 0xFFL) << 32
| (bytes[offset + 4] & 0xFFL) << 24
| (bytes[offset + 5] & 0xFFL) << 16
| (bytes[offset + 6] & 0xFFL) << 8
| (bytes[offset + 7] & 0xFFL);
}
/**
* Stores the big-endian representation of {@code value} in the {@code dest} starting from the
* {@code destOffset}.
*
* @param value the value to be converted.
* @param dest the destination byte array.
* @param destOffset the starting offset in the destination byte array.
*/
static void longToByteArray(long value, byte[] dest, int destOffset) {
Utils.checkArgument(dest.length >= destOffset + LONG_BYTES, "array too small");
dest[destOffset + 7] = (byte) (value & 0xFFL);
dest[destOffset + 6] = (byte) (value >> 8 & 0xFFL);
dest[destOffset + 5] = (byte) (value >> 16 & 0xFFL);
dest[destOffset + 4] = (byte) (value >> 24 & 0xFFL);
dest[destOffset + 3] = (byte) (value >> 32 & 0xFFL);
dest[destOffset + 2] = (byte) (value >> 40 & 0xFFL);
dest[destOffset + 1] = (byte) (value >> 48 & 0xFFL);
dest[destOffset] = (byte) (value >> 56 & 0xFFL);
}
/**
* Returns the {@code long} value whose base16 representation is stored in the first 16 chars of
* {@code chars} starting from the {@code offset}.
*
* @param chars the base16 representation of the {@code long}.
* @param offset the starting offset in the {@code CharSequence}.
*/
static long longFromBase16String(CharSequence chars, int offset) {
Utils.checkArgument(chars.length() >= offset + LONG_BASE16, "chars too small");
return (decodeByte(chars.charAt(offset), chars.charAt(offset + 1)) & 0xFFL) << 56
| (decodeByte(chars.charAt(offset + 2), chars.charAt(offset + 3)) & 0xFFL) << 48
| (decodeByte(chars.charAt(offset + 4), chars.charAt(offset + 5)) & 0xFFL) << 40
| (decodeByte(chars.charAt(offset + 6), chars.charAt(offset + 7)) & 0xFFL) << 32
| (decodeByte(chars.charAt(offset + 8), chars.charAt(offset + 9)) & 0xFFL) << 24
| (decodeByte(chars.charAt(offset + 10), chars.charAt(offset + 11)) & 0xFFL) << 16
| (decodeByte(chars.charAt(offset + 12), chars.charAt(offset + 13)) & 0xFFL) << 8
| (decodeByte(chars.charAt(offset + 14), chars.charAt(offset + 15)) & 0xFFL);
}
/**
* Appends the base16 encoding of the specified {@code value} to the {@code dest}.
*
* @param value the value to be converted.
* @param dest the destination char array.
* @param destOffset the starting offset in the destination char array.
*/
static void longToBase16String(long value, char[] dest, int destOffset) {
byteToBase16((byte) (value >> 56 & 0xFFL), dest, destOffset);
byteToBase16((byte) (value >> 48 & 0xFFL), dest, destOffset + BYTE_BASE16);
byteToBase16((byte) (value >> 40 & 0xFFL), dest, destOffset + 2 * BYTE_BASE16);
byteToBase16((byte) (value >> 32 & 0xFFL), dest, destOffset + 3 * BYTE_BASE16);
byteToBase16((byte) (value >> 24 & 0xFFL), dest, destOffset + 4 * BYTE_BASE16);
byteToBase16((byte) (value >> 16 & 0xFFL), dest, destOffset + 5 * BYTE_BASE16);
byteToBase16((byte) (value >> 8 & 0xFFL), dest, destOffset + 6 * BYTE_BASE16);
byteToBase16((byte) (value & 0xFFL), dest, destOffset + 7 * BYTE_BASE16);
}
/**
* Encodes the specified byte, and returns the encoded {@code String}.
*
* @param value the value to be converted.
* @param dest the destination char array.
* @param destOffset the starting offset in the destination char array.
*/
static void byteToBase16String(byte value, char[] dest, int destOffset) {
byteToBase16(value, dest, destOffset);
}
/**
* Decodes the specified two character sequence, and returns the resulting {@code byte}.
*
* @param chars the character sequence to be decoded.
* @param offset the starting offset in the {@code CharSequence}.
* @return the resulting {@code byte}
* @throws IllegalArgumentException if the input is not a valid encoded string according to this
* encoding.
*/
static byte byteFromBase16String(CharSequence chars, int offset) {
Utils.checkArgument(chars.length() >= offset + 2, "chars too small");
return decodeByte(chars.charAt(offset), chars.charAt(offset + 1));
}
private static byte decodeByte(char hi, char lo) {
Utils.checkArgument(lo < ASCII_CHARACTERS && DECODING[lo] != -1, "invalid character " + lo);
Utils.checkArgument(hi < ASCII_CHARACTERS && DECODING[hi] != -1, "invalid character " + hi);
int decoded = DECODING[hi] << 4 | DECODING[lo];
return (byte) decoded;
}
private static void byteToBase16(byte value, char[] dest, int destOffset) {
int b = value & 0xFF;
dest[destOffset] = ENCODING[b];
dest[destOffset + 1] = ENCODING[b | 0x100];
}
private BigendianEncoding() {}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import openconsensus.internal.Utils;
import java.util.Map;
import javax.annotation.concurrent.Immutable;
/**
* The {@code BlankSpan} is a singleton class, which is the default {@link Span} that is used when
* no {@code Span} implementation is available. All operations are no-op.
*
* <p>Used also to stop tracing, see {@link Tracer#withSpan}.
*
* @since 0.1.0
*/
@Immutable
public final class BlankSpan extends Span {
/**
* Singleton instance of this class.
*
* @since 0.1.0
*/
public static final BlankSpan INSTANCE = new BlankSpan();
private BlankSpan() {
super(SpanContext.INVALID, null);
}
/** No-op implementation of the {@link Span#putAttribute(String, AttributeValue)} method. */
@Override
public void putAttribute(String key, AttributeValue value) {
Utils.checkNotNull(key, "key");
Utils.checkNotNull(value, "value");
}
/** No-op implementation of the {@link Span#putAttributes(Map)} method. */
@Override
public void putAttributes(Map<String, AttributeValue> attributes) {
Utils.checkNotNull(attributes, "attributes");
}
/** No-op implementation of the {@link Span#addAnnotation(String, Map)} method. */
@Override
public void addAnnotation(String description, Map<String, AttributeValue> attributes) {
Utils.checkNotNull(description, "description");
Utils.checkNotNull(attributes, "attributes");
}
/** No-op implementation of the {@link Span#addAnnotation(Annotation)} method. */
@Override
public void addAnnotation(Annotation annotation) {
Utils.checkNotNull(annotation, "annotation");
}
/** No-op implementation of the {@link Span#addNetworkEvent(NetworkEvent)} method. */
@Override
@Deprecated
public void addNetworkEvent(NetworkEvent networkEvent) {}
/** No-op implementation of the {@link Span#addMessageEvent(MessageEvent)} method. */
@Override
public void addMessageEvent(MessageEvent messageEvent) {
Utils.checkNotNull(messageEvent, "messageEvent");
}
/** No-op implementation of the {@link Span#addLink(Link)} method. */
@Override
public void addLink(Link link) {
Utils.checkNotNull(link, "link");
}
@Override
public void setStatus(Status status) {
Utils.checkNotNull(status, "status");
}
/** No-op implementation of the {@link Span#end(EndSpanOptions)} method. */
@Override
public void end(EndSpanOptions options) {
Utils.checkNotNull(options, "options");
}
@Override
public String toString() {
return "BlankSpan";
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import io.grpc.Context;
import openconsensus.common.Scope;
import openconsensus.trace.unsafe.ContextUtils;
import java.util.concurrent.Callable;
import javax.annotation.Nullable;
/** Util methods/functionality to interact with the {@link Span} in the {@link io.grpc.Context}. */
final class CurrentSpanUtils {
// No instance of this class.
private CurrentSpanUtils() {}
/**
* Returns The {@link Span} from the current context.
*
* @return The {@code Span} from the current context.
*/
@Nullable
static Span getCurrentSpan() {
return ContextUtils.CONTEXT_SPAN_KEY.get();
}
/**
* Enters the scope of code where the given {@link Span} is in the current context, and returns an
* object that represents that scope. The scope is exited when the returned object is closed.
*
* <p>Supports try-with-resource idiom.
*
* @param span The {@code Span} to be set to the current context.
* @param endSpan if {@code true} the returned {@code Scope} will close the {@code Span}.
* @return An object that defines a scope where the given {@code Span} is set to the current
* context.
*/
static Scope withSpan(Span span, boolean endSpan) {
return new ScopeInSpan(span, endSpan);
}
/**
* Wraps a {@link Runnable} so that it executes with the {@code span} as the current {@code Span}.
*
* @param span the {@code Span} to be set as current.
* @param endSpan if {@code true} the returned {@code Runnable} will close the {@code Span}.
* @param runnable the {@code Runnable} to run in the {@code Span}.
* @return the wrapped {@code Runnable}.
*/
static Runnable withSpan(Span span, boolean endSpan, Runnable runnable) {
return new RunnableInSpan(span, runnable, endSpan);
}
/**
* Wraps a {@link Callable} so that it executes with the {@code span} as the current {@code Span}.
*
* @param span the {@code Span} to be set as current.
* @param endSpan if {@code true} the returned {@code Runnable} will close the {@code Span}.
* @param callable the {@code Callable} to run in the {@code Span}.
* @return the wrapped {@code Callable}.
*/
static <C> Callable<C> withSpan(Span span, boolean endSpan, Callable<C> callable) {
return new CallableInSpan<C>(span, callable, endSpan);
}
// Defines an arbitrary scope of code as a traceable operation. Supports try-with-resources idiom.
private static final class ScopeInSpan implements Scope {
private final Context origContext;
private final Span span;
private final boolean endSpan;
/**
* Constructs a new {@link ScopeInSpan}.
*
* @param span is the {@code Span} to be added to the current {@code io.grpc.Context}.
*/
private ScopeInSpan(Span span, boolean endSpan) {
this.span = span;
this.endSpan = endSpan;
origContext = Context.current().withValue(ContextUtils.CONTEXT_SPAN_KEY, span).attach();
}
@Override
public void close() {
Context.current().detach(origContext);
if (endSpan) {
span.end();
}
}
}
private static final class RunnableInSpan implements Runnable {
// TODO(bdrutu): Investigate if the extra private visibility increases the generated bytecode.
private final Span span;
private final Runnable runnable;
private final boolean endSpan;
private RunnableInSpan(Span span, Runnable runnable, boolean endSpan) {
this.span = span;
this.runnable = runnable;
this.endSpan = endSpan;
}
@Override
public void run() {
Context origContext =
Context.current().withValue(ContextUtils.CONTEXT_SPAN_KEY, span).attach();
try {
runnable.run();
} catch (Throwable t) {
setErrorStatus(span, t);
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof Error) {
throw (Error) t;
}
throw new RuntimeException("unexpected", t);
} finally {
Context.current().detach(origContext);
if (endSpan) {
span.end();
}
}
}
}
private static final class CallableInSpan<V> implements Callable<V> {
private final Span span;
private final Callable<V> callable;
private final boolean endSpan;
private CallableInSpan(Span span, Callable<V> callable, boolean endSpan) {
this.span = span;
this.callable = callable;
this.endSpan = endSpan;
}
@Override
public V call() throws Exception {
Context origContext =
Context.current().withValue(ContextUtils.CONTEXT_SPAN_KEY, span).attach();
try {
return callable.call();
} catch (Exception e) {
setErrorStatus(span, e);
throw e;
} catch (Throwable t) {
setErrorStatus(span, t);
if (t instanceof Error) {
throw (Error) t;
}
throw new RuntimeException("unexpected", t);
} finally {
Context.current().detach(origContext);
if (endSpan) {
span.end();
}
}
}
}
private static void setErrorStatus(Span span, Throwable t) {
span.setStatus(
Status.UNKNOWN.withDescription(
t.getMessage() == null ? t.getClass().getSimpleName() : t.getMessage()));
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import com.google.auto.value.AutoValue;
import openconsensus.common.ExperimentalApi;
import java.util.Collection;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import openconsensus.trace.export.SampledSpanStore;
/**
* A class that enables overriding the default values used when ending a {@link Span}. Allows
* overriding the {@link Status status}.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class EndSpanOptions {
/**
* The default {@code EndSpanOptions}.
*
* @since 0.1.0
*/
public static final EndSpanOptions DEFAULT = builder().build();
/**
* Returns a new {@link Builder} with default options.
*
* @return a new {@code Builder} with default options.
* @since 0.1.0
*/
public static Builder builder() {
return new AutoValue_EndSpanOptions.Builder().setSampleToLocalSpanStore(false);
}
/**
* If {@code true} this is equivalent with calling the {@link
* SampledSpanStore#registerSpanNamesForCollection(Collection)} in
* advance for this span name.
*
* @return {@code true} if the name of the {@code Span} should be registered to the {@code
* SampledSpanStore}.
* @since 0.1.0
*/
@ExperimentalApi
public abstract boolean getSampleToLocalSpanStore();
/**
* Returns the status.
*
* <p>If {@code null} then the {@link Span} will record the {@link Status} set via {@link
* Span#setStatus(Status)} or the default {@link Status#OK} if no status was set.
*
* @return the status.
* @since 0.1.0
*/
@Nullable
public abstract Status getStatus();
/**
* Builder class for {@link EndSpanOptions}.
*
* @since 0.1.0
*/
@AutoValue.Builder
public abstract static class Builder {
/**
* Sets the status for the {@link Span}.
*
* <p>If set, this will override the status set via {@link Span#setStatus(Status)}.
*
* @param status the status.
* @return this.
* @since 0.1.0
*/
public abstract Builder setStatus(Status status);
/**
* If set to {@code true} this is equivalent with calling the {@link
* SampledSpanStore#registerSpanNamesForCollection(Collection)} in
* advance for the given span name.
*
* <p>WARNING: setting this option to a randomly generated span name can OOM your process
* because the library will save samples for each name.
*
* <p>It is strongly recommended to use the {@link
* SampledSpanStore#registerSpanNamesForCollection(Collection)} API
* instead.
*
* @return this.
* @since 0.1.0
*/
@ExperimentalApi
public abstract Builder setSampleToLocalSpanStore(boolean sampleToLocalSpanStore);
/**
* Builds and returns a {@code EndSpanOptions} with the desired settings.
*
* @return a {@code EndSpanOptions} with the desired settings.
* @since 0.1.0
*/
public abstract EndSpanOptions build();
Builder() {}
}
EndSpanOptions() {}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import com.google.auto.value.AutoValue;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.concurrent.Immutable;
/**
* A link to a {@link Span} from a different trace.
*
* <p>It requires a {@link Type} which describes the relationship with the linked {@code Span} and
* the identifiers of the linked {@code Span}.
*
* <p>Used (for example) in batching operations, where a single batch handler processes multiple
* requests from different traces.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
public abstract class Link {
private static final Map<String, AttributeValue> EMPTY_ATTRIBUTES = Collections.emptyMap();
/**
* The relationship with the linked {@code Span} relative to the current {@code Span}.
*
* @since 0.1.0
*/
public enum Type {
/**
* When the linked {@code Span} is a child of the current {@code Span}.
*
* @since 0.1.0
*/
CHILD_LINKED_SPAN,
/**
* When the linked {@code Span} is a parent of the current {@code Span}.
*
* @since 0.1.0
*/
PARENT_LINKED_SPAN
}
/**
* Returns a new {@code Link}.
*
* @param context the context of the linked {@code Span}.
* @param type the type of the relationship with the linked {@code Span}.
* @return a new {@code Link}.
* @since 0.1.0
*/
public static Link fromSpanContext(SpanContext context, Type type) {
return new AutoValue_Link(context.getTraceId(), context.getSpanId(), type, EMPTY_ATTRIBUTES);
}
/**
* Returns a new {@code Link}.
*
* @param context the context of the linked {@code Span}.
* @param type the type of the relationship with the linked {@code Span}.
* @param attributes the attributes of the {@code Link}.
* @return a new {@code Link}.
* @since 0.1.0
*/
public static Link fromSpanContext(
SpanContext context, Type type, Map<String, AttributeValue> attributes) {
return new AutoValue_Link(
context.getTraceId(),
context.getSpanId(),
type,
Collections.unmodifiableMap(new HashMap<String, AttributeValue>(attributes)));
}
/**
* Returns the {@code TraceId}.
*
* @return the {@code TraceId}.
* @since 0.1.0
*/
public abstract TraceId getTraceId();
/**
* Returns the {@code SpanId}.
*
* @return the {@code SpanId}
* @since 0.1.0
*/
public abstract SpanId getSpanId();
/**
* Returns the {@code Type}.
*
* @return the {@code Type}.
* @since 0.1.0
*/
public abstract Type getType();
/**
* Returns the set of attributes.
*
* @return the set of attributes.
* @since 0.1.0
*/
public abstract Map<String, AttributeValue> getAttributes();
Link() {}
}

View File

@ -0,0 +1,151 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import com.google.auto.value.AutoValue;
import openconsensus.internal.Utils;
import javax.annotation.concurrent.Immutable;
/**
* A class that represents a generic messaging event. This class can represent messaging happened in
* any layer, especially higher application layer. Thus, it can be used when recording events in
* pipeline works, in-process bidirectional streams and batch processing.
*
* <p>It requires a {@link Type type} and a message id that serves to uniquely identify each
* message. It can optionally have information about the message size.
*
* @since 0.1.0
*/
@Immutable
@AutoValue
@SuppressWarnings("deprecation")
public abstract class MessageEvent extends BaseMessageEvent {
/**
* Available types for a {@code MessageEvent}.
*
* @since 0.1.0
*/
public enum Type {
/**
* When the message was sent.
*
* @since 0.1.0
*/
SENT,
/**
* When the message was received.
*
* @since 0.1.0
*/
RECEIVED,
}
/**
* Returns a new {@link Builder} with default values.
*
* @param type designates whether this is a send or receive message.
* @param messageId serves to uniquely identify each message.
* @return a new {@code Builder} with default values.
* @throws NullPointerException if {@code type} is {@code null}.
* @since 0.1.0
*/
public static Builder builder(Type type, long messageId) {
return new AutoValue_MessageEvent.Builder()
.setType(Utils.checkNotNull(type, "type"))
.setMessageId(messageId)
// We need to set a value for the message size because the autovalue requires all
// primitives to be initialized.
.setUncompressedMessageSize(0)
.setCompressedMessageSize(0);
}
/**
* Returns the type of the {@code MessageEvent}.
*
* @return the type of the {@code MessageEvent}.
* @since 0.1.0
*/
public abstract Type getType();
/**
* Returns the message id argument that serves to uniquely identify each message.
*
* @return the message id of the {@code MessageEvent}.
* @since 0.1.0
*/
public abstract long getMessageId();
/**
* Returns the uncompressed size in bytes of the {@code MessageEvent}.
*
* @return the uncompressed size in bytes of the {@code MessageEvent}.
* @since 0.1.0
*/
public abstract long getUncompressedMessageSize();
/**
* Returns the compressed size in bytes of the {@code MessageEvent}.
*
* @return the compressed size in bytes of the {@code MessageEvent}.
* @since 0.1.0
*/
public abstract long getCompressedMessageSize();
/**
* Builder class for {@link MessageEvent}.
*
* @since 0.1.0
*/
@AutoValue.Builder
public abstract static class Builder {
// Package protected methods because these values are mandatory and set only in the
// MessageEvent#builder() function.
abstract Builder setType(Type type);
abstract Builder setMessageId(long messageId);
/**
* Sets the uncompressed message size.
*
* @param uncompressedMessageSize represents the uncompressed size in bytes of this message.
* @return this.
* @since 0.1.0
*/
public abstract Builder setUncompressedMessageSize(long uncompressedMessageSize);
/**
* Sets the compressed message size.
*
* @param compressedMessageSize represents the compressed size in bytes of this message.
* @return this.
* @since 0.1.0
*/
public abstract Builder setCompressedMessageSize(long compressedMessageSize);
/**
* Builds and returns a {@code MessageEvent} with the desired values.
*
* @return a {@code MessageEvent} with the desired values.
* @since 0.1.0
*/
public abstract MessageEvent build();
Builder() {}
}
MessageEvent() {}
}

View File

@ -0,0 +1,200 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import com.google.auto.value.AutoValue;
import openconsensus.common.Timestamp;
import openconsensus.internal.Utils;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A class that represents a network event. It requires a {@link Type type} and a message id that
* serves to uniquely identify each network message. It can optionally can have information about
* the kernel time and message size.
*
* @deprecated Use {@link MessageEvent}.
* @since 0.1.0
*/
@Immutable
@AutoValue
@AutoValue.CopyAnnotations
@Deprecated
public abstract class NetworkEvent extends BaseMessageEvent {
/**
* Available types for a {@code NetworkEvent}.
*
* @since 0.1.0
*/
public enum Type {
/**
* When the message was sent.
*
* @since 0.1.0
*/
SENT,
/**
* When the message was received.
*
* @since 0.1.0
*/
RECV,
}
/**
* Returns a new {@link Builder} with default values.
*
* @param type designates whether this is a network send or receive message.
* @param messageId serves to uniquely identify each network message.
* @return a new {@code Builder} with default values.
* @throws NullPointerException if {@code type} is {@code null}.
* @since 0.1.0
*/
public static Builder builder(Type type, long messageId) {
return new AutoValue_NetworkEvent.Builder()
.setType(Utils.checkNotNull(type, "type"))
.setMessageId(messageId)
// We need to set a value for the message size because the autovalue requires all
// primitives to be initialized.
.setUncompressedMessageSize(0)
.setCompressedMessageSize(0);
}
/**
* Returns the kernel timestamp associated with the {@code NetworkEvent} or {@code null} if not
* set.
*
* @return the kernel timestamp associated with the {@code NetworkEvent} or {@code null} if not
* set.
* @since 0.1.0
*/
@Nullable
public abstract Timestamp getKernelTimestamp();
/**
* Returns the type of the {@code NetworkEvent}.
*
* @return the type of the {@code NetworkEvent}.
* @since 0.1.0
*/
public abstract Type getType();
/**
* Returns the message id argument that serves to uniquely identify each network message.
*
* @return the message id of the {@code NetworkEvent}.
* @since 0.1.0
*/
public abstract long getMessageId();
/**
* Returns the uncompressed size in bytes of the {@code NetworkEvent}.
*
* @return the uncompressed size in bytes of the {@code NetworkEvent}.
* @since 0.1.0
*/
public abstract long getUncompressedMessageSize();
/**
* Returns the compressed size in bytes of the {@code NetworkEvent}.
*
* @return the compressed size in bytes of the {@code NetworkEvent}.
* @since 0.1.0
*/
public abstract long getCompressedMessageSize();
/**
* Returns the uncompressed size in bytes of the {@code NetworkEvent}.
*
* @deprecated Use {@link #getUncompressedMessageSize}.
* @return the uncompressed size in bytes of the {@code NetworkEvent}.
* @since 0.1.0
*/
@Deprecated
public long getMessageSize() {
return getUncompressedMessageSize();
}
/**
* Builder class for {@link NetworkEvent}.
*
* @deprecated {@link NetworkEvent} is deprecated. Please use {@link MessageEvent} and its builder
* {@link MessageEvent.Builder}.
* @since 0.1.0
*/
@AutoValue.Builder
@Deprecated
public abstract static class Builder {
// Package protected methods because these values are mandatory and set only in the
// NetworkEvent#builder() function.
abstract Builder setType(Type type);
abstract Builder setMessageId(long messageId);
/**
* Sets the kernel timestamp.
*
* @param kernelTimestamp The kernel timestamp of the event.
* @return this.
* @since 0.1.0
*/
public abstract Builder setKernelTimestamp(@Nullable Timestamp kernelTimestamp);
/**
* Sets the uncompressed message size.
*
* @deprecated Use {@link #setUncompressedMessageSize}.
* @param messageSize represents the uncompressed size in bytes of this message.
* @return this.
* @since 0.1.0
*/
@Deprecated
public Builder setMessageSize(long messageSize) {
return setUncompressedMessageSize(messageSize);
}
/**
* Sets the uncompressed message size.
*
* @param uncompressedMessageSize represents the uncompressed size in bytes of this message.
* @return this.
* @since 0.1.0
*/
public abstract Builder setUncompressedMessageSize(long uncompressedMessageSize);
/**
* Sets the compressed message size.
*
* @param compressedMessageSize represents the compressed size in bytes of this message.
* @return this.
* @since 0.1.0
*/
public abstract Builder setCompressedMessageSize(long compressedMessageSize);
/**
* Builds and returns a {@code NetworkEvent} with the desired values.
*
* @return a {@code NetworkEvent} with the desired values.
* @since 0.1.0
*/
public abstract NetworkEvent build();
Builder() {}
}
NetworkEvent() {}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import java.util.List;
import javax.annotation.Nullable;
/**
* Sampler is used to make decisions on {@link Span} sampling.
*
* @since 0.1.0
*/
public abstract class Sampler {
/**
* Called during {@link Span} creation to make a sampling decision.
*
* @param parentContext the parent span's {@link SpanContext}. {@code null} if this is a root
* span.
* @param hasRemoteParent {@code true} if the parent {@code Span} is remote. {@code null} if this
* is a root span.
* @param traceId the {@link TraceId} for the new {@code Span}. This will be identical to that in
* the parentContext, unless this is a root span.
* @param spanId the {@link SpanId} for the new {@code Span}.
* @param name the name of the new {@code Span}.
* @param parentLinks the parentLinks associated with the new {@code Span}.
* @return {@code true} if the {@code Span} is sampled.
* @since 0.1.0
*/
public abstract boolean shouldSample(
@Nullable SpanContext parentContext,
@Nullable Boolean hasRemoteParent,
TraceId traceId,
SpanId spanId,
String name,
List<Span> parentLinks);
/**
* Returns the description of this {@code Sampler}. This may be displayed on debug pages or in the
* logs.
*
* <p>Example: "ProbabilitySampler{0.000100}"
*
* @return the description of this {@code Sampler}.
* @since 0.1.0
*/
public abstract String getDescription();
}

View File

@ -0,0 +1,288 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import openconsensus.internal.Utils;
import openconsensus.trace.internal.BaseMessageEventUtils;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* An abstract class that represents a span. It has an associated {@link SpanContext} and a set of
* {@link Options}.
*
* <p>Spans are created by the {@link SpanBuilder#startSpan} method.
*
* <p>{@code Span} <b>must</b> be ended by calling {@link #end()} or {@link #end(EndSpanOptions)}
*
* @since 0.1.0
*/
public abstract class Span {
private static final Map<String, AttributeValue> EMPTY_ATTRIBUTES = Collections.emptyMap();
// Contains the identifiers associated with this Span.
private final SpanContext context;
// Contains the options associated with this Span. This object is immutable.
private final Set<Options> options;
/**
* {@code Span} options. These options are NOT propagated to child spans. These options determine
* features such as whether a {@code Span} should record any annotations or events.
*
* @since 0.1.0
*/
public enum Options {
/**
* This option is set if the Span is part of a sampled distributed trace OR {@link
* SpanBuilder#setRecordEvents(boolean)} was called with true.
*
* @since 0.1.0
*/
RECORD_EVENTS;
}
private static final Set<Options> DEFAULT_OPTIONS =
Collections.unmodifiableSet(EnumSet.noneOf(Options.class));
/**
* Creates a new {@code Span}.
*
* @param context the context associated with this {@code Span}.
* @param options the options associated with this {@code Span}. If {@code null} then default
* options will be set.
* @throws NullPointerException if context is {@code null}.
* @throws IllegalArgumentException if the {@code SpanContext} is sampled but no RECORD_EVENTS
* options.
* @since 0.1.0
*/
protected Span(SpanContext context, @Nullable EnumSet<Options> options) {
this.context = Utils.checkNotNull(context, "context");
this.options =
options == null
? DEFAULT_OPTIONS
: Collections.<Options>unmodifiableSet(EnumSet.copyOf(options));
Utils.checkArgument(
!context.getTraceOptions().isSampled() || this.options.contains(Options.RECORD_EVENTS),
"Span is sampled, but does not have RECORD_EVENTS set.");
}
/**
* Sets an attribute to the {@code Span}. If the {@code Span} previously contained a mapping for
* the key, the old value is replaced by the specified value.
*
* @param key the key for this attribute.
* @param value the value for this attribute.
* @since 0.1.0
*/
public void putAttribute(String key, AttributeValue value) {
// Not final because for performance reasons we want to override this in the implementation.
// Also a default implementation is needed to not break the compatibility (users may extend this
// for testing).
Utils.checkNotNull(key, "key");
Utils.checkNotNull(value, "value");
putAttributes(Collections.singletonMap(key, value));
}
/**
* Sets a set of attributes to the {@code Span}. The effect of this call is equivalent to that of
* calling {@link #putAttribute(String, AttributeValue)} once for each element in the specified
* map.
*
* @param attributes the attributes that will be added and associated with the {@code Span}.
* @since 0.1.0
*/
public void putAttributes(Map<String, AttributeValue> attributes) {
// Not final because we want to start overriding this method from the beginning, this will
// allow us to remove the addAttributes faster. All implementations MUST override this method.
Utils.checkNotNull(attributes, "attributes");
addAttributes(attributes);
}
/**
* Sets a set of attributes to the {@code Span}. The effect of this call is equivalent to that of
* calling {@link #putAttribute(String, AttributeValue)} once for each element in the specified
* map.
*
* @deprecated Use {@link #putAttributes(Map)}
* @param attributes the attributes that will be added and associated with the {@code Span}.
* @since 0.1.0
*/
@Deprecated
public void addAttributes(Map<String, AttributeValue> attributes) {
putAttributes(attributes);
}
/**
* Adds an annotation to the {@code Span}.
*
* @param description the description of the annotation time event.
* @since 0.1.0
*/
public final void addAnnotation(String description) {
Utils.checkNotNull(description, "description");
addAnnotation(description, EMPTY_ATTRIBUTES);
}
/**
* Adds an annotation to the {@code Span}.
*
* @param description the description of the annotation time event.
* @param attributes the attributes that will be added; these are associated with this annotation,
* not the {@code Span} as for {@link #putAttributes(Map)}.
* @since 0.1.0
*/
public abstract void addAnnotation(String description, Map<String, AttributeValue> attributes);
/**
* Adds an annotation to the {@code Span}.
*
* @param annotation the annotations to add.
* @since 0.1.0
*/
public abstract void addAnnotation(Annotation annotation);
/**
* Adds a NetworkEvent to the {@code Span}.
*
* <p>This function is only intended to be used by RPC systems (either client or server), not by
* higher level applications.
*
* @param networkEvent the network event to add.
* @deprecated Use {@link #addMessageEvent}.
* @since 0.1.0
*/
@Deprecated
public void addNetworkEvent(NetworkEvent networkEvent) {
addMessageEvent(BaseMessageEventUtils.asMessageEvent(networkEvent));
}
/**
* Adds a MessageEvent to the {@code Span}.
*
* <p>This function can be used by higher level applications to record messaging event.
*
* <p>This method should always be overridden by users whose API versions are larger or equal to
* {@code 0.12}.
*
* @param messageEvent the message to add.
* @since 0.1.0
*/
public void addMessageEvent(MessageEvent messageEvent) {
// Default implementation by invoking addNetworkEvent() so that any existing derived classes,
// including implementation and the mocked ones, do not need to override this method explicitly.
Utils.checkNotNull(messageEvent, "messageEvent");
addNetworkEvent(BaseMessageEventUtils.asNetworkEvent(messageEvent));
}
/**
* Adds a {@link Link} to the {@code Span}.
*
* <p>Used (for example) in batching operations, where a single batch handler processes multiple
* requests from different traces.
*
* @param link the link to add.
* @since 0.1.0
*/
public abstract void addLink(Link link);
/**
* Sets the {@link Status} to the {@code Span}.
*
* <p>If used, this will override the default {@code Span} status. Default is {@link Status#OK}.
*
* <p>Only the value of the last call will be recorded, and implementations are free to ignore
* previous calls. If the status is set via {@link EndSpanOptions.Builder#setStatus(Status)} that
* will always be the last call.
*
* @param status the {@link Status} to set.
* @since 0.1.0
*/
public void setStatus(Status status) {
// Implemented as no-op for backwards compatibility (for example gRPC extends Span in tests).
// Implementation must override this method.
Utils.checkNotNull(status, "status");
}
/**
* Marks the end of {@code Span} execution with the given options.
*
* <p>Only the timing of the first end call for a given {@code Span} will be recorded, and
* implementations are free to ignore all further calls.
*
* @param options the options to be used for the end of the {@code Span}.
* @since 0.1.0
*/
public abstract void end(EndSpanOptions options);
/**
* Marks the end of {@code Span} execution with the default options.
*
* <p>Only the timing of the first end call for a given {@code Span} will be recorded, and
* implementations are free to ignore all further calls.
*
* @since 0.1.0
*/
public final void end() {
end(EndSpanOptions.DEFAULT);
}
/**
* Returns the {@code SpanContext} associated with this {@code Span}.
*
* @return the {@code SpanContext} associated with this {@code Span}.
* @since 0.1.0
*/
public final SpanContext getContext() {
return context;
}
/**
* Returns the options associated with this {@code Span}.
*
* @return the options associated with this {@code Span}.
* @since 0.1.0
*/
public final Set<Options> getOptions() {
return options;
}
/**
* Type of span. Can be used to specify additional relationships between spans in addition to a
* parent/child relationship.
*
* @since 0.1.0
*/
public enum Kind {
/**
* Indicates that the span covers server-side handling of an RPC or other remote request.
*
* @since 0.1.0
*/
SERVER,
/**
* Indicates that the span covers the client-side wrapper around an RPC or other remote request.
*
* @since 0.1.0
*/
CLIENT
}
}

View File

@ -0,0 +1,356 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import com.google.errorprone.annotations.MustBeClosed;
import openconsensus.common.Scope;
import openconsensus.internal.Utils;
import java.util.List;
import java.util.concurrent.Callable;
import javax.annotation.Nullable;
/**
* {@link SpanBuilder} is used to construct {@link Span} instances which define arbitrary scopes of
* code that are sampled for distributed tracing as a single atomic unit.
*
* <p>This is a simple example where all the work is being done within a single scope and a single
* thread and the Context is automatically propagated:
*
* <pre>{@code
* class MyClass {
* private static final Tracer tracer = Tracing.getTracer();
* void doWork {
* // Create a Span as a child of the current Span.
* try (Scope ss = tracer.spanBuilder("MyChildSpan").startScopedSpan()) {
* tracer.getCurrentSpan().addAnnotation("my annotation");
* doSomeWork(); // Here the new span is in the current Context, so it can be used
* // implicitly anywhere down the stack.
* }
* }
* }
* }</pre>
*
* <p>There might be cases where you do not perform all the work inside one static scope and the
* Context is automatically propagated:
*
* <pre>{@code
* class MyRpcServerInterceptorListener implements RpcServerInterceptor.Listener {
* private static final Tracer tracer = Tracing.getTracer();
* private Span mySpan;
*
* public MyRpcInterceptor() {}
*
* public void onRequest(String rpcName, Metadata metadata) {
* // Create a Span as a child of the remote Span.
* mySpan = tracer.spanBuilderWithRemoteParent(
* getTraceContextFromMetadata(metadata), rpcName).startSpan();
* }
*
* public void onExecuteHandler(ServerCallHandler serverCallHandler) {
* try (Scope ws = tracer.withSpan(mySpan)) {
* tracer.getCurrentSpan().addAnnotation("Start rpc execution.");
* serverCallHandler.run(); // Here the new span is in the current Context, so it can be
* // used implicitly anywhere down the stack.
* }
* }
*
* // Called when the RPC is canceled and guaranteed onComplete will not be called.
* public void onCancel() {
* // IMPORTANT: DO NOT forget to ended the Span here as the work is done.
* mySpan.end(EndSpanOptions.builder().setStatus(Status.CANCELLED));
* }
*
* // Called when the RPC is done and guaranteed onCancel will not be called.
* public void onComplete(RpcStatus rpcStatus) {
* // IMPORTANT: DO NOT forget to ended the Span here as the work is done.
* mySpan.end(EndSpanOptions.builder().setStatus(rpcStatusToCanonicalTraceStatus(status));
* }
* }
* }</pre>
*
* <p>This is a simple example where all the work is being done within a single scope and the
* Context is manually propagated:
*
* <pre>{@code
* class MyClass {
* private static final Tracer tracer = Tracing.getTracer();
* void DoWork(Span parent) {
* Span childSpan = tracer.spanBuilderWithExplicitParent("MyChildSpan", parent).startSpan();
* childSpan.addAnnotation("my annotation");
* try {
* doSomeWork(childSpan); // Manually propagate the new span down the stack.
* } finally {
* // To make sure we end the span even in case of an exception.
* childSpan.end(); // Manually end the span.
* }
* }
* }
* }</pre>
*
* <p>If your Java version is less than Java SE 7, see {@link SpanBuilder#startSpan} and {@link
* SpanBuilder#startScopedSpan} for usage examples.
*
* @since 0.1.0
*/
public abstract class SpanBuilder {
/**
* Sets the {@link Sampler} to use. If not set, the implementation will provide a default.
*
* @param sampler the {@code Sampler} to use when determining sampling for a {@code Span}.
* @return this.
* @since 0.1.0
*/
public abstract SpanBuilder setSampler(Sampler sampler);
/**
* Sets the {@code List} of parent links. Links are used to link {@link Span}s in different
* traces. Used (for example) in batching operations, where a single batch handler processes
* multiple requests from different traces.
*
* @param parentLinks new links to be added.
* @return this.
* @throws NullPointerException if {@code parentLinks} is {@code null}.
* @since 0.1.0
*/
public abstract SpanBuilder setParentLinks(List<Span> parentLinks);
/**
* Sets the option {@link Span.Options#RECORD_EVENTS} for the newly created {@code Span}. If not
* called, the implementation will provide a default.
*
* @param recordEvents new value determining if this {@code Span} should have events recorded.
* @return this.
* @since 0.1.0
*/
public abstract SpanBuilder setRecordEvents(boolean recordEvents);
/**
* Sets the {@link Span.Kind} for the newly created {@code Span}. If not called, the
* implementation will provide a default.
*
* @param spanKind the kind of the newly created {@code Span}.
* @return this.
* @since 0.1.0
*/
public SpanBuilder setSpanKind(@Nullable Span.Kind spanKind) {
return this;
}
/**
* Starts a new {@link Span}.
*
* <p>Users <b>must</b> manually call {@link Span#end()} or {@link Span#end(EndSpanOptions)} to
* end this {@code Span}.
*
* <p>Does not install the newly created {@code Span} to the current Context.
*
* <p>Example of usage:
*
* <pre>{@code
* class MyClass {
* private static final Tracer tracer = Tracing.getTracer();
* void DoWork(Span parent) {
* Span childSpan = tracer.spanBuilderWithExplicitParent("MyChildSpan", parent).startSpan();
* childSpan.addAnnotation("my annotation");
* try {
* doSomeWork(childSpan); // Manually propagate the new span down the stack.
* } finally {
* // To make sure we end the span even in case of an exception.
* childSpan.end(); // Manually end the span.
* }
* }
* }
* }</pre>
*
* @return the newly created {@code Span}.
* @since 0.1.0
*/
public abstract Span startSpan();
/**
* Starts a new span and sets it as the {@link Tracer#getCurrentSpan current span}.
*
* <p>Enters the scope of code where the newly created {@code Span} is in the current Context, and
* returns an object that represents that scope. When the returned object is closed, the scope is
* exited, the previous Context is restored, and the newly created {@code Span} is ended using
* {@link Span#end}.
*
* <p>Supports try-with-resource idiom.
*
* <p>Example of usage:
*
* <pre>{@code
* class MyClass {
* private static final Tracer tracer = Tracing.getTracer();
* void doWork {
* // Create a Span as a child of the current Span.
* try (Scope ss = tracer.spanBuilder("MyChildSpan").startScopedSpan()) {
* tracer.getCurrentSpan().addAnnotation("my annotation");
* doSomeWork(); // Here the new span is in the current Context, so it can be used
* // implicitly anywhere down the stack. Anytime in this closure the span
* // can be accessed via tracer.getCurrentSpan().
* }
* }
* }
* }</pre>
*
* <p>Prior to Java SE 7, you can use a finally block to ensure that a resource is closed (the
* {@code Span} is ended and removed from the Context) regardless of whether the try statement
* completes normally or abruptly.
*
* <p>Example of usage prior to Java SE7:
*
* <pre>{@code
* class MyClass {
* private static Tracer tracer = Tracing.getTracer();
* void doWork {
* // Create a Span as a child of the current Span.
* Scope ss = tracer.spanBuilder("MyChildSpan").startScopedSpan();
* try {
* tracer.getCurrentSpan().addAnnotation("my annotation");
* doSomeWork(); // Here the new span is in the current Context, so it can be used
* // implicitly anywhere down the stack. Anytime in this closure the span
* // can be accessed via tracer.getCurrentSpan().
* } finally {
* ss.close();
* }
* }
* }
* }</pre>
*
* <p>WARNING: The try-with-resources feature to auto-close spans as described above can sound
* very tempting due to its convenience, but it comes with an important and easy-to-miss
* trade-off: the span will be closed before any {@code catch} or {@code finally} blocks get a
* chance to execute. So if you need to catch any exceptions and log information about them (for
* example), then you do not want to use the try-with-resources shortcut because that logging will
* not be tagged with the span info of the span it logically falls under, and if you try to
* retrieve {@code Tracer.getCurrentSpan()} then you'll either get the parent span if one exists
* or {@code BlankSpan} if there was no parent span. This can be confusing and seem
* counter-intuitive, but it's the way try-with-resources works.
*
* @return an object that defines a scope where the newly created {@code Span} will be set to the
* current Context.
* @since 0.1.0
*/
@MustBeClosed
public final Scope startScopedSpan() {
return CurrentSpanUtils.withSpan(startSpan(), /* endSpan= */ true);
}
/**
* Starts a new span and runs the given {@code Runnable} with the newly created {@code Span} as
* the current {@code Span}, and ends the {@code Span} after the {@code Runnable} is run.
*
* <p>Any error will end up as a {@link Status#UNKNOWN}.
*
* <pre><code>
* tracer.spanBuilder("MyRunnableSpan").startSpanAndRun(myRunnable);
* </code></pre>
*
* <p>It is equivalent with the following code:
*
* <pre><code>
* Span span = tracer.spanBuilder("MyRunnableSpan").startSpan();
* Runnable newRunnable = tracer.withSpan(span, myRunnable);
* try {
* newRunnable.run();
* } finally {
* span.end();
* }
* </code></pre>
*
* @param runnable the {@code Runnable} to run in the {@code Span}.
* @since 0.1.0
*/
public final void startSpanAndRun(final Runnable runnable) {
final Span span = startSpan();
CurrentSpanUtils.withSpan(span, /* endSpan= */ true, runnable).run();
}
/**
* Starts a new span and calls the given {@code Callable} with the newly created {@code Span} as
* the current {@code Span}, and ends the {@code Span} after the {@code Callable} is called.
*
* <p>Any error will end up as a {@link Status#UNKNOWN}.
*
* <pre><code>
* MyResult myResult = tracer.spanBuilder("MyCallableSpan").startSpanAndCall(myCallable);
* </code></pre>
*
* <p>It is equivalent with the following code:
*
* <pre><code>
* Span span = tracer.spanBuilder("MyCallableSpan").startSpan();
* {@code Callable<MyResult>} newCallable = tracer.withSpan(span, myCallable);
* MyResult myResult = null;
* try {
* myResult = newCallable.call();
* } finally {
* span.end();
* }
* );
* </code></pre>
*
* @param callable the {@code Callable} to run in the {@code Span}.
* @since 0.1.0
*/
public final <V> V startSpanAndCall(Callable<V> callable) throws Exception {
final Span span = startSpan();
return CurrentSpanUtils.withSpan(span, /* endSpan= */ true, callable).call();
}
static final class NoopSpanBuilder extends SpanBuilder {
static NoopSpanBuilder createWithParent(String spanName, @Nullable Span parent) {
return new NoopSpanBuilder(spanName);
}
static NoopSpanBuilder createWithRemoteParent(
String spanName, @Nullable SpanContext remoteParentSpanContext) {
return new NoopSpanBuilder(spanName);
}
@Override
public Span startSpan() {
return BlankSpan.INSTANCE;
}
@Override
public SpanBuilder setSampler(@Nullable Sampler sampler) {
return this;
}
@Override
public SpanBuilder setParentLinks(List<Span> parentLinks) {
return this;
}
@Override
public SpanBuilder setRecordEvents(boolean recordEvents) {
return this;
}
@Override
public SpanBuilder setSpanKind(@Nullable Span.Kind spanKind) {
return this;
}
private NoopSpanBuilder(String name) {
Utils.checkNotNull(name, "name");
}
}
}

View File

@ -0,0 +1,165 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import java.util.Arrays;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A class that represents a span context. A span context contains the state that must propagate to
* child {@link Span}s and across process boundaries. It contains the identifiers (a {@link TraceId
* trace_id} and {@link SpanId span_id}) associated with the {@link Span} and a set of {@link
* TraceOptions options}.
*
* @since 0.1.0
*/
@Immutable
public final class SpanContext {
private static final Tracestate TRACESTATE_DEFAULT = Tracestate.builder().build();
private final TraceId traceId;
private final SpanId spanId;
private final TraceOptions traceOptions;
private final Tracestate tracestate;
/**
* The invalid {@code SpanContext}.
*
* @since 0.1.0
*/
public static final SpanContext INVALID =
new SpanContext(TraceId.INVALID, SpanId.INVALID, TraceOptions.DEFAULT, TRACESTATE_DEFAULT);
/**
* Creates a new {@code SpanContext} with the given identifiers and options.
*
* @param traceId the trace identifier of the span context.
* @param spanId the span identifier of the span context.
* @param traceOptions the trace options for the span context.
* @return a new {@code SpanContext} with the given identifiers and options.
* @deprecated use {@link #create(TraceId, SpanId, TraceOptions, Tracestate)}.
*/
@Deprecated
public static SpanContext create(TraceId traceId, SpanId spanId, TraceOptions traceOptions) {
return create(traceId, spanId, traceOptions, TRACESTATE_DEFAULT);
}
/**
* Creates a new {@code SpanContext} with the given identifiers and options.
*
* @param traceId the trace identifier of the span context.
* @param spanId the span identifier of the span context.
* @param traceOptions the trace options for the span context.
* @param tracestate the trace state for the span context.
* @return a new {@code SpanContext} with the given identifiers and options.
* @since 0.1.0
*/
public static SpanContext create(
TraceId traceId, SpanId spanId, TraceOptions traceOptions, Tracestate tracestate) {
return new SpanContext(traceId, spanId, traceOptions, tracestate);
}
/**
* Returns the trace identifier associated with this {@code SpanContext}.
*
* @return the trace identifier associated with this {@code SpanContext}.
* @since 0.1.0
*/
public TraceId getTraceId() {
return traceId;
}
/**
* Returns the span identifier associated with this {@code SpanContext}.
*
* @return the span identifier associated with this {@code SpanContext}.
* @since 0.1.0
*/
public SpanId getSpanId() {
return spanId;
}
/**
* Returns the {@code TraceOptions} associated with this {@code SpanContext}.
*
* @return the {@code TraceOptions} associated with this {@code SpanContext}.
* @since 0.1.0
*/
public TraceOptions getTraceOptions() {
return traceOptions;
}
/**
* Returns the {@code Tracestate} associated with this {@code SpanContext}.
*
* @return the {@code Tracestate} associated with this {@code SpanContext}.
* @since 0.1.0
*/
public Tracestate getTracestate() {
return tracestate;
}
/**
* Returns true if this {@code SpanContext} is valid.
*
* @return true if this {@code SpanContext} is valid.
* @since 0.1.0
*/
public boolean isValid() {
return traceId.isValid() && spanId.isValid();
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof SpanContext)) {
return false;
}
SpanContext that = (SpanContext) obj;
return traceId.equals(that.traceId)
&& spanId.equals(that.spanId)
&& traceOptions.equals(that.traceOptions);
}
@Override
public int hashCode() {
return Arrays.hashCode(new Object[] {traceId, spanId, traceOptions});
}
@Override
public String toString() {
return "SpanContext{traceId="
+ traceId
+ ", spanId="
+ spanId
+ ", traceOptions="
+ traceOptions
+ "}";
}
private SpanContext(
TraceId traceId, SpanId spanId, TraceOptions traceOptions, Tracestate tracestate) {
this.traceId = traceId;
this.spanId = spanId;
this.traceOptions = traceOptions;
this.tracestate = tracestate;
}
}

View File

@ -0,0 +1,237 @@
/*
* Copyright 2019, OpenConsensus Authors
*
* 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 openconsensus.trace;
import openconsensus.internal.Utils;
import java.util.Random;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A class that represents a span identifier. A valid span identifier is an 8-byte array with at
* least one non-zero byte.
*
* @since 0.1.0
*/
@Immutable
public final class SpanId implements Comparable<SpanId> {
/**
* The size in bytes of the {@code SpanId}.
*
* @since 0.1.0
*/
public static final int SIZE = 8;
/**
* The invalid {@code SpanId}. All bytes are 0.
*
* @since 0.1.0
*/
public static final SpanId INVALID = new SpanId(0);
private static final int BASE16_SIZE = 2 * SIZE;
private static final long INVALID_ID = 0;
// The internal representation of the SpanId.
private final long id;
private SpanId(long id) {
this.id = id;
}
/**
* Returns a {@code SpanId} built from a byte representation.
*
* @param src the representation of the {@code SpanId}.
* @return a {@code SpanId} whose representation is given by the {@code src} parameter.
* @throws NullPointerException if {@code src} is null.
* @throws IllegalArgumentException if {@code src.length} is not {@link SpanId#SIZE}.
* @since 0.1.0
*/
public static SpanId fromBytes(byte[] src) {
Utils.checkNotNull(src, "src");
// TODO: Remove this extra condition.
Utils.checkArgument(src.length == SIZE, "Invalid size: expected %s, got %s", SIZE, src.length);
return fromBytes(src, 0);
}
/**
* Returns a {@code SpanId} whose representation is copied from the {@code src} beginning at the
* {@code srcOffset} offset.
*
* @param src the buffer where the representation of the {@code SpanId} is copied.
* @param srcOffset the offset in the buffer where the representation of the {@code SpanId}
* begins.
* @return a {@code SpanId} whose representation is copied from the buffer.
* @throws NullPointerException if {@code src} is null.
* @throws IndexOutOfBoundsException if {@code srcOffset+SpanId.SIZE} is greater than {@code
* src.length}.
* @since 0.1.0
*/
public static SpanId fromBytes(byte[] src, int srcOffset) {
Utils.checkNotNull(src, "src");
return new SpanId(BigendianEncoding.longFromByteArray(src, srcOffset));
}
/**
* Returns a {@code SpanId} built from a lowercase base16 representation.
*
* @param src the lowercase base16 representation.
* @return a {@code SpanId} built from a lowercase base16 representation.
* @throws NullPointerException if {@code src} is null.
* @throws IllegalArgumentException if {@code src.length} is not {@code 2 * SpanId.SIZE} OR if the
* {@code str} has invalid characters.
* @since 0.1.0
*/
public static SpanId fromLowerBase16(CharSequence src) {
Utils.checkNotNull(src, "src");
// TODO: Remove this extra condition.
Utils.checkArgument(
src.length() == BASE16_SIZE,
"Invalid size: expected %s, got %s",
BASE16_SIZE,
src.length());
return fromLowerBase16(src, 0);
}
/**
* Returns a {@code SpanId} built from a lowercase base16 representation.
*
* @param src the lowercase base16 representation.
* @param srcOffset the offset in the buffer where the representation of the {@code SpanId}
* begins.
* @return a {@code SpanId} built from a lowercase base16 representation.
* @throws NullPointerException if {@code src} is null.
* @throws IllegalArgumentException if not enough characters in the {@code src} from the {@code
* srcOffset}.
* @since 0.1.0
*/
public static SpanId fromLowerBase16(CharSequence src, int srcOffset) {
Utils.checkNotNull(src, "src");
return new SpanId(BigendianEncoding.longFromBase16String(src, srcOffset));
}
/**
* Generates a new random {@code SpanId}.
*
* @param random The random number generator.
* @return a valid new {@code SpanId}.
* @since 0.1.0
*/
public static SpanId generateRandomId(Random random) {
long id;
do {
id = random.nextLong();
} while (id == INVALID_ID);
return new SpanId(id);
}
/**
* Returns the byte representation of the {@code SpanId}.
*
* @return the byte representation of the {@code SpanId}.
* @since 0.1.0
*/
public byte[] getBytes() {
byte[] bytes = new byte[SIZE];
BigendianEncoding.longToByteArray(id, bytes, 0);
return bytes;
}
/**
* Copies the byte array representations of the {@code SpanId} into the {@code dest} beginning at
* the {@code destOffset} offset.
*
* @param dest the destination buffer.
* @param destOffset the starting offset in the destination buffer.
* @throws NullPointerException if {@code dest} is null.
* @throws IndexOutOfBoundsException if {@code destOffset+SpanId.SIZE} is greater than {@code
* dest.length}.
* @since 0.1.0
*/
public void copyBytesTo(byte[] dest, int destOffset) {
BigendianEncoding.longToByteArray(id, dest, destOffset);
}
/**
* Copies the lowercase base16 representations of the {@code SpanId} into the {@code dest}
* beginning at the {@code destOffset} offset.
*
* @param dest the destination buffer.
* @param destOffset the starting offset in the destination buffer.
* @throws IndexOutOfBoundsException if {@code destOffset + 2 * SpanId.SIZE} is greater than
* {@code dest.length}.
* @since 0.1.0
*/
public void copyLowerBase16To(char[] dest, int destOffset) {
BigendianEncoding.longToBase16String(id, dest, destOffset);
}
/**
* Returns whether the span identifier is valid. A valid span identifier is an 8-byte array with
* at least one non-zero byte.
*
* @return {@code true} if the span identifier is valid.
* @since 0.1.0
*/
public boolean isValid() {
return id != INVALID_ID;
}
/**
* Returns the lowercase base16 encoding of this {@code SpanId}.
*
* @return the lowercase base16 encoding of this {@code SpanId}.
* @since 0.1.0
*/
public String toLowerBase16() {
char[] chars = new char[BASE16_SIZE];
copyLowerBase16To(chars, 0);
return new String(chars);
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof SpanId)) {
return false;
}
SpanId that = (SpanId) obj;
return id == that.id;
}
@Override
public int hashCode() {
// Copied from Long.hashCode in java8.
return (int) (id ^ (id >>> 32));
}
@Override
public String toString() {
return "SpanId{spanId=" + toLowerBase16() + "}";
}
@Override
public int compareTo(SpanId that) {
// Copied from Long.compare in java8.
return (id < that.id) ? -1 : ((id == that.id) ? 0 : 1);
}
}

Some files were not shown because too many files have changed in this diff Show More