diff --git a/pom.xml b/pom.xml index dc35b5955fe13..6f5c169e9cf23 100644 --- a/pom.xml +++ b/pom.xml @@ -189,6 +189,7 @@ presto-hudi presto-native-execution presto-router + presto-open-telemetry @@ -850,6 +851,12 @@ ${project.version} + + com.facebook.presto + presto-open-telemetry + ${project.version} + + com.facebook.hive hive-dwrf @@ -2094,6 +2101,60 @@ stream 2.9.5 + + + io.opentelemetry + opentelemetry-api + 1.19.0 + + + + io.opentelemetry + opentelemetry-context + 1.19.0 + + + + io.opentelemetry + opentelemetry-exporter-otlp + 1.19.0 + + + com.squareup.okhttp3 + okhttp + + + + + + io.opentelemetry + opentelemetry-extension-trace-propagators + 1.19.0 + + + + io.opentelemetry + opentelemetry-sdk + 1.19.0 + + + + io.opentelemetry + opentelemetry-sdk-common + 1.19.0 + + + + io.opentelemetry + opentelemetry-sdk-trace + 1.19.0 + + + + io.opentelemetry + opentelemetry-semconv + 1.19.0-alpha + diff --git a/presto-main/src/main/java/com/facebook/presto/server/HttpRequestSessionContext.java b/presto-main/src/main/java/com/facebook/presto/server/HttpRequestSessionContext.java index a7b6be3f7a34b..1dce82d662f7c 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/HttpRequestSessionContext.java +++ b/presto-main/src/main/java/com/facebook/presto/server/HttpRequestSessionContext.java @@ -22,6 +22,7 @@ import com.facebook.presto.spi.security.SelectedRole; import com.facebook.presto.spi.session.ResourceEstimates; import com.facebook.presto.spi.tracing.Tracer; +import com.facebook.presto.spi.tracing.TracerHandle; import com.facebook.presto.spi.tracing.TracerProvider; import com.facebook.presto.sql.parser.ParsingException; import com.facebook.presto.sql.parser.ParsingOptions; @@ -211,19 +212,39 @@ else if (nameParts.size() == 2) { this.sessionFunctions = parseSessionFunctionHeader(servletRequest); this.sessionPropertyManager = requireNonNull(sessionPropertyManager, "sessionPropertyManager is null"); - String tunnelTraceId = trimEmptyToNull(servletRequest.getHeader(PRESTO_TRACE_TOKEN)); + + Map requestHeaders = getRequestHeaders(servletRequest); + TracerHandle tracerHandle = tracerProvider.getHandleGenerator().apply(requestHeaders); + if (isTracingEnabled()) { - this.tracer = Optional.of(requireNonNull(tracerProvider.getNewTracer(), "tracer is null")); + this.tracer = Optional.of(requireNonNull(tracerProvider.getNewTracer(tracerHandle), "tracer is null")); + traceToken = Optional.ofNullable(this.tracer.get().getTracerId()); + } + else { + this.tracer = Optional.of(NoopTracerProvider.NOOP_TRACER); // If tunnel trace token is null, we expose the Presto tracing id. // Otherwise we preserve the ability of trace token tunneling but // still trace Presto internally for aggregation purposes. - traceToken = Optional.ofNullable(tunnelTraceId == null ? this.tracer.get().getTracerId() : tunnelTraceId); + String tunnelTraceId = trimEmptyToNull(servletRequest.getHeader(PRESTO_TRACE_TOKEN)); + if (tunnelTraceId != null) { + traceToken = Optional.of(tunnelTraceId); + } + else { + traceToken = Optional.ofNullable(tracerHandle.getTraceToken()); + } } - else { - this.tracer = Optional.of(NoopTracerProvider.NOOP_TRACER); - traceToken = Optional.ofNullable(tunnelTraceId); + } + + private static Map getRequestHeaders(HttpServletRequest servletRequest) + { + ImmutableMap.Builder headers = ImmutableMap.builder(); + Enumeration headerNames = servletRequest.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String header = headerNames.nextElement(); + headers.put(header, servletRequest.getHeader(header)); } + return headers.build(); } public static List splitSessionHeader(Enumeration headers) @@ -502,7 +523,7 @@ public Optional getTracer() */ private boolean isTracingEnabled() { - String clientValue = systemProperties.getOrDefault(DISTRIBUTED_TRACING_MODE, TracingConfig.DistributedTracingMode.NO_TRACE.name()); + String clientValue = systemProperties.getOrDefault(DISTRIBUTED_TRACING_MODE, ""); // Client session setting overrides everything. if (clientValue.equalsIgnoreCase(TracingConfig.DistributedTracingMode.ALWAYS_TRACE.name())) { @@ -511,13 +532,13 @@ private boolean isTracingEnabled() if (clientValue.equalsIgnoreCase(TracingConfig.DistributedTracingMode.NO_TRACE.name())) { return false; } + if (clientValue.equalsIgnoreCase(TracingConfig.DistributedTracingMode.SAMPLE_BASED.name())) { + return true; + } - // Client not set, we then take system default value, and only init - // tracing if it's SAMPLE_BASED (TracingConfig prohibits you to - // configure system default to be ALWAYS_TRACE). If property manager - // not provided then false. + // Client not set, we then take system default value if ALWAYS_TRACE (SAMPLE_BASED disabled). If property manager not provided then false. return sessionPropertyManager - .map(manager -> manager.decodeSystemPropertyValue(DISTRIBUTED_TRACING_MODE, null, TracingConfig.DistributedTracingMode.class) == TracingConfig.DistributedTracingMode.SAMPLE_BASED) + .map(manager -> manager.decodeSystemPropertyValue(DISTRIBUTED_TRACING_MODE, null, String.class).equalsIgnoreCase(TracingConfig.DistributedTracingMode.ALWAYS_TRACE.name())) .orElse(false); } diff --git a/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java b/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java index b6c205a47deba..5caf081fce71d 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java +++ b/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java @@ -39,9 +39,11 @@ import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; import com.facebook.presto.spi.statistics.HistoryBasedPlanStatisticsProvider; import com.facebook.presto.spi.storage.TempStorageFactory; +import com.facebook.presto.spi.tracing.TracerProvider; import com.facebook.presto.spi.ttl.ClusterTtlProviderFactory; import com.facebook.presto.spi.ttl.NodeTtlFetcherFactory; import com.facebook.presto.storage.TempStorageManager; +import com.facebook.presto.tracing.TracerProviderManager; import com.facebook.presto.ttl.clusterttlprovidermanagers.ClusterTtlProviderManager; import com.facebook.presto.ttl.nodettlfetchermanagers.NodeTtlFetcherManager; import com.google.common.collect.ImmutableList; @@ -117,6 +119,7 @@ public class PluginManager private final AtomicBoolean pluginsLoaded = new AtomicBoolean(); private final ImmutableSet disabledConnectors; private final HistoryBasedPlanStatisticsManager historyBasedPlanStatisticsManager; + private final TracerProviderManager tracerProviderManager; @Inject public PluginManager( @@ -134,7 +137,8 @@ public PluginManager( SessionPropertyDefaults sessionPropertyDefaults, NodeTtlFetcherManager nodeTtlFetcherManager, ClusterTtlProviderManager clusterTtlProviderManager, - HistoryBasedPlanStatisticsManager historyBasedPlanStatisticsManager) + HistoryBasedPlanStatisticsManager historyBasedPlanStatisticsManager, + TracerProviderManager tracerProviderManager) { requireNonNull(nodeInfo, "nodeInfo is null"); requireNonNull(config, "config is null"); @@ -162,6 +166,7 @@ public PluginManager( this.clusterTtlProviderManager = requireNonNull(clusterTtlProviderManager, "clusterTtlProviderManager is null"); this.disabledConnectors = requireNonNull(config.getDisabledConnectors(), "disabledConnectors is null"); this.historyBasedPlanStatisticsManager = requireNonNull(historyBasedPlanStatisticsManager, "historyBasedPlanStatisticsManager is null"); + this.tracerProviderManager = requireNonNull(tracerProviderManager, "tracerProviderManager is null"); } public void loadPlugins() @@ -297,6 +302,11 @@ public void installPlugin(Plugin plugin) log.info("Registering plan statistics provider %s", historyBasedPlanStatisticsProvider.getName()); historyBasedPlanStatisticsManager.addHistoryBasedPlanStatisticsProviderFactory(historyBasedPlanStatisticsProvider); } + + for (TracerProvider tracerProvider : plugin.getTracerProviders()) { + log.info("Registering tracer provider %s", tracerProvider.getName()); + tracerProviderManager.addTracerProviderFactory(tracerProvider); + } } private URLClassLoader buildClassLoader(String plugin) diff --git a/presto-main/src/main/java/com/facebook/presto/server/PrestoServer.java b/presto-main/src/main/java/com/facebook/presto/server/PrestoServer.java index 3c2143e5ea3fb..fa119ff0c6840 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/PrestoServer.java +++ b/presto-main/src/main/java/com/facebook/presto/server/PrestoServer.java @@ -50,6 +50,7 @@ import com.facebook.presto.sql.parser.SqlParserOptions; import com.facebook.presto.storage.TempStorageManager; import com.facebook.presto.storage.TempStorageModule; +import com.facebook.presto.tracing.TracerProviderManager; import com.facebook.presto.ttl.clusterttlprovidermanagers.ClusterTtlProviderManager; import com.facebook.presto.ttl.clusterttlprovidermanagers.ClusterTtlProviderManagerModule; import com.facebook.presto.ttl.nodettlfetchermanagers.NodeTtlFetcherManager; @@ -174,6 +175,7 @@ public void run() injector.getInstance(QueryPrerequisitesManager.class).loadQueryPrerequisites(); injector.getInstance(NodeTtlFetcherManager.class).loadNodeTtlFetcher(); injector.getInstance(ClusterTtlProviderManager.class).loadClusterTtlProvider(); + injector.getInstance(TracerProviderManager.class).loadTracerProvider(); startAssociatedProcesses(injector); diff --git a/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java b/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java index 48a7c1fd8c7ba..86d8fdf5a450b 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java +++ b/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java @@ -149,7 +149,6 @@ import com.facebook.presto.spi.relation.DomainTranslator; import com.facebook.presto.spi.relation.PredicateCompiler; import com.facebook.presto.spi.relation.VariableReferenceExpression; -import com.facebook.presto.spi.tracing.TracerProvider; import com.facebook.presto.spiller.FileSingleStreamSpillerFactory; import com.facebook.presto.spiller.GenericPartitioningSpillerFactory; import com.facebook.presto.spiller.GenericSpillerFactory; @@ -195,8 +194,7 @@ import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; import com.facebook.presto.statusservice.NodeStatusService; -import com.facebook.presto.tracing.NoopTracerProvider; -import com.facebook.presto.tracing.SimpleTracerProvider; +import com.facebook.presto.tracing.TracerProviderManager; import com.facebook.presto.tracing.TracingConfig; import com.facebook.presto.transaction.TransactionManagerConfig; import com.facebook.presto.type.TypeDeserializer; @@ -245,8 +243,6 @@ import static com.facebook.drift.server.guice.DriftServerBinder.driftServerBinder; import static com.facebook.presto.execution.scheduler.NodeSchedulerConfig.NetworkTopologyType.FLAT; import static com.facebook.presto.execution.scheduler.NodeSchedulerConfig.NetworkTopologyType.LEGACY; -import static com.facebook.presto.tracing.TracingConfig.TracerType.NOOP; -import static com.facebook.presto.tracing.TracingConfig.TracerType.SIMPLE; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; @@ -741,15 +737,7 @@ public ListeningExecutorService createResourceManagerExecutor(ResourceManagerCon // Distributed tracing configBinder(binder).bindConfig(TracingConfig.class); - install(installModuleIf( - TracingConfig.class, - config -> !config.getEnableDistributedTracing() || NOOP.equalsIgnoreCase(config.getTracerType()), - moduleBinder -> moduleBinder.bind(TracerProvider.class).to(NoopTracerProvider.class).in(Scopes.SINGLETON))); - - install(installModuleIf( - TracingConfig.class, - config -> config.getEnableDistributedTracing() && SIMPLE.equalsIgnoreCase(config.getTracerType()), - moduleBinder -> moduleBinder.bind(TracerProvider.class).to(SimpleTracerProvider.class).in(Scopes.SINGLETON))); + binder.bind(TracerProviderManager.class).in(Scopes.SINGLETON); //Optional Status Detector newOptionalBinder(binder, NodeStatusService.class); diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/QueuedStatementResource.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/QueuedStatementResource.java index f0d76308d891e..67f4eefcef8dc 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/QueuedStatementResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/protocol/QueuedStatementResource.java @@ -30,8 +30,8 @@ import com.facebook.presto.server.SessionContext; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.tracing.TracerProvider; import com.facebook.presto.sql.parser.SqlParserOptions; +import com.facebook.presto.tracing.TracerProviderManager; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Ordering; @@ -125,7 +125,7 @@ public class QueuedStatementResource private final boolean compressionEnabled; private final SqlParserOptions sqlParserOptions; - private final TracerProvider tracerProvider; + private final TracerProviderManager tracerProviderManager; private final SessionPropertyManager sessionPropertyManager; // We may need some system default session property values at early query stage even before session is created. private final QueryBlockingRateLimiter queryRateLimiter; @@ -138,7 +138,7 @@ public QueuedStatementResource( LocalQueryProvider queryResultsProvider, SqlParserOptions sqlParserOptions, ServerConfig serverConfig, - TracerProvider tracerProvider, + TracerProviderManager tracerProviderManager, SessionPropertyManager sessionPropertyManager, QueryBlockingRateLimiter queryRateLimiter) { @@ -149,7 +149,7 @@ public QueuedStatementResource( this.responseExecutor = requireNonNull(executor, "responseExecutor is null").getExecutor(); this.timeoutExecutor = requireNonNull(executor, "timeoutExecutor is null").getScheduledExecutor(); - this.tracerProvider = requireNonNull(tracerProvider, "tracerProvider is null"); + this.tracerProviderManager = requireNonNull(tracerProviderManager, "tracerProviderManager is null"); this.sessionPropertyManager = sessionPropertyManager; this.queryRateLimiter = requireNonNull(queryRateLimiter, "queryRateLimiter is null"); @@ -215,7 +215,7 @@ public Response postStatement( SessionContext sessionContext = new HttpRequestSessionContext( servletRequest, sqlParserOptions, - tracerProvider, + tracerProviderManager.getTracerProvider(), Optional.of(sessionPropertyManager)); Query query = new Query(statement, sessionContext, dispatchManager, queryResultsProvider, 0); queries.put(query.getQueryId(), query); diff --git a/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java b/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java index 45b0330aa4115..15921db08084a 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java @@ -201,6 +201,7 @@ import com.facebook.presto.sql.tree.Statement; import com.facebook.presto.sql.tree.TruncateTable; import com.facebook.presto.testing.PageConsumerOperator.PageConsumerOutputFactory; +import com.facebook.presto.tracing.TracerProviderManager; import com.facebook.presto.tracing.TracingConfig; import com.facebook.presto.transaction.InMemoryTransactionManager; import com.facebook.presto.transaction.TransactionManager; @@ -480,7 +481,8 @@ private LocalQueryRunner(Session defaultSession, FeaturesConfig featuresConfig, new SessionPropertyDefaults(nodeInfo), new ThrowingNodeTtlFetcherManager(), new ThrowingClusterTtlProviderManager(), - historyBasedPlanStatisticsManager); + historyBasedPlanStatisticsManager, + new TracerProviderManager(new TracingConfig())); connectorManager.addConnectorFactory(globalSystemConnectorFactory); connectorManager.createConnection(GlobalSystemConnector.NAME, GlobalSystemConnector.NAME, ImmutableMap.of()); diff --git a/presto-main/src/main/java/com/facebook/presto/tracing/NoopTracerHandle.java b/presto-main/src/main/java/com/facebook/presto/tracing/NoopTracerHandle.java new file mode 100644 index 0000000000000..a182bf535b419 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/tracing/NoopTracerHandle.java @@ -0,0 +1,33 @@ +/* + * 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 com.facebook.presto.tracing; + +import com.facebook.presto.spi.tracing.TracerHandle; + +public class NoopTracerHandle + implements TracerHandle +{ + private final String traceToken; + + public NoopTracerHandle() + { + this.traceToken = "noop_dummy_id"; + } + + @Override + public String getTraceToken() + { + return traceToken; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/tracing/NoopTracerProvider.java b/presto-main/src/main/java/com/facebook/presto/tracing/NoopTracerProvider.java index ef2918e6e455a..44bc11d4c2424 100644 --- a/presto-main/src/main/java/com/facebook/presto/tracing/NoopTracerProvider.java +++ b/presto-main/src/main/java/com/facebook/presto/tracing/NoopTracerProvider.java @@ -15,19 +15,44 @@ import com.facebook.presto.spi.tracing.NoopTracer; import com.facebook.presto.spi.tracing.Tracer; +import com.facebook.presto.spi.tracing.TracerHandle; import com.facebook.presto.spi.tracing.TracerProvider; import com.google.inject.Inject; +import java.util.Map; +import java.util.function.Function; + public class NoopTracerProvider implements TracerProvider { public static final NoopTracerProvider NOOP_TRACER_PROVIDER = new NoopTracerProvider(); public static final NoopTracer NOOP_TRACER = new NoopTracer(); + public static final TracerHandle NOOP_TRACER_HANDLE = new NoopTracerHandle(); + public static final Function, TracerHandle> NOOP_HANDLE_GENERATOR = headers -> NOOP_TRACER_HANDLE; + @Inject public NoopTracerProvider() {} @Override - public Tracer getNewTracer() + public String getName() + { + return "NOOP tracer provider"; + } + + @Override + public String getTracerType() + { + return TracingConfig.TracerType.NOOP; + } + + @Override + public Function, TracerHandle> getHandleGenerator() + { + return NOOP_HANDLE_GENERATOR; + } + + @Override + public Tracer getNewTracer(TracerHandle handle) { return NOOP_TRACER; } diff --git a/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracer.java b/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracer.java index 4507261594c30..fb20bbe0fc993 100644 --- a/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracer.java +++ b/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracer.java @@ -31,9 +31,11 @@ public class SimpleTracer public final Map blockMap = new ConcurrentHashMap<>(); public final Map recorderBlockMap = new LinkedHashMap<>(); public final List pointList = new CopyOnWriteArrayList<>(); + public final String traceToken; - public SimpleTracer() + public SimpleTracer(String traceToken) { + this.traceToken = traceToken; addPoint("Start tracing"); } @@ -86,7 +88,10 @@ public void endTrace(String annotation) @Override public String getTracerId() { - return "simple_dummy_id"; + if (traceToken != null) { + return traceToken; + } + return tracerName; } public String toString() diff --git a/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracerHandle.java b/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracerHandle.java new file mode 100644 index 0000000000000..c700db6ffcecc --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracerHandle.java @@ -0,0 +1,33 @@ +/* + * 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 com.facebook.presto.tracing; + +import com.facebook.presto.spi.tracing.TracerHandle; + +public class SimpleTracerHandle + implements TracerHandle +{ + private final String traceToken; + + public SimpleTracerHandle(String traceToken) + { + this.traceToken = traceToken; + } + + @Override + public String getTraceToken() + { + return traceToken; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracerProvider.java b/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracerProvider.java index 7f723b9a2d452..3fe11a5661e0b 100644 --- a/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracerProvider.java +++ b/presto-main/src/main/java/com/facebook/presto/tracing/SimpleTracerProvider.java @@ -14,9 +14,15 @@ package com.facebook.presto.tracing; import com.facebook.presto.spi.tracing.Tracer; +import com.facebook.presto.spi.tracing.TracerHandle; import com.facebook.presto.spi.tracing.TracerProvider; import com.google.inject.Inject; +import java.util.Map; +import java.util.function.Function; + +import static com.facebook.presto.client.PrestoHeaders.PRESTO_TRACE_TOKEN; + public class SimpleTracerProvider implements TracerProvider { @@ -26,8 +32,27 @@ public SimpleTracerProvider() } @Override - public Tracer getNewTracer() + public String getName() + { + return "Simple tracer provider"; + } + + @Override + public String getTracerType() + { + return TracingConfig.TracerType.SIMPLE; + } + + @Override + public Function, TracerHandle> getHandleGenerator() + { + return headers -> new SimpleTracerHandle(headers.get(PRESTO_TRACE_TOKEN)); + } + + @Override + public Tracer getNewTracer(TracerHandle handle) { - return new SimpleTracer(); + SimpleTracerHandle tracerHandle = (SimpleTracerHandle) handle; + return new SimpleTracer(tracerHandle.getTraceToken()); } } diff --git a/presto-main/src/main/java/com/facebook/presto/tracing/TracerProviderManager.java b/presto-main/src/main/java/com/facebook/presto/tracing/TracerProviderManager.java new file mode 100644 index 0000000000000..6c1f35cd061fa --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/tracing/TracerProviderManager.java @@ -0,0 +1,82 @@ +/* + * 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 com.facebook.presto.tracing; + +import com.facebook.presto.spi.tracing.TracerProvider; +import com.google.inject.Inject; + +import javax.annotation.Nullable; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +public class TracerProviderManager +{ + private final String tracerType; + private final boolean systemTracingEnabled; + @Nullable + private TracerProvider tracerProvider; + + @Inject + public TracerProviderManager(TracingConfig config) + { + requireNonNull(config, "config is null"); + + this.tracerType = config.getTracerType(); + boolean enableDistributedTracing = config.getEnableDistributedTracing(); + TracingConfig.DistributedTracingMode distributedTracingMode = config.getDistributedTracingMode(); + this.systemTracingEnabled = enableDistributedTracing && distributedTracingMode.name().equalsIgnoreCase(TracingConfig.DistributedTracingMode.ALWAYS_TRACE.name()); + } + + public void addTracerProviderFactory(TracerProvider provider) + { + if (!tracerType.equals(provider.getTracerType())) { + throw new IllegalArgumentException( + format( + "Plugin-configured tracer provider ('%s') does not match system-configured provider ('%s').", + provider.getName(), + tracerType)); + } + if (systemTracingEnabled) { + if (tracerProvider != null) { + throw new IllegalArgumentException(format("Only a single plugin should set the tracer provider ('%s').", tracerProvider.getTracerType())); + } + tracerProvider = provider; + } + } + + public void loadTracerProvider() + { + if (tracerProvider != null) { + return; + } + // open-telemetry plugin not used / tracer provider not specified or not matching system config / tracing disabled + // Check if SimpleTracer is configured and tracing is enabled + // Otherwise, use Noop implementation + if (tracerType.equals(TracingConfig.TracerType.SIMPLE) && systemTracingEnabled) { + tracerProvider = new SimpleTracerProvider(); + } + else { + tracerProvider = new NoopTracerProvider(); + } + } + + public TracerProvider getTracerProvider() + { + if (tracerProvider != null) { + return tracerProvider; + } + return new NoopTracerProvider(); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/tracing/TracingConfig.java b/presto-main/src/main/java/com/facebook/presto/tracing/TracingConfig.java index 75325129da37e..e92d658930490 100644 --- a/presto-main/src/main/java/com/facebook/presto/tracing/TracingConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/tracing/TracingConfig.java @@ -24,6 +24,7 @@ public static class TracerType { public static final String NOOP = "noop"; public static final String SIMPLE = "simple"; + public static final String OTEL = "otel"; } public enum DistributedTracingMode diff --git a/presto-main/src/test/java/com/facebook/presto/tracing/testing/TestSimpleTracer.java b/presto-main/src/test/java/com/facebook/presto/tracing/testing/TestSimpleTracer.java index c12fa9b322803..810903c26df60 100644 --- a/presto-main/src/test/java/com/facebook/presto/tracing/testing/TestSimpleTracer.java +++ b/presto-main/src/test/java/com/facebook/presto/tracing/testing/TestSimpleTracer.java @@ -14,12 +14,15 @@ package com.facebook.presto.tracing.testing; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.tracing.TracerHandle; import com.facebook.presto.tracing.SimpleTracer; import com.facebook.presto.tracing.SimpleTracerProvider; import org.testng.annotations.Test; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; @@ -44,7 +47,9 @@ public TestSimpleTracer() @Test public void testAddPoint() { - SimpleTracer tracer = (SimpleTracer) tracerProvider.getNewTracer(); + Map testHeaders = new HashMap<>(); + TracerHandle testTracerHandle = tracerProvider.getHandleGenerator().apply(testHeaders); + SimpleTracer tracer = (SimpleTracer) tracerProvider.getNewTracer(testTracerHandle); List> futures = new ArrayList<>(); for (int i = 0; i < numThreads; i++) { CompletableFuture future = new CompletableFuture<>(); @@ -72,7 +77,9 @@ public void testAddPoint() @Test public void testAddBlock() { - SimpleTracer tracer = (SimpleTracer) tracerProvider.getNewTracer(); + Map testHeaders = new HashMap<>(); + TracerHandle testTracerHandle = tracerProvider.getHandleGenerator().apply(testHeaders); + SimpleTracer tracer = (SimpleTracer) tracerProvider.getNewTracer(testTracerHandle); List> futures = new ArrayList<>(); for (int i = 0; i < numThreads; i++) { CompletableFuture future = new CompletableFuture<>(); @@ -103,7 +110,9 @@ public void testAddBlock() @Test public void testBlockErrors() { - SimpleTracer tracer = (SimpleTracer) tracerProvider.getNewTracer(); + Map testHeaders = new HashMap<>(); + TracerHandle testTracerHandle = tracerProvider.getHandleGenerator().apply(testHeaders); + SimpleTracer tracer = (SimpleTracer) tracerProvider.getNewTracer(testTracerHandle); // Duplicate block PrestoException exception = expectThrows(PrestoException.class, () -> { diff --git a/presto-open-telemetry/pom.xml b/presto-open-telemetry/pom.xml new file mode 100644 index 0000000000000..a92f4ab6c7c44 --- /dev/null +++ b/presto-open-telemetry/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + + com.facebook.presto + presto-root + 0.279-SNAPSHOT + + + presto-open-telemetry + Presto - Open Telemetry Tracing + presto-plugin + + + ${project.parent.basedir} + + + + + com.google.inject + guice + + + + com.google.guava + guava + + + + io.opentelemetry + opentelemetry-api + + + + io.opentelemetry + opentelemetry-context + + + + io.opentelemetry + opentelemetry-exporter-otlp + + + + io.opentelemetry + opentelemetry-extension-trace-propagators + + + + io.opentelemetry + opentelemetry-sdk + + + + io.opentelemetry + opentelemetry-sdk-common + + + + io.opentelemetry + opentelemetry-sdk-trace + + + + io.opentelemetry + opentelemetry-semconv + + + + + com.facebook.presto + presto-spi + provided + + + + com.facebook.presto + presto-common + provided + + + + com.facebook.drift + drift-api + provided + + + + io.airlift + slice + provided + + + + io.airlift + units + provided + + + + com.fasterxml.jackson.core + jackson-annotations + provided + + + + org.openjdk.jol + jol-core + provided + + + diff --git a/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryBuilder.java b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryBuilder.java new file mode 100644 index 0000000000000..e50d52f2b6918 --- /dev/null +++ b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryBuilder.java @@ -0,0 +1,70 @@ +/* + * 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 com.facebook.presto.opentelemetry; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.extension.trace.propagation.B3Propagator; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; + +public final class OpenTelemetryBuilder +{ + private OpenTelemetryBuilder() + { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated."); + } + + /** + * Get instance of propagator. + * Currently, only B3_SINGLE_HEADER can be passed in. + */ + private static TextMapPropagator getPropagatorInstance(String contextPropagator) + { + TextMapPropagator propagator; + if (contextPropagator.equals(OpenTelemetryContextPropagator.W3C)) { + propagator = W3CTraceContextPropagator.getInstance(); + } + else if (contextPropagator.equals(OpenTelemetryContextPropagator.B3_SINGLE_HEADER)) { + propagator = B3Propagator.injectingSingleHeader(); + } + else { + propagator = B3Propagator.injectingMultiHeaders(); + } + return propagator; + } + + public static OpenTelemetry build(String contextPropagator) + { + Resource resource = Resource.getDefault() + .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "presto"))); + + SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() + .addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder().setEndpoint(System.getenv("OTEL_EXPORTER_OTLP_ENDPOINT")).build()).build()) + .setResource(resource) + .build(); + + return OpenTelemetrySdk.builder() + .setTracerProvider(sdkTracerProvider) + .setPropagators(ContextPropagators.create(OpenTelemetryBuilder.getPropagatorInstance(contextPropagator))) + .buildAndRegisterGlobal(); + } +} diff --git a/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryContextPropagator.java b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryContextPropagator.java new file mode 100644 index 0000000000000..89e10342377fc --- /dev/null +++ b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryContextPropagator.java @@ -0,0 +1,22 @@ +/* + * 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 com.facebook.presto.opentelemetry; + +public final class OpenTelemetryContextPropagator +{ + private OpenTelemetryContextPropagator() {}; + + public static final String W3C = "w3c"; + public static final String B3_SINGLE_HEADER = "b3_single_header"; +} diff --git a/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryErrorCode.java b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryErrorCode.java new file mode 100644 index 0000000000000..8ec04599d7891 --- /dev/null +++ b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryErrorCode.java @@ -0,0 +1,39 @@ +/* + * 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 com.facebook.presto.opentelemetry; + +import com.facebook.presto.common.ErrorCode; +import com.facebook.presto.common.ErrorType; +import com.facebook.presto.spi.ErrorCodeSupplier; + +import static com.facebook.presto.common.ErrorType.EXTERNAL; + +public enum OpenTelemetryErrorCode + implements ErrorCodeSupplier +{ + OPEN_TELEMETRY_CONTEXT_PROPAGATOR_ERROR(0, EXTERNAL); + + private final ErrorCode errorCode; + + OpenTelemetryErrorCode(int code, ErrorType type) + { + errorCode = new ErrorCode(code + 0x0507_0000, name(), type); + } + + @Override + public ErrorCode toErrorCode() + { + return errorCode; + } +} diff --git a/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryHeaders.java b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryHeaders.java new file mode 100644 index 0000000000000..4e80c84c7c041 --- /dev/null +++ b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryHeaders.java @@ -0,0 +1,24 @@ +/* + * 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 com.facebook.presto.opentelemetry; + +public class OpenTelemetryHeaders +{ + public static final String PRESTO_W3C_PROPAGATION = "traceparent"; + public static final String PRESTO_B3_SINGLE_HEADER_PROPAGATION = "b3"; + public static final String PRESTO_TRACE_TOKEN = "X-Presto-Trace-Token"; + public static final String PRESTO_BAGGAGE_HEADER = "baggage"; + + private OpenTelemetryHeaders() {} +} diff --git a/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryPlugin.java b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryPlugin.java new file mode 100644 index 0000000000000..807f1ba810f0b --- /dev/null +++ b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryPlugin.java @@ -0,0 +1,28 @@ +/* + * 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 com.facebook.presto.opentelemetry; + +import com.facebook.presto.spi.Plugin; +import com.facebook.presto.spi.tracing.TracerProvider; +import com.google.common.collect.ImmutableList; + +public class OpenTelemetryPlugin + implements Plugin +{ + @Override + public Iterable getTracerProviders() + { + return ImmutableList.of(new OpenTelemetryTracerProvider()); + } +} diff --git a/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryTracer.java b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryTracer.java new file mode 100644 index 0000000000000..2538a8caa500b --- /dev/null +++ b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryTracer.java @@ -0,0 +1,202 @@ +/* + * 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 com.facebook.presto.opentelemetry; + +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.tracing.Tracer; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.context.propagation.TextMapGetter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static com.facebook.presto.spi.StandardErrorCode.DISTRIBUTED_TRACING_ERROR; + +public class OpenTelemetryTracer + implements Tracer +{ + private static String currentContextPropagator = OpenTelemetryContextPropagator.B3_SINGLE_HEADER; + private static OpenTelemetry openTelemetry = OpenTelemetryBuilder.build(currentContextPropagator); + private final io.opentelemetry.api.trace.Tracer openTelemetryTracer; + private final String traceToken; + private final Span parentSpan; + private final Context parentContext; + + public final Map spanMap = new ConcurrentHashMap<>(); + public final Map recorderSpanMap = new LinkedHashMap<>(); + + public OpenTelemetryTracer(String traceToken, String contextPropagator, String propagatedContext, String baggage) + { + // Trivial getter method to return carrier + // Carrier will be equal to the data to be fetched + TextMapGetter trivialGetter = new TextMapGetter() + { + @Override + public String get(String carrier, String key) + { + return carrier; + } + + @Override + public Iterable keys(String carrier) + { + return Arrays.asList(get(carrier, null)); + } + }; + + // Rebuild OPEN_TELEMETRY instance if necessary (to use different context propagator) + // Will only occur once at max, if contextPropagator is different from B3_SINGLE_HEADER + if (contextPropagator != null && !contextPropagator.equals(currentContextPropagator)) { + this.openTelemetry = OpenTelemetryBuilder.build(contextPropagator); + this.currentContextPropagator = contextPropagator; + } + + this.openTelemetryTracer = openTelemetry.getTracer(tracerName); + this.traceToken = traceToken; + + if (propagatedContext != null) { + // Only process baggage headers if context propagation is successful + Context extractedContext = openTelemetry.getPropagators().getTextMapPropagator() + .extract(Context.current(), propagatedContext, trivialGetter); + Context contextWithBaggage = W3CBaggagePropagator.getInstance().extract( + extractedContext, baggage, trivialGetter); + try (Scope ignored = contextWithBaggage.makeCurrent()) { + this.parentSpan = createParentSpan(); + } + this.parentContext = contextWithBaggage; + } + else { + this.parentSpan = createParentSpan(); + this.parentContext = Context.current(); + } + addBaggageToSpanAttributes(this.parentSpan); + + synchronized (recorderSpanMap) { + recorderSpanMap.put("Trace start", this.parentSpan); + } + } + + /** + * Take parent context baggage and set as span attributes. + * Call during each span and nested span creation to properly propagate tags. + */ + private void addBaggageToSpanAttributes(Span span) + { + Baggage baggage = Baggage.fromContext(parentContext); + baggage.forEach((s, baggageEntry) -> span.setAttribute(s, baggageEntry.getValue())); + } + + private Span createParentSpan() + { + Span parentSpan = openTelemetryTracer.spanBuilder("Trace start").startSpan(); + parentSpan.setAttribute("trace_id", traceToken); + return parentSpan; + } + + private void endUnendedBlocks() + { + List blocks = new ArrayList<>(spanMap.keySet()); + for (String currBlock : blocks) { + endBlock(currBlock, ""); + } + } + + /** + * Add annotation as event to parent span + * @param annotation event to add + */ + @Override + public void addPoint(String annotation) + { + parentSpan.addEvent(annotation); + } + + /** + * Create new span with Open Telemetry tracer + * @param blockName name of span + * @param annotation event to add to span + */ + + @Override + public void startBlock(String blockName, String annotation) + { + if (spanMap.containsKey(blockName)) { + throw new PrestoException(DISTRIBUTED_TRACING_ERROR, "Duplicated block inserted: " + blockName); + } + Span span = openTelemetryTracer.spanBuilder(blockName) + .setParent(Context.current().with(parentSpan)) + .startSpan(); + span.addEvent(annotation); + addBaggageToSpanAttributes(span); + + spanMap.put(blockName, span); + synchronized (recorderSpanMap) { + recorderSpanMap.put(blockName, span); + } + } + + @Override + public void addPointToBlock(String blockName, String annotation) + { + if (!spanMap.containsKey(blockName)) { + throw new PrestoException(DISTRIBUTED_TRACING_ERROR, "Adding point to non-existing block: " + blockName); + } + spanMap.get(blockName).addEvent(annotation); + } + + /** + * End Open Telemetry span + * @param blockName name of span + * @param annotation event to add to span + */ + @Override + public void endBlock(String blockName, String annotation) + { + if (!spanMap.containsKey(blockName)) { + throw new PrestoException(DISTRIBUTED_TRACING_ERROR, "Trying to end a non-existing block: " + blockName); + } + spanMap.remove(blockName); + synchronized (recorderSpanMap) { + Span span = recorderSpanMap.get(blockName); + span.addEvent(annotation); + span.end(); + } + } + + @Override + public void endTrace(String annotation) + { + parentSpan.addEvent(annotation); + endUnendedBlocks(); + parentSpan.end(); + } + + @Override + public String getTracerId() + { + if (traceToken != null) { + return traceToken; + } + return tracerName; + } +} diff --git a/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryTracerHandle.java b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryTracerHandle.java new file mode 100644 index 0000000000000..d68945b10761d --- /dev/null +++ b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryTracerHandle.java @@ -0,0 +1,54 @@ +/* + * 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 com.facebook.presto.opentelemetry; + +import com.facebook.presto.spi.tracing.TracerHandle; + +public class OpenTelemetryTracerHandle + implements TracerHandle +{ + private final String traceToken; + private final String contextPropagator; + private final String propagatedContext; + private final String baggage; + + public OpenTelemetryTracerHandle(String traceToken, String contextPropagator, String propagatedContext, String baggage) + { + this.traceToken = traceToken; + this.contextPropagator = contextPropagator; + this.propagatedContext = propagatedContext; + this.baggage = baggage; + } + + @Override + public String getTraceToken() + { + return traceToken; + } + + public String getContextPropagator() + { + return contextPropagator; + } + + public String getPropagatedContext() + { + return propagatedContext; + } + + public String getBaggage() + { + return baggage; + } +} diff --git a/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryTracerProvider.java b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryTracerProvider.java new file mode 100644 index 0000000000000..722b60937938d --- /dev/null +++ b/presto-open-telemetry/src/main/java/com/facebook/presto/opentelemetry/OpenTelemetryTracerProvider.java @@ -0,0 +1,110 @@ +/* + * 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 com.facebook.presto.opentelemetry; + +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.tracing.Tracer; +import com.facebook.presto.spi.tracing.TracerHandle; +import com.facebook.presto.spi.tracing.TracerProvider; +import com.google.inject.Inject; + +import java.util.Map; +import java.util.function.Function; + +import static com.facebook.presto.opentelemetry.OpenTelemetryErrorCode.OPEN_TELEMETRY_CONTEXT_PROPAGATOR_ERROR; +import static com.facebook.presto.opentelemetry.OpenTelemetryHeaders.PRESTO_B3_SINGLE_HEADER_PROPAGATION; +import static com.facebook.presto.opentelemetry.OpenTelemetryHeaders.PRESTO_BAGGAGE_HEADER; +import static com.facebook.presto.opentelemetry.OpenTelemetryHeaders.PRESTO_TRACE_TOKEN; +import static com.facebook.presto.opentelemetry.OpenTelemetryHeaders.PRESTO_W3C_PROPAGATION; + +public class OpenTelemetryTracerProvider + implements TracerProvider +{ + @Inject + public OpenTelemetryTracerProvider() {} + + @Override + public String getName() + { + return "Open telemetry tracer provider"; + } + + @Override + public String getTracerType() + { + return "otel"; + } + + @Override + public Function, TracerHandle> getHandleGenerator() + { + return headers -> { + String contextPropagator = determineContextPropagationMode(headers); + return new OpenTelemetryTracerHandle( + headers.get(PRESTO_TRACE_TOKEN), + contextPropagator, + getPropagatedContextFromHeader(contextPropagator, headers), + headers.get(PRESTO_BAGGAGE_HEADER)); + }; + } + + @Override + public Tracer getNewTracer(TracerHandle handle) + { + OpenTelemetryTracerHandle tracerHandle = (OpenTelemetryTracerHandle) handle; + return new OpenTelemetryTracer( + tracerHandle.getTraceToken(), + tracerHandle.getContextPropagator(), + tracerHandle.getPropagatedContext(), + tracerHandle.getBaggage()); + } + + /** + * Take header values and determine which context propagation mode is used + * Currently only supports b3 single header + * @param headers HTTP request headers + * @return context propagation mode + */ + private String determineContextPropagationMode(Map headers) + { + if (headers.containsKey(PRESTO_B3_SINGLE_HEADER_PROPAGATION)) { + return OpenTelemetryContextPropagator.B3_SINGLE_HEADER; + } + if (headers.containsKey(PRESTO_W3C_PROPAGATION)) { + throw new PrestoException( + OPEN_TELEMETRY_CONTEXT_PROPAGATOR_ERROR, + "Only b3 single header context propagation mode is currently supported."); + } + return null; + } + + /** + * Currently only supports b3 single header and w3c propagation + * @param contextPropagator context propagator to use + * @param headers http request headers + * @return header value extracted from http request based on context propagator + */ + private static String getPropagatedContextFromHeader(String contextPropagator, Map headers) + { + if (contextPropagator != null) { + if (contextPropagator.equals(OpenTelemetryContextPropagator.B3_SINGLE_HEADER)) { + return headers.get(PRESTO_B3_SINGLE_HEADER_PROPAGATION); + } + if (contextPropagator.equals(OpenTelemetryContextPropagator.W3C)) { + return headers.get(PRESTO_W3C_PROPAGATION); + } + } + return null; + } +} diff --git a/presto-open-telemetry/src/main/resources/META-INF/services/com.facebook.presto.spi.Plugin b/presto-open-telemetry/src/main/resources/META-INF/services/com.facebook.presto.spi.Plugin new file mode 100644 index 0000000000000..821a4cf114184 --- /dev/null +++ b/presto-open-telemetry/src/main/resources/META-INF/services/com.facebook.presto.spi.Plugin @@ -0,0 +1 @@ +com.facebook.presto.opentelemetry.OpenTelemetryPlugin \ No newline at end of file diff --git a/presto-spark-base/src/main/java/com/facebook/presto/spark/PrestoSparkModule.java b/presto-spark-base/src/main/java/com/facebook/presto/spark/PrestoSparkModule.java index 4837530ce46a7..9e39441912816 100644 --- a/presto-spark-base/src/main/java/com/facebook/presto/spark/PrestoSparkModule.java +++ b/presto-spark-base/src/main/java/com/facebook/presto/spark/PrestoSparkModule.java @@ -171,6 +171,7 @@ import com.facebook.presto.sql.planner.sanity.PlanChecker; import com.facebook.presto.sql.relational.RowExpressionDeterminismEvaluator; import com.facebook.presto.sql.relational.RowExpressionDomainTranslator; +import com.facebook.presto.tracing.TracerProviderManager; import com.facebook.presto.tracing.TracingConfig; import com.facebook.presto.transaction.InMemoryTransactionManager; import com.facebook.presto.transaction.TransactionManager; @@ -311,6 +312,9 @@ protected void setup(Binder binder) binder.bind(AnalyzePropertyManager.class).in(Scopes.SINGLETON); binder.bind(QuerySessionSupplier.class).in(Scopes.SINGLETON); + // tracer provider managers + binder.bind(TracerProviderManager.class).in(Scopes.SINGLETON); + // block encodings binder.bind(BlockEncodingManager.class).in(Scopes.SINGLETON); binder.bind(BlockEncodingSerde.class).to(BlockEncodingManager.class).in(Scopes.SINGLETON); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/Plugin.java b/presto-spi/src/main/java/com/facebook/presto/spi/Plugin.java index 2a7f9bc935edc..9a8d3d9f3577e 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/Plugin.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/Plugin.java @@ -26,6 +26,7 @@ import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; import com.facebook.presto.spi.statistics.HistoryBasedPlanStatisticsProvider; import com.facebook.presto.spi.storage.TempStorageFactory; +import com.facebook.presto.spi.tracing.TracerProvider; import com.facebook.presto.spi.ttl.ClusterTtlProviderFactory; import com.facebook.presto.spi.ttl.NodeTtlFetcherFactory; @@ -115,4 +116,12 @@ default Iterable getHistoryBasedPlanStatisti { return emptyList(); } + + /** + * Return list of tracer providers specified by tracer plugin + */ + default Iterable getTracerProviders() + { + return emptyList(); + } } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/tracing/Tracer.java b/presto-spi/src/main/java/com/facebook/presto/spi/tracing/Tracer.java index b24d0bd3712fc..645d7a739c576 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/tracing/Tracer.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/tracing/Tracer.java @@ -15,6 +15,7 @@ public interface Tracer { + String tracerName = "com.facebook.presto"; /** * Add a single trace point with current time to the main block * @param annotation message associated with the trace point diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/tracing/TracerHandle.java b/presto-spi/src/main/java/com/facebook/presto/spi/tracing/TracerHandle.java new file mode 100644 index 0000000000000..bf9104e1545ec --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/tracing/TracerHandle.java @@ -0,0 +1,19 @@ +/* + * 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 com.facebook.presto.spi.tracing; + +public interface TracerHandle +{ + String getTraceToken(); +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/tracing/TracerProvider.java b/presto-spi/src/main/java/com/facebook/presto/spi/tracing/TracerProvider.java index d8b25863b1e7c..a7fd4988cdfcd 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/tracing/TracerProvider.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/tracing/TracerProvider.java @@ -13,11 +13,26 @@ */ package com.facebook.presto.spi.tracing; +import java.util.Map; +import java.util.function.Function; + public interface TracerProvider { + String getName(); + + /** + * Return tracer type provided by TracerProvider + */ + String getTracerType(); + + /** + * The method returns a function to take a set of HTTP headers and generate the tracer handle + */ + Function, TracerHandle> getHandleGenerator(); + /** * * @return A @Tracer that should be kept throughout the whole duration of tracing. */ - Tracer getNewTracer(); + Tracer getNewTracer(TracerHandle handle); }