public final class

Tracing

extends java.lang.Object

 java.lang.Object

↳androidx.test.platform.tracing.Tracing

Gradle dependencies

compile group: 'androidx.test', name: 'monitor', version: '1.6.0-alpha03'

  • groupId: androidx.test
  • artifactId: monitor
  • version: 1.6.0-alpha03

Artifact androidx.test:monitor:1.6.0-alpha03 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.test:monitor com.android.support.test:monitor

Overview

Tracing is a singleton to interact with registered Tracer implementations.

Support for actual tracing libraries is done by implementing wrappers following the Tracer interface. The actual Tracer implementations must be registered using Tracing.registerTracer(Tracer).

The Tracing singleton is also an entry point used to create the root span for any tests by invoking the Tracing.beginSpan(String) method. The returned Tracer.Span object must be properly closed by invoking the Tracer.Span.close() method or wrapping them in a try-resource block.

Summary

Methods
public Tracer.SpanbeginSpan(java.lang.String name)

Returns a new Span as a managed resource in a try{} block.

public static TracinggetInstance()

Static getter for external access to the singleton.

public voidregisterTracer(Tracer tracer)

Registers a new tracer.

public voidunregisterTracer(Tracer tracer)

Unregisters a tracer.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Methods

public static Tracing getInstance()

Static getter for external access to the singleton.
Preferred method for internal Espresso is getting this singleton via dagger injection.

public void registerTracer(Tracer tracer)

Registers a new tracer.

Once a tracer is registered, it starts being invoked for new root Tracing.beginSpan(String) calls. Existing in-flight spans do not invoke the new tracer.

Parameters:

tracer: A non-null Tracer object. A previously registered instance is ignored.

public void unregisterTracer(Tracer tracer)

Unregisters a tracer.

Once a tracer is unregistered, it will stop being invoked for any new root Tracing.beginSpan(String) calls made or any new Tracer.Span.beginChildSpan(String). However the tracer is still be called for any in-flight spans being closed.

Parameters:

tracer: A Tracer object. A null reference or non-registered instance is ignored.

public Tracer.Span beginSpan(java.lang.String name)

Returns a new Span as a managed resource in a try{} block. Tracer.Span.close() is automatically called when the resource is released.

Parameters:

name: A non-null name describing this span.

Source

/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * 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 androidx.test.platform.tracing;

import static androidx.test.internal.util.Checks.checkNotNull;

import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.test.annotation.ExperimentalTestApi;
import androidx.test.platform.tracing.Tracer.Span;
import com.google.errorprone.annotations.MustBeClosed;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * {@link Tracing} is a singleton to interact with registered {@link Tracer} implementations.
 *
 * <p>Support for actual tracing libraries is done by implementing wrappers following the {@link
 * Tracer} interface. The actual {@link Tracer} implementations must be registered using {@link
 * Tracing#registerTracer(Tracer)}.
 *
 * <p>The {@link Tracing} singleton is also an entry point used to create the root span for any
 * tests by invoking the {@link #beginSpan(String)} method. The returned {@link Span} object must be
 * properly closed by invoking the {@link Span#close()} method or wrapping them in a try-resource
 * block.
 */
@ExperimentalTestApi
public final class Tracing {
  private static final String TAG = Tracing.class.getSimpleName();
  private static final Tracing singleton = new Tracing();
  private final List<Tracer> tracers = Collections.synchronizedList(new ArrayList<>());

  private Tracing() {
    // The Android Tracing API only exists starting with JB MR2 (API 18).
    if (Build.VERSION.SDK_INT >= 18) {
      registerTracer(new AndroidXTracer());
    }
  }

  /**
   * Static getter for external access to the singleton. <br>
   * Preferred method for internal Espresso is getting this singleton via dagger injection.
   */
  @NonNull
  public static Tracing getInstance() {
    return singleton;
  }

  /**
   * Registers a new tracer.
   *
   * <p>Once a tracer is registered, it starts being invoked for new root {@link #beginSpan(String)}
   * calls. Existing in-flight spans do not invoke the new tracer.
   *
   * @param tracer A non-null Tracer object. A previously registered instance is ignored.
   */
  public void registerTracer(@NonNull Tracer tracer) {
    checkNotNull(tracer, "Tracer cannot be null.");
    if (tracers.contains(tracer)) {
      Log.w(TAG, "Tracer already present: " + tracer.getClass());
    } else {
      Log.i(TAG, "Tracer added: " + tracer.getClass());
      tracers.add(tracer);
    }
  }

  /**
   * Unregisters a tracer.
   *
   * <p>Once a tracer is unregistered, it will stop being invoked for any new root {@link
   * #beginSpan(String)} calls made or any new {@link Span#beginChildSpan(String)}. However the
   * tracer is still be called for any in-flight spans being closed.
   *
   * @param tracer A Tracer object. A null reference or non-registered instance is ignored.
   */
  public void unregisterTracer(Tracer tracer) {
    tracers.remove(tracer);
    Log.i(TAG, "Tracer removed: " + (tracer == null ? null : tracer.getClass()));
  }

  /**
   * Returns a new Span as a managed resource in a try{} block. {@link Span#close()} is
   * automatically called when the resource is released.
   *
   * @param name A non-null name describing this span.
   */
  @NonNull
  @MustBeClosed
  public Span beginSpan(@NonNull String name) {
    checkNotNull(name);
    Map<Tracer, Span> spans;
    synchronized (tracers) {
      spans = new HashMap<>(tracers.size());
      for (Tracer tracer : tracers) {
        spans.put(tracer, createUnmanagedSpan(tracer, name));
      }
    }
    return new TracerSpan(spans);
  }

  /**
   * TracerSpan is an internal implementation class of a parent span.
   *
   * <p>Each Tracer creates its own Span instance wrapping a specific trace event; TracerSpan keeps
   * track of which Tracer creates which Span. <br>
   * When creating child spans, only tracers involved in the original parent span are called if they
   * are still registered.
   */
  class TracerSpan implements Span {
    private final Map<Tracer, Span> spans;

    private TracerSpan(@NonNull Map<Tracer, Span> spans) {
      this.spans = spans;
    }

    /**
     * Creates a child span for any tracer that participated in the parent span if that tracer is
     * still registered.
     */
    @NonNull
    @Override
    public Span beginChildSpan(@NonNull String name) {
      checkNotNull(name);
      Map<Tracer, Span> childSpans;
      synchronized (tracers) {
        childSpans = new HashMap<>(tracers.size());
        for (Tracer tracer : tracers) {
          Span parentSpan = spans.get(tracer);
          if (parentSpan != null) {
            childSpans.put(tracer, createUnmanagedChildSpan(parentSpan, name));
          }
        }
      }

      return new TracerSpan(childSpans);
    }

    /** All spans are closed, even if the tracer has been unregistered in between. */
    @Override
    public void close() {
      for (Span span : spans.values()) {
        span.close();
      }
    }
  }

  /**
   * Internal method to create a Span that does not enforce try-resource usage. Caller <em>must</em>
   * manually call {@link Span#close()} later on the resource.
   */
  @SuppressWarnings("MustBeClosedChecker")
  private static Span createUnmanagedSpan(Tracer tracer, String name) {
    return tracer.beginSpan(name);
  }

  /**
   * Internal method to create a Span that does not enforce try-resource usage. Caller <em>must</em>
   * manually call {@link Span#close()} later on the resource.
   */
  @SuppressWarnings("MustBeClosedChecker")
  private static Span createUnmanagedChildSpan(Span parentSpan, String name) {
    return parentSpan.beginChildSpan(name);
  }
}