com.lightstep.tracer.shared.Options Maven / Gradle / Ivy
Show all versions of java-common Show documentation
package com.lightstep.tracer.shared;
import io.opentracing.ScopeManager;
import io.opentracing.propagation.Format;
import io.opentracing.util.ThreadLocalScopeManager;
import java.net.MalformedURLException;
import java.net.InetAddress;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Options control behaviors specific to the LightStep tracer.
*/
public final class Options {
// DEFAULT OPTIONS
/**
* Java System property that will be used as the component name when no other value is provided.
*/
private static final String COMPONENT_NAME_SYSTEM_PROPERTY_KEY = "sun.java.command";
/**
* Default maximum number of Spans buffered locally (a protective mechanism)
*/
@SuppressWarnings("WeakerAccess")
public static final int DEFAULT_MAX_BUFFERED_SPANS = 1000;
/**
* Default interval at which spans will be flushed
*/
private static final long DEFAULT_REPORTING_INTERVAL_MILLIS = 3000;
/**
* Default duration the tracer should wait for a response from the collector when sending a report.
*/
private static final long DEFAULT_DEADLINE_MILLIS = 30000;
// BUILTIN PROPAGATORS
static final Map, Propagator> BUILTIN_PROPAGATORS = Collections.unmodifiableMap(
new HashMap, Propagator>() {{
put(Format.Builtin.HTTP_HEADERS, Propagator.HTTP_HEADERS);
put(Format.Builtin.TEXT_MAP, Propagator.TEXT_MAP);
put(Format.Builtin.TEXT_MAP_INJECT, Propagator.TEXT_MAP);
put(Format.Builtin.TEXT_MAP_EXTRACT, Propagator.TEXT_MAP);
put(Format.Builtin.BINARY, Propagator.BINARY);
put(Format.Builtin.BINARY_INJECT, Propagator.BINARY);
put(Format.Builtin.BINARY_EXTRACT, Propagator.BINARY);
}}
);
/**
* A DNS service passed to the OkHttp transport to resolve IP addresses
* for hostnames. Designed to allow users to provide custom DNS resolution.
*/
public interface OkHttpDns {
public List lookup(String hostname);
}
// LOG LEVELS
/**
* all internal log statements, including debugging details
*/
@SuppressWarnings("WeakerAccess")
public static final int VERBOSITY_DEBUG = 4;
/**
* all errors, warnings, and info statements are echoed locally
*/
@SuppressWarnings("WeakerAccess")
public static final int VERBOSITY_INFO = 3;
/**
* all errors are echoed locally
*/
@SuppressWarnings("WeakerAccess")
public static final int VERBOSITY_ERRORS_ONLY = 2;
/**
* only the first error encountered will be echoed locally
*/
@SuppressWarnings("WeakerAccess")
public static final int VERBOSITY_FIRST_ERROR_ONLY = 1;
/**
* never produce local output
*/
@SuppressWarnings("WeakerAccess")
public static final int VERBOSITY_NONE = 0;
/**
* Disable Meta Event Reporting
* even if a server command requested it.
*/
final boolean disableMetaEventLogging;
final String accessToken;
final URL collectorUrl;
final Map tags;
final long maxReportingIntervalMillis;
final int maxBufferedSpans;
final int verbosity;
final boolean disableReportingLoop;
// reset GRPC client at regular intervals (for load balancing)
final boolean resetClient;
final boolean useClockCorrection;
final ScopeManager scopeManager;
final Map, Propagator> propagators;
final String grpcCollectorTarget;
final boolean grpcRoundRobin;
final OkHttpDns okhttpDns;
/**
* The maximum amount of time the tracer should wait for a response from the collector when sending a report.
*/
final long deadlineMillis;
private Options(
String accessToken,
URL collectorUrl,
long maxReportingIntervalMillis,
int maxBufferedSpans,
int verbosity,
boolean disableReportingLoop,
boolean resetClient,
Map tags,
boolean useClockCorrection,
ScopeManager scopeManager,
long deadlineMillis,
Map, Propagator> propagators,
String grpcCollectorTarget,
boolean grpcRoundRobin,
OkHttpDns okhttpDns,
boolean disableMetaEventLogging
) {
this.accessToken = accessToken;
this.collectorUrl = collectorUrl;
this.maxReportingIntervalMillis = maxReportingIntervalMillis;
this.maxBufferedSpans = maxBufferedSpans;
this.verbosity = verbosity;
this.disableReportingLoop = disableReportingLoop;
this.resetClient = resetClient;
this.tags = tags;
this.useClockCorrection = useClockCorrection;
this.scopeManager = scopeManager;
this.deadlineMillis = deadlineMillis;
this.propagators = propagators;
this.grpcCollectorTarget = grpcCollectorTarget;
this.grpcRoundRobin = grpcRoundRobin;
this.okhttpDns = okhttpDns;
this.disableMetaEventLogging = disableMetaEventLogging;
}
long getGuid() {
return (long) tags.get(LightStepConstants.Tags.GUID_KEY);
}
@SuppressWarnings({"WeakerAccess"})
public static class OptionsBuilder {
private String accessToken = "";
private String collectorProtocol = LightStepConstants.Collector.PROTOCOL_HTTPS;
private String collectorHost = LightStepConstants.Collector.DEFAULT_HOST;
private int collectorPort = -1;
private long maxReportingIntervalMillis;
private int maxBufferedSpans = -1;
private int verbosity = 1;
private boolean disableReportingLoop = false;
private boolean resetClient = true;
private boolean useClockCorrection = true;
private Map tags = new HashMap<>();
private ScopeManager scopeManager;
private long deadlineMillis = -1;
private Map, Propagator> propagators = new HashMap<>();
private boolean disableMetaEventLogging = false;
private String grpcCollectorTarget;
private boolean grpcRoundRobin = false;
private OkHttpDns okhttpDns;
public OptionsBuilder() {
}
public OptionsBuilder(Options options) {
this.accessToken = options.accessToken;
this.collectorProtocol = options.collectorUrl.getProtocol();
this.collectorHost = options.collectorUrl.getHost();
this.collectorPort = options.collectorUrl.getPort();
this.maxReportingIntervalMillis = options.maxReportingIntervalMillis;
this.maxBufferedSpans = options.maxBufferedSpans;
this.verbosity = options.verbosity;
this.disableReportingLoop = options.disableReportingLoop;
this.resetClient = options.resetClient;
this.tags = options.tags;
this.scopeManager = options.scopeManager;
this.useClockCorrection = options.useClockCorrection;
this.deadlineMillis = options.deadlineMillis;
this.propagators = options.propagators;
this.disableMetaEventLogging = options.disableMetaEventLogging;
this.grpcCollectorTarget = options.grpcCollectorTarget;
this.grpcRoundRobin = options.grpcRoundRobin;
this.okhttpDns = options.okhttpDns;
}
/**
* Adds a user defined {@link Propagator} to be used during
* {@link io.opentracing.Tracer#inject} and {@link io.opentracing.Tracer#extract} for
* the given type. This can be used to provide custom handling for a specified
* {@link Format} (such as {@link Format.Builtin#TEXT_MAP}).
*
* {@link Propagator#inject} and {@link Propagator#extract} are expected
* to fail silently in case of error during injection and extraction, respectively.
*
* Observe that a new {@link Format} should *not* be created if the data is being
* propagated through http headers or a text map. Instead, use the respective
* builtin {@link Format} and specify a custom {@link Propagator} here. In case of
* doubt, contact LightStep for advice.
*
* @param format Instance of {@link Format} for which custom Propagator will be used.
* @param propagator Instance of {@link Propagator} to be used
* @param Type of the carrier.
*/
public OptionsBuilder withPropagator(Format format, Propagator propagator) {
if (format == null) {
throw new IllegalArgumentException("format cannot be null");
}
if (propagator == null) {
throw new IllegalArgumentException("propagator cannot be null");
}
this.propagators.put(format, propagator);
return this;
}
/**
* Sets the unique identifier for this application.
*
* @param accessToken Your specific token for LightStep access.
*/
public OptionsBuilder withAccessToken(String accessToken) {
if (accessToken == null)
accessToken = "";
this.accessToken = accessToken;
return this;
}
/**
* Sets the protocol which will be used when sending data to the tracer. Valid values
* are either {@code LightStepConstants.Collector.PROTOCOL_HTTPS} or
* {@code LightStepConstants.Collector.PROTOCOL_HTTP}.
*
* @param protocol Either {@code PROTOCOL_HTTPS} or {@code PROTOCOL_HTTP}.
* @throws IllegalArgumentException If the protocol argument is invalid.
*/
public OptionsBuilder withCollectorProtocol(String protocol) {
if (!LightStepConstants.Collector.PROTOCOL_HTTPS.equals(protocol) &&
!LightStepConstants.Collector.PROTOCOL_HTTP.equals(protocol)) {
throw new IllegalArgumentException("Invalid protocol for collector: " + protocol);
}
this.collectorProtocol = protocol;
return this;
}
/**
* Sets the host to which the tracer will send data. If not set, will default to the
* primary LightStep collector address.
*
* @param collectorHost The hostname for the LightStep collector.
* @throws IllegalArgumentException If the collectorHost argument is invalid.
*/
public OptionsBuilder withCollectorHost(String collectorHost) {
if (collectorHost == null || "".equals(collectorHost.trim())) {
throw new IllegalArgumentException("Invalid collector host: " + collectorHost);
}
this.collectorHost = collectorHost;
return this;
}
/**
* Sets the port to which the tracer will send data. If not set, will default to
* {@code LightStepConstants.Collector.DEFAULT_SECURE_PORT} when the protocol is https and
* {@code LightStepConstants.Collector.DEFAULT_PLAINTEXT_PORT} when the protocol is http.
*
* @param collectorPort The port for the LightStep collector.
* @throws IllegalArgumentException If the collectorPort is invalid.
*/
public OptionsBuilder withCollectorPort(int collectorPort) {
if (collectorPort <= 0) {
throw new IllegalArgumentException("Invalid collector port: " + collectorPort);
}
this.collectorPort = collectorPort;
return this;
}
/**
* Sets the target address when using gRPC for transport. Useful when used in conjunction
* with supplying a custom gRPC NameResolverProvider to lookup the satellites via your
* preferred service discovery system.
*
* @param grpcCollectorTarget The target URI for the LightStep collector.
* @throws IllegalArgumentException If the grpcCollectorTarget is invalid.
*/
public OptionsBuilder withGrpcCollectorTarget(String grpcCollectorTarget) {
if (grpcCollectorTarget == null) {
throw new IllegalArgumentException(
"Invalid grpc collector target: " + grpcCollectorTarget);
}
this.grpcCollectorTarget = grpcCollectorTarget;
return this;
}
/**
* Instructs gRPC to round-robin between satellites instances in a pool when sending traces.
* If not enabled defaults to picking the first record returned by the operating system
* resolver.
*
* @param grpcRoundRobin Use round robin on a per request basis when sending requests to
* the satellites.
*/
public OptionsBuilder withGrpcRoundRobin(boolean grpcRoundRobin) {
this.grpcRoundRobin = grpcRoundRobin;
return this;
}
/**
* Sets the DNS service used to lookup IP addresses for hostnames when using the
* OkHttp transport. If not set, the default DNS service used by OkHttp will be used.
*
* This has no effect when using gRPC as transport system.
*
* @param okhttpDns the Dns service object.
*/
public OptionsBuilder withOkHttpDns(OkHttpDns okhttpDns) {
if (okhttpDns == null) {
throw new IllegalArgumentException("dns cannot be null");
}
this.okhttpDns = okhttpDns;
return this;
}
/**
* Sets scope manager attribute. If not set, will default to the ThreadLocalScopeManager
*
* @param scopeManager The scopeManager for the LightStep tracer.
* @throws IllegalArgumentException If the scopeManager is null.
*/
public OptionsBuilder withScopeManager(ScopeManager scopeManager) {
if (scopeManager == null) {
throw new IllegalArgumentException("scopeManager cannot be null");
}
this.scopeManager = scopeManager;
return this;
}
/**
* Sets the component name attribute. If not set, will default to the Java runtime
* command.
*
* @param name The name of the component being traced.
*/
public OptionsBuilder withComponentName(String name) {
return withTag(LightStepConstants.Tags.COMPONENT_NAME_KEY, name);
}
/**
* Sets a user-defined key-value pair that should be associated with all of the
* data produced by this tracer.
*/
public OptionsBuilder withTag(String key, Object value) {
tags.put(key, value);
return this;
}
/**
* Sets the maximum interval between reports.
*
* @param maxReportingIntervalMillis The maximum interval of time that will pass between
* reports.
*/
public OptionsBuilder withMaxReportingIntervalMillis(int maxReportingIntervalMillis) {
this.maxReportingIntervalMillis = maxReportingIntervalMillis;
return this;
}
/**
* Sets the maximum number of finished Spans buffered locally before flushing. This is a protective mechanism
* which bounds the memory usage of the LightStep library.
*
* @param maxBufferedSpans The maximum number of Spans buffered locally.
*/
public OptionsBuilder withMaxBufferedSpans(int maxBufferedSpans) {
this.maxBufferedSpans = maxBufferedSpans;
return this;
}
/**
* Controls the amount of local output produced by the tracer. It does not
* affect which spans are sent to the collector. It is useful for
* diagnosing problems in the tracer itself. The default value is 1.
*
* 0 - never produce local output
* 1 - only the first error encountered will be echoed locally
* 2 - all errors are echoed locally
* 3 - all errors, warnings, and info statements are echoed locally
* 4 - all internal log statements, including debugging details
*/
public OptionsBuilder withVerbosity(int verbosity) {
this.verbosity = verbosity;
return this;
}
/**
* If true, the background reporting loop will be disabled. Reports will
* only occur on explicit calls to Flush(); If not set, will default to
* {@code DEFAULT_REPORTING_INTERVAL_MILLIS}.
*/
@SuppressWarnings("SameParameterValue")
public OptionsBuilder withDisableReportingLoop(boolean disable) {
this.disableReportingLoop = disable;
return this;
}
/**
* If true, the GRPC client connection will be reset at regular intevals
* Used to load balance on server side
*/
public OptionsBuilder withResetClient(boolean reset) {
this.resetClient = reset;
return this;
}
/**
* Overrides the default deadlineMillis with the provided value.
*/
public OptionsBuilder withDeadlineMillis(long deadlineMillis) {
this.deadlineMillis = deadlineMillis;
return this;
}
/**
* Disables LightStep Meta Event Reporting
* even if a server command requested it.
* @param disableMetaEventLogging
*/
public OptionsBuilder withDisableMetaEventLogging(boolean disableMetaEventLogging) {
this.disableMetaEventLogging = disableMetaEventLogging;
return this;
}
@SuppressWarnings("SameParameterValue")
public OptionsBuilder withClockSkewCorrection(boolean clockCorrection) {
this.useClockCorrection = clockCorrection;
return this;
}
/**
* Sets the defaults for values not provided and constructs a new Options object.
*
* @return Options object configured with the built values.
* @throws MalformedURLException If the combination of collector protocol, host, and port
* are not valid
*/
public Options build() throws MalformedURLException {
defaultComponentName();
defaultGuid();
defaultMaxReportingIntervalMillis();
defaultMaxBufferedSpans();
defaultPropagators();
defaultScopeManager();
defaultDeadlineMillis();
return new Options(
accessToken,
getCollectorUrl(),
maxReportingIntervalMillis,
maxBufferedSpans,
verbosity,
disableReportingLoop,
resetClient,
tags,
useClockCorrection,
scopeManager,
deadlineMillis,
propagators,
grpcCollectorTarget,
grpcRoundRobin,
okhttpDns,
disableMetaEventLogging
);
}
private void defaultScopeManager() {
if(scopeManager == null) {
scopeManager = new ThreadLocalScopeManager();
}
}
private void defaultMaxReportingIntervalMillis() {
if (maxReportingIntervalMillis <= 0) {
maxReportingIntervalMillis = DEFAULT_REPORTING_INTERVAL_MILLIS;
}
}
private void defaultMaxBufferedSpans() {
if (maxBufferedSpans < 0) {
maxBufferedSpans = DEFAULT_MAX_BUFFERED_SPANS;
}
}
private void defaultGuid() {
if (tags.get(LightStepConstants.Tags.GUID_KEY) == null) {
withTag(LightStepConstants.Tags.GUID_KEY, Util.generateRandomGUID());
}
}
/**
* If not set, provides a default value for the component name.
*/
private void defaultComponentName() {
if (tags.get(LightStepConstants.Tags.COMPONENT_NAME_KEY) == null) {
String componentNameSystemProperty = System.getProperty(COMPONENT_NAME_SYSTEM_PROPERTY_KEY);
if (componentNameSystemProperty != null) {
StringTokenizer st = new StringTokenizer(componentNameSystemProperty);
if (st.hasMoreTokens()) {
String name = st.nextToken();
withComponentName(name);
}
}
}
}
private void defaultDeadlineMillis() {
if (deadlineMillis < 0) {
deadlineMillis = DEFAULT_DEADLINE_MILLIS;
}
}
private void defaultPropagators() {
for (Map.Entry, Propagator> entry: BUILTIN_PROPAGATORS.entrySet()) {
Format> format = entry.getKey();
if (!propagators.containsKey(format)) {
propagators.put(format, entry.getValue());
}
}
}
private int getPort() {
if (collectorPort > 0) {
return collectorPort;
} else if (collectorProtocol.equals(LightStepConstants.Collector.PROTOCOL_HTTPS)) {
return LightStepConstants.Collector.DEFAULT_SECURE_PORT;
} else {
return LightStepConstants.Collector.DEFAULT_PLAINTEXT_PORT;
}
}
private URL getCollectorUrl() throws MalformedURLException {
int port = getPort();
return new URL(collectorProtocol, collectorHost, port, LightStepConstants.Collector.PATH);
}
}
/**
* If this instance of Options has an overridden maxReportingIntervalMillis, returns this
* instance of Options with that value.
*
* If this instance of Options is using the default {@code DEFAULT_REPORTING_INTERVAL_MILLIS}
* then creates a new instance of Options and overrides maxReportingIntervalMillis with the
* provided value.
*
* @param value A new value for maxReportingIntervalMillis. Will only be used if this Options
* object is current set to the default value for maxReportingIntervalMillis.
* @throws IllegalArgumentException If this Options object has an malformed collector url.
*/
@SuppressWarnings({"WeakerAccess", "SameParameterValue"})
public Options setDefaultReportingIntervalMillis(int value) {
if (maxReportingIntervalMillis != DEFAULT_REPORTING_INTERVAL_MILLIS) {
return this;
}
try {
return new Options.OptionsBuilder(this).withMaxReportingIntervalMillis(value).build();
} catch (MalformedURLException e) {
// not possible given that we are constructing Options from a valid set of Options
throw new IllegalArgumentException("Unexpected error when building a new set of" +
"options from a valid set of existing options. collectorUrl=" +
this.collectorUrl);
}
}
/**
* Provided so implementations of AbstractTracer can turn off resetClient by default.
* For example, Android tracer may not want resetClient.
*/
@SuppressWarnings("unused")
public Options disableResetClient() {
try {
return new OptionsBuilder(this).withResetClient(false).build();
} catch (MalformedURLException e) {
// not possible given that we are constructing Options from a valid set of Options
throw new IllegalArgumentException("Unexpected error when building a new set of" +
"options from a valid set of existing options. collectorUrl=" +
this.collectorUrl);
}
}
}