From 6aa186b6a04d63abb2781df1daf15a88048d2b29 Mon Sep 17 00:00:00 2001 From: Simon Stewart Date: Sat, 10 Nov 2018 18:52:58 +0000 Subject: [PATCH] Start binding distributed tracing into selenium There are two major camps in the java world that are important for distributed tracing. The first is OpenTracing, the second is OpenCensus. By supporting both of these, it becomes significantly easier to hook Selenium into any system that can be used for visualising distributed traces. Right now, we've wired up a no-op implementation that is mostly harmless. The next step along the path will be to make it easy to use either Jaeger or Zipkin with this. --- .../src/org/openqa/selenium/remote/BUCK | 3 + .../selenium/remote/HttpCommandExecutor.java | 32 ++-- .../selenium/remote/tracing/CompoundSpan.java | 86 +++++++++++ .../remote/tracing/DistributedTracer.java | 142 ++++++++++++++++++ .../remote/tracing/OpenCensusSpan.java | 85 +++++++++++ .../remote/tracing/OpenTracingSpan.java | 84 +++++++++++ .../openqa/selenium/remote/tracing/Span.java | 51 +++++++ .../org/openqa/selenium/remote/tracing/BUCK | 13 ++ .../remote/tracing/DistributedTracerTest.java | 69 +++++++++ third_party/java/opencensus/BUCK | 2 +- third_party/java/opentracing/BUCK | 6 +- 11 files changed, 557 insertions(+), 16 deletions(-) create mode 100644 java/client/src/org/openqa/selenium/remote/tracing/CompoundSpan.java create mode 100644 java/client/src/org/openqa/selenium/remote/tracing/DistributedTracer.java create mode 100644 java/client/src/org/openqa/selenium/remote/tracing/OpenCensusSpan.java create mode 100644 java/client/src/org/openqa/selenium/remote/tracing/OpenTracingSpan.java create mode 100644 java/client/src/org/openqa/selenium/remote/tracing/Span.java create mode 100644 java/client/test/org/openqa/selenium/remote/tracing/BUCK create mode 100644 java/client/test/org/openqa/selenium/remote/tracing/DistributedTracerTest.java diff --git a/java/client/src/org/openqa/selenium/remote/BUCK b/java/client/src/org/openqa/selenium/remote/BUCK index 61e11dfefc6e7..9971aab48072b 100644 --- a/java/client/src/org/openqa/selenium/remote/BUCK +++ b/java/client/src/org/openqa/selenium/remote/BUCK @@ -122,6 +122,7 @@ java_library(name = 'remote-lib', 'mobile/RemoteNetworkConnection.java', ] + glob([ 'service/*.java', + 'tracing/*.java', ]), resources = [ ':get-attribute', @@ -137,6 +138,8 @@ java_library(name = 'remote-lib', deps = [ ':http-session-id', '//java/client/src/org/openqa/selenium:selenium', + '//third_party/java/opencensus:opencensus-api', + '//third_party/java/opentracing:opentracing-noop', '//third_party/java/guava:guava', ], ) diff --git a/java/client/src/org/openqa/selenium/remote/HttpCommandExecutor.java b/java/client/src/org/openqa/selenium/remote/HttpCommandExecutor.java index 6ce998490c06c..2169b4adef32d 100644 --- a/java/client/src/org/openqa/selenium/remote/HttpCommandExecutor.java +++ b/java/client/src/org/openqa/selenium/remote/HttpCommandExecutor.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableMap; +import org.openqa.selenium.BuildInfo; import org.openqa.selenium.NoSuchSessionException; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.UnsupportedCommandException; @@ -36,6 +37,8 @@ import org.openqa.selenium.remote.http.HttpClient; import org.openqa.selenium.remote.http.HttpRequest; import org.openqa.selenium.remote.http.HttpResponse; +import org.openqa.selenium.remote.tracing.DistributedTracer; +import org.openqa.selenium.remote.tracing.Span; import java.io.IOException; import java.net.MalformedURLException; @@ -127,30 +130,35 @@ public Response execute(Command command) throws IOException { } } + DistributedTracer tracer = DistributedTracer.getInstance(); + if (NEW_SESSION.equals(command.getName())) { if (commandCodec != null) { throw new SessionNotCreatedException("Session already exists"); } - ProtocolHandshake handshake = new ProtocolHandshake(); - log(LogType.PROFILER, new HttpProfilerLogEntry(command.getName(), true)); - ProtocolHandshake.Result result = handshake.createSession(client, command); - Dialect dialect = result.getDialect(); - commandCodec = dialect.getCommandCodec(); - for (Map.Entry entry : additionalCommands.entrySet()) { - defineCommand(entry.getKey(), entry.getValue()); + try (Span span = tracer.createSpan("new-session", tracer.getActiveSpan())) { + ProtocolHandshake handshake = new ProtocolHandshake(); + log(LogType.PROFILER, new HttpProfilerLogEntry(command.getName(), true)); + ProtocolHandshake.Result result = handshake.createSession(client, command); + Dialect dialect = result.getDialect(); + commandCodec = dialect.getCommandCodec(); + for (Map.Entry entry : additionalCommands.entrySet()) { + defineCommand(entry.getKey(), entry.getValue()); + } + responseCodec = dialect.getResponseCodec(); + log(LogType.PROFILER, new HttpProfilerLogEntry(command.getName(), false)); + return result.createResponse(); } - responseCodec = dialect.getResponseCodec(); - log(LogType.PROFILER, new HttpProfilerLogEntry(command.getName(), false)); - return result.createResponse(); } if (commandCodec == null || responseCodec == null) { throw new WebDriverException( - "No command or response codec has been defined. Unable to proceed"); + "No command or response codec has been defined. Unable to proceed"); } HttpRequest httpRequest = commandCodec.encode(command); - try { + try (Span span = tracer.createSpan(command.getName(), tracer.getActiveSpan())) { + span.addTag("selenium-sessionid", String.valueOf(command.getSessionId())); log(LogType.PROFILER, new HttpProfilerLogEntry(command.getName(), true)); HttpResponse httpResponse = client.execute(httpRequest); log(LogType.PROFILER, new HttpProfilerLogEntry(command.getName(), false)); diff --git a/java/client/src/org/openqa/selenium/remote/tracing/CompoundSpan.java b/java/client/src/org/openqa/selenium/remote/tracing/CompoundSpan.java new file mode 100644 index 0000000000000..79a23dbe332f2 --- /dev/null +++ b/java/client/src/org/openqa/selenium/remote/tracing/CompoundSpan.java @@ -0,0 +1,86 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.tracing; + +import com.google.common.collect.ImmutableSet; + +import java.util.Objects; + +class CompoundSpan implements Span { + + private final DistributedTracer tracer; + private final ImmutableSet allSpans; + + public CompoundSpan(DistributedTracer tracer, ImmutableSet allSpans) { + this.tracer = Objects.requireNonNull(tracer); + this.allSpans = Objects.requireNonNull(allSpans); + } + + @Override + public Span activate() { + allSpans.forEach(Span::activate); + + // It's important we do this last, since the activations of all the wrapped spans has caused + // them to _also_ attempt to set themselves as the active span. + tracer.setActiveSpan(this); + return this; + } + + @Override + public Span createChild(String operation) { + ImmutableSet.Builder allChildren = ImmutableSet.builder(); + allSpans.forEach(span -> allChildren.add(span.createChild(operation))); + + CompoundSpan child = new CompoundSpan(tracer, allChildren.build()); + return child.activate(); + } + + @Override + public Span addTraceTag(String key, String value) { + Objects.requireNonNull(key, "Key must be set"); + allSpans.forEach(span -> span.addTraceTag(key, value)); + return this; + } + + @Override + public Span addTag(String key, String value) { + Objects.requireNonNull(key, "Key must be set"); + allSpans.forEach(span -> span.addTag(key, value)); + return this; + } + + @Override + public Span addTag(String key, boolean value) { + Objects.requireNonNull(key, "Key must be set"); + allSpans.forEach(span -> span.addTag(key, value)); + return this; + } + + @Override + public Span addTag(String key, long value) { + Objects.requireNonNull(key, "Key must be set"); + allSpans.forEach(span -> span.addTag(key, value)); + return this; + } + + @Override + public void close() { + allSpans.forEach(Span::close); + tracer.remove(this); + } +} diff --git a/java/client/src/org/openqa/selenium/remote/tracing/DistributedTracer.java b/java/client/src/org/openqa/selenium/remote/tracing/DistributedTracer.java new file mode 100644 index 0000000000000..044a5b944c557 --- /dev/null +++ b/java/client/src/org/openqa/selenium/remote/tracing/DistributedTracer.java @@ -0,0 +1,142 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.tracing; + +import com.google.common.collect.ImmutableSet; + +import org.openqa.selenium.BuildInfo; + +import io.opentracing.noop.NoopTracerFactory; + +import java.util.LinkedList; +import java.util.Objects; + +/** + * Represents an entry point for accessing all aspects of distributed tracing. + */ +public class DistributedTracer { + + private static volatile DistributedTracer INSTANCE = DistributedTracer.builder().build(); + private static final ThreadLocal> ACTIVE_SPANS = + ThreadLocal.withInitial(LinkedList::new); + private final ImmutableSet ocTracers; + private final ImmutableSet otTracers; + + private DistributedTracer( + ImmutableSet ocTracers, + ImmutableSet otTracers) { + this.ocTracers = Objects.requireNonNull(ocTracers); + this.otTracers = Objects.requireNonNull(otTracers); + } + + public static DistributedTracer getInstance() { + return INSTANCE; + } + + public synchronized void setInstance(DistributedTracer distributedTracer) { + INSTANCE = distributedTracer; + } + + public static Builder builder() { + return new Builder(); + } + + /** + * A distributed trace is made of a series of {@link Span}s, which are either + * independent or have a parent/child relationship. Creating a span will make + * it the currently active {@code Span}, as returned by + * {@link #getActiveSpan()}. + * + * @param parent The parent span. If this is {@code null}, then the span is + * assumed to be independent. + */ + public Span createSpan(String operation, Span parent) { + if (parent != null) { + Span child = parent.createChild(operation); + setActiveSpan(child); + return child; + } + + ImmutableSet.Builder spans = ImmutableSet.builder(); + + for (io.opencensus.trace.Tracer tracer : ocTracers) { + spans.add(new OpenCensusSpan(this, tracer, null, "root")); + } + + for (io.opentracing.Tracer tracer : otTracers) { + spans.add(new OpenTracingSpan(this, tracer, null, "root")); + } + + Span child = new CompoundSpan(this, spans.build()); + child.addTraceTag("selenium-version", new BuildInfo().getReleaseLabel()); + child.addTag("selenium-client", "java"); + setActiveSpan(child); + return child; + } + + /** + * Each thread can have one currently active span. This can be accessed via + * this method. If there is no currently active span, then a new one will + * be created. Should a new span be created, it will be set as the currently + * active span. + */ + public Span getActiveSpan() { + if (ACTIVE_SPANS.get().isEmpty()) { + return null; + } + + return ACTIVE_SPANS.get().getLast().activate(); + } + + void setActiveSpan(Span span) { + ACTIVE_SPANS.get().add(span); + } + + void remove(Span span) { + Objects.requireNonNull(span, "Span to remove must not be null"); + + ACTIVE_SPANS.get().removeIf(span::equals); + } + + public static class Builder { + + private ImmutableSet.Builder ocTracers = ImmutableSet.builder(); + private ImmutableSet.Builder otTracers = ImmutableSet.builder(); + + private Builder() { + // Only accessible through the parent class + + // Make sure we have at least one tracer, but make it one that does nothing. + register(NoopTracerFactory.create()); + } + + public Builder register(io.opentracing.Tracer openTracingTracer) { + otTracers.add(Objects.requireNonNull(openTracingTracer, "Tracer must be set.")); + return this; + } + + public Builder register(io.opencensus.trace.Tracer openCensusTracer) { + ocTracers.add(Objects.requireNonNull(openCensusTracer, "Tracer must be set.")); + return this; + } + + public DistributedTracer build() { + return new DistributedTracer(ocTracers.build(), otTracers.build()); + } + } +} diff --git a/java/client/src/org/openqa/selenium/remote/tracing/OpenCensusSpan.java b/java/client/src/org/openqa/selenium/remote/tracing/OpenCensusSpan.java new file mode 100644 index 0000000000000..4f20883584225 --- /dev/null +++ b/java/client/src/org/openqa/selenium/remote/tracing/OpenCensusSpan.java @@ -0,0 +1,85 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.tracing; + +import io.opencensus.trace.AttributeValue; +import io.opencensus.trace.Tracer; + +import java.util.Objects; + +class OpenCensusSpan implements Span { + + private final io.opencensus.trace.Span span; + private final DistributedTracer distributedTracer; + private final Tracer tracer; + + OpenCensusSpan( + DistributedTracer distributedTracer, + Tracer tracer, + io.opencensus.trace.Span parent, + String operation) { + this.distributedTracer = Objects.requireNonNull(distributedTracer); + this.tracer = Objects.requireNonNull(tracer); + this.span = tracer.spanBuilderWithExplicitParent(operation, parent).startSpan(); + activate(); + } + + @Override + public Span activate() { + tracer.withSpan(span); + distributedTracer.setActiveSpan(this); + return this; + } + + @Override + public Span addTraceTag(String key, String value) { + span.putAttribute(Objects.requireNonNull(key), AttributeValue.stringAttributeValue(value)); + return this; + } + + @Override + public Span addTag(String key, String value) { + span.putAttribute(Objects.requireNonNull(key), AttributeValue.stringAttributeValue(value)); + return this; + } + + @Override + public Span addTag(String key, boolean value) { + span.putAttribute(Objects.requireNonNull(key), AttributeValue.booleanAttributeValue(value)); + return this; + } + + @Override + public Span addTag(String key, long value) { + span.putAttribute(Objects.requireNonNull(key), AttributeValue.longAttributeValue(value)); + return this; + } + + @Override + public Span createChild(String operation) { + Span child = new OpenCensusSpan(distributedTracer, tracer, span, operation); + return child.activate(); + } + + @Override + public void close() { + span.end(); + distributedTracer.remove(this); + } + +} diff --git a/java/client/src/org/openqa/selenium/remote/tracing/OpenTracingSpan.java b/java/client/src/org/openqa/selenium/remote/tracing/OpenTracingSpan.java new file mode 100644 index 0000000000000..bc97f86f63436 --- /dev/null +++ b/java/client/src/org/openqa/selenium/remote/tracing/OpenTracingSpan.java @@ -0,0 +1,84 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.tracing; + +import io.opentracing.Tracer; + +import java.util.Objects; + +class OpenTracingSpan implements Span { + + private final DistributedTracer distributedTracer; + private final Tracer tracer; + private final io.opentracing.Span span; + + OpenTracingSpan( + DistributedTracer distributedTracer, + Tracer tracer, + io.opentracing.Span parent, + String operation) { + this.distributedTracer = Objects.requireNonNull(distributedTracer); + this.tracer = Objects.requireNonNull(tracer); + + this.span = tracer.buildSpan(operation).asChildOf(parent).ignoreActiveSpan().start(); + activate(); + } + + @Override + public Span activate() { + tracer.scopeManager().activate(span, false); + distributedTracer.setActiveSpan(this); + return this; + } + + @Override + public Span addTraceTag(String key, String value) { + span.setBaggageItem(Objects.requireNonNull(key), value); + return this; + } + + @Override + public Span addTag(String key, String value) { + span.setTag(key, value); + return this; + } + + @Override + public Span addTag(String key, boolean value) { + span.setTag(Objects.requireNonNull(key), value); + return this; + } + + @Override + public Span addTag(String key, long value) { + span.setTag(Objects.requireNonNull(key), value); + return this; + } + + @Override + public Span createChild(String operation) { + Span child = new OpenTracingSpan(distributedTracer, tracer, span, operation); + return child.activate(); + } + + @Override + public void close() { + span.finish(); + distributedTracer.remove(this); + } +} diff --git a/java/client/src/org/openqa/selenium/remote/tracing/Span.java b/java/client/src/org/openqa/selenium/remote/tracing/Span.java new file mode 100644 index 0000000000000..747560dc6a4ff --- /dev/null +++ b/java/client/src/org/openqa/selenium/remote/tracing/Span.java @@ -0,0 +1,51 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.tracing; + +import java.io.Closeable; + +public interface Span extends Closeable { + + Span createChild(String operation); + + /** + * Allows subclasses to indicate that this is the currently active span + */ + Span activate(); + + /** + * Add a tag that will be transmitted across the wire to allow remote traces + * to also have the value. This is equivalent to OpenTracing's concept of + * "baggage". + */ + Span addTraceTag(String key, String value); + + /** + * Add a piece of metadata to the span, which allows high cardinality data to + * be added to the span. This data will not be propogated to other spans. + */ + Span addTag(String key, String value); + + Span addTag(String key, boolean value); + + Span addTag(String key, long value); + + @Override + void close(); + +} diff --git a/java/client/test/org/openqa/selenium/remote/tracing/BUCK b/java/client/test/org/openqa/selenium/remote/tracing/BUCK new file mode 100644 index 0000000000000..978b071296713 --- /dev/null +++ b/java/client/test/org/openqa/selenium/remote/tracing/BUCK @@ -0,0 +1,13 @@ + +java_test( + name = "tracing", + labels = [ + "small", + ], + srcs = glob(["*.java"]), + deps = [ + "//java/client/src/org/openqa/selenium/remote:remote", + "//third_party/java/assertj:assertj", + "//third_party/java/junit:junit", + ], +) diff --git a/java/client/test/org/openqa/selenium/remote/tracing/DistributedTracerTest.java b/java/client/test/org/openqa/selenium/remote/tracing/DistributedTracerTest.java new file mode 100644 index 0000000000000..75f5d41d06b7f --- /dev/null +++ b/java/client/test/org/openqa/selenium/remote/tracing/DistributedTracerTest.java @@ -0,0 +1,69 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.tracing; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +public class DistributedTracerTest { + + @Test + public void creatingASpanImplicitlyMakesItActive() { + DistributedTracer tracer = DistributedTracer.builder().build(); + + try (Span span = tracer.createSpan("welcome", null)) { + assertThat(tracer.getActiveSpan()).isEqualTo(span); + } + } + + @Test + public void shouldBeAbleToSetASpanAsActive() { + DistributedTracer tracer = DistributedTracer.builder().build(); + + try (Span span = tracer.createSpan("welcome", null)) { + tracer.setActiveSpan(span); + + assertThat(tracer.getActiveSpan()).isEqualTo(span); + } + } + + @Test + public void childSpansAutomaticallyBecomeActive() { + DistributedTracer tracer = DistributedTracer.builder().build(); + + try (Span parent = tracer.createSpan("parent", null)) { + try (Span child = tracer.createSpan("child", parent)) { + assertThat(tracer.getActiveSpan()).isEqualTo(child); + } + } + } + + @Test + public void closingAChildSpanResultsInTheParentSpanBecomingActive() { + DistributedTracer tracer = DistributedTracer.builder().build(); + + try (Span parent = tracer.createSpan("parent", null)) { + try (Span child = tracer.createSpan("child", parent)) { + assertThat(tracer.getActiveSpan()).isEqualTo(child); + } + + assertThat(tracer.getActiveSpan()).isEqualTo(parent); + } + } +} diff --git a/third_party/java/opencensus/BUCK b/third_party/java/opencensus/BUCK index 75af9910a9724..8f8b60df16a20 100644 --- a/third_party/java/opencensus/BUCK +++ b/third_party/java/opencensus/BUCK @@ -7,7 +7,7 @@ prebuilt_jar( '//third_party/java/grpc:grpc-context' ], visibility = [ - '//java/client/src/org/openqa/selenium/remote/tracing/opencensus:', + '//java/client/src/org/openqa/selenium/remote:', ], ) diff --git a/third_party/java/opentracing/BUCK b/third_party/java/opentracing/BUCK index 66d22ac512209..3b54efe758875 100644 --- a/third_party/java/opentracing/BUCK +++ b/third_party/java/opentracing/BUCK @@ -5,8 +5,8 @@ prebuilt_jar( source_jar = 'opentracing-api-0.31.0-sources.jar', visibility = [ '//third_party/java/contrib:opentracing-concurrent', - '//third_party/java/contrib:opentracing-okhttp3' - '//java/client/src/org/openqa/selenium/remote/tracing/opentracing:', + '//third_party/java/contrib:opentracing-okhttp3', + '//java/client/src/org/openqa/selenium/remote:', ], ) @@ -21,7 +21,7 @@ prebuilt_jar( visibility = [ '//third_party/java/contrib:opentracing-concurrent', '//third_party/java/contrib:opentracing-okhttp3', - '//java/client/src/org/openqa/selenium/remote/tracing/opentracing:', + '//java/client/src/org/openqa/selenium/remote:', ], )