Merge pull request #13454 from yang-g/lockfree_event

Avoid calling dtor on grpc_fd before putting it in freelist
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 12e143c..11fc050 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1619,6 +1619,7 @@
   test/core/util/port.cc
   test/core/util/port_server_client.cc
   test/core/util/slice_splitter.cc
+  test/core/util/tracer_util.cc
   test/core/util/trickle_endpoint.cc
   src/core/lib/backoff/backoff.cc
   src/core/lib/channel/channel_args.cc
@@ -1884,6 +1885,7 @@
   test/core/util/port.cc
   test/core/util/port_server_client.cc
   test/core/util/slice_splitter.cc
+  test/core/util/tracer_util.cc
   test/core/util/trickle_endpoint.cc
   src/core/lib/backoff/backoff.cc
   src/core/lib/channel/channel_args.cc
diff --git a/Makefile b/Makefile
index 5db1163..fd8b35e 100644
--- a/Makefile
+++ b/Makefile
@@ -3606,6 +3606,7 @@
     test/core/util/port.cc \
     test/core/util/port_server_client.cc \
     test/core/util/slice_splitter.cc \
+    test/core/util/tracer_util.cc \
     test/core/util/trickle_endpoint.cc \
     src/core/lib/backoff/backoff.cc \
     src/core/lib/channel/channel_args.cc \
@@ -3861,6 +3862,7 @@
     test/core/util/port.cc \
     test/core/util/port_server_client.cc \
     test/core/util/slice_splitter.cc \
+    test/core/util/tracer_util.cc \
     test/core/util/trickle_endpoint.cc \
     src/core/lib/backoff/backoff.cc \
     src/core/lib/channel/channel_args.cc \
diff --git a/build.yaml b/build.yaml
index a82a783..9018e51 100644
--- a/build.yaml
+++ b/build.yaml
@@ -713,6 +713,7 @@
   - test/core/util/port.h
   - test/core/util/port_server_client.h
   - test/core/util/slice_splitter.h
+  - test/core/util/tracer_util.h
   - test/core/util/trickle_endpoint.h
   src:
   - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc
@@ -729,6 +730,7 @@
   - test/core/util/port.cc
   - test/core/util/port_server_client.cc
   - test/core/util/slice_splitter.cc
+  - test/core/util/tracer_util.cc
   - test/core/util/trickle_endpoint.cc
   deps:
   - gpr_test_util
diff --git a/grpc.gyp b/grpc.gyp
index de7af7b..fb15391 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -514,6 +514,7 @@
         'test/core/util/port.cc',
         'test/core/util/port_server_client.cc',
         'test/core/util/slice_splitter.cc',
+        'test/core/util/tracer_util.cc',
         'test/core/util/trickle_endpoint.cc',
         'src/core/lib/backoff/backoff.cc',
         'src/core/lib/channel/channel_args.cc',
@@ -721,6 +722,7 @@
         'test/core/util/port.cc',
         'test/core/util/port_server_client.cc',
         'test/core/util/slice_splitter.cc',
+        'test/core/util/tracer_util.cc',
         'test/core/util/trickle_endpoint.cc',
         'src/core/lib/backoff/backoff.cc',
         'src/core/lib/channel/channel_args.cc',
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index fb4bfc3..1906886 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -297,6 +297,27 @@
 #endif
 #endif /* GPR_NO_AUTODETECT_PLATFORM */
 
+/*
+ *  There are platforms for which TLS should not be used even though the
+ * compiler makes it seem like it's supported (Android NDK < r12b for example).
+ * This is primarily because of linker problems and toolchain misconfiguration:
+ * TLS isn't supported until NDK r12b per
+ * https://developer.android.com/ndk/downloads/revision_history.html
+ * Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in
+ * <android/ndk-version.h>. For NDK < r16, users should define these macros,
+ * e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11. */
+#if defined(__ANDROID__) && defined(__clang__) && defined(GPR_GCC_TLS)
+#if __has_include(<android/ndk-version.h>)
+#include <android/ndk-version.h>
+#endif /* __has_include(<android/ndk-version.h>) */
+#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \
+    defined(__NDK_MINOR__) &&                                               \
+    ((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))
+#undef GPR_GCC_TLS
+#define GPR_PTHREAD_TLS 1
+#endif
+#endif /*defined(__ANDROID__) && defined(__clang__) && defined(GPR_GCC_TLS) */
+
 #if defined(__has_include)
 #if __has_include(<atomic>)
 #define GRPC_HAS_CXX11_ATOMIC
diff --git a/src/core/ext/filters/client_channel/channel_connectivity.cc b/src/core/ext/filters/client_channel/channel_connectivity.cc
index d3627a2..7eaf5d9 100644
--- a/src/core/ext/filters/client_channel/channel_connectivity.cc
+++ b/src/core/ext/filters/client_channel/channel_connectivity.cc
@@ -122,7 +122,7 @@
   gpr_mu_lock(&w->mu);
 
   if (due_to_completion) {
-    if (GRPC_TRACER_ON(grpc_trace_operation_failures)) {
+    if (grpc_trace_operation_failures.enabled()) {
       GRPC_LOG_IF_ERROR("watch_completion_error", GRPC_ERROR_REF(error));
     }
     GRPC_ERROR_UNREF(error);
diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc
index 8b8d512..03c1b6f 100644
--- a/src/core/ext/filters/client_channel/client_channel.cc
+++ b/src/core/ext/filters/client_channel/client_channel.cc
@@ -56,8 +56,7 @@
 
 /* Client channel implementation */
 
-grpc_tracer_flag grpc_client_channel_trace =
-    GRPC_TRACER_INITIALIZER(false, "client_channel");
+grpc_core::TraceFlag grpc_client_channel_trace(false, "client_channel");
 
 /*************************************************************************
  * METHOD-CONFIG TABLE
@@ -248,7 +247,7 @@
                                          GRPC_ERROR_REF(error));
     }
   }
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p: setting connectivity state to %s", chand,
             grpc_connectivity_state_name(state));
   }
@@ -262,7 +261,7 @@
   grpc_connectivity_state publish_state = w->state;
   /* check if the notification is for the latest policy */
   if (w->lb_policy == w->chand->lb_policy) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p: lb_policy=%p state changed to %s", w->chand,
               w->lb_policy, grpc_connectivity_state_name(w->state));
     }
@@ -300,7 +299,7 @@
 
 static void start_resolving_locked(grpc_exec_ctx* exec_ctx,
                                    channel_data* chand) {
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p: starting name resolution", chand);
   }
   GPR_ASSERT(!chand->started_resolving);
@@ -373,7 +372,7 @@
 static void on_resolver_result_changed_locked(grpc_exec_ctx* exec_ctx,
                                               void* arg, grpc_error* error) {
   channel_data* chand = (channel_data*)arg;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p: got resolver result: error=%s", chand,
             grpc_error_string(error));
   }
@@ -483,7 +482,7 @@
     grpc_channel_args_destroy(exec_ctx, chand->resolver_result);
     chand->resolver_result = nullptr;
   }
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p: resolver result: lb_policy_name=\"%s\"%s, "
             "service_config=\"%s\"",
@@ -524,7 +523,7 @@
   if (new_lb_policy != nullptr || error != GRPC_ERROR_NONE ||
       chand->resolver == nullptr) {
     if (chand->lb_policy != nullptr) {
-      if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+      if (grpc_client_channel_trace.enabled()) {
         gpr_log(GPR_DEBUG, "chand=%p: unreffing lb_policy=%p", chand,
                 chand->lb_policy);
       }
@@ -538,11 +537,11 @@
   // Now that we've swapped out the relevant fields of chand, check for
   // error or shutdown.
   if (error != GRPC_ERROR_NONE || chand->resolver == nullptr) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p: shutting down", chand);
     }
     if (chand->resolver != nullptr) {
-      if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+      if (grpc_client_channel_trace.enabled()) {
         gpr_log(GPR_DEBUG, "chand=%p: shutting down resolver", chand);
       }
       grpc_resolver_shutdown_locked(exec_ctx, chand->resolver);
@@ -565,7 +564,7 @@
     grpc_error* state_error =
         GRPC_ERROR_CREATE_FROM_STATIC_STRING("No load balancing policy");
     if (new_lb_policy != nullptr) {
-      if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+      if (grpc_client_channel_trace.enabled()) {
         gpr_log(GPR_DEBUG, "chand=%p: initializing new LB policy", chand);
       }
       GRPC_ERROR_UNREF(state_error);
@@ -899,7 +898,7 @@
                                           grpc_call_element* elem,
                                           grpc_error* error) {
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p calld=%p: failing %" PRIuPTR " pending batches: %s",
             elem->channel_data, calld, calld->waiting_for_pick_batches_count,
@@ -942,7 +941,7 @@
                                             grpc_call_element* elem) {
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p calld=%p: sending %" PRIuPTR
             " pending batches to subchannel_call=%p",
@@ -969,7 +968,7 @@
                                                 grpc_call_element* elem) {
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p calld=%p: applying service config to call",
             chand, calld);
   }
@@ -1015,7 +1014,7 @@
   grpc_error* new_error = grpc_connected_subchannel_create_call(
       exec_ctx, calld->connected_subchannel, &call_args,
       &calld->subchannel_call);
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p calld=%p: create subchannel_call=%p: error=%s",
             chand, calld, calld->subchannel_call, grpc_error_string(new_error));
   }
@@ -1041,7 +1040,7 @@
                              "Call dropped by load balancing policy")
                        : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
                              "Failed to create subchannel", &error, 1);
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "chand=%p calld=%p: failed to create subchannel: error=%s", chand,
               calld, grpc_error_string(calld->error));
@@ -1075,7 +1074,7 @@
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
   if (calld->lb_policy != nullptr) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: cancelling pick from LB policy %p",
               chand, calld, calld->lb_policy);
     }
@@ -1093,7 +1092,7 @@
   grpc_call_element* elem = (grpc_call_element*)arg;
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed asynchronously",
             chand, calld);
   }
@@ -1110,7 +1109,7 @@
                                        grpc_call_element* elem) {
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting pick on lb_policy=%p",
             chand, calld, chand->lb_policy);
   }
@@ -1148,7 +1147,7 @@
       calld->subchannel_call_context, nullptr, &calld->lb_pick_closure);
   if (pick_done) {
     /* synchronous grpc_lb_policy_pick call. Unref the LB policy. */
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed synchronously",
               chand, calld);
     }
@@ -1193,7 +1192,7 @@
   grpc_call_element* elem = args->elem;
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p calld=%p: cancelling pick waiting for resolver result",
             chand, calld);
@@ -1217,7 +1216,7 @@
   pick_after_resolver_result_args* args = (pick_after_resolver_result_args*)arg;
   if (args->finished) {
     /* cancelled, do nothing */
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "call cancelled before resolver result");
     }
     gpr_free(args);
@@ -1228,13 +1227,13 @@
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
   if (error != GRPC_ERROR_NONE) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver failed to return data",
               chand, calld);
     }
     async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_REF(error));
   } else if (chand->lb_policy != nullptr) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver returned, doing pick",
               chand, calld);
     }
@@ -1256,7 +1255,7 @@
   // right way to deal with it.
   else if (chand->resolver != nullptr) {
     // No LB policy, so try again.
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "chand=%p calld=%p: resolver returned but no LB policy, "
               "trying again",
@@ -1264,7 +1263,7 @@
     }
     pick_after_resolver_result_start_locked(exec_ctx, elem);
   } else {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver disconnected", chand,
               calld);
     }
@@ -1277,7 +1276,7 @@
                                                     grpc_call_element* elem) {
   channel_data* chand = (channel_data*)elem->channel_data;
   call_data* calld = (call_data*)elem->call_data;
-  if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+  if (grpc_client_channel_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "chand=%p calld=%p: deferring pick pending resolver result", chand,
             calld);
@@ -1362,7 +1361,7 @@
   GPR_TIMER_BEGIN("cc_start_transport_stream_op_batch", 0);
   // If we've previously been cancelled, immediately fail any new batches.
   if (calld->error != GRPC_ERROR_NONE) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: failing batch with error: %s",
               chand, calld, grpc_error_string(calld->error));
     }
@@ -1378,7 +1377,7 @@
     // error to the caller when the first batch does get passed down.
     GRPC_ERROR_UNREF(calld->error);
     calld->error = GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error);
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: recording cancel_error=%s", chand,
               calld, grpc_error_string(calld->error));
     }
@@ -1407,7 +1406,7 @@
   // the channel combiner, which is more efficient (especially for
   // streaming calls).
   if (calld->subchannel_call != nullptr) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "chand=%p calld=%p: sending batch to subchannel_call=%p", chand,
               calld, calld->subchannel_call);
@@ -1421,7 +1420,7 @@
   // For batches containing a send_initial_metadata op, enter the channel
   // combiner to start a pick.
   if (batch->send_initial_metadata) {
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG, "chand=%p calld=%p: entering client_channel combiner",
               chand, calld);
     }
@@ -1432,7 +1431,7 @@
         GRPC_ERROR_NONE);
   } else {
     // For all other batches, release the call combiner.
-    if (GRPC_TRACER_ON(grpc_client_channel_trace)) {
+    if (grpc_client_channel_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "chand=%p calld=%p: saved batch, yeilding call combiner", chand,
               calld);
diff --git a/src/core/ext/filters/client_channel/client_channel.h b/src/core/ext/filters/client_channel/client_channel.h
index 27862cf..f58a8c1 100644
--- a/src/core/ext/filters/client_channel/client_channel.h
+++ b/src/core/ext/filters/client_channel/client_channel.h
@@ -23,7 +23,7 @@
 #include "src/core/ext/filters/client_channel/resolver.h"
 #include "src/core/lib/channel/channel_stack.h"
 
-extern grpc_tracer_flag grpc_client_channel_trace;
+extern grpc_core::TraceFlag grpc_client_channel_trace;
 
 // Channel arg key for server URI string.
 #define GRPC_ARG_SERVER_URI "grpc.server_uri"
diff --git a/src/core/ext/filters/client_channel/client_channel_plugin.cc b/src/core/ext/filters/client_channel/client_channel_plugin.cc
index eebef68..c1b57d0 100644
--- a/src/core/ext/filters/client_channel/client_channel_plugin.cc
+++ b/src/core/ext/filters/client_channel/client_channel_plugin.cc
@@ -78,10 +78,6 @@
       GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, append_filter,
       (void*)&grpc_client_channel_filter);
   grpc_http_connect_register_handshaker_factory();
-  grpc_register_tracer(&grpc_client_channel_trace);
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_resolver_refcount);
-#endif
 }
 
 extern "C" void grpc_client_channel_shutdown(void) {
diff --git a/src/core/ext/filters/client_channel/lb_policy.cc b/src/core/ext/filters/client_channel/lb_policy.cc
index 387c26e..6276c3e 100644
--- a/src/core/ext/filters/client_channel/lb_policy.cc
+++ b/src/core/ext/filters/client_channel/lb_policy.cc
@@ -21,10 +21,8 @@
 
 #define WEAK_REF_BITS 16
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_lb_policy_refcount =
-    GRPC_TRACER_INITIALIZER(false, "lb_policy_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount(
+    false, "lb_policy_refcount");
 
 void grpc_lb_policy_init(grpc_lb_policy* policy,
                          const grpc_lb_policy_vtable* vtable,
@@ -52,7 +50,7 @@
   gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta)
                             : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_lb_policy_refcount)) {
+  if (grpc_trace_lb_policy_refcount.enabled()) {
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "LB_POLICY: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c,
             purpose, old_val, old_val + delta, reason);
diff --git a/src/core/ext/filters/client_channel/lb_policy.h b/src/core/ext/filters/client_channel/lb_policy.h
index 590094e..cd40b4d 100644
--- a/src/core/ext/filters/client_channel/lb_policy.h
+++ b/src/core/ext/filters/client_channel/lb_policy.h
@@ -33,9 +33,7 @@
 typedef struct grpc_lb_policy_vtable grpc_lb_policy_vtable;
 typedef struct grpc_lb_policy_args grpc_lb_policy_args;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_lb_policy_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount;
 
 struct grpc_lb_policy {
   const grpc_lb_policy_vtable* vtable;
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
index ad9a2bb..5fb502e 100644
--- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
@@ -126,7 +126,7 @@
 #define GRPC_GRPCLB_RECONNECT_JITTER 0.2
 #define GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS 10000
 
-grpc_tracer_flag grpc_lb_glb_trace = GRPC_TRACER_INITIALIZER(false, "glb");
+grpc_core::TraceFlag grpc_lb_glb_trace(false, "glb");
 
 /* add lb_token of selected subchannel (address) to the call's initial
  * metadata */
@@ -217,7 +217,7 @@
     } else {
       grpc_grpclb_client_stats_unref(wc_arg->client_stats);
     }
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO, "[grpclb %p] Unreffing RR %p", wc_arg->glb_policy,
               wc_arg->rr_policy);
     }
@@ -623,7 +623,7 @@
       GPR_ASSERT(rr_state_error == GRPC_ERROR_NONE);
   }
 
-  if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+  if (grpc_lb_glb_trace.enabled()) {
     gpr_log(
         GPR_INFO,
         "[grpclb %p] Setting grpclb's state to %s from new RR policy %p state.",
@@ -654,7 +654,7 @@
     }
     if (server->drop) {
       // Not using the RR policy, so unref it.
-      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO, "[grpclb %p] Unreffing RR %p for drop", glb_policy,
                 wc_arg->rr_policy);
       }
@@ -684,7 +684,7 @@
       (void**)&wc_arg->lb_token, &wc_arg->wrapper_closure);
   if (pick_done) {
     /* synchronous grpc_lb_policy_pick call. Unref the RR policy. */
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO, "[grpclb %p] Unreffing RR %p", glb_policy,
               wc_arg->rr_policy);
     }
@@ -806,7 +806,7 @@
     pp->wrapped_on_complete_arg.rr_policy = glb_policy->rr_policy;
     pp->wrapped_on_complete_arg.client_stats =
         grpc_grpclb_client_stats_ref(glb_policy->client_stats);
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO,
               "[grpclb %p] Pending pick about to (async) PICK from RR %p",
               glb_policy, glb_policy->rr_policy);
@@ -821,7 +821,7 @@
     glb_policy->pending_pings = pping->next;
     GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_ping");
     pping->wrapped_notify_arg.rr_policy = glb_policy->rr_policy;
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO, "[grpclb %p] Pending ping about to PING from RR %p",
               glb_policy, glb_policy->rr_policy);
     }
@@ -837,14 +837,14 @@
   grpc_lb_policy_args* args = lb_policy_args_create(exec_ctx, glb_policy);
   GPR_ASSERT(args != nullptr);
   if (glb_policy->rr_policy != nullptr) {
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_DEBUG, "[grpclb %p] Updating RR policy %p", glb_policy,
               glb_policy->rr_policy);
     }
     grpc_lb_policy_update_locked(exec_ctx, glb_policy->rr_policy, args);
   } else {
     create_rr_locked(exec_ctx, glb_policy, args);
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_DEBUG, "[grpclb %p] Created new RR policy %p", glb_policy,
               glb_policy->rr_policy);
     }
@@ -1186,7 +1186,7 @@
     // need to make sure we aren't trying to pick from a RR policy instance
     // that's in shutdown.
     if (rr_connectivity_state == GRPC_CHANNEL_SHUTDOWN) {
-      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "[grpclb %p] NOT picking from from RR %p: RR conn state=%s",
                 glb_policy, glb_policy->rr_policy,
@@ -1196,7 +1196,7 @@
                        on_complete);
       pick_done = false;
     } else {  // RR not in shutdown
-      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO, "[grpclb %p] about to PICK from RR %p", glb_policy,
                 glb_policy->rr_policy);
       }
@@ -1221,7 +1221,7 @@
                                        false /* force_async */, target, wc_arg);
     }
   } else {  // glb_policy->rr_policy == NULL
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "[grpclb %p] No RR policy. Adding to grpclb's pending picks",
               glb_policy);
@@ -1272,7 +1272,7 @@
   glb_policy->retry_timer_active = false;
   if (!glb_policy->shutting_down && glb_policy->lb_call == nullptr &&
       error == GRPC_ERROR_NONE) {
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_INFO, "[grpclb %p] Restarting call to LB server", glb_policy);
     }
     query_for_backends_locked(exec_ctx, glb_policy);
@@ -1293,7 +1293,7 @@
     grpc_millis next_try =
         grpc_backoff_step(exec_ctx, &glb_policy->lb_call_backoff_state)
             .next_attempt_start_time;
-    if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+    if (grpc_lb_glb_trace.enabled()) {
       gpr_log(GPR_DEBUG, "[grpclb %p] Connection to LB server lost...",
               glb_policy);
       grpc_millis timeout = next_try - grpc_exec_ctx_now(exec_ctx);
@@ -1499,7 +1499,7 @@
 
   lb_call_init_locked(exec_ctx, glb_policy);
 
-  if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+  if (grpc_lb_glb_trace.enabled()) {
     gpr_log(GPR_INFO,
             "[grpclb %p] Query for backends (lb_channel: %p, lb_call: %p)",
             glb_policy, glb_policy->lb_channel, glb_policy->lb_call);
@@ -1590,7 +1590,7 @@
         glb_policy->client_stats_report_interval = GPR_MAX(
             GPR_MS_PER_SEC, grpc_grpclb_duration_to_millis(
                                 &response->client_stats_report_interval));
-        if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+        if (grpc_lb_glb_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "[grpclb %p] Received initial LB response message; "
                   "client load reporting interval = %" PRIdPTR " milliseconds",
@@ -1602,7 +1602,7 @@
         glb_policy->client_load_report_timer_pending = true;
         GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "client_load_report");
         schedule_next_client_load_report(exec_ctx, glb_policy);
-      } else if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      } else if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "[grpclb %p] Received initial LB response message; client load "
                 "reporting NOT enabled",
@@ -1615,7 +1615,7 @@
           grpc_grpclb_response_parse_serverlist(response_slice);
       if (serverlist != nullptr) {
         GPR_ASSERT(glb_policy->lb_call != nullptr);
-        if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+        if (grpc_lb_glb_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "[grpclb %p] Serverlist with %" PRIuPTR " servers received",
                   glb_policy, serverlist->num_servers);
@@ -1633,7 +1633,7 @@
         if (serverlist->num_servers > 0) {
           if (grpc_grpclb_serverlist_equals(glb_policy->serverlist,
                                             serverlist)) {
-            if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+            if (grpc_lb_glb_trace.enabled()) {
               gpr_log(GPR_INFO,
                       "[grpclb %p] Incoming server list identical to current, "
                       "ignoring.",
@@ -1662,7 +1662,7 @@
             rr_handover_locked(exec_ctx, glb_policy);
           }
         } else {
-          if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+          if (grpc_lb_glb_trace.enabled()) {
             gpr_log(GPR_INFO,
                     "[grpclb %p] Received empty server list, ignoring.",
                     glb_policy);
@@ -1710,7 +1710,7 @@
    * actually runs, don't fall back. */
   if (glb_policy->serverlist == nullptr) {
     if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) {
-      if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+      if (grpc_lb_glb_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "[grpclb %p] Falling back to use backends from resolver",
                 glb_policy);
@@ -1727,7 +1727,7 @@
                                                 void* arg, grpc_error* error) {
   glb_lb_policy* glb_policy = (glb_lb_policy*)arg;
   GPR_ASSERT(glb_policy->lb_call != nullptr);
-  if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+  if (grpc_lb_glb_trace.enabled()) {
     char* status_details =
         grpc_slice_to_c_string(glb_policy->lb_call_status_details);
     gpr_log(GPR_INFO,
@@ -1909,7 +1909,7 @@
   GPR_ASSERT(uri->path[0] != '\0');
   glb_policy->server_name =
       gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path);
-  if (GRPC_TRACER_ON(grpc_lb_glb_trace)) {
+  if (grpc_lb_glb_trace.enabled()) {
     gpr_log(GPR_INFO,
             "[grpclb %p] Will use '%s' as the server name for LB request.",
             glb_policy, glb_policy->server_name);
@@ -2003,10 +2003,6 @@
 
 extern "C" void grpc_lb_policy_grpclb_init() {
   grpc_register_lb_policy(grpc_glb_lb_factory_create());
-  grpc_register_tracer(&grpc_lb_glb_trace);
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_lb_policy_refcount);
-#endif
   grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
                                    GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
                                    maybe_add_client_load_reporting_filter,
diff --git a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
index dbd4754..b15ca82 100644
--- a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc
@@ -29,8 +29,7 @@
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/transport/connectivity_state.h"
 
-grpc_tracer_flag grpc_lb_pick_first_trace =
-    GRPC_TRACER_INITIALIZER(false, "pick_first");
+grpc_core::TraceFlag grpc_lb_pick_first_trace(false, "pick_first");
 
 typedef struct pending_pick {
   struct pending_pick* next;
@@ -66,14 +65,14 @@
   grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker);
   gpr_free(p);
   grpc_subchannel_index_unref();
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Pick First %p destroyed.", (void*)p);
   }
 }
 
 static void shutdown_locked(grpc_exec_ctx* exec_ctx, pick_first_lb_policy* p,
                             grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Pick First %p Shutting down", p);
   }
   p->shutdown = true;
@@ -261,7 +260,7 @@
   }
   const grpc_lb_addresses* addresses =
       (const grpc_lb_addresses*)arg->value.pointer.p;
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_INFO, "Pick First %p received update with %lu addresses",
             (void*)p, (unsigned long)addresses->num_addresses);
   }
@@ -298,7 +297,7 @@
       grpc_lb_subchannel_data* sd = &subchannel_list->subchannels[i];
       if (sd->subchannel == p->selected->subchannel) {
         // The currently selected subchannel is in the update: we are done.
-        if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+        if (grpc_lb_pick_first_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "Pick First %p found already selected subchannel %p "
                   "at update index %" PRIuPTR " of %" PRIuPTR "; update done",
@@ -336,7 +335,7 @@
     // for it to report READY before swapping it into the current
     // subchannel list.
     if (p->latest_pending_subchannel_list != nullptr) {
-      if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+      if (grpc_lb_pick_first_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "Pick First %p Shutting down latest pending subchannel list "
                 "%p, about to be replaced by newer latest %p",
@@ -363,7 +362,7 @@
                                            grpc_error* error) {
   grpc_lb_subchannel_data* sd = (grpc_lb_subchannel_data*)arg;
   pick_first_lb_policy* p = (pick_first_lb_policy*)sd->subchannel_list->policy;
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "Pick First %p connectivity changed for subchannel %p (%" PRIuPTR
             " of %" PRIuPTR
@@ -460,7 +459,7 @@
           grpc_subchannel_get_connected_subchannel(sd->subchannel),
           "connected");
       p->selected = sd;
-      if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+      if (grpc_lb_pick_first_trace.enabled()) {
         gpr_log(GPR_INFO, "Pick First %p selected subchannel %p", (void*)p,
                 (void*)sd->subchannel);
       }
@@ -472,7 +471,7 @@
         p->pending_picks = pp->next;
         *pp->target = GRPC_CONNECTED_SUBCHANNEL_REF(
             p->selected->connected_subchannel, "picked");
-        if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+        if (grpc_lb_pick_first_trace.enabled()) {
           gpr_log(GPR_INFO,
                   "Servicing pending pick with selected subchannel %p",
                   (void*)p->selected);
@@ -571,7 +570,7 @@
                                          grpc_lb_policy_args* args) {
   GPR_ASSERT(args->client_channel_factory != nullptr);
   pick_first_lb_policy* p = (pick_first_lb_policy*)gpr_zalloc(sizeof(*p));
-  if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) {
+  if (grpc_lb_pick_first_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Pick First %p created.", (void*)p);
   }
   pf_update_locked(exec_ctx, &p->base, args);
@@ -595,7 +594,6 @@
 
 extern "C" void grpc_lb_policy_pick_first_init() {
   grpc_register_lb_policy(pick_first_lb_factory_create());
-  grpc_register_tracer(&grpc_lb_pick_first_trace);
 }
 
 extern "C" void grpc_lb_policy_pick_first_shutdown() {}
diff --git a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
index 6ea1f02..5e54d1f 100644
--- a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc
@@ -39,8 +39,7 @@
 #include "src/core/lib/transport/connectivity_state.h"
 #include "src/core/lib/transport/static_metadata.h"
 
-grpc_tracer_flag grpc_lb_round_robin_trace =
-    GRPC_TRACER_INITIALIZER(false, "round_robin");
+grpc_core::TraceFlag grpc_lb_round_robin_trace(false, "round_robin");
 
 /** List of entities waiting for a pick.
  *
@@ -101,7 +100,7 @@
 static size_t get_next_ready_subchannel_index_locked(
     const round_robin_lb_policy* p) {
   GPR_ASSERT(p->subchannel_list != nullptr);
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_INFO,
             "[RR %p] getting next ready subchannel (out of %lu), "
             "last_ready_subchannel_index=%lu",
@@ -111,7 +110,7 @@
   for (size_t i = 0; i < p->subchannel_list->num_subchannels; ++i) {
     const size_t index = (i + p->last_ready_subchannel_index + 1) %
                          p->subchannel_list->num_subchannels;
-    if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+    if (grpc_lb_round_robin_trace.enabled()) {
       gpr_log(
           GPR_DEBUG,
           "[RR %p] checking subchannel %p, subchannel_list %p, index %lu: "
@@ -123,7 +122,7 @@
     }
     if (p->subchannel_list->subchannels[index].curr_connectivity_state ==
         GRPC_CHANNEL_READY) {
-      if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+      if (grpc_lb_round_robin_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "[RR %p] found next ready subchannel (%p) at index %lu of "
                 "subchannel_list %p",
@@ -134,7 +133,7 @@
       return index;
     }
   }
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] no subchannels in ready state", (void*)p);
   }
   return p->subchannel_list->num_subchannels;
@@ -145,7 +144,7 @@
                                                       size_t last_ready_index) {
   GPR_ASSERT(last_ready_index < p->subchannel_list->num_subchannels);
   p->last_ready_subchannel_index = last_ready_index;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "[RR %p] setting last_ready_subchannel_index=%lu (SC %p, CSC %p)",
             (void*)p, (unsigned long)last_ready_index,
@@ -157,7 +156,7 @@
 
 static void rr_destroy(grpc_exec_ctx* exec_ctx, grpc_lb_policy* pol) {
   round_robin_lb_policy* p = (round_robin_lb_policy*)pol;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] Destroying Round Robin policy at %p",
             (void*)pol, (void*)pol);
   }
@@ -170,7 +169,7 @@
 
 static void shutdown_locked(grpc_exec_ctx* exec_ctx, round_robin_lb_policy* p,
                             grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] Shutting down", p);
   }
   p->shutdown = true;
@@ -276,7 +275,7 @@
                           grpc_call_context_element* context, void** user_data,
                           grpc_closure* on_complete) {
   round_robin_lb_policy* p = (round_robin_lb_policy*)pol;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_INFO, "[RR %p] Trying to pick (shutdown: %d)", (void*)pol,
             p->shutdown);
   }
@@ -292,7 +291,7 @@
       if (user_data != nullptr) {
         *user_data = sd->user_data;
       }
-      if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+      if (grpc_lb_round_robin_trace.enabled()) {
         gpr_log(
             GPR_DEBUG,
             "[RR %p] Picked target <-- Subchannel %p (connected %p) (sl %p, "
@@ -393,7 +392,7 @@
                                 "rr_shutdown");
     p->shutdown = true;
     new_state = GRPC_CHANNEL_SHUTDOWN;
-    if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+    if (grpc_lb_round_robin_trace.enabled()) {
       gpr_log(GPR_INFO,
               "[RR %p] Shutting down: all subchannels have gone into shutdown",
               (void*)p);
@@ -419,7 +418,7 @@
   grpc_lb_subchannel_data* sd = (grpc_lb_subchannel_data*)arg;
   round_robin_lb_policy* p =
       (round_robin_lb_policy*)sd->subchannel_list->policy;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(
         GPR_DEBUG,
         "[RR %p] connectivity changed for subchannel %p, subchannel_list %p: "
@@ -484,7 +483,7 @@
         // for sds belonging to outdated subchannel lists.
         GPR_ASSERT(sd->subchannel_list == p->latest_pending_subchannel_list);
         GPR_ASSERT(!sd->subchannel_list->shutting_down);
-        if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+        if (grpc_lb_round_robin_trace.enabled()) {
           const unsigned long num_subchannels =
               p->subchannel_list != nullptr
                   ? (unsigned long)p->subchannel_list->num_subchannels
@@ -523,7 +522,7 @@
         if (pp->user_data != nullptr) {
           *pp->user_data = selected->user_data;
         }
-        if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+        if (grpc_lb_round_robin_trace.enabled()) {
           gpr_log(GPR_DEBUG,
                   "[RR %p] Fulfilling pending pick. Target <-- subchannel %p "
                   "(subchannel_list %p, index %lu)",
@@ -590,7 +589,7 @@
     return;
   }
   grpc_lb_addresses* addresses = (grpc_lb_addresses*)arg->value.pointer.p;
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] received update with %" PRIuPTR " addresses", p,
             addresses->num_addresses);
   }
@@ -611,7 +610,7 @@
   }
   if (p->started_picking) {
     if (p->latest_pending_subchannel_list != nullptr) {
-      if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+      if (grpc_lb_round_robin_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "[RR %p] Shutting down latest pending subchannel list %p, "
                 "about to be replaced by newer latest %p",
@@ -669,7 +668,7 @@
   grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE,
                                "round_robin");
   rr_update_locked(exec_ctx, &p->base, args);
-  if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) {
+  if (grpc_lb_round_robin_trace.enabled()) {
     gpr_log(GPR_DEBUG, "[RR %p] Created with %lu subchannels", (void*)p,
             (unsigned long)p->subchannel_list->num_subchannels);
   }
@@ -691,7 +690,6 @@
 
 extern "C" void grpc_lb_policy_round_robin_init() {
   grpc_register_lb_policy(round_robin_lb_factory_create());
-  grpc_register_tracer(&grpc_lb_round_robin_trace);
 }
 
 extern "C" void grpc_lb_policy_round_robin_shutdown() {}
diff --git a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
index 27d9598..b6fce4d 100644
--- a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
+++ b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc
@@ -32,11 +32,11 @@
                                               grpc_lb_subchannel_data* sd,
                                               const char* reason) {
   if (sd->subchannel != nullptr) {
-    if (GRPC_TRACER_ON(*sd->subchannel_list->tracer)) {
+    if (sd->subchannel_list->tracer->enabled()) {
       gpr_log(GPR_DEBUG,
               "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
               " (subchannel %p): unreffing subchannel",
-              sd->subchannel_list->tracer->name, sd->subchannel_list->policy,
+              sd->subchannel_list->tracer->name(), sd->subchannel_list->policy,
               sd->subchannel_list,
               (size_t)(sd - sd->subchannel_list->subchannels),
               sd->subchannel_list->num_subchannels, sd->subchannel);
@@ -58,11 +58,11 @@
 
 void grpc_lb_subchannel_data_start_connectivity_watch(
     grpc_exec_ctx* exec_ctx, grpc_lb_subchannel_data* sd) {
-  if (GRPC_TRACER_ON(*sd->subchannel_list->tracer)) {
+  if (sd->subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): requesting connectivity change notification",
-            sd->subchannel_list->tracer->name, sd->subchannel_list->policy,
+            sd->subchannel_list->tracer->name(), sd->subchannel_list->policy,
             sd->subchannel_list,
             (size_t)(sd - sd->subchannel_list->subchannels),
             sd->subchannel_list->num_subchannels, sd->subchannel);
@@ -76,11 +76,11 @@
 
 void grpc_lb_subchannel_data_stop_connectivity_watch(
     grpc_exec_ctx* exec_ctx, grpc_lb_subchannel_data* sd) {
-  if (GRPC_TRACER_ON(*sd->subchannel_list->tracer)) {
+  if (sd->subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): stopping connectivity watch",
-            sd->subchannel_list->tracer->name, sd->subchannel_list->policy,
+            sd->subchannel_list->tracer->name(), sd->subchannel_list->policy,
             sd->subchannel_list,
             (size_t)(sd - sd->subchannel_list->subchannels),
             sd->subchannel_list->num_subchannels, sd->subchannel);
@@ -90,15 +90,15 @@
 }
 
 grpc_lb_subchannel_list* grpc_lb_subchannel_list_create(
-    grpc_exec_ctx* exec_ctx, grpc_lb_policy* p, grpc_tracer_flag* tracer,
+    grpc_exec_ctx* exec_ctx, grpc_lb_policy* p, grpc_core::TraceFlag* tracer,
     const grpc_lb_addresses* addresses, const grpc_lb_policy_args* args,
     grpc_iomgr_cb_func connectivity_changed_cb) {
   grpc_lb_subchannel_list* subchannel_list =
       (grpc_lb_subchannel_list*)gpr_zalloc(sizeof(*subchannel_list));
-  if (GRPC_TRACER_ON(*tracer)) {
+  if (tracer->enabled()) {
     gpr_log(GPR_DEBUG,
             "[%s %p] Creating subchannel list %p for %" PRIuPTR " subchannels",
-            tracer->name, p, subchannel_list, addresses->num_addresses);
+            tracer->name(), p, subchannel_list, addresses->num_addresses);
   }
   subchannel_list->policy = p;
   subchannel_list->tracer = tracer;
@@ -128,24 +128,24 @@
     grpc_channel_args_destroy(exec_ctx, new_args);
     if (subchannel == nullptr) {
       // Subchannel could not be created.
-      if (GRPC_TRACER_ON(*tracer)) {
+      if (tracer->enabled()) {
         char* address_uri =
             grpc_sockaddr_to_uri(&addresses->addresses[i].address);
         gpr_log(GPR_DEBUG,
                 "[%s %p] could not create subchannel for address uri %s, "
                 "ignoring",
-                tracer->name, subchannel_list->policy, address_uri);
+                tracer->name(), subchannel_list->policy, address_uri);
         gpr_free(address_uri);
       }
       continue;
     }
-    if (GRPC_TRACER_ON(*tracer)) {
+    if (tracer->enabled()) {
       char* address_uri =
           grpc_sockaddr_to_uri(&addresses->addresses[i].address);
       gpr_log(GPR_DEBUG,
               "[%s %p] subchannel list %p index %" PRIuPTR
               ": Created subchannel %p for address uri %s",
-              tracer->name, p, subchannel_list, subchannel_index, subchannel,
+              tracer->name(), p, subchannel_list, subchannel_index, subchannel,
               address_uri);
       gpr_free(address_uri);
     }
@@ -174,9 +174,9 @@
 
 static void subchannel_list_destroy(grpc_exec_ctx* exec_ctx,
                                     grpc_lb_subchannel_list* subchannel_list) {
-  if (GRPC_TRACER_ON(*subchannel_list->tracer)) {
+  if (subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG, "[%s %p] Destroying subchannel_list %p",
-            subchannel_list->tracer->name, subchannel_list->policy,
+            subchannel_list->tracer->name(), subchannel_list->policy,
             subchannel_list);
   }
   for (size_t i = 0; i < subchannel_list->num_subchannels; i++) {
@@ -191,10 +191,10 @@
 void grpc_lb_subchannel_list_ref(grpc_lb_subchannel_list* subchannel_list,
                                  const char* reason) {
   gpr_ref_non_zero(&subchannel_list->refcount);
-  if (GRPC_TRACER_ON(*subchannel_list->tracer)) {
+  if (subchannel_list->tracer->enabled()) {
     const gpr_atm count = gpr_atm_acq_load(&subchannel_list->refcount.count);
     gpr_log(GPR_DEBUG, "[%s %p] subchannel_list %p REF %lu->%lu (%s)",
-            subchannel_list->tracer->name, subchannel_list->policy,
+            subchannel_list->tracer->name(), subchannel_list->policy,
             subchannel_list, (unsigned long)(count - 1), (unsigned long)count,
             reason);
   }
@@ -204,10 +204,10 @@
                                    grpc_lb_subchannel_list* subchannel_list,
                                    const char* reason) {
   const bool done = gpr_unref(&subchannel_list->refcount);
-  if (GRPC_TRACER_ON(*subchannel_list->tracer)) {
+  if (subchannel_list->tracer->enabled()) {
     const gpr_atm count = gpr_atm_acq_load(&subchannel_list->refcount.count);
     gpr_log(GPR_DEBUG, "[%s %p] subchannel_list %p UNREF %lu->%lu (%s)",
-            subchannel_list->tracer->name, subchannel_list->policy,
+            subchannel_list->tracer->name(), subchannel_list->policy,
             subchannel_list, (unsigned long)(count + 1), (unsigned long)count,
             reason);
   }
@@ -231,11 +231,11 @@
 
 static void subchannel_data_cancel_connectivity_watch(
     grpc_exec_ctx* exec_ctx, grpc_lb_subchannel_data* sd, const char* reason) {
-  if (GRPC_TRACER_ON(*sd->subchannel_list->tracer)) {
+  if (sd->subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG,
             "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR
             " (subchannel %p): canceling connectivity watch (%s)",
-            sd->subchannel_list->tracer->name, sd->subchannel_list->policy,
+            sd->subchannel_list->tracer->name(), sd->subchannel_list->policy,
             sd->subchannel_list,
             (size_t)(sd - sd->subchannel_list->subchannels),
             sd->subchannel_list->num_subchannels, sd->subchannel, reason);
@@ -248,9 +248,9 @@
 void grpc_lb_subchannel_list_shutdown_and_unref(
     grpc_exec_ctx* exec_ctx, grpc_lb_subchannel_list* subchannel_list,
     const char* reason) {
-  if (GRPC_TRACER_ON(*subchannel_list->tracer)) {
+  if (subchannel_list->tracer->enabled()) {
     gpr_log(GPR_DEBUG, "[%s %p] Shutting down subchannel_list %p (%s)",
-            subchannel_list->tracer->name, subchannel_list->policy,
+            subchannel_list->tracer->name(), subchannel_list->policy,
             subchannel_list, reason);
   }
   GPR_ASSERT(!subchannel_list->shutting_down);
diff --git a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
index e18ad49..6538bd0 100644
--- a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
+++ b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h
@@ -88,7 +88,7 @@
   /** backpointer to owning policy */
   grpc_lb_policy* policy;
 
-  grpc_tracer_flag* tracer;
+  grpc_core::TraceFlag* tracer;
 
   /** all our subchannels */
   size_t num_subchannels;
@@ -121,7 +121,7 @@
 };
 
 grpc_lb_subchannel_list* grpc_lb_subchannel_list_create(
-    grpc_exec_ctx* exec_ctx, grpc_lb_policy* p, grpc_tracer_flag* tracer,
+    grpc_exec_ctx* exec_ctx, grpc_lb_policy* p, grpc_core::TraceFlag* tracer,
     const grpc_lb_addresses* addresses, const grpc_lb_policy_args* args,
     grpc_iomgr_cb_func connectivity_changed_cb);
 
diff --git a/src/core/ext/filters/client_channel/resolver.cc b/src/core/ext/filters/client_channel/resolver.cc
index 7e84b98..c16b151 100644
--- a/src/core/ext/filters/client_channel/resolver.cc
+++ b/src/core/ext/filters/client_channel/resolver.cc
@@ -19,10 +19,8 @@
 #include "src/core/ext/filters/client_channel/resolver.h"
 #include "src/core/lib/iomgr/combiner.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_resolver_refcount =
-    GRPC_TRACER_INITIALIZER(false, "resolver_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount(false,
+                                                           "resolver_refcount");
 
 void grpc_resolver_init(grpc_resolver* resolver,
                         const grpc_resolver_vtable* vtable,
@@ -35,7 +33,7 @@
 #ifndef NDEBUG
 void grpc_resolver_ref(grpc_resolver* resolver, const char* file, int line,
                        const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_resolver_refcount)) {
+  if (grpc_trace_resolver_refcount.enabled()) {
     gpr_atm old_refs = gpr_atm_no_barrier_load(&resolver->refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "RESOLVER:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", resolver,
@@ -50,7 +48,7 @@
 #ifndef NDEBUG
 void grpc_resolver_unref(grpc_exec_ctx* exec_ctx, grpc_resolver* resolver,
                          const char* file, int line, const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_resolver_refcount)) {
+  if (grpc_trace_resolver_refcount.enabled()) {
     gpr_atm old_refs = gpr_atm_no_barrier_load(&resolver->refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "RESOLVER:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", resolver,
diff --git a/src/core/ext/filters/client_channel/resolver.h b/src/core/ext/filters/client_channel/resolver.h
index a0eb0bc..b5806ad 100644
--- a/src/core/ext/filters/client_channel/resolver.h
+++ b/src/core/ext/filters/client_channel/resolver.h
@@ -29,9 +29,7 @@
 typedef struct grpc_resolver grpc_resolver;
 typedef struct grpc_resolver_vtable grpc_resolver_vtable;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_resolver_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount;
 
 /** \a grpc_resolver provides \a grpc_channel_args objects to its caller */
 struct grpc_resolver {
diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc
index 2720e68..58e294d 100644
--- a/src/core/ext/filters/client_channel/subchannel.cc
+++ b/src/core/ext/filters/client_channel/subchannel.cc
@@ -199,7 +199,7 @@
   gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta)
                             : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) {
+  if (grpc_trace_stream_refcount.enabled()) {
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SUBCHANNEL: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c,
             purpose, old_val, old_val + delta, reason);
diff --git a/src/core/ext/filters/http/http_filters_plugin.cc b/src/core/ext/filters/http/http_filters_plugin.cc
index 69dbde4..ac31ace 100644
--- a/src/core/ext/filters/http/http_filters_plugin.cc
+++ b/src/core/ext/filters/http/http_filters_plugin.cc
@@ -65,7 +65,6 @@
 }
 
 extern "C" void grpc_http_filters_init(void) {
-  grpc_register_tracer(&grpc_compression_trace);
   grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL,
                                    GRPC_CHANNEL_INIT_BUILTIN_PRIORITY,
                                    maybe_add_optional_filter, &compress_filter);
diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.cc b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
index 8d95133..d070b56 100644
--- a/src/core/ext/filters/http/message_compress/message_compress_filter.cc
+++ b/src/core/ext/filters/http/message_compress/message_compress_filter.cc
@@ -243,7 +243,7 @@
   bool did_compress = grpc_msg_compress(exec_ctx, calld->compression_algorithm,
                                         &calld->slices, &tmp);
   if (did_compress) {
-    if (GRPC_TRACER_ON(grpc_compression_trace)) {
+    if (grpc_compression_trace.enabled()) {
       const char* algo_name;
       const size_t before_size = calld->slices.length;
       const size_t after_size = tmp.length;
@@ -258,7 +258,7 @@
     grpc_slice_buffer_swap(&calld->slices, &tmp);
     send_flags |= GRPC_WRITE_INTERNAL_COMPRESS;
   } else {
-    if (GRPC_TRACER_ON(grpc_compression_trace)) {
+    if (grpc_compression_trace.enabled()) {
       const char* algo_name;
       GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm,
                                                  &algo_name));
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
index ac9ae5c..2569347 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc
@@ -20,13 +20,6 @@
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/transport/metadata.h"
 
-extern "C" void grpc_chttp2_plugin_init(void) {
-  grpc_register_tracer(&grpc_http_trace);
-  grpc_register_tracer(&grpc_flowctl_trace);
-  grpc_register_tracer(&grpc_trace_http2_stream_state);
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_chttp2_refcount);
-#endif
-}
+extern "C" void grpc_chttp2_plugin_init(void) {}
 
 extern "C" void grpc_chttp2_plugin_shutdown(void) {}
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
index 43788bf..5bd6478 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc
@@ -90,13 +90,9 @@
 static int g_default_max_ping_strikes = DEFAULT_MAX_PING_STRIKES;
 
 #define MAX_CLIENT_STREAM_ID 0x7fffffffu
-grpc_tracer_flag grpc_http_trace = GRPC_TRACER_INITIALIZER(false, "http");
-grpc_tracer_flag grpc_flowctl_trace = GRPC_TRACER_INITIALIZER(false, "flowctl");
-
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_chttp2_refcount =
-    GRPC_TRACER_INITIALIZER(false, "chttp2_refcount");
-#endif
+grpc_core::TraceFlag grpc_http_trace(false, "http");
+grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount(false,
+                                                         "chttp2_refcount");
 
 /* forward declarations of various callbacks that we'll build closures around */
 static void write_action_begin_locked(grpc_exec_ctx* exec_ctx, void* t,
@@ -235,7 +231,7 @@
 void grpc_chttp2_unref_transport(grpc_exec_ctx* exec_ctx,
                                  grpc_chttp2_transport* t, const char* reason,
                                  const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_chttp2_refcount)) {
+  if (grpc_trace_chttp2_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&t->refs.count);
     gpr_log(GPR_DEBUG, "chttp2:unref:%p %" PRIdPTR "->%" PRIdPTR " %s [%s:%d]",
             t, val, val - 1, reason, file, line);
@@ -246,7 +242,7 @@
 
 void grpc_chttp2_ref_transport(grpc_chttp2_transport* t, const char* reason,
                                const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_chttp2_refcount)) {
+  if (grpc_trace_chttp2_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&t->refs.count);
     gpr_log(GPR_DEBUG, "chttp2:  ref:%p %" PRIdPTR "->%" PRIdPTR " %s [%s:%d]",
             t, val, val + 1, reason, file, line);
@@ -1237,7 +1233,7 @@
     return;
   }
   closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT;
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     const char* errstr = grpc_error_string(error);
     gpr_log(
         GPR_DEBUG,
@@ -1396,7 +1392,7 @@
 
   GRPC_STATS_INC_HTTP2_OP_BATCHES(exec_ctx);
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* str = grpc_transport_stream_op_batch_string(op);
     gpr_log(GPR_DEBUG, "perform_stream_op_locked: %s; on_complete = %p", str,
             op->on_complete);
@@ -1686,7 +1682,7 @@
     }
   }
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* str = grpc_transport_stream_op_batch_string(op);
     gpr_log(GPR_DEBUG, "perform_stream_op[s=%p]: %s", s, str);
     gpr_free(str);
@@ -2594,7 +2590,7 @@
 static void start_bdp_ping_locked(grpc_exec_ctx* exec_ctx, void* tp,
                                   grpc_error* error) {
   grpc_chttp2_transport* t = (grpc_chttp2_transport*)tp;
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "%s: Start BDP ping err=%s", t->peer_string,
             grpc_error_string(error));
   }
@@ -2608,7 +2604,7 @@
 static void finish_bdp_ping_locked(grpc_exec_ctx* exec_ctx, void* tp,
                                    grpc_error* error) {
   grpc_chttp2_transport* t = (grpc_chttp2_transport*)tp;
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "%s: Complete BDP ping err=%s", t->peer_string,
             grpc_error_string(error));
   }
@@ -3109,7 +3105,7 @@
       grpc_chttp2_stream_map_size(&t->stream_map) == 0) {
     /* Channel with no active streams: send a goaway to try and make it
      * disconnect cleanly */
-    if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+    if (grpc_resource_quota_trace.enabled()) {
       gpr_log(GPR_DEBUG, "HTTP2: %s - send goaway to free memory",
               t->peer_string);
     }
@@ -3117,8 +3113,7 @@
                 grpc_error_set_int(
                     GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"),
                     GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM));
-  } else if (error == GRPC_ERROR_NONE &&
-             GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  } else if (error == GRPC_ERROR_NONE && grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR
             " streams",
@@ -3140,7 +3135,7 @@
   if (error == GRPC_ERROR_NONE && n > 0) {
     grpc_chttp2_stream* s =
         (grpc_chttp2_stream*)grpc_chttp2_stream_map_rand(&t->stream_map);
-    if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+    if (grpc_resource_quota_trace.enabled()) {
       gpr_log(GPR_DEBUG, "HTTP2: %s - abandon stream id %d", t->peer_string,
               s->id);
     }
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
index 972104f..54abbe6 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.h
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.h
@@ -23,18 +23,14 @@
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/transport/transport.h"
 
+extern grpc_core::TraceFlag grpc_http_trace;
+extern grpc_core::TraceFlag grpc_trace_http2_stream_state;
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-extern grpc_tracer_flag grpc_http_trace;
-extern grpc_tracer_flag grpc_flowctl_trace;
-extern grpc_tracer_flag grpc_trace_http2_stream_state;
-
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_chttp2_refcount;
-#endif
-
 grpc_transport* grpc_create_chttp2_transport(
     grpc_exec_ctx* exec_ctx, const grpc_channel_args* channel_args,
     grpc_endpoint* ep, int is_client);
diff --git a/src/core/ext/transport/chttp2/transport/flow_control.cc b/src/core/ext/transport/chttp2/transport/flow_control.cc
index b4e3118..8a057bd 100644
--- a/src/core/ext/transport/chttp2/transport/flow_control.cc
+++ b/src/core/ext/transport/chttp2/transport/flow_control.cc
@@ -31,6 +31,8 @@
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 #include "src/core/lib/support/string.h"
 
+grpc_core::TraceFlag grpc_flowctl_trace(false, "flowctl");
+
 namespace grpc_core {
 namespace chttp2 {
 
diff --git a/src/core/ext/transport/chttp2/transport/flow_control.h b/src/core/ext/transport/chttp2/transport/flow_control.h
index 7dd348e..bb710fe 100644
--- a/src/core/ext/transport/chttp2/transport/flow_control.h
+++ b/src/core/ext/transport/chttp2/transport/flow_control.h
@@ -30,7 +30,7 @@
 struct grpc_chttp2_transport;
 struct grpc_chttp2_stream;
 
-extern "C" grpc_tracer_flag grpc_flowctl_trace;
+extern grpc_core::TraceFlag grpc_flowctl_trace;
 
 namespace grpc {
 namespace testing {
@@ -118,7 +118,7 @@
             StreamFlowControl* sfc);
   void Finish();
 
-  const bool enabled_ = GRPC_TRACER_ON(grpc_flowctl_trace);
+  const bool enabled_ = grpc_flowctl_trace.enabled();
 
   TransportFlowControl* tfc_;
   StreamFlowControl* sfc_;
diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.cc b/src/core/ext/transport/chttp2/transport/frame_settings.cc
index d33da72..75bb78d 100644
--- a/src/core/ext/transport/chttp2/transport/frame_settings.cc
+++ b/src/core/ext/transport/chttp2/transport/frame_settings.cc
@@ -204,20 +204,19 @@
               parser->incoming_settings[id] != parser->value) {
             t->initial_window_update +=
                 (int64_t)parser->value - parser->incoming_settings[id];
-            if (GRPC_TRACER_ON(grpc_http_trace) ||
-                GRPC_TRACER_ON(grpc_flowctl_trace)) {
+            if (grpc_http_trace.enabled() || grpc_flowctl_trace.enabled()) {
               gpr_log(GPR_DEBUG, "%p[%s] adding %d for initial_window change",
                       t, t->is_client ? "cli" : "svr",
                       (int)t->initial_window_update);
             }
           }
           parser->incoming_settings[id] = parser->value;
-          if (GRPC_TRACER_ON(grpc_http_trace)) {
+          if (grpc_http_trace.enabled()) {
             gpr_log(GPR_DEBUG, "CHTTP2:%s:%s: got setting %s = %d",
                     t->is_client ? "CLI" : "SVR", t->peer_string, sp->name,
                     parser->value);
           }
-        } else if (GRPC_TRACER_ON(grpc_http_trace)) {
+        } else if (grpc_http_trace.enabled()) {
           gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)",
                   parser->id, parser->value);
         }
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
index e6e4ff2..e76d92e 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc
@@ -57,8 +57,6 @@
     {{nullptr, 0}}            /* data.refcounted */
 };
 
-extern "C" grpc_tracer_flag grpc_http_trace;
-
 typedef struct {
   int is_first_frame;
   /* number of bytes in 'output' when we started the frame - used to calculate
@@ -475,7 +473,7 @@
         "Reserved header (colon-prefixed) happening after regular ones.");
   }
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* k = grpc_slice_to_c_string(GRPC_MDKEY(elem));
     char* v = nullptr;
     if (grpc_is_binary_header(GRPC_MDKEY(elem))) {
@@ -669,7 +667,7 @@
     }
   }
   c->advertise_table_size_change = 1;
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "set max table size from encoder to %d", max_table_size);
   }
 }
diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
index fd01d16..96d8e99 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_encoder.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.h
@@ -34,6 +34,8 @@
 /* maximum table size we'll actually use */
 #define GRPC_CHTTP2_HPACKC_MAX_TABLE_SIZE (1024 * 1024)
 
+extern grpc_core::TraceFlag grpc_http_trace;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
index 960610e..18cb27f 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.cc
@@ -651,7 +651,7 @@
 /* emission helpers */
 static grpc_error* on_hdr(grpc_exec_ctx* exec_ctx, grpc_chttp2_hpack_parser* p,
                           grpc_mdelem md, int add_to_table) {
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* k = grpc_slice_to_c_string(GRPC_MDKEY(md));
     char* v = nullptr;
     if (grpc_is_binary_header(GRPC_MDKEY(md))) {
@@ -1047,7 +1047,7 @@
 static grpc_error* finish_max_tbl_size(grpc_exec_ctx* exec_ctx,
                                        grpc_chttp2_hpack_parser* p,
                                        const uint8_t* cur, const uint8_t* end) {
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index);
   }
   grpc_error* err =
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.cc b/src/core/ext/transport/chttp2/transport/hpack_table.cc
index 7970d2a..75b83b8 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.cc
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.cc
@@ -28,7 +28,7 @@
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/support/murmur_hash.h"
 
-extern "C" grpc_tracer_flag grpc_http_trace;
+extern grpc_core::TraceFlag grpc_http_trace;
 
 static struct {
   const char* key;
@@ -246,7 +246,7 @@
   if (tbl->max_bytes == max_bytes) {
     return;
   }
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Update hpack parser max size to %d", max_bytes);
   }
   while (tbl->mem_used > max_bytes) {
@@ -270,7 +270,7 @@
     gpr_free(msg);
     return err;
   }
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes);
   }
   while (tbl->mem_used > bytes) {
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index 60cc280..4555b7b 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -674,13 +674,13 @@
 #define GRPC_CHTTP2_CLIENT_CONNECT_STRLEN \
   (sizeof(GRPC_CHTTP2_CLIENT_CONNECT_STRING) - 1)
 
-extern grpc_tracer_flag grpc_http_trace;
-extern grpc_tracer_flag grpc_flowctl_trace;
+// extern grpc_core::TraceFlag grpc_http_trace;
+// extern grpc_core::TraceFlag grpc_flowctl_trace;
 
-#define GRPC_CHTTP2_IF_TRACING(stmt)      \
-  if (!(GRPC_TRACER_ON(grpc_http_trace))) \
-    ;                                     \
-  else                                    \
+#define GRPC_CHTTP2_IF_TRACING(stmt) \
+  if (!(grpc_http_trace.enabled()))  \
+    ;                                \
+  else                               \
     stmt
 
 void grpc_chttp2_fake_status(grpc_exec_ctx* exec_ctx, grpc_chttp2_transport* t,
diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc
index 5f07dec..46ec3fb 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.cc
+++ b/src/core/ext/transport/chttp2/transport/parsing.cc
@@ -312,7 +312,7 @@
     case GRPC_CHTTP2_FRAME_GOAWAY:
       return init_goaway_parser(exec_ctx, t);
     default:
-      if (GRPC_TRACER_ON(grpc_http_trace)) {
+      if (grpc_http_trace.enabled()) {
         gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type);
       }
       return init_skip_frame_parser(exec_ctx, t, 0);
@@ -417,7 +417,7 @@
 
   GPR_ASSERT(s != nullptr);
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
     char* value =
         grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -501,7 +501,7 @@
 
   GPR_ASSERT(s != nullptr);
 
-  if (GRPC_TRACER_ON(grpc_http_trace)) {
+  if (grpc_http_trace.enabled()) {
     char* key = grpc_slice_to_c_string(GRPC_MDKEY(md));
     char* value =
         grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -758,7 +758,7 @@
   if (err == GRPC_ERROR_NONE) {
     return err;
   } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, nullptr)) {
-    if (GRPC_TRACER_ON(grpc_http_trace)) {
+    if (grpc_http_trace.enabled()) {
       const char* msg = grpc_error_string(err);
       gpr_log(GPR_ERROR, "%s", msg);
     }
diff --git a/src/core/ext/transport/chttp2/transport/stream_lists.cc b/src/core/ext/transport/chttp2/transport/stream_lists.cc
index d92527f..c95d025 100644
--- a/src/core/ext/transport/chttp2/transport/stream_lists.cc
+++ b/src/core/ext/transport/chttp2/transport/stream_lists.cc
@@ -39,8 +39,7 @@
   GPR_UNREACHABLE_CODE(return "unknown");
 }
 
-grpc_tracer_flag grpc_trace_http2_stream_state =
-    GRPC_TRACER_INITIALIZER(false, "http2_stream_state");
+grpc_core::TraceFlag grpc_trace_http2_stream_state(false, "http2_stream_state");
 
 /* core list management */
 
@@ -66,7 +65,7 @@
     s->included[id] = 0;
   }
   *stream = s;
-  if (s && GRPC_TRACER_ON(grpc_trace_http2_stream_state)) {
+  if (s && grpc_trace_http2_stream_state.enabled()) {
     gpr_log(GPR_DEBUG, "%p[%d][%s]: pop from %s", t, s->id,
             t->is_client ? "cli" : "svr", stream_list_id_string(id));
   }
@@ -88,7 +87,7 @@
   } else {
     t->lists[id].tail = s->links[id].prev;
   }
-  if (GRPC_TRACER_ON(grpc_trace_http2_stream_state)) {
+  if (grpc_trace_http2_stream_state.enabled()) {
     gpr_log(GPR_DEBUG, "%p[%d][%s]: remove from %s", t, s->id,
             t->is_client ? "cli" : "svr", stream_list_id_string(id));
   }
@@ -120,7 +119,7 @@
   }
   t->lists[id].tail = s;
   s->included[id] = 1;
-  if (GRPC_TRACER_ON(grpc_trace_http2_stream_state)) {
+  if (grpc_trace_http2_stream_state.enabled()) {
     gpr_log(GPR_DEBUG, "%p[%d][%s]: add to %s", t, s->id,
             t->is_client ? "cli" : "svr", stream_list_id_string(id));
   }
diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc
index c36b4bd..15869b8 100644
--- a/src/core/ext/transport/chttp2/transport/writing.cc
+++ b/src/core/ext/transport/chttp2/transport/writing.cc
@@ -51,8 +51,7 @@
   }
   if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) {
     /* ping already in-flight: wait */
-    if (GRPC_TRACER_ON(grpc_http_trace) ||
-        GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "%s: Ping delayed [%p]: already pinging",
               t->is_client ? "CLIENT" : "SERVER", t->peer_string);
     }
@@ -61,8 +60,7 @@
   if (t->ping_state.pings_before_data_required == 0 &&
       t->ping_policy.max_pings_without_data != 0) {
     /* need to receive something of substance before sending a ping again */
-    if (GRPC_TRACER_ON(grpc_http_trace) ||
-        GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "%s: Ping delayed [%p]: too many recent pings: %d/%d",
               t->is_client ? "CLIENT" : "SERVER", t->peer_string,
               t->ping_state.pings_before_data_required,
@@ -81,8 +79,7 @@
   }
   if (next_allowed_ping > now) {
     /* not enough elapsed time between successive pings */
-    if (GRPC_TRACER_ON(grpc_http_trace) ||
-        GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "%s: Ping delayed [%p]: not enough time elapsed since last ping",
               t->is_client ? "CLIENT" : "SERVER", t->peer_string);
@@ -103,8 +100,7 @@
                         grpc_chttp2_ping_create(false, pq->inflight_id));
   GRPC_STATS_INC_HTTP2_PINGS_SENT(exec_ctx);
   t->ping_state.last_ping_sent_time = now;
-  if (GRPC_TRACER_ON(grpc_http_trace) ||
-      GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+  if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) {
     gpr_log(GPR_DEBUG, "%s: Ping sent [%p]: %d/%d",
             t->is_client ? "CLIENT" : "SERVER", t->peer_string,
             t->ping_state.pings_before_data_required,
diff --git a/src/core/ext/transport/inproc/inproc_plugin.cc b/src/core/ext/transport/inproc/inproc_plugin.cc
index 5d8a1c7..2526dbf 100644
--- a/src/core/ext/transport/inproc/inproc_plugin.cc
+++ b/src/core/ext/transport/inproc/inproc_plugin.cc
@@ -19,12 +19,9 @@
 #include "src/core/ext/transport/inproc/inproc_transport.h"
 #include "src/core/lib/debug/trace.h"
 
-grpc_tracer_flag grpc_inproc_trace = GRPC_TRACER_INITIALIZER(false, "inproc");
+grpc_core::TraceFlag grpc_inproc_trace(false, "inproc");
 
-extern "C" void grpc_inproc_plugin_init(void) {
-  grpc_register_tracer(&grpc_inproc_trace);
-  grpc_inproc_transport_init();
-}
+extern "C" void grpc_inproc_plugin_init(void) { grpc_inproc_transport_init(); }
 
 extern "C" void grpc_inproc_plugin_shutdown(void) {
   grpc_inproc_transport_shutdown();
diff --git a/src/core/ext/transport/inproc/inproc_transport.cc b/src/core/ext/transport/inproc/inproc_transport.cc
index 7ea08b0..2579060 100644
--- a/src/core/ext/transport/inproc/inproc_transport.cc
+++ b/src/core/ext/transport/inproc/inproc_transport.cc
@@ -32,9 +32,9 @@
 #include "src/core/lib/transport/error_utils.h"
 #include "src/core/lib/transport/transport_impl.h"
 
-#define INPROC_LOG(...)                                          \
-  do {                                                           \
-    if (GRPC_TRACER_ON(grpc_inproc_trace)) gpr_log(__VA_ARGS__); \
+#define INPROC_LOG(...)                                    \
+  do {                                                     \
+    if (grpc_inproc_trace.enabled()) gpr_log(__VA_ARGS__); \
   } while (0)
 
 static grpc_slice g_empty_slice;
@@ -199,7 +199,7 @@
                                     const grpc_metadata_batch* metadata,
                                     uint32_t flags, grpc_metadata_batch* out_md,
                                     uint32_t* outflags, bool* markfilled) {
-  if (GRPC_TRACER_ON(grpc_inproc_trace)) {
+  if (grpc_inproc_trace.enabled()) {
     log_metadata(metadata, s->t->is_client, outflags != nullptr);
   }
 
@@ -873,7 +873,7 @@
   gpr_mu* mu = &s->t->mu->mu;  // save aside in case s gets closed
   gpr_mu_lock(mu);
 
-  if (GRPC_TRACER_ON(grpc_inproc_trace)) {
+  if (grpc_inproc_trace.enabled()) {
     if (op->send_initial_metadata) {
       log_metadata(op->payload->send_initial_metadata.send_initial_metadata,
                    s->t->is_client, true);
diff --git a/src/core/ext/transport/inproc/inproc_transport.h b/src/core/ext/transport/inproc/inproc_transport.h
index 6e83af3..f27789a 100644
--- a/src/core/ext/transport/inproc/inproc_transport.h
+++ b/src/core/ext/transport/inproc/inproc_transport.h
@@ -29,7 +29,7 @@
                                          grpc_channel_args* args,
                                          void* reserved);
 
-extern grpc_tracer_flag grpc_inproc_trace;
+extern grpc_core::TraceFlag grpc_inproc_trace;
 
 void grpc_inproc_transport_init(void);
 void grpc_inproc_transport_shutdown(void);
diff --git a/src/core/lib/channel/channel_stack.cc b/src/core/lib/channel/channel_stack.cc
index 6e50809..7629d18 100644
--- a/src/core/lib/channel/channel_stack.cc
+++ b/src/core/lib/channel/channel_stack.cc
@@ -23,7 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-grpc_tracer_flag grpc_trace_channel = GRPC_TRACER_INITIALIZER(false, "channel");
+grpc_core::TraceFlag grpc_trace_channel(false, "channel");
 
 /* Memory layouts.
 
diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
index aa99311..6b41ad1 100644
--- a/src/core/lib/channel/channel_stack.h
+++ b/src/core/lib/channel/channel_stack.h
@@ -285,10 +285,10 @@
                       grpc_call_element* elem,
                       grpc_transport_stream_op_batch* op);
 
-extern grpc_tracer_flag grpc_trace_channel;
+extern grpc_core::TraceFlag grpc_trace_channel;
 
 #define GRPC_CALL_LOG_OP(sev, elem, op) \
-  if (GRPC_TRACER_ON(grpc_trace_channel)) grpc_call_log_op(sev, elem, op)
+  if (grpc_trace_channel.enabled()) grpc_call_log_op(sev, elem, op)
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/channel/channel_stack_builder.cc b/src/core/lib/channel/channel_stack_builder.cc
index 1e553bf..77b7854 100644
--- a/src/core/lib/channel/channel_stack_builder.cc
+++ b/src/core/lib/channel/channel_stack_builder.cc
@@ -23,8 +23,8 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/string_util.h>
 
-grpc_tracer_flag grpc_trace_channel_stack_builder =
-    GRPC_TRACER_INITIALIZER(false, "channel_stack_builder");
+grpc_core::TraceFlag grpc_trace_channel_stack_builder(false,
+                                                      "channel_stack_builder");
 
 typedef struct filter_node {
   struct filter_node* next;
diff --git a/src/core/lib/channel/channel_stack_builder.h b/src/core/lib/channel/channel_stack_builder.h
index 23134b7..8e3ec2e 100644
--- a/src/core/lib/channel/channel_stack_builder.h
+++ b/src/core/lib/channel/channel_stack_builder.h
@@ -160,7 +160,7 @@
 void grpc_channel_stack_builder_destroy(grpc_exec_ctx* exec_ctx,
                                         grpc_channel_stack_builder* builder);
 
-extern grpc_tracer_flag grpc_trace_channel_stack_builder;
+extern grpc_core::TraceFlag grpc_trace_channel_stack_builder;
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/debug/trace.cc b/src/core/lib/debug/trace.cc
index 9c75ef1..4c63983 100644
--- a/src/core/lib/debug/trace.cc
+++ b/src/core/lib/debug/trace.cc
@@ -27,26 +27,61 @@
 
 int grpc_tracer_set_enabled(const char* name, int enabled);
 
-typedef struct tracer {
-  grpc_tracer_flag* flag;
-  struct tracer* next;
-} tracer;
-static tracer* tracers;
+namespace grpc_core {
 
-#ifdef GRPC_THREADSAFE_TRACER
-#define TRACER_SET(flag, on) gpr_atm_no_barrier_store(&(flag).value, (on))
-#else
-#define TRACER_SET(flag, on) (flag).value = (on)
-#endif
+TraceFlag* TraceFlagList::root_tracer_ = nullptr;
 
-void grpc_register_tracer(grpc_tracer_flag* flag) {
-  tracer* t = (tracer*)gpr_malloc(sizeof(*t));
-  t->flag = flag;
-  t->next = tracers;
-  TRACER_SET(*flag, false);
-  tracers = t;
+bool TraceFlagList::Set(const char* name, bool enabled) {
+  TraceFlag* t;
+  if (0 == strcmp(name, "all")) {
+    for (t = root_tracer_; t; t = t->next_tracer_) {
+      t->set_enabled(enabled);
+    }
+  } else if (0 == strcmp(name, "list_tracers")) {
+    LogAllTracers();
+  } else if (0 == strcmp(name, "refcount")) {
+    for (t = root_tracer_; t; t = t->next_tracer_) {
+      if (strstr(t->name_, "refcount") != nullptr) {
+        t->set_enabled(enabled);
+      }
+    }
+  } else {
+    bool found = false;
+    for (t = root_tracer_; t; t = t->next_tracer_) {
+      if (0 == strcmp(name, t->name_)) {
+        t->set_enabled(enabled);
+        found = true;
+      }
+    }
+    if (!found) {
+      gpr_log(GPR_ERROR, "Unknown trace var: '%s'", name);
+      return false; /* early return */
+    }
+  }
+  return true;
 }
 
+void TraceFlagList::Add(TraceFlag* flag) {
+  flag->next_tracer_ = root_tracer_;
+  root_tracer_ = flag;
+}
+
+void TraceFlagList::LogAllTracers() {
+  gpr_log(GPR_DEBUG, "available tracers:");
+  TraceFlag* t;
+  for (t = root_tracer_; t != nullptr; t = t->next_tracer_) {
+    gpr_log(GPR_DEBUG, "\t%s", t->name_);
+  }
+}
+
+// Flags register themselves on the list during construction
+TraceFlag::TraceFlag(bool default_enabled, const char* name)
+    : name_(name), value_(default_enabled) {
+  TraceFlagList::Add(this);
+}
+
+}  // namespace grpc_core
+
 static void add(const char* beg, const char* end, char*** ss, size_t* ns) {
   size_t n = *ns;
   size_t np = n + 1;
@@ -80,9 +115,9 @@
 
   for (i = 0; i < nstrings; i++) {
     if (strings[i][0] == '-') {
-      grpc_tracer_set_enabled(strings[i] + 1, 0);
+      grpc_core::TraceFlagList::Set(strings[i] + 1, false);
     } else {
-      grpc_tracer_set_enabled(strings[i], 1);
+      grpc_core::TraceFlagList::Set(strings[i], true);
     }
   }
 
@@ -92,14 +127,6 @@
   gpr_free(strings);
 }
 
-static void list_tracers() {
-  gpr_log(GPR_DEBUG, "available tracers:");
-  tracer* t;
-  for (t = tracers; t; t = t->next) {
-    gpr_log(GPR_DEBUG, "\t%s", t->flag->name);
-  }
-}
-
 void grpc_tracer_init(const char* env_var) {
   char* e = gpr_getenv(env_var);
   if (e != nullptr) {
@@ -108,40 +135,8 @@
   }
 }
 
-void grpc_tracer_shutdown(void) {
-  while (tracers) {
-    tracer* t = tracers;
-    tracers = t->next;
-    gpr_free(t);
-  }
-}
+void grpc_tracer_shutdown(void) {}
 
 int grpc_tracer_set_enabled(const char* name, int enabled) {
-  tracer* t;
-  if (0 == strcmp(name, "all")) {
-    for (t = tracers; t; t = t->next) {
-      TRACER_SET(*t->flag, enabled);
-    }
-  } else if (0 == strcmp(name, "list_tracers")) {
-    list_tracers();
-  } else if (0 == strcmp(name, "refcount")) {
-    for (t = tracers; t; t = t->next) {
-      if (strstr(t->flag->name, "refcount") != nullptr) {
-        TRACER_SET(*t->flag, enabled);
-      }
-    }
-  } else {
-    int found = 0;
-    for (t = tracers; t; t = t->next) {
-      if (0 == strcmp(name, t->flag->name)) {
-        TRACER_SET(*t->flag, enabled);
-        found = 1;
-      }
-    }
-    if (!found) {
-      gpr_log(GPR_ERROR, "Unknown trace var: '%s'", name);
-      return 0; /* early return */
-    }
-  }
-  return 1;
+  return grpc_core::TraceFlagList::Set(name, enabled != 0);
 }
diff --git a/src/core/lib/debug/trace.h b/src/core/lib/debug/trace.h
index 7447d5d..b58c16f 100644
--- a/src/core/lib/debug/trace.h
+++ b/src/core/lib/debug/trace.h
@@ -27,32 +27,6 @@
 extern "C" {
 #endif
 
-#if defined(__has_feature)
-#if __has_feature(thread_sanitizer)
-#define GRPC_THREADSAFE_TRACER
-#endif
-#endif
-
-typedef struct {
-#ifdef GRPC_THREADSAFE_TRACER
-  gpr_atm value;
-#else
-  bool value;
-#endif
-  const char* name;
-} grpc_tracer_flag;
-
-#ifdef GRPC_THREADSAFE_TRACER
-#define GRPC_TRACER_ON(flag) (gpr_atm_no_barrier_load(&(flag).value) != 0)
-#define GRPC_TRACER_INITIALIZER(on, name) \
-  { (gpr_atm)(on), (name) }
-#else
-#define GRPC_TRACER_ON(flag) ((flag).value)
-#define GRPC_TRACER_INITIALIZER(on, name) \
-  { (on), (name) }
-#endif
-
-void grpc_register_tracer(grpc_tracer_flag* flag);
 void grpc_tracer_init(const char* env_var_name);
 void grpc_tracer_shutdown(void);
 
@@ -60,4 +34,82 @@
 }
 #endif
 
+#if defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#define GRPC_THREADSAFE_TRACER
+#endif
+#endif
+
+#ifdef __cplusplus
+
+namespace grpc_core {
+
+class TraceFlag;
+class TraceFlagList {
+ public:
+  static bool Set(const char* name, bool enabled);
+  static void Add(TraceFlag* flag);
+
+ private:
+  static void LogAllTracers();
+  static TraceFlag* root_tracer_;
+};
+
+namespace testing {
+void grpc_tracer_enable_flag(grpc_core::TraceFlag* flag);
+}
+
+class TraceFlag {
+ public:
+  TraceFlag(bool default_enabled, const char* name);
+  ~TraceFlag() {}
+
+  const char* name() const { return name_; }
+
+  bool enabled() {
+#ifdef GRPC_THREADSAFE_TRACER
+    return gpr_atm_no_barrier_load(&value_) != 0;
+#else
+    return value_;
+#endif
+  }
+
+ private:
+  friend void grpc_core::testing::grpc_tracer_enable_flag(TraceFlag* flag);
+  friend class TraceFlagList;
+
+  void set_enabled(bool enabled) {
+#ifdef GRPC_THREADSAFE_TRACER
+    gpr_atm_no_barrier_store(&value_, enabled);
+#else
+    value_ = enabled;
+#endif
+  }
+
+  TraceFlag* next_tracer_;
+  const char* const name_;
+#ifdef GRPC_THREADSAFE_TRACER
+  gpr_atm value_;
+#else
+  bool value_;
+#endif
+};
+
+#ifndef NDEBUG
+typedef TraceFlag DebugOnlyTraceFlag;
+#else
+class DebugOnlyTraceFlag {
+ public:
+  DebugOnlyTraceFlag(bool default_enabled, const char* name) {}
+  bool enabled() { return false; }
+
+ private:
+  void set_enabled(bool enabled) {}
+};
+#endif
+
+}  // namespace grpc_core
+
+#endif  // __cplusplus
+
 #endif /* GRPC_CORE_LIB_DEBUG_TRACE_H */
diff --git a/src/core/lib/http/parser.cc b/src/core/lib/http/parser.cc
index 9134b0c..fb4eb23 100644
--- a/src/core/lib/http/parser.cc
+++ b/src/core/lib/http/parser.cc
@@ -25,7 +25,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/useful.h>
 
-grpc_tracer_flag grpc_http1_trace = GRPC_TRACER_INITIALIZER(false, "http1");
+grpc_core::TraceFlag grpc_http1_trace(false, "http1");
 
 static char* buf2str(void* buffer, size_t length) {
   char* out = (char*)gpr_malloc(length + 1);
@@ -294,7 +294,7 @@
     case GRPC_HTTP_FIRST_LINE:
     case GRPC_HTTP_HEADERS:
       if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) {
-        if (GRPC_TRACER_ON(grpc_http1_trace))
+        if (grpc_http1_trace.enabled())
           gpr_log(GPR_ERROR, "HTTP header max line length (%d) exceeded",
                   GRPC_HTTP_PARSER_MAX_HEADER_LENGTH);
         return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h
index 3d28481..391bd35 100644
--- a/src/core/lib/http/parser.h
+++ b/src/core/lib/http/parser.h
@@ -111,7 +111,7 @@
 void grpc_http_request_destroy(grpc_http_request* request);
 void grpc_http_response_destroy(grpc_http_response* response);
 
-extern grpc_tracer_flag grpc_http1_trace;
+extern grpc_core::TraceFlag grpc_http1_trace;
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/iomgr/call_combiner.cc b/src/core/lib/iomgr/call_combiner.cc
index 74b077d..b5910b4 100644
--- a/src/core/lib/iomgr/call_combiner.cc
+++ b/src/core/lib/iomgr/call_combiner.cc
@@ -24,8 +24,7 @@
 #include "src/core/lib/debug/stats.h"
 #include "src/core/lib/profiling/timers.h"
 
-grpc_tracer_flag grpc_call_combiner_trace =
-    GRPC_TRACER_INITIALIZER(false, "call_combiner");
+grpc_core::TraceFlag grpc_call_combiner_trace(false, "call_combiner");
 
 static grpc_error* decode_cancel_state_error(gpr_atm cancel_state) {
   if (cancel_state & 1) {
@@ -63,7 +62,7 @@
                               grpc_error* error DEBUG_ARGS,
                               const char* reason) {
   GPR_TIMER_BEGIN("call_combiner_start", 0);
-  if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "==> grpc_call_combiner_start() [%p] closure=%p [" DEBUG_FMT_STR
             "%s] error=%s",
@@ -72,7 +71,7 @@
   }
   size_t prev_size =
       (size_t)gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)1);
-  if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  size: %" PRIdPTR " -> %" PRIdPTR, prev_size,
             prev_size + 1);
   }
@@ -80,13 +79,13 @@
   if (prev_size == 0) {
     GRPC_STATS_INC_CALL_COMBINER_LOCKS_INITIATED(exec_ctx);
     GPR_TIMER_MARK("call_combiner_initiate", 0);
-    if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+    if (grpc_call_combiner_trace.enabled()) {
       gpr_log(GPR_DEBUG, "  EXECUTING IMMEDIATELY");
     }
     // Queue was empty, so execute this closure immediately.
     GRPC_CLOSURE_SCHED(exec_ctx, closure, error);
   } else {
-    if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+    if (grpc_call_combiner_trace.enabled()) {
       gpr_log(GPR_INFO, "  QUEUING");
     }
     // Queue was not empty, so add closure to queue.
@@ -100,21 +99,21 @@
                              grpc_call_combiner* call_combiner DEBUG_ARGS,
                              const char* reason) {
   GPR_TIMER_BEGIN("call_combiner_stop", 0);
-  if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "==> grpc_call_combiner_stop() [%p] [" DEBUG_FMT_STR "%s]",
             call_combiner DEBUG_FMT_ARGS, reason);
   }
   size_t prev_size =
       (size_t)gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)-1);
-  if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  size: %" PRIdPTR " -> %" PRIdPTR, prev_size,
             prev_size - 1);
   }
   GPR_ASSERT(prev_size >= 1);
   if (prev_size > 1) {
     while (true) {
-      if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+      if (grpc_call_combiner_trace.enabled()) {
         gpr_log(GPR_DEBUG, "  checking queue");
       }
       bool empty;
@@ -123,19 +122,19 @@
       if (closure == nullptr) {
         // This can happen either due to a race condition within the mpscq
         // code or because of a race with grpc_call_combiner_start().
-        if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+        if (grpc_call_combiner_trace.enabled()) {
           gpr_log(GPR_DEBUG, "  queue returned no result; checking again");
         }
         continue;
       }
-      if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+      if (grpc_call_combiner_trace.enabled()) {
         gpr_log(GPR_DEBUG, "  EXECUTING FROM QUEUE: closure=%p error=%s",
                 closure, grpc_error_string(closure->error_data.error));
       }
       GRPC_CLOSURE_SCHED(exec_ctx, closure, closure->error_data.error);
       break;
     }
-  } else if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+  } else if (grpc_call_combiner_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  queue empty");
   }
   GPR_TIMER_END("call_combiner_stop", 0);
@@ -152,7 +151,7 @@
     // If error is set, invoke the cancellation closure immediately.
     // Otherwise, store the new closure.
     if (original_error != GRPC_ERROR_NONE) {
-      if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+      if (grpc_call_combiner_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "call_combiner=%p: scheduling notify_on_cancel callback=%p "
                 "for pre-existing cancellation",
@@ -163,7 +162,7 @@
     } else {
       if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state,
                            (gpr_atm)closure)) {
-        if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+        if (grpc_call_combiner_trace.enabled()) {
           gpr_log(GPR_DEBUG, "call_combiner=%p: setting notify_on_cancel=%p",
                   call_combiner, closure);
         }
@@ -172,7 +171,7 @@
         // up any resources they may be holding for the callback.
         if (original_state != 0) {
           closure = (grpc_closure*)original_state;
-          if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+          if (grpc_call_combiner_trace.enabled()) {
             gpr_log(GPR_DEBUG,
                     "call_combiner=%p: scheduling old cancel callback=%p",
                     call_combiner, closure);
@@ -201,7 +200,7 @@
                          encode_cancel_state_error(error))) {
       if (original_state != 0) {
         grpc_closure* notify_on_cancel = (grpc_closure*)original_state;
-        if (GRPC_TRACER_ON(grpc_call_combiner_trace)) {
+        if (grpc_call_combiner_trace.enabled()) {
           gpr_log(GPR_DEBUG,
                   "call_combiner=%p: scheduling notify_on_cancel callback=%p",
                   call_combiner, notify_on_cancel);
diff --git a/src/core/lib/iomgr/call_combiner.h b/src/core/lib/iomgr/call_combiner.h
index 527f84f..77420fa 100644
--- a/src/core/lib/iomgr/call_combiner.h
+++ b/src/core/lib/iomgr/call_combiner.h
@@ -40,7 +40,7 @@
 // when it is done with the action that was kicked off by the original
 // callback.
 
-extern grpc_tracer_flag grpc_call_combiner_trace;
+extern grpc_core::TraceFlag grpc_call_combiner_trace;
 
 typedef struct {
   gpr_atm size;  // size_t, num closures in queue or currently executing
diff --git a/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h
index 8b99d4a..46793dd 100644
--- a/src/core/lib/iomgr/closure.h
+++ b/src/core/lib/iomgr/closure.h
@@ -33,9 +33,7 @@
 struct grpc_closure;
 typedef struct grpc_closure grpc_closure;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_closure;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_closure;
 
 typedef struct grpc_closure_list {
   grpc_closure* head;
diff --git a/src/core/lib/iomgr/combiner.cc b/src/core/lib/iomgr/combiner.cc
index b28ca34..15c009d 100644
--- a/src/core/lib/iomgr/combiner.cc
+++ b/src/core/lib/iomgr/combiner.cc
@@ -29,14 +29,13 @@
 #include "src/core/lib/iomgr/executor.h"
 #include "src/core/lib/profiling/timers.h"
 
-grpc_tracer_flag grpc_combiner_trace =
-    GRPC_TRACER_INITIALIZER(false, "combiner");
+grpc_core::TraceFlag grpc_combiner_trace(false, "combiner");
 
-#define GRPC_COMBINER_TRACE(fn)                \
-  do {                                         \
-    if (GRPC_TRACER_ON(grpc_combiner_trace)) { \
-      fn;                                      \
-    }                                          \
+#define GRPC_COMBINER_TRACE(fn)          \
+  do {                                   \
+    if (grpc_combiner_trace.enabled()) { \
+      fn;                                \
+    }                                    \
   } while (0)
 
 #define STATE_UNORPHANED 1
@@ -106,7 +105,7 @@
 
 #ifndef NDEBUG
 #define GRPC_COMBINER_DEBUG_SPAM(op, delta)                                \
-  if (GRPC_TRACER_ON(grpc_combiner_trace)) {                               \
+  if (grpc_combiner_trace.enabled()) {                                     \
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,                            \
             "C:%p %s %" PRIdPTR " --> %" PRIdPTR " %s", lock, (op),        \
             gpr_atm_no_barrier_load(&lock->refs.count),                    \
diff --git a/src/core/lib/iomgr/combiner.h b/src/core/lib/iomgr/combiner.h
index f8a8b9d..e99b063 100644
--- a/src/core/lib/iomgr/combiner.h
+++ b/src/core/lib/iomgr/combiner.h
@@ -65,7 +65,7 @@
 
 bool grpc_combiner_continue_exec_ctx(grpc_exec_ctx* exec_ctx);
 
-extern grpc_tracer_flag grpc_combiner_trace;
+extern grpc_core::TraceFlag grpc_combiner_trace;
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/iomgr/error.cc b/src/core/lib/iomgr/error.cc
index f3dfac3..e6d640c 100644
--- a/src/core/lib/iomgr/error.cc
+++ b/src/core/lib/iomgr/error.cc
@@ -37,11 +37,9 @@
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_error_refcount =
-    GRPC_TRACER_INITIALIZER(false, "error_refcount");
-grpc_tracer_flag grpc_trace_closure = GRPC_TRACER_INITIALIZER(false, "closure");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_error_refcount(false,
+                                                        "error_refcount");
+grpc_core::DebugOnlyTraceFlag grpc_trace_closure(false, "closure");
 
 static const char* error_int_name(grpc_error_ints key) {
   switch (key) {
@@ -131,7 +129,7 @@
 #ifndef NDEBUG
 grpc_error* grpc_error_ref(grpc_error* err, const char* file, int line) {
   if (grpc_error_is_special(err)) return err;
-  if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+  if (grpc_trace_error_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err,
             gpr_atm_no_barrier_load(&err->atomics.refs.count),
             gpr_atm_no_barrier_load(&err->atomics.refs.count) + 1, file, line);
@@ -184,7 +182,7 @@
 #ifndef NDEBUG
 void grpc_error_unref(grpc_error* err, const char* file, int line) {
   if (grpc_error_is_special(err)) return;
-  if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+  if (grpc_trace_error_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err,
             gpr_atm_no_barrier_load(&err->atomics.refs.count),
             gpr_atm_no_barrier_load(&err->atomics.refs.count) - 1, file, line);
@@ -217,7 +215,7 @@
     *err = (grpc_error*)gpr_realloc(
         *err, sizeof(grpc_error) + (*err)->arena_capacity * sizeof(intptr_t));
 #ifndef NDEBUG
-    if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+    if (grpc_trace_error_refcount.enabled()) {
       if (*err != orig) {
         gpr_log(GPR_DEBUG, "realloc %p -> %p", orig, *err);
       }
@@ -330,7 +328,7 @@
     return GRPC_ERROR_OOM;
   }
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+  if (grpc_trace_error_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "%p create [%s:%d]", err, file, line);
   }
 #endif
@@ -412,7 +410,7 @@
     out = (grpc_error*)gpr_malloc(sizeof(*in) +
                                   new_arena_capacity * sizeof(intptr_t));
 #ifndef NDEBUG
-    if (GRPC_TRACER_ON(grpc_trace_error_refcount)) {
+    if (grpc_trace_error_refcount.enabled()) {
       gpr_log(GPR_DEBUG, "%p create copying %p", out, in);
     }
 #endif
diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h
index 8d7aea4..d10bf0b 100644
--- a/src/core/lib/iomgr/error.h
+++ b/src/core/lib/iomgr/error.h
@@ -39,9 +39,7 @@
 
 typedef struct grpc_error grpc_error;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_error_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_error_refcount;
 
 typedef enum {
   /// 'errno' from the operating system
diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc
index 8aada4e..0dda1d9 100644
--- a/src/core/lib/iomgr/ev_epoll1_linux.cc
+++ b/src/core/lib/iomgr/ev_epoll1_linux.cc
@@ -278,7 +278,7 @@
   gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
   grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name);
   }
 #endif
@@ -653,7 +653,7 @@
 
   GRPC_STATS_INC_POLL_EVENTS_RETURNED(exec_ctx, r);
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "ps: %p poll got %d events", ps, r);
   }
 
@@ -675,7 +675,7 @@
   worker->schedule_on_end_work = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT;
   pollset->begin_refs++;
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_ERROR, "PS:%p BEGIN_STARTS:%p", pollset, worker);
   }
 
@@ -694,7 +694,7 @@
   retry_lock_neighborhood:
     gpr_mu_lock(&neighborhood->mu);
     gpr_mu_lock(&pollset->mu);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, "PS:%p BEGIN_REORG:%p kick_state=%s is_reassigning=%d",
               pollset, worker, kick_state_string(worker->state),
               is_reassigning);
@@ -746,7 +746,7 @@
     worker->initialized_cv = true;
     gpr_cv_init(&worker->cv);
     while (worker->state == UNKICKED && !pollset->shutting_down) {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_ERROR, "PS:%p BEGIN_WAIT:%p kick_state=%s shutdown=%d",
                 pollset, worker, kick_state_string(worker->state),
                 pollset->shutting_down);
@@ -763,7 +763,7 @@
     grpc_exec_ctx_invalidate_now(exec_ctx);
   }
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_ERROR,
             "PS:%p BEGIN_DONE:%p kick_state=%s shutdown=%d "
             "kicked_without_poller: %d",
@@ -808,7 +808,7 @@
           case UNKICKED:
             if (gpr_atm_no_barrier_cas(&g_active_poller, 0,
                                        (gpr_atm)inspect_worker)) {
-              if (GRPC_TRACER_ON(grpc_polling_trace)) {
+              if (grpc_polling_trace.enabled()) {
                 gpr_log(GPR_DEBUG, " .. choose next poller to be %p",
                         inspect_worker);
               }
@@ -819,7 +819,7 @@
                 gpr_cv_signal(&inspect_worker->cv);
               }
             } else {
-              if (GRPC_TRACER_ON(grpc_polling_trace)) {
+              if (grpc_polling_trace.enabled()) {
                 gpr_log(GPR_DEBUG, " .. beaten to choose next poller");
               }
             }
@@ -837,7 +837,7 @@
       } while (!found_worker && inspect_worker != inspect->root_worker);
     }
     if (!found_worker) {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, " .. mark pollset %p inactive", inspect);
       }
       inspect->seen_inactive = true;
@@ -859,7 +859,7 @@
                        grpc_pollset_worker* worker,
                        grpc_pollset_worker** worker_hdl) {
   GPR_TIMER_BEGIN("end_worker", 0);
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PS:%p END_WORKER:%p", pollset, worker);
   }
   if (worker_hdl != nullptr) *worker_hdl = nullptr;
@@ -869,7 +869,7 @@
                          &exec_ctx->closure_list);
   if (gpr_atm_no_barrier_load(&g_active_poller) == (gpr_atm)worker) {
     if (worker->next != worker && worker->next->state == UNKICKED) {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, " .. choose next poller to be peer %p", worker);
       }
       GPR_ASSERT(worker->next->initialized_cv);
@@ -923,7 +923,7 @@
   if (worker->initialized_cv) {
     gpr_cv_destroy(&worker->cv);
   }
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, " .. remove worker");
   }
   if (EMPTIED == worker_remove(pollset, worker)) {
@@ -995,7 +995,7 @@
   GPR_TIMER_BEGIN("pollset_kick", 0);
   GRPC_STATS_INC_POLLSET_KICK(exec_ctx);
   grpc_error* ret_err = GRPC_ERROR_NONE;
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_strvec log;
     gpr_strvec_init(&log);
     char* tmp;
@@ -1028,7 +1028,7 @@
       if (root_worker == nullptr) {
         GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER(exec_ctx);
         pollset->kicked_without_poller = true;
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. kicked_without_poller");
         }
         goto done;
@@ -1036,14 +1036,14 @@
       grpc_pollset_worker* next_worker = root_worker->next;
       if (root_worker->state == KICKED) {
         GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx);
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. already kicked %p", root_worker);
         }
         SET_KICK_STATE(root_worker, KICKED);
         goto done;
       } else if (next_worker->state == KICKED) {
         GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx);
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. already kicked %p", next_worker);
         }
         SET_KICK_STATE(next_worker, KICKED);
@@ -1054,7 +1054,7 @@
                  root_worker == (grpc_pollset_worker*)gpr_atm_no_barrier_load(
                                     &g_active_poller)) {
         GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx);
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. kicked %p", root_worker);
         }
         SET_KICK_STATE(root_worker, KICKED);
@@ -1062,7 +1062,7 @@
         goto done;
       } else if (next_worker->state == UNKICKED) {
         GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx);
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_ERROR, " .. kicked %p", next_worker);
         }
         GPR_ASSERT(next_worker->initialized_cv);
@@ -1071,7 +1071,7 @@
         goto done;
       } else if (next_worker->state == DESIGNATED_POLLER) {
         if (root_worker->state != DESIGNATED_POLLER) {
-          if (GRPC_TRACER_ON(grpc_polling_trace)) {
+          if (grpc_polling_trace.enabled()) {
             gpr_log(
                 GPR_ERROR,
                 " .. kicked root non-poller %p (initialized_cv=%d) (poller=%p)",
@@ -1085,7 +1085,7 @@
           goto done;
         } else {
           GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx);
-          if (GRPC_TRACER_ON(grpc_polling_trace)) {
+          if (grpc_polling_trace.enabled()) {
             gpr_log(GPR_ERROR, " .. non-root poller %p (root=%p)", next_worker,
                     root_worker);
           }
@@ -1101,7 +1101,7 @@
       }
     } else {
       GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx);
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_ERROR, " .. kicked while waking up");
       }
       goto done;
@@ -1111,14 +1111,14 @@
   }
 
   if (specific_worker->state == KICKED) {
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. specific worker already kicked");
     }
     goto done;
   } else if (gpr_tls_get(&g_current_thread_worker) ==
              (intptr_t)specific_worker) {
     GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. mark %p kicked", specific_worker);
     }
     SET_KICK_STATE(specific_worker, KICKED);
@@ -1126,7 +1126,7 @@
   } else if (specific_worker ==
              (grpc_pollset_worker*)gpr_atm_no_barrier_load(&g_active_poller)) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. kick active poller");
     }
     SET_KICK_STATE(specific_worker, KICKED);
@@ -1134,7 +1134,7 @@
     goto done;
   } else if (specific_worker->initialized_cv) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. kick waiting worker");
     }
     SET_KICK_STATE(specific_worker, KICKED);
@@ -1142,7 +1142,7 @@
     goto done;
   } else {
     GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, " .. kick non-waiting worker");
     }
     SET_KICK_STATE(specific_worker, KICKED);
diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc
index a6f069e..62643df 100644
--- a/src/core/lib/iomgr/ev_epollex_linux.cc
+++ b/src/core/lib/iomgr/ev_epollex_linux.cc
@@ -59,10 +59,8 @@
 #define MAX_EPOLL_EVENTS 100
 #define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 5
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_pollable_refcount =
-    GRPC_TRACER_INITIALIZER(false, "pollable_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_pollable_refcount(false,
+                                                           "pollable_refcount");
 
 /*******************************************************************************
  * pollable Declarations
@@ -263,7 +261,7 @@
   unref_by(ec, fd, n, reason, __FILE__, __LINE__)
 static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                    int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -297,7 +295,7 @@
 #ifndef NDEBUG
 static void unref_by(grpc_exec_ctx* exec_ctx, grpc_fd* fd, int n,
                      const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -362,7 +360,7 @@
   gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
   grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name);
   }
 #endif
@@ -485,7 +483,7 @@
 static pollable* pollable_ref(pollable* p) {
 #else
 static pollable* pollable_ref(pollable* p, int line, const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_pollable_refcount)) {
+  if (grpc_trace_pollable_refcount.enabled()) {
     int r = (int)gpr_atm_no_barrier_load(&p->refs.count);
     gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG,
             "POLLABLE:%p   ref %d->%d %s", p, r, r + 1, reason);
@@ -500,7 +498,7 @@
 #else
 static void pollable_unref(pollable* p, int line, const char* reason) {
   if (p == nullptr) return;
-  if (GRPC_TRACER_ON(grpc_trace_pollable_refcount)) {
+  if (grpc_trace_pollable_refcount.enabled()) {
     int r = (int)gpr_atm_no_barrier_load(&p->refs.count);
     gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG,
             "POLLABLE:%p unref %d->%d %s", p, r, r - 1, reason);
@@ -518,7 +516,7 @@
   static const char* err_desc = "pollable_add_fd";
   const int epfd = p->epfd;
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "add fd %p (%d) to pollable %p", fd, fd->fd, p);
   }
 
@@ -560,7 +558,7 @@
 /* pollset->mu must be held while calling this function */
 static void pollset_maybe_finish_shutdown(grpc_exec_ctx* exec_ctx,
                                           grpc_pollset* pollset) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "PS:%p (pollable:%p) maybe_finish_shutdown sc=%p (target:!NULL) "
             "rw=%p (target:NULL) cpsc=%d (target:0)",
@@ -583,14 +581,14 @@
   grpc_core::mu_guard lock(&p->mu);
   GPR_ASSERT(specific_worker != nullptr);
   if (specific_worker->kicked) {
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_DEBUG, "PS:%p kicked_specific_but_already_kicked", p);
     }
     GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx);
     return GRPC_ERROR_NONE;
   }
   if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) {
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_DEBUG, "PS:%p kicked_specific_but_awake", p);
     }
     GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx);
@@ -599,7 +597,7 @@
   }
   if (specific_worker == p->root_worker) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_DEBUG, "PS:%p kicked_specific_via_wakeup_fd", p);
     }
     specific_worker->kicked = true;
@@ -608,7 +606,7 @@
   }
   if (specific_worker->initialized_cv) {
     GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_DEBUG, "PS:%p kicked_specific_via_cv", p);
     }
     specific_worker->kicked = true;
@@ -623,7 +621,7 @@
 static grpc_error* pollset_kick(grpc_exec_ctx* exec_ctx, grpc_pollset* pollset,
                                 grpc_pollset_worker* specific_worker) {
   GRPC_STATS_INC_POLLSET_KICK(exec_ctx);
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "PS:%p kick %p tls_pollset=%p tls_worker=%p pollset.root_worker=%p",
             pollset, specific_worker,
@@ -633,7 +631,7 @@
   if (specific_worker == nullptr) {
     if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) {
       if (pollset->root_worker == nullptr) {
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_DEBUG, "PS:%p kicked_any_without_poller", pollset);
         }
         GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER(exec_ctx);
@@ -659,7 +657,7 @@
             exec_ctx, pollset->root_worker->links[PWLINK_POLLSET].next);
       }
     } else {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, "PS:%p kicked_any_but_awake", pollset);
       }
       GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx);
@@ -767,7 +765,7 @@
     struct epoll_event* ev = &pollable_obj->events[n];
     void* data_ptr = ev->data.ptr;
     if (1 & (intptr_t)data_ptr) {
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, "PS:%p got pollset_wakeup %p", pollset, data_ptr);
       }
       append_error(&error,
@@ -779,7 +777,7 @@
       bool cancel = (ev->events & (EPOLLERR | EPOLLHUP)) != 0;
       bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0;
       bool write_ev = (ev->events & EPOLLOUT) != 0;
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "PS:%p got fd %p: cancel=%d read=%d "
                 "write=%d",
@@ -807,7 +805,7 @@
                                   grpc_millis deadline) {
   int timeout = poll_deadline_to_millis_timeout(exec_ctx, deadline);
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     char* desc = pollable_desc(p);
     gpr_log(GPR_DEBUG, "POLLABLE:%p[%s] poll for %dms", p, desc, timeout);
     gpr_free(desc);
@@ -827,7 +825,7 @@
 
   if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait");
 
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "POLLABLE:%p got %d events", p, r);
   }
 
@@ -895,7 +893,7 @@
     worker->initialized_cv = true;
     gpr_cv_init(&worker->cv);
     gpr_mu_unlock(&pollset->mu);
-    if (GRPC_TRACER_ON(grpc_polling_trace) &&
+    if (grpc_polling_trace.enabled() &&
         worker->pollable_obj->root_worker != worker) {
       gpr_log(GPR_DEBUG, "PS:%p wait %p w=%p for %dms", pollset,
               worker->pollable_obj, worker,
@@ -904,18 +902,18 @@
     while (do_poll && worker->pollable_obj->root_worker != worker) {
       if (gpr_cv_wait(&worker->cv, &worker->pollable_obj->mu,
                       grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) {
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_DEBUG, "PS:%p timeout_wait %p w=%p", pollset,
                   worker->pollable_obj, worker);
         }
         do_poll = false;
       } else if (worker->kicked) {
-        if (GRPC_TRACER_ON(grpc_polling_trace)) {
+        if (grpc_polling_trace.enabled()) {
           gpr_log(GPR_DEBUG, "PS:%p wakeup %p w=%p", pollset,
                   worker->pollable_obj, worker);
         }
         do_poll = false;
-      } else if (GRPC_TRACER_ON(grpc_polling_trace) &&
+      } else if (grpc_polling_trace.enabled() &&
                  worker->pollable_obj->root_worker != worker) {
         gpr_log(GPR_DEBUG, "PS:%p spurious_wakeup %p w=%p", pollset,
                 worker->pollable_obj, worker);
@@ -986,7 +984,7 @@
 #ifndef NDEBUG
   WORKER_PTR->originator = gettid();
 #endif
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "PS:%p work hdl=%p worker=%p now=%" PRIdPTR " deadline=%" PRIdPTR
             " kwp=%d pollable=%p",
@@ -1029,7 +1027,7 @@
     grpc_exec_ctx* exec_ctx, grpc_pollset* pollset, grpc_fd* fd) {
   static const char* err_desc = "pollset_transition_pollable_from_empty_to_fd";
   grpc_error* error = GRPC_ERROR_NONE;
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "PS:%p add fd %p (%d); transition pollable from empty to fd",
             pollset, fd, fd->fd);
@@ -1045,7 +1043,7 @@
     grpc_exec_ctx* exec_ctx, grpc_pollset* pollset, grpc_fd* and_add_fd) {
   static const char* err_desc = "pollset_transition_pollable_from_fd_to_multi";
   grpc_error* error = GRPC_ERROR_NONE;
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(
         GPR_DEBUG,
         "PS:%p add fd %p (%d); transition pollable from fd %p to multipoller",
@@ -1195,7 +1193,7 @@
 
 static void pollset_set_add_fd(grpc_exec_ctx* exec_ctx, grpc_pollset_set* pss,
                                grpc_fd* fd) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS:%p: add fd %p (%d)", pss, fd, fd->fd);
   }
   grpc_error* error = GRPC_ERROR_NONE;
@@ -1219,7 +1217,7 @@
 
 static void pollset_set_del_fd(grpc_exec_ctx* exec_ctx, grpc_pollset_set* pss,
                                grpc_fd* fd) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS:%p: del fd %p", pss, fd);
   }
   pss = pss_lock_adam(pss);
@@ -1240,7 +1238,7 @@
 
 static void pollset_set_del_pollset(grpc_exec_ctx* exec_ctx,
                                     grpc_pollset_set* pss, grpc_pollset* ps) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS:%p: del pollset %p", pss, ps);
   }
   pss = pss_lock_adam(pss);
@@ -1291,7 +1289,7 @@
 
 static void pollset_set_add_pollset(grpc_exec_ctx* exec_ctx,
                                     grpc_pollset_set* pss, grpc_pollset* ps) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS:%p: add pollset %p", pss, ps);
   }
   grpc_error* error = GRPC_ERROR_NONE;
@@ -1328,7 +1326,7 @@
 static void pollset_set_add_pollset_set(grpc_exec_ctx* exec_ctx,
                                         grpc_pollset_set* a,
                                         grpc_pollset_set* b) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS: merge (%p, %p)", a, b);
   }
   grpc_error* error = GRPC_ERROR_NONE;
@@ -1362,7 +1360,7 @@
   if (b_size > a_size) {
     GPR_SWAP(grpc_pollset_set*, a, b);
   }
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_log(GPR_DEBUG, "PSS: parent %p to %p", b, a);
   }
   gpr_ref(&a->refs);
@@ -1463,10 +1461,6 @@
     return nullptr;
   }
 
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_pollable_refcount);
-#endif
-
   fd_global_init();
 
   if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
diff --git a/src/core/lib/iomgr/ev_epollsig_linux.cc b/src/core/lib/iomgr/ev_epollsig_linux.cc
index d51c665..12c8483 100644
--- a/src/core/lib/iomgr/ev_epollsig_linux.cc
+++ b/src/core/lib/iomgr/ev_epollsig_linux.cc
@@ -54,9 +54,9 @@
 
 #define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1)
 
-#define GRPC_POLLING_TRACE(...)             \
-  if (GRPC_TRACER_ON(grpc_polling_trace)) { \
-    gpr_log(GPR_INFO, __VA_ARGS__);         \
+#define GRPC_POLLING_TRACE(...)       \
+  if (grpc_polling_trace.enabled()) { \
+    gpr_log(GPR_INFO, __VA_ARGS__);   \
   }
 
 static int grpc_wakeup_signal = -1;
@@ -289,7 +289,7 @@
 #ifndef NDEBUG
 static void pi_add_ref_dbg(polling_island* pi, const char* reason,
                            const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count);
     gpr_log(GPR_DEBUG,
             "Add ref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR
@@ -301,7 +301,7 @@
 
 static void pi_unref_dbg(grpc_exec_ctx* exec_ctx, polling_island* pi,
                          const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_polling_trace)) {
+  if (grpc_polling_trace.enabled()) {
     gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count);
     gpr_log(GPR_DEBUG,
             "Unref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR
@@ -733,7 +733,7 @@
 #define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
 static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                    int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -750,7 +750,7 @@
 #ifndef NDEBUG
 static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                      int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
diff --git a/src/core/lib/iomgr/ev_poll_posix.cc b/src/core/lib/iomgr/ev_poll_posix.cc
index f8b9629..8659559 100644
--- a/src/core/lib/iomgr/ev_poll_posix.cc
+++ b/src/core/lib/iomgr/ev_poll_posix.cc
@@ -288,7 +288,7 @@
 #define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
 static void ref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                    int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p   ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -305,7 +305,7 @@
 #ifndef NDEBUG
 static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file,
                      int line) {
-  if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) {
+  if (grpc_trace_fd_refcount.enabled()) {
     gpr_log(GPR_DEBUG,
             "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]",
             fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst),
@@ -992,7 +992,7 @@
       r = grpc_poll_function(pfds, pfd_count, timeout);
       GRPC_SCHEDULING_END_BLOCKING_REGION_WITH_EXEC_CTX(exec_ctx);
 
-      if (GRPC_TRACER_ON(grpc_polling_trace)) {
+      if (grpc_polling_trace.enabled()) {
         gpr_log(GPR_DEBUG, "%p poll=%d", pollset, r);
       }
 
@@ -1016,7 +1016,7 @@
         }
       } else {
         if (pfds[0].revents & POLLIN_CHECK) {
-          if (GRPC_TRACER_ON(grpc_polling_trace)) {
+          if (grpc_polling_trace.enabled()) {
             gpr_log(GPR_DEBUG, "%p: got_wakeup", pollset);
           }
           work_combine_error(
@@ -1026,7 +1026,7 @@
           if (watchers[i].fd == nullptr) {
             fd_end_poll(exec_ctx, &watchers[i], 0, 0, nullptr);
           } else {
-            if (GRPC_TRACER_ON(grpc_polling_trace)) {
+            if (grpc_polling_trace.enabled()) {
               gpr_log(GPR_DEBUG, "%p got_event: %d r:%d w:%d [%d]", pollset,
                       pfds[i].fd, (pfds[i].revents & POLLIN_CHECK) != 0,
                       (pfds[i].revents & POLLOUT_CHECK) != 0, pfds[i].revents);
diff --git a/src/core/lib/iomgr/ev_posix.cc b/src/core/lib/iomgr/ev_posix.cc
index 076d2e6..80dde6d 100644
--- a/src/core/lib/iomgr/ev_posix.cc
+++ b/src/core/lib/iomgr/ev_posix.cc
@@ -36,13 +36,9 @@
 #include "src/core/lib/iomgr/ev_poll_posix.h"
 #include "src/core/lib/support/env.h"
 
-grpc_tracer_flag grpc_polling_trace =
-    GRPC_TRACER_INITIALIZER(false, "polling"); /* Disabled by default */
-
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_fd_refcount =
-    GRPC_TRACER_INITIALIZER(false, "fd_refcount");
-#endif
+grpc_core::TraceFlag grpc_polling_trace(false,
+                                        "polling"); /* Disabled by default */
+grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
 
 /** Default poll() function - a pointer so that it can be overridden by some
  *  tests */
@@ -153,8 +149,6 @@
 const char* grpc_get_poll_strategy_name() { return g_poll_strategy_name; }
 
 void grpc_event_engine_init(void) {
-  grpc_register_tracer(&grpc_polling_trace);
-
   char* s = gpr_getenv("GRPC_POLL_STRATEGY");
   if (s == nullptr) {
     s = gpr_strdup("all");
diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h
index d719b8f..8f45d2e 100644
--- a/src/core/lib/iomgr/ev_posix.h
+++ b/src/core/lib/iomgr/ev_posix.h
@@ -31,7 +31,7 @@
 extern "C" {
 #endif
 
-extern grpc_tracer_flag grpc_polling_trace; /* Disabled by default */
+extern grpc_core::TraceFlag grpc_polling_trace; /* Disabled by default */
 
 typedef struct grpc_fd grpc_fd;
 
diff --git a/src/core/lib/iomgr/ev_windows.cc b/src/core/lib/iomgr/ev_windows.cc
index c24dfae..697697d 100644
--- a/src/core/lib/iomgr/ev_windows.cc
+++ b/src/core/lib/iomgr/ev_windows.cc
@@ -22,7 +22,7 @@
 
 #include "src/core/lib/debug/trace.h"
 
-grpc_tracer_flag grpc_polling_trace =
-    GRPC_TRACER_INITIALIZER(false, "polling"); /* Disabled by default */
+grpc_core::TraceFlag grpc_polling_trace(false,
+                                        "polling"); /* Disabled by default */
 
 #endif  // GRPC_WINSOCK_SOCKET
diff --git a/src/core/lib/iomgr/exec_ctx.cc b/src/core/lib/iomgr/exec_ctx.cc
index 257ada0..1777456 100644
--- a/src/core/lib/iomgr/exec_ctx.cc
+++ b/src/core/lib/iomgr/exec_ctx.cc
@@ -60,7 +60,7 @@
                          grpc_error* error) {
 #ifndef NDEBUG
   closure->scheduled = false;
-  if (GRPC_TRACER_ON(grpc_trace_closure)) {
+  if (grpc_trace_closure.enabled()) {
     gpr_log(GPR_DEBUG, "running closure %p: created [%s:%d]: %s [%s:%d]",
             closure, closure->file_created, closure->line_created,
             closure->run ? "run" : "scheduled", closure->file_initiated,
@@ -69,7 +69,7 @@
 #endif
   closure->cb(exec_ctx, closure->cb_arg, error);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_closure)) {
+  if (grpc_trace_closure.enabled()) {
     gpr_log(GPR_DEBUG, "closure %p finished", closure);
   }
 #endif
diff --git a/src/core/lib/iomgr/executor.cc b/src/core/lib/iomgr/executor.cc
index 6097d66..d8a195f 100644
--- a/src/core/lib/iomgr/executor.cc
+++ b/src/core/lib/iomgr/executor.cc
@@ -51,8 +51,7 @@
 
 GPR_TLS_DECL(g_this_thread_state);
 
-static grpc_tracer_flag executor_trace =
-    GRPC_TRACER_INITIALIZER(false, "executor");
+grpc_core::TraceFlag executor_trace(false, "executor");
 
 static void executor_thread(void* arg);
 
@@ -63,7 +62,7 @@
   while (c != nullptr) {
     grpc_closure* next = c->next_data.next;
     grpc_error* error = c->error_data.error;
-    if (GRPC_TRACER_ON(executor_trace)) {
+    if (executor_trace.enabled()) {
 #ifndef NDEBUG
       gpr_log(GPR_DEBUG, "EXECUTOR: run %p [created by %s:%d]", c,
               c->file_created, c->line_created);
@@ -134,7 +133,6 @@
 }
 
 void grpc_executor_init(grpc_exec_ctx* exec_ctx) {
-  grpc_register_tracer(&executor_trace);
   gpr_atm_no_barrier_store(&g_cur_threads, 0);
   grpc_executor_set_threading(exec_ctx, true);
 }
@@ -152,7 +150,7 @@
 
   size_t subtract_depth = 0;
   for (;;) {
-    if (GRPC_TRACER_ON(executor_trace)) {
+    if (executor_trace.enabled()) {
       gpr_log(GPR_DEBUG, "EXECUTOR[%d]: step (sub_depth=%" PRIdPTR ")",
               (int)(ts - g_thread_state), subtract_depth);
     }
@@ -163,7 +161,7 @@
       gpr_cv_wait(&ts->cv, &ts->mu, gpr_inf_future(GPR_CLOCK_REALTIME));
     }
     if (ts->shutdown) {
-      if (GRPC_TRACER_ON(executor_trace)) {
+      if (executor_trace.enabled()) {
         gpr_log(GPR_DEBUG, "EXECUTOR[%d]: shutdown",
                 (int)(ts - g_thread_state));
       }
@@ -174,7 +172,7 @@
     grpc_closure_list exec = ts->elems;
     ts->elems = GRPC_CLOSURE_LIST_INIT;
     gpr_mu_unlock(&ts->mu);
-    if (GRPC_TRACER_ON(executor_trace)) {
+    if (executor_trace.enabled()) {
       gpr_log(GPR_DEBUG, "EXECUTOR[%d]: execute", (int)(ts - g_thread_state));
     }
 
@@ -196,7 +194,7 @@
     retry_push = false;
     size_t cur_thread_count = (size_t)gpr_atm_no_barrier_load(&g_cur_threads);
     if (cur_thread_count == 0) {
-      if (GRPC_TRACER_ON(executor_trace)) {
+      if (executor_trace.enabled()) {
 #ifndef NDEBUG
         gpr_log(GPR_DEBUG, "EXECUTOR: schedule %p (created %s:%d) inline",
                 closure, closure->file_created, closure->line_created);
@@ -217,7 +215,7 @@
 
     bool try_new_thread;
     for (;;) {
-      if (GRPC_TRACER_ON(executor_trace)) {
+      if (executor_trace.enabled()) {
 #ifndef NDEBUG
         gpr_log(
             GPR_DEBUG,
diff --git a/src/core/lib/iomgr/iomgr_posix.cc b/src/core/lib/iomgr/iomgr_posix.cc
index f5875a2..f8f6fe2 100644
--- a/src/core/lib/iomgr/iomgr_posix.cc
+++ b/src/core/lib/iomgr/iomgr_posix.cc
@@ -28,7 +28,6 @@
 void grpc_iomgr_platform_init(void) {
   grpc_wakeup_fd_global_init();
   grpc_event_engine_init();
-  grpc_register_tracer(&grpc_tcp_trace);
 }
 
 void grpc_iomgr_platform_flush(void) {}
diff --git a/src/core/lib/iomgr/iomgr_uv.cc b/src/core/lib/iomgr/iomgr_uv.cc
index df5d23a..b8a10f2 100644
--- a/src/core/lib/iomgr/iomgr_uv.cc
+++ b/src/core/lib/iomgr/iomgr_uv.cc
@@ -31,7 +31,7 @@
 void grpc_iomgr_platform_init(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_pollset_global_init();
-  grpc_register_tracer(&grpc_tcp_trace);
+
   grpc_executor_set_threading(&exec_ctx, false);
   g_init_thread = gpr_thd_currentid();
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/src/core/lib/iomgr/lockfree_event.cc b/src/core/lib/iomgr/lockfree_event.cc
index edd997f..f0e798e 100644
--- a/src/core/lib/iomgr/lockfree_event.cc
+++ b/src/core/lib/iomgr/lockfree_event.cc
@@ -22,7 +22,7 @@
 
 #include "src/core/lib/debug/trace.h"
 
-extern grpc_tracer_flag grpc_polling_trace;
+extern grpc_core::TraceFlag grpc_polling_trace;
 
 /* 'state' holds the to call when the fd is readable or writable respectively.
    It can contain one of the following values:
@@ -88,7 +88,7 @@
 void LockfreeEvent::NotifyOn(grpc_exec_ctx* exec_ctx, grpc_closure* closure) {
   while (true) {
     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, "LockfreeEvent::NotifyOn: %p curr=%p closure=%p", this,
               (void*)curr, closure);
     }
@@ -155,7 +155,7 @@
 
   while (true) {
     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, "LockfreeEvent::SetShutdown: %p curr=%p err=%s",
               &state_, (void*)curr, grpc_error_string(shutdown_err));
     }
@@ -204,7 +204,7 @@
   while (true) {
     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
 
-    if (GRPC_TRACER_ON(grpc_polling_trace)) {
+    if (grpc_polling_trace.enabled()) {
       gpr_log(GPR_ERROR, "LockfreeEvent::SetReady: %p curr=%p", &state_,
               (void*)curr);
     }
diff --git a/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h
index c99b930..6911a8e 100644
--- a/src/core/lib/iomgr/pollset.h
+++ b/src/core/lib/iomgr/pollset.h
@@ -29,9 +29,7 @@
 extern "C" {
 #endif
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_fd_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount;
 
 /* A grpc_pollset is a set of file descriptors that a higher level item is
    interested in. For example:
diff --git a/src/core/lib/iomgr/pollset_uv.cc b/src/core/lib/iomgr/pollset_uv.cc
index 1d54942..16132f3 100644
--- a/src/core/lib/iomgr/pollset_uv.cc
+++ b/src/core/lib/iomgr/pollset_uv.cc
@@ -34,10 +34,7 @@
 
 #include "src/core/lib/debug/trace.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_fd_refcount =
-    GRPC_TRACER_INITIALIZER(false, "fd_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
 
 struct grpc_pollset {
   uv_timer_t* timer;
diff --git a/src/core/lib/iomgr/pollset_windows.cc b/src/core/lib/iomgr/pollset_windows.cc
index 5998b3f..95dd7d7 100644
--- a/src/core/lib/iomgr/pollset_windows.cc
+++ b/src/core/lib/iomgr/pollset_windows.cc
@@ -30,10 +30,7 @@
 
 #define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1)
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_fd_refcount =
-    GRPC_TRACER_INITIALIZER(false, "fd_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount");
 
 gpr_mu grpc_polling_mu;
 static grpc_pollset_worker* g_active_poller;
diff --git a/src/core/lib/iomgr/resource_quota.cc b/src/core/lib/iomgr/resource_quota.cc
index c2179cf..ccd8d9f 100644
--- a/src/core/lib/iomgr/resource_quota.cc
+++ b/src/core/lib/iomgr/resource_quota.cc
@@ -31,8 +31,7 @@
 
 #include "src/core/lib/iomgr/combiner.h"
 
-grpc_tracer_flag grpc_resource_quota_trace =
-    GRPC_TRACER_INITIALIZER(false, "resource_quota");
+grpc_core::TraceFlag grpc_resource_quota_trace(false, "resource_quota");
 
 #define MEMORY_USAGE_ESTIMATION_MAX 65536
 
@@ -293,7 +292,7 @@
   while ((resource_user = rulist_pop_head(resource_quota,
                                           GRPC_RULIST_AWAITING_ALLOCATION))) {
     gpr_mu_lock(&resource_user->mu);
-    if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+    if (grpc_resource_quota_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "RQ: check allocation for user %p shutdown=%" PRIdPTR
               " free_pool=%" PRId64,
@@ -319,14 +318,14 @@
       resource_user->free_pool = 0;
       resource_quota->free_pool -= amt;
       rq_update_estimate(resource_quota);
-      if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+      if (grpc_resource_quota_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "RQ %s %s: grant alloc %" PRId64
                 " bytes; rq_free_pool -> %" PRId64,
                 resource_quota->name, resource_user->name, amt,
                 resource_quota->free_pool);
       }
-    } else if (GRPC_TRACER_ON(grpc_resource_quota_trace) &&
+    } else if (grpc_resource_quota_trace.enabled() &&
                resource_user->free_pool >= 0) {
       gpr_log(GPR_DEBUG, "RQ %s %s: discard already satisfied alloc request",
               resource_quota->name, resource_user->name);
@@ -357,7 +356,7 @@
       resource_user->free_pool = 0;
       resource_quota->free_pool += amt;
       rq_update_estimate(resource_quota);
-      if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+      if (grpc_resource_quota_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64
                 " bytes; rq_free_pool -> %" PRId64,
@@ -381,7 +380,7 @@
                                  : GRPC_RULIST_RECLAIMER_BENIGN;
   grpc_resource_user* resource_user = rulist_pop_head(resource_quota, list);
   if (resource_user == nullptr) return false;
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RQ %s %s: initiate %s reclamation",
             resource_quota->name, resource_user->name,
             destructive ? "destructive" : "benign");
@@ -515,7 +514,7 @@
 }
 
 static void ru_shutdown(grpc_exec_ctx* exec_ctx, void* ru, grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RU shutdown %p", ru);
   }
   grpc_resource_user* resource_user = (grpc_resource_user*)ru;
@@ -813,7 +812,7 @@
   ru_ref_by(resource_user, (gpr_atm)size);
   resource_user->free_pool -= (int64_t)size;
   resource_user->outstanding_allocations += (int64_t)size;
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64,
             resource_user->resource_quota->name, resource_user->name, size,
             resource_user->free_pool);
@@ -838,7 +837,7 @@
   gpr_mu_lock(&resource_user->mu);
   bool was_zero_or_negative = resource_user->free_pool <= 0;
   resource_user->free_pool += (int64_t)size;
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RQ %s %s: free %" PRIdPTR "; free_pool -> %" PRId64,
             resource_user->resource_quota->name, resource_user->name, size,
             resource_user->free_pool);
@@ -867,7 +866,7 @@
 
 void grpc_resource_user_finish_reclamation(grpc_exec_ctx* exec_ctx,
                                            grpc_resource_user* resource_user) {
-  if (GRPC_TRACER_ON(grpc_resource_quota_trace)) {
+  if (grpc_resource_quota_trace.enabled()) {
     gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete",
             resource_user->resource_quota->name, resource_user->name);
   }
diff --git a/src/core/lib/iomgr/resource_quota.h b/src/core/lib/iomgr/resource_quota.h
index fcdf9c2..3af93a8 100644
--- a/src/core/lib/iomgr/resource_quota.h
+++ b/src/core/lib/iomgr/resource_quota.h
@@ -65,7 +65,7 @@
     maintain lists of users (which users arrange to leave before they are
     destroyed) */
 
-extern grpc_tracer_flag grpc_resource_quota_trace;
+extern grpc_core::TraceFlag grpc_resource_quota_trace;
 
 grpc_resource_quota* grpc_resource_quota_ref_internal(
     grpc_resource_quota* resource_quota);
diff --git a/src/core/lib/iomgr/tcp_client_posix.cc b/src/core/lib/iomgr/tcp_client_posix.cc
index cb0f627..8a6262b 100644
--- a/src/core/lib/iomgr/tcp_client_posix.cc
+++ b/src/core/lib/iomgr/tcp_client_posix.cc
@@ -43,7 +43,7 @@
 #include "src/core/lib/iomgr/unix_sockets_posix.h"
 #include "src/core/lib/support/string.h"
 
-extern grpc_tracer_flag grpc_tcp_trace;
+extern grpc_core::TraceFlag grpc_tcp_trace;
 
 typedef struct {
   gpr_mu mu;
@@ -99,7 +99,7 @@
 static void tc_on_alarm(grpc_exec_ctx* exec_ctx, void* acp, grpc_error* error) {
   int done;
   async_connect* ac = (async_connect*)acp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str,
             str);
@@ -138,7 +138,7 @@
 
   GRPC_ERROR_REF(error);
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: error=%s",
             ac->addr_str, str);
@@ -317,7 +317,7 @@
                     grpc_schedule_on_exec_ctx);
   ac->channel_args = grpc_channel_args_copy(channel_args);
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting fd %p",
             ac->addr_str, fdobj);
   }
diff --git a/src/core/lib/iomgr/tcp_client_uv.cc b/src/core/lib/iomgr/tcp_client_uv.cc
index 15345c8..7a5727e 100644
--- a/src/core/lib/iomgr/tcp_client_uv.cc
+++ b/src/core/lib/iomgr/tcp_client_uv.cc
@@ -32,7 +32,7 @@
 #include "src/core/lib/iomgr/tcp_uv.h"
 #include "src/core/lib/iomgr/timer.h"
 
-extern grpc_tracer_flag grpc_tcp_trace;
+extern grpc_core::TraceFlag grpc_tcp_trace;
 
 typedef struct grpc_uv_tcp_connect {
   uv_connect_t connect_req;
@@ -59,7 +59,7 @@
                            grpc_error* error) {
   int done;
   grpc_uv_tcp_connect* connect = (grpc_uv_tcp_connect*)acp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s",
             connect->addr_name, str);
@@ -147,7 +147,7 @@
   connect->connect_req.data = connect;
   connect->refs = 2;  // One for the connect operation, one for the timer.
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting",
             connect->addr_name);
   }
diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc
index cb90933..d09cfca 100644
--- a/src/core/lib/iomgr/tcp_posix.cc
+++ b/src/core/lib/iomgr/tcp_posix.cc
@@ -61,7 +61,7 @@
 typedef size_t msg_iovlen_type;
 #endif
 
-grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp");
+grpc_core::TraceFlag grpc_tcp_trace(false, "tcp");
 
 typedef struct {
   grpc_endpoint base;
@@ -81,9 +81,7 @@
 
   grpc_slice_buffer* incoming_buffer;
   grpc_slice_buffer* outgoing_buffer;
-  /** slice within outgoing_buffer to write next */
-  size_t outgoing_slice_idx;
-  /** byte within outgoing_buffer->slices[outgoing_slice_idx] to write next */
+  /** byte within outgoing_buffer->slices[0] to write next */
   size_t outgoing_byte_idx;
 
   grpc_closure* read_cb;
@@ -121,7 +119,7 @@
 static void done_poller(grpc_exec_ctx* exec_ctx, void* bp,
                         grpc_error* error_ignored) {
   backup_poller* p = (backup_poller*)bp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p destroy", p);
   }
   grpc_pollset_destroy(exec_ctx, BACKUP_POLLER_POLLSET(p));
@@ -131,7 +129,7 @@
 static void run_poller(grpc_exec_ctx* exec_ctx, void* bp,
                        grpc_error* error_ignored) {
   backup_poller* p = (backup_poller*)bp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p run", p);
   }
   gpr_mu_lock(p->pollset_mu);
@@ -147,18 +145,18 @@
       gpr_atm_full_cas(&g_uncovered_notifications_pending, 1, 0)) {
     gpr_mu_lock(p->pollset_mu);
     bool cas_ok = gpr_atm_full_cas(&g_backup_poller, (gpr_atm)p, 0);
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p done cas_ok=%d", p, cas_ok);
     }
     gpr_mu_unlock(p->pollset_mu);
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p shutdown", p);
     }
     grpc_pollset_shutdown(exec_ctx, BACKUP_POLLER_POLLSET(p),
                           GRPC_CLOSURE_INIT(&p->run_poller, done_poller, p,
                                             grpc_schedule_on_exec_ctx));
   } else {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p reschedule", p);
     }
     GRPC_CLOSURE_SCHED(exec_ctx, &p->run_poller, GRPC_ERROR_NONE);
@@ -169,7 +167,7 @@
   backup_poller* p = (backup_poller*)gpr_atm_acq_load(&g_backup_poller);
   gpr_atm old_count =
       gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, -1);
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p uncover cnt %d->%d", p, (int)old_count,
             (int)old_count - 1);
   }
@@ -180,14 +178,14 @@
   backup_poller* p;
   gpr_atm old_count =
       gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, 2);
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER: cover cnt %d->%d", (int)old_count,
             2 + (int)old_count);
   }
   if (old_count == 0) {
     GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED(exec_ctx);
     p = (backup_poller*)gpr_zalloc(sizeof(*p) + grpc_pollset_size());
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p create", p);
     }
     grpc_pollset_init(BACKUP_POLLER_POLLSET(p), &p->pollset_mu);
@@ -203,7 +201,7 @@
       // spin waiting for backup poller
     }
   }
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p add %p", p, tcp);
   }
   grpc_pollset_add_fd(exec_ctx, BACKUP_POLLER_POLLSET(p), tcp->em_fd);
@@ -213,7 +211,7 @@
 }
 
 static void notify_on_read(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p notify_on_read", tcp);
   }
   GRPC_CLOSURE_INIT(&tcp->read_done_closure, tcp_handle_read, tcp,
@@ -222,7 +220,7 @@
 }
 
 static void notify_on_write(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p notify_on_write", tcp);
   }
   cover_self(exec_ctx, tcp);
@@ -234,7 +232,7 @@
 
 static void tcp_drop_uncovered_then_handle_write(grpc_exec_ctx* exec_ctx,
                                                  void* arg, grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p got_write: %s", arg, grpc_error_string(error));
   }
   drop_uncovered(exec_ctx, (grpc_tcp*)arg);
@@ -311,7 +309,7 @@
 #define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
 static void tcp_unref(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp,
                       const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -324,7 +322,7 @@
 
 static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
                     int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -355,7 +353,7 @@
                          grpc_error* error) {
   grpc_closure* cb = tcp->read_cb;
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg);
     size_t i;
     const char* str = grpc_error_string(error);
@@ -451,7 +449,7 @@
 static void tcp_read_allocation_done(grpc_exec_ctx* exec_ctx, void* tcpp,
                                      grpc_error* error) {
   grpc_tcp* tcp = (grpc_tcp*)tcpp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp,
             grpc_error_string(error));
   }
@@ -470,13 +468,13 @@
   size_t target_read_size = get_target_read_size(tcp);
   if (tcp->incoming_buffer->length < target_read_size &&
       tcp->incoming_buffer->count < MAX_READ_IOVEC) {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "TCP:%p alloc_slices", tcp);
     }
     grpc_resource_user_alloc_slices(exec_ctx, &tcp->slice_allocator,
                                     target_read_size, 1, tcp->incoming_buffer);
   } else {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "TCP:%p do_read", tcp);
     }
     tcp_do_read(exec_ctx, tcp);
@@ -487,7 +485,7 @@
                             grpc_error* error) {
   grpc_tcp* tcp = (grpc_tcp*)arg;
   GPR_ASSERT(!tcp->finished_edge);
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p got_read: %s", tcp, grpc_error_string(error));
   }
 
@@ -532,23 +530,26 @@
   size_t unwind_slice_idx;
   size_t unwind_byte_idx;
 
+  // We always start at zero, because we eagerly unref and trim the slice
+  // buffer as we write
+  size_t outgoing_slice_idx = 0;
+
   for (;;) {
     sending_length = 0;
-    unwind_slice_idx = tcp->outgoing_slice_idx;
+    unwind_slice_idx = outgoing_slice_idx;
     unwind_byte_idx = tcp->outgoing_byte_idx;
-    for (iov_size = 0; tcp->outgoing_slice_idx != tcp->outgoing_buffer->count &&
+    for (iov_size = 0; outgoing_slice_idx != tcp->outgoing_buffer->count &&
                        iov_size != MAX_WRITE_IOVEC;
          iov_size++) {
       iov[iov_size].iov_base =
           GRPC_SLICE_START_PTR(
-              tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) +
+              tcp->outgoing_buffer->slices[outgoing_slice_idx]) +
           tcp->outgoing_byte_idx;
       iov[iov_size].iov_len =
-          GRPC_SLICE_LENGTH(
-              tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) -
+          GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]) -
           tcp->outgoing_byte_idx;
       sending_length += iov[iov_size].iov_len;
-      tcp->outgoing_slice_idx++;
+      outgoing_slice_idx++;
       tcp->outgoing_byte_idx = 0;
     }
     GPR_ASSERT(iov_size > 0);
@@ -574,16 +575,25 @@
 
     if (sent_length < 0) {
       if (errno == EAGAIN) {
-        tcp->outgoing_slice_idx = unwind_slice_idx;
         tcp->outgoing_byte_idx = unwind_byte_idx;
+        // unref all and forget about all slices that have been written to this
+        // point
+        for (size_t idx = 0; idx < unwind_slice_idx; ++idx) {
+          grpc_slice_unref_internal(
+              exec_ctx, grpc_slice_buffer_take_first(tcp->outgoing_buffer));
+        }
         return false;
       } else if (errno == EPIPE) {
         *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"),
                                     GRPC_ERROR_INT_GRPC_STATUS,
                                     GRPC_STATUS_UNAVAILABLE);
+        grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                                   tcp->outgoing_buffer);
         return true;
       } else {
         *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp);
+        grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                                   tcp->outgoing_buffer);
         return true;
       }
     }
@@ -593,9 +603,9 @@
     while (trailing > 0) {
       size_t slice_length;
 
-      tcp->outgoing_slice_idx--;
-      slice_length = GRPC_SLICE_LENGTH(
-          tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]);
+      outgoing_slice_idx--;
+      slice_length =
+          GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]);
       if (slice_length > trailing) {
         tcp->outgoing_byte_idx = slice_length - trailing;
         break;
@@ -604,11 +614,13 @@
       }
     }
 
-    if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) {
+    if (outgoing_slice_idx == tcp->outgoing_buffer->count) {
       *error = GRPC_ERROR_NONE;
+      grpc_slice_buffer_reset_and_unref_internal(exec_ctx,
+                                                 tcp->outgoing_buffer);
       return true;
     }
-  };
+  }
 }
 
 static void tcp_handle_write(grpc_exec_ctx* exec_ctx, void* arg /* grpc_tcp */,
@@ -625,14 +637,14 @@
   }
 
   if (!tcp_flush(exec_ctx, tcp, &error)) {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "write: delayed");
     }
     notify_on_write(exec_ctx, tcp);
   } else {
     cb = tcp->write_cb;
     tcp->write_cb = nullptr;
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       const char* str = grpc_error_string(error);
       gpr_log(GPR_DEBUG, "write: %s", str);
     }
@@ -647,7 +659,7 @@
   grpc_tcp* tcp = (grpc_tcp*)ep;
   grpc_error* error = GRPC_ERROR_NONE;
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     size_t i;
 
     for (i = 0; i < buf->count; i++) {
@@ -672,18 +684,17 @@
     return;
   }
   tcp->outgoing_buffer = buf;
-  tcp->outgoing_slice_idx = 0;
   tcp->outgoing_byte_idx = 0;
 
   if (!tcp_flush(exec_ctx, tcp, &error)) {
     TCP_REF(tcp, "write");
     tcp->write_cb = cb;
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "write: delayed");
     }
     notify_on_write(exec_ctx, tcp);
   } else {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       const char* str = grpc_error_string(error);
       gpr_log(GPR_DEBUG, "write: %s", str);
     }
diff --git a/src/core/lib/iomgr/tcp_posix.h b/src/core/lib/iomgr/tcp_posix.h
index ff1060b..ba85146 100644
--- a/src/core/lib/iomgr/tcp_posix.h
+++ b/src/core/lib/iomgr/tcp_posix.h
@@ -37,7 +37,7 @@
 extern "C" {
 #endif
 
-extern grpc_tracer_flag grpc_tcp_trace;
+extern grpc_core::TraceFlag grpc_tcp_trace;
 
 /* Create a tcp endpoint given a file desciptor and a read slice size.
    Takes ownership of fd. */
diff --git a/src/core/lib/iomgr/tcp_server_posix.cc b/src/core/lib/iomgr/tcp_server_posix.cc
index f84fa97..6fed13c 100644
--- a/src/core/lib/iomgr/tcp_server_posix.cc
+++ b/src/core/lib/iomgr/tcp_server_posix.cc
@@ -243,7 +243,7 @@
     addr_str = grpc_sockaddr_to_uri(&addr);
     gpr_asprintf(&name, "tcp-server-connection:%s", addr_str);
 
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       gpr_log(GPR_DEBUG, "SERVER_CONNECT: incoming connection: %s", addr_str);
     }
 
diff --git a/src/core/lib/iomgr/tcp_server_uv.cc b/src/core/lib/iomgr/tcp_server_uv.cc
index 0eed4d4..2c76fae 100644
--- a/src/core/lib/iomgr/tcp_server_uv.cc
+++ b/src/core/lib/iomgr/tcp_server_uv.cc
@@ -213,7 +213,7 @@
   } else {
     gpr_log(GPR_INFO, "uv_tcp_getpeername error: %s", uv_strerror(err));
   }
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     if (peer_name_string) {
       gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection: %s",
               sp->server, peer_name_string);
@@ -247,7 +247,7 @@
 
   GPR_ASSERT(!sp->has_pending_connection);
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p incoming connection", sp->server);
   }
 
@@ -403,7 +403,7 @@
 
   gpr_free(allocated_addr);
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     char* port_string;
     grpc_sockaddr_to_string(&port_string, addr, 0);
     const char* str = grpc_error_string(error);
@@ -435,7 +435,7 @@
   (void)pollsets;
   (void)pollset_count;
   GRPC_UV_ASSERT_SAME_THREAD();
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "SERVER_START %p", server);
   }
   GPR_ASSERT(on_accept_cb);
diff --git a/src/core/lib/iomgr/tcp_uv.cc b/src/core/lib/iomgr/tcp_uv.cc
index de7ec43..40f4006 100644
--- a/src/core/lib/iomgr/tcp_uv.cc
+++ b/src/core/lib/iomgr/tcp_uv.cc
@@ -38,7 +38,7 @@
 #include "src/core/lib/slice/slice_string_helpers.h"
 #include "src/core/lib/support/string.h"
 
-grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp");
+grpc_core::TraceFlag grpc_tcp_trace(false, "tcp");
 
 typedef struct {
   grpc_endpoint base;
@@ -78,7 +78,7 @@
 #define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
 static void tcp_unref(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp,
                       const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -91,7 +91,7 @@
 
 static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
                     int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -134,7 +134,7 @@
 static void call_read_cb(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp,
                          grpc_error* error) {
   grpc_closure* cb = tcp->read_cb;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg);
     size_t i;
     const char* str = grpc_error_string(error);
@@ -192,7 +192,7 @@
                                      grpc_error* error) {
   int status;
   grpc_tcp* tcp = (grpc_tcp*)tcpp;
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp,
             grpc_error_string(error));
   }
@@ -211,7 +211,7 @@
     call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error));
     TCP_UNREF(exec_ctx, tcp, "read");
   }
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp, str);
   }
@@ -243,7 +243,7 @@
   } else {
     error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Write failed");
   }
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     const char* str = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "write complete on %p: error=%s", tcp, str);
   }
@@ -263,7 +263,7 @@
   uv_write_t* write_req;
   GRPC_UV_ASSERT_SAME_THREAD();
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     size_t j;
 
     for (j = 0; j < write_slices->count; j++) {
@@ -341,7 +341,7 @@
                                  grpc_error* why) {
   grpc_tcp* tcp = (grpc_tcp*)ep;
   if (!tcp->shutting_down) {
-    if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+    if (grpc_tcp_trace.enabled()) {
       const char* str = grpc_error_string(why);
       gpr_log(GPR_DEBUG, "TCP %p shutdown why=%s", tcp->handle, str);
     }
@@ -388,7 +388,7 @@
   grpc_tcp* tcp = (grpc_tcp*)gpr_malloc(sizeof(grpc_tcp));
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", tcp);
   }
 
diff --git a/src/core/lib/iomgr/tcp_uv.h b/src/core/lib/iomgr/tcp_uv.h
index 708e846..4b4da36 100644
--- a/src/core/lib/iomgr/tcp_uv.h
+++ b/src/core/lib/iomgr/tcp_uv.h
@@ -38,7 +38,7 @@
 
 #include <uv.h>
 
-extern grpc_tracer_flag grpc_tcp_trace;
+extern grpc_core::TraceFlag grpc_tcp_trace;
 
 #define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192
 
diff --git a/src/core/lib/iomgr/tcp_windows.cc b/src/core/lib/iomgr/tcp_windows.cc
index 04922b4..33868cd 100644
--- a/src/core/lib/iomgr/tcp_windows.cc
+++ b/src/core/lib/iomgr/tcp_windows.cc
@@ -49,7 +49,7 @@
 #define GRPC_FIONBIO FIONBIO
 #endif
 
-grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp");
+grpc_core::TraceFlag grpc_tcp_trace(false, "tcp");
 
 static grpc_error* set_non_block(SOCKET sock) {
   int status;
@@ -124,7 +124,7 @@
 #define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__)
 static void tcp_unref(grpc_exec_ctx* exec_ctx, grpc_tcp* tcp,
                       const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
@@ -137,7 +137,7 @@
 
 static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file,
                     int line) {
-  if (GRPC_TRACER_ON(grpc_tcp_trace)) {
+  if (grpc_tcp_trace.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "TCP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val,
diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc
index a4bfbcb..fa95c43 100644
--- a/src/core/lib/iomgr/timer_generic.cc
+++ b/src/core/lib/iomgr/timer_generic.cc
@@ -42,11 +42,8 @@
 #define MIN_QUEUE_WINDOW_DURATION 0.01
 #define MAX_QUEUE_WINDOW_DURATION 1
 
-extern "C" {
-grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer");
-grpc_tracer_flag grpc_timer_check_trace =
-    GRPC_TRACER_INITIALIZER(false, "timer_check");
-}
+grpc_core::TraceFlag grpc_timer_trace(false, "timer");
+grpc_core::TraceFlag grpc_timer_check_trace(false, "timer_check");
 
 /* A "timer shard". Contains a 'heap' and a 'list' of timers. All timers with
  * deadlines earlier than 'queue_deadline" cap are maintained in the heap and
@@ -253,8 +250,6 @@
   g_shared_mutables.min_timer = grpc_exec_ctx_now(exec_ctx);
   gpr_tls_init(&g_last_seen_min_timer);
   gpr_tls_set(&g_last_seen_min_timer, 0);
-  grpc_register_tracer(&grpc_timer_trace);
-  grpc_register_tracer(&grpc_timer_check_trace);
 
   for (i = 0; i < g_num_shards; i++) {
     timer_shard* shard = &g_shards[i];
@@ -339,7 +334,7 @@
   timer->hash_table_next = nullptr;
 #endif
 
-  if (GRPC_TRACER_ON(grpc_timer_trace)) {
+  if (grpc_timer_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "TIMER %p: SET %" PRIdPTR " now %" PRIdPTR " call %p[%p]", timer,
             deadline, grpc_exec_ctx_now(exec_ctx), closure, closure->cb);
@@ -375,7 +370,7 @@
     timer->heap_index = INVALID_HEAP_INDEX;
     list_join(&shard->list, timer);
   }
-  if (GRPC_TRACER_ON(grpc_timer_trace)) {
+  if (grpc_timer_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "  .. add to shard %d with queue_deadline_cap=%" PRIdPTR
             " => is_first_timer=%s",
@@ -397,7 +392,7 @@
      grpc_timer_check. */
   if (is_first_timer) {
     gpr_mu_lock(&g_shared_mutables.mu);
-    if (GRPC_TRACER_ON(grpc_timer_trace)) {
+    if (grpc_timer_trace.enabled()) {
       gpr_log(GPR_DEBUG, "  .. old shard min_deadline=%" PRIdPTR,
               shard->min_deadline);
     }
@@ -427,7 +422,7 @@
 
   timer_shard* shard = &g_shards[GPR_HASH_POINTER(timer, g_num_shards)];
   gpr_mu_lock(&shard->mu);
-  if (GRPC_TRACER_ON(grpc_timer_trace)) {
+  if (grpc_timer_trace.enabled()) {
     gpr_log(GPR_DEBUG, "TIMER %p: CANCEL pending=%s", timer,
             timer->pending ? "true" : "false");
   }
@@ -468,7 +463,7 @@
       saturating_add(GPR_MAX(now, shard->queue_deadline_cap),
                      (gpr_atm)(deadline_delta * 1000.0));
 
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  .. shard[%d]->queue_deadline_cap --> %" PRIdPTR,
             (int)(shard - g_shards), shard->queue_deadline_cap);
   }
@@ -476,7 +471,7 @@
     next = timer->next;
 
     if (timer->deadline < shard->queue_deadline_cap) {
-      if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+      if (grpc_timer_check_trace.enabled()) {
         gpr_log(GPR_DEBUG, "  .. add timer with deadline %" PRIdPTR " to heap",
                 timer->deadline);
       }
@@ -493,7 +488,7 @@
 static grpc_timer* pop_one(timer_shard* shard, gpr_atm now) {
   grpc_timer* timer;
   for (;;) {
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG, "  .. shard[%d]: heap_empty=%s",
               (int)(shard - g_shards),
               grpc_timer_heap_is_empty(&shard->heap) ? "true" : "false");
@@ -503,13 +498,13 @@
       if (!refill_heap(shard, now)) return nullptr;
     }
     timer = grpc_timer_heap_top(&shard->heap);
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "  .. check top timer deadline=%" PRIdPTR " now=%" PRIdPTR,
               timer->deadline, now);
     }
     if (timer->deadline > now) return nullptr;
-    if (GRPC_TRACER_ON(grpc_timer_trace)) {
+    if (grpc_timer_trace.enabled()) {
       gpr_log(GPR_DEBUG, "TIMER %p: FIRE %" PRIdPTR "ms late via %s scheduler",
               timer, now - timer->deadline,
               timer->closure->scheduler->vtable->name);
@@ -534,7 +529,7 @@
   }
   *new_min_deadline = compute_min_deadline(shard);
   gpr_mu_unlock(&shard->mu);
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "  .. shard[%d] popped %" PRIdPTR,
             (int)(shard - g_shards), n);
   }
@@ -558,7 +553,7 @@
     gpr_mu_lock(&g_shared_mutables.mu);
     result = GRPC_TIMERS_CHECKED_AND_EMPTY;
 
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG, "  .. shard[%d]->min_deadline = %" PRIdPTR,
               (int)(g_shard_queue[0] - g_shards),
               g_shard_queue[0]->min_deadline);
@@ -576,7 +571,7 @@
         result = GRPC_TIMERS_FIRED;
       }
 
-      if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+      if (grpc_timer_check_trace.enabled()) {
         gpr_log(GPR_DEBUG,
                 "  .. result --> %d"
                 ", shard[%d]->min_deadline %" PRIdPTR " --> %" PRIdPTR
@@ -621,7 +616,7 @@
     if (next != nullptr) {
       *next = GPR_MIN(*next, min_timer);
     }
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG,
               "TIMER CHECK SKIP: now=%" PRIdPTR " min_timer=%" PRIdPTR, now,
               min_timer);
@@ -635,7 +630,7 @@
           : GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system");
 
   // tracing
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     char* next_str;
     if (next == nullptr) {
       next_str = gpr_strdup("NULL");
@@ -653,7 +648,7 @@
   grpc_timer_check_result r =
       run_some_expired_timers(exec_ctx, now, next, shutdown_error);
   // tracing
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     char* next_str;
     if (next == nullptr) {
       next_str = gpr_strdup("NULL");
diff --git a/src/core/lib/iomgr/timer_manager.cc b/src/core/lib/iomgr/timer_manager.cc
index 163c9b5..dac74ae 100644
--- a/src/core/lib/iomgr/timer_manager.cc
+++ b/src/core/lib/iomgr/timer_manager.cc
@@ -33,7 +33,7 @@
   struct completed_thread* next;
 } completed_thread;
 
-extern "C" grpc_tracer_flag grpc_timer_check_trace;
+extern grpc_core::TraceFlag grpc_timer_check_trace;
 
 // global mutex
 static gpr_mu g_mu;
@@ -81,7 +81,7 @@
   ++g_waiter_count;
   ++g_thread_count;
   gpr_mu_unlock(&g_mu);
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "Spawn timer thread");
   }
   gpr_thd_options opt = gpr_thd_options_default();
@@ -115,7 +115,7 @@
     // if there's no thread waiting with a timeout, kick an existing
     // waiter so that the next deadline is not missed
     if (!g_has_timed_waiter) {
-      if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+      if (grpc_timer_check_trace.enabled()) {
         gpr_log(GPR_DEBUG, "kick untimed waiter");
       }
       gpr_cv_signal(&g_cv_wait);
@@ -123,7 +123,7 @@
     gpr_mu_unlock(&g_mu);
   }
   // without our lock, flush the exec_ctx
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "flush exec_ctx");
   }
   grpc_exec_ctx_flush(exec_ctx);
@@ -178,7 +178,7 @@
         g_has_timed_waiter = true;
         g_timed_waiter_deadline = next;
 
-        if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+        if (grpc_timer_check_trace.enabled()) {
           grpc_millis wait_time = next - grpc_exec_ctx_now(exec_ctx);
           gpr_log(GPR_DEBUG, "sleep for a %" PRIdPTR " milliseconds",
                   wait_time);
@@ -188,15 +188,14 @@
       }
     }
 
-    if (GRPC_TRACER_ON(grpc_timer_check_trace) &&
-        next == GRPC_MILLIS_INF_FUTURE) {
+    if (grpc_timer_check_trace.enabled() && next == GRPC_MILLIS_INF_FUTURE) {
       gpr_log(GPR_DEBUG, "sleep until kicked");
     }
 
     gpr_cv_wait(&g_cv_wait, &g_mu,
                 grpc_millis_to_timespec(next, GPR_CLOCK_REALTIME));
 
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG, "wait ended: was_timed:%d kicked:%d",
               my_timed_waiter_generation == g_timed_waiter_generation,
               g_kicked);
@@ -241,7 +240,7 @@
 
            Consequently, we can just sleep forever here and be happy at some
            saved wakeup cycles. */
-        if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+        if (grpc_timer_check_trace.enabled()) {
           gpr_log(GPR_DEBUG, "timers not checked: expect another thread to");
         }
         next = GRPC_MILLIS_INF_FUTURE;
@@ -267,7 +266,7 @@
   ct->next = g_completed_threads;
   g_completed_threads = ct;
   gpr_mu_unlock(&g_mu);
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "End timer thread");
   }
 }
@@ -310,18 +309,18 @@
 
 static void stop_threads(void) {
   gpr_mu_lock(&g_mu);
-  if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+  if (grpc_timer_check_trace.enabled()) {
     gpr_log(GPR_DEBUG, "stop timer threads: threaded=%d", g_threaded);
   }
   if (g_threaded) {
     g_threaded = false;
     gpr_cv_broadcast(&g_cv_wait);
-    if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+    if (grpc_timer_check_trace.enabled()) {
       gpr_log(GPR_DEBUG, "num timer threads: %d", g_thread_count);
     }
     while (g_thread_count > 0) {
       gpr_cv_wait(&g_cv_shutdown, &g_mu, gpr_inf_future(GPR_CLOCK_REALTIME));
-      if (GRPC_TRACER_ON(grpc_timer_check_trace)) {
+      if (grpc_timer_check_trace.enabled()) {
         gpr_log(GPR_DEBUG, "num timer threads: %d", g_thread_count);
       }
       gc_completed_threads();
diff --git a/src/core/lib/iomgr/timer_uv.cc b/src/core/lib/iomgr/timer_uv.cc
index df40e54..fac2026 100644
--- a/src/core/lib/iomgr/timer_uv.cc
+++ b/src/core/lib/iomgr/timer_uv.cc
@@ -29,11 +29,8 @@
 
 #include <uv.h>
 
-extern "C" {
-grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer");
-grpc_tracer_flag grpc_timer_check_trace =
-    GRPC_TRACER_INITIALIZER(false, "timer_check");
-}
+grpc_core::TraceFlag grpc_timer_trace(false, "timer");
+grpc_core::TraceFlag grpc_timer_check_trace(false, "timer_check");
 
 static void timer_close_callback(uv_handle_t* handle) { gpr_free(handle); }
 
diff --git a/src/core/lib/security/context/security_context.cc b/src/core/lib/security/context/security_context.cc
index 36362d9..19c6148 100644
--- a/src/core/lib/security/context/security_context.cc
+++ b/src/core/lib/security/context/security_context.cc
@@ -29,10 +29,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_auth_context_refcount =
-    GRPC_TRACER_INITIALIZER(false, "auth_context_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_auth_context_refcount(
+    false, "auth_context_refcount");
 
 /* --- grpc_call --- */
 
@@ -135,7 +133,7 @@
                                          const char* file, int line,
                                          const char* reason) {
   if (ctx == nullptr) return nullptr;
-  if (GRPC_TRACER_ON(grpc_trace_auth_context_refcount)) {
+  if (grpc_trace_auth_context_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&ctx->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "AUTH_CONTEXT:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", ctx, val,
@@ -153,7 +151,7 @@
 void grpc_auth_context_unref(grpc_auth_context* ctx, const char* file, int line,
                              const char* reason) {
   if (ctx == nullptr) return;
-  if (GRPC_TRACER_ON(grpc_trace_auth_context_refcount)) {
+  if (grpc_trace_auth_context_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&ctx->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "AUTH_CONTEXT:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", ctx, val,
diff --git a/src/core/lib/security/context/security_context.h b/src/core/lib/security/context/security_context.h
index 4f049c4..5b27d1a 100644
--- a/src/core/lib/security/context/security_context.h
+++ b/src/core/lib/security/context/security_context.h
@@ -22,9 +22,7 @@
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/security/credentials/credentials.h"
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_auth_context_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_auth_context_refcount;
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/src/core/lib/security/credentials/jwt/jwt_credentials.cc
index d666e6b..77163c0 100644
--- a/src/core/lib/security/credentials/jwt/jwt_credentials.cc
+++ b/src/core/lib/security/credentials/jwt/jwt_credentials.cc
@@ -172,7 +172,7 @@
 
 grpc_call_credentials* grpc_service_account_jwt_access_credentials_create(
     const char* json_key, gpr_timespec token_lifetime, void* reserved) {
-  if (GRPC_TRACER_ON(grpc_api_trace)) {
+  if (grpc_api_trace.enabled()) {
     char* clean_json = redact_private_key(json_key);
     gpr_log(GPR_INFO,
             "grpc_service_account_jwt_access_credentials_create("
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
index 943d23f..ccefb4d 100644
--- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc
@@ -467,7 +467,7 @@
     const char* json_refresh_token, void* reserved) {
   grpc_auth_refresh_token token =
       grpc_auth_refresh_token_create_from_string(json_refresh_token);
-  if (GRPC_TRACER_ON(grpc_api_trace)) {
+  if (grpc_api_trace.enabled()) {
     char* loggable_token = create_loggable_refresh_token(&token);
     gpr_log(GPR_INFO,
             "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.cc b/src/core/lib/security/credentials/plugin/plugin_credentials.cc
index b83a1b4..1f1efd0 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.cc
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.cc
@@ -31,8 +31,7 @@
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/validate_metadata.h"
 
-grpc_tracer_flag grpc_plugin_credentials_trace =
-    GRPC_TRACER_INITIALIZER(false, "plugin_credentials");
+grpc_core::TraceFlag grpc_plugin_credentials_trace(false, "plugin_credentials");
 
 static void plugin_destruct(grpc_exec_ctx* exec_ctx,
                             grpc_call_credentials* creds) {
@@ -123,7 +122,7 @@
       nullptr, nullptr);
   grpc_plugin_credentials_pending_request* r =
       (grpc_plugin_credentials_pending_request*)request;
-  if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+  if (grpc_plugin_credentials_trace.enabled()) {
     gpr_log(GPR_INFO,
             "plugin_credentials[%p]: request %p: plugin returned "
             "asynchronously",
@@ -136,7 +135,7 @@
     grpc_error* error =
         process_plugin_result(&exec_ctx, r, md, num_md, status, error_details);
     GRPC_CLOSURE_SCHED(&exec_ctx, r->on_request_metadata, error);
-  } else if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+  } else if (grpc_plugin_credentials_trace.enabled()) {
     gpr_log(GPR_INFO,
             "plugin_credentials[%p]: request %p: plugin was previously "
             "cancelled",
@@ -172,7 +171,7 @@
     c->pending_requests = pending_request;
     gpr_mu_unlock(&c->mu);
     // Invoke the plugin.  The callback holds a ref to us.
-    if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+    if (grpc_plugin_credentials_trace.enabled()) {
       gpr_log(GPR_INFO, "plugin_credentials[%p]: request %p: invoking plugin",
               c, pending_request);
     }
@@ -185,7 +184,7 @@
                                 plugin_md_request_metadata_ready,
                                 pending_request, creds_md, &num_creds_md,
                                 &status, &error_details)) {
-      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+      if (grpc_plugin_credentials_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "plugin_credentials[%p]: request %p: plugin will return "
                 "asynchronously",
@@ -200,7 +199,7 @@
     // asynchronously by plugin_cancel_get_request_metadata(), so return
     // false.  Otherwise, process the result.
     if (pending_request->cancelled) {
-      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+      if (grpc_plugin_credentials_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "plugin_credentials[%p]: request %p was cancelled, error "
                 "will be returned asynchronously",
@@ -208,7 +207,7 @@
       }
       retval = false;
     } else {
-      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+      if (grpc_plugin_credentials_trace.enabled()) {
         gpr_log(GPR_INFO,
                 "plugin_credentials[%p]: request %p: plugin returned "
                 "synchronously",
@@ -237,7 +236,7 @@
            c->pending_requests;
        pending_request != nullptr; pending_request = pending_request->next) {
     if (pending_request->md_array == md_array) {
-      if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) {
+      if (grpc_plugin_credentials_trace.enabled()) {
         gpr_log(GPR_INFO, "plugin_credentials[%p]: cancelling request %p", c,
                 pending_request);
       }
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.h b/src/core/lib/security/credentials/plugin/plugin_credentials.h
index fc0955c..e1467b0 100644
--- a/src/core/lib/security/credentials/plugin/plugin_credentials.h
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.h
@@ -21,7 +21,7 @@
 
 #include "src/core/lib/security/credentials/credentials.h"
 
-extern grpc_tracer_flag grpc_plugin_credentials_trace;
+extern grpc_core::TraceFlag grpc_plugin_credentials_trace;
 
 struct grpc_plugin_credentials;
 
diff --git a/src/core/lib/security/transport/secure_endpoint.cc b/src/core/lib/security/transport/secure_endpoint.cc
index 3ba987a..4cd317a 100644
--- a/src/core/lib/security/transport/secure_endpoint.cc
+++ b/src/core/lib/security/transport/secure_endpoint.cc
@@ -61,8 +61,7 @@
   gpr_refcount ref;
 } secure_endpoint;
 
-grpc_tracer_flag grpc_trace_secure_endpoint =
-    GRPC_TRACER_INITIALIZER(false, "secure_endpoint");
+grpc_core::TraceFlag grpc_trace_secure_endpoint(false, "secure_endpoint");
 
 static void destroy(grpc_exec_ctx* exec_ctx, secure_endpoint* secure_ep) {
   secure_endpoint* ep = secure_ep;
@@ -86,7 +85,7 @@
 static void secure_endpoint_unref(grpc_exec_ctx* exec_ctx, secure_endpoint* ep,
                                   const char* reason, const char* file,
                                   int line) {
-  if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) {
+  if (grpc_trace_secure_endpoint.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECENDP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val,
@@ -99,7 +98,7 @@
 
 static void secure_endpoint_ref(secure_endpoint* ep, const char* reason,
                                 const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) {
+  if (grpc_trace_secure_endpoint.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECENDP   ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val,
@@ -131,7 +130,7 @@
 
 static void call_read_cb(grpc_exec_ctx* exec_ctx, secure_endpoint* ep,
                          grpc_error* error) {
-  if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) {
+  if (grpc_trace_secure_endpoint.enabled()) {
     size_t i;
     for (i = 0; i < ep->read_buffer->count; i++) {
       char* data = grpc_dump_slice(ep->read_buffer->slices[i],
@@ -271,7 +270,7 @@
 
   grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->output_buffer);
 
-  if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) {
+  if (grpc_trace_secure_endpoint.enabled()) {
     for (i = 0; i < slices->count; i++) {
       char* data =
           grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
diff --git a/src/core/lib/security/transport/secure_endpoint.h b/src/core/lib/security/transport/secure_endpoint.h
index db8233f..92c4574 100644
--- a/src/core/lib/security/transport/secure_endpoint.h
+++ b/src/core/lib/security/transport/secure_endpoint.h
@@ -29,7 +29,7 @@
 struct tsi_frame_protector;
 struct tsi_zero_copy_grpc_protector;
 
-extern grpc_tracer_flag grpc_trace_secure_endpoint;
+extern grpc_core::TraceFlag grpc_trace_secure_endpoint;
 
 /* Takes ownership of protector, zero_copy_protector, and to_wrap, and refs
  * leftover_slices. If zero_copy_protector is not NULL, protector will never be
diff --git a/src/core/lib/security/transport/security_connector.cc b/src/core/lib/security/transport/security_connector.cc
index b996cc8..c56e459 100644
--- a/src/core/lib/security/transport/security_connector.cc
+++ b/src/core/lib/security/transport/security_connector.cc
@@ -44,10 +44,8 @@
 #include "src/core/tsi/ssl_transport_security.h"
 #include "src/core/tsi/transport_security_adapter.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_security_connector_refcount =
-    GRPC_TRACER_INITIALIZER(false, "security_connector_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_security_connector_refcount(
+    false, "security_connector_refcount");
 
 /* -- Constants. -- */
 
@@ -198,7 +196,7 @@
     grpc_security_connector* sc, const char* file, int line,
     const char* reason) {
   if (sc == nullptr) return nullptr;
-  if (GRPC_TRACER_ON(grpc_trace_security_connector_refcount)) {
+  if (grpc_trace_security_connector_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&sc->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECURITY_CONNECTOR:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", sc,
@@ -219,7 +217,7 @@
                                    const char* file, int line,
                                    const char* reason) {
   if (sc == nullptr) return;
-  if (GRPC_TRACER_ON(grpc_trace_security_connector_refcount)) {
+  if (grpc_trace_security_connector_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&sc->refcount.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "SECURITY_CONNECTOR:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", sc,
diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/transport/security_connector.h
index 79fdbc1..7cde358 100644
--- a/src/core/lib/security/transport/security_connector.h
+++ b/src/core/lib/security/transport/security_connector.h
@@ -33,9 +33,7 @@
 extern "C" {
 #endif
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_security_connector_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_security_connector_refcount;
 
 /* --- status enum. --- */
 
diff --git a/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h
index 2439fc0..10527dc 100644
--- a/src/core/lib/slice/slice_internal.h
+++ b/src/core/lib/slice/slice_internal.h
@@ -32,6 +32,9 @@
 void grpc_slice_unref_internal(grpc_exec_ctx* exec_ctx, grpc_slice slice);
 void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx* exec_ctx,
                                                 grpc_slice_buffer* sb);
+void grpc_slice_buffer_partial_unref_internal(grpc_exec_ctx* exec_ctx,
+                                              grpc_slice_buffer* sb,
+                                              size_t idx);
 void grpc_slice_buffer_destroy_internal(grpc_exec_ctx* exec_ctx,
                                         grpc_slice_buffer* sb);
 
diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc
index 503a96b..bca14a0 100644
--- a/src/core/lib/support/cpu_posix.cc
+++ b/src/core/lib/support/cpu_posix.cc
@@ -18,21 +18,23 @@
 
 #include <grpc/support/port_platform.h>
 
-#ifdef GPR_CPU_POSIX
+#if defined(GPR_CPU_POSIX)
 
 #include <errno.h>
+#include <pthread.h>
 #include <string.h>
 #include <unistd.h>
 
+#include <grpc/support/alloc.h>
 #include <grpc/support/cpu.h>
 #include <grpc/support/log.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/useful.h>
 
-static __thread char magic_thread_local;
-
 static long ncpus = 0;
 
+static pthread_key_t thread_id_key;
+
 static void init_ncpus() {
   ncpus = sysconf(_SC_NPROCESSORS_ONLN);
   if (ncpus < 1 || ncpus > INT32_MAX) {
@@ -47,12 +49,32 @@
   return (unsigned)ncpus;
 }
 
+static void delete_thread_id(void* value) {
+  if (value) {
+    gpr_free(value);
+  }
+}
+
+static void init_thread_id_key(void) {
+  pthread_key_create(&thread_id_key, delete_thread_id);
+}
+
 unsigned gpr_cpu_current_cpu(void) {
   /* NOTE: there's no way I know to return the actual cpu index portably...
      most code that's using this is using it to shard across work queues though,
      so here we use thread identity instead to achieve a similar though not
      identical effect */
-  return (unsigned)GPR_HASH_POINTER(&magic_thread_local, gpr_cpu_num_cores());
+  static gpr_once once = GPR_ONCE_INIT;
+  gpr_once_init(&once, init_thread_id_key);
+
+  unsigned int* thread_id =
+      static_cast<unsigned int*>(pthread_getspecific(thread_id_key));
+  if (thread_id == nullptr) {
+    thread_id = static_cast<unsigned int*>(gpr_malloc(sizeof(unsigned int)));
+    pthread_setspecific(thread_id_key, thread_id);
+  }
+
+  return (unsigned)GPR_HASH_POINTER(thread_id, gpr_cpu_num_cores());
 }
 
 #endif /* GPR_CPU_POSIX */
diff --git a/src/core/lib/surface/alarm.cc b/src/core/lib/surface/alarm.cc
index d8b1f18..b1c9f7b 100644
--- a/src/core/lib/surface/alarm.cc
+++ b/src/core/lib/surface/alarm.cc
@@ -27,10 +27,8 @@
 #include "src/core/lib/iomgr/timer.h"
 #include "src/core/lib/surface/completion_queue.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_alarm_refcount =
-    GRPC_TRACER_INITIALIZER(false, "alarm_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_alarm_refcount(false,
+                                                        "alarm_refcount");
 
 struct grpc_alarm {
   gpr_refcount refs;
@@ -59,7 +57,7 @@
 #ifndef NDEBUG
 static void alarm_ref_dbg(grpc_alarm* alarm, const char* reason,
                           const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) {
+  if (grpc_trace_alarm_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "Alarm:%p  ref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val,
@@ -71,7 +69,7 @@
 
 static void alarm_unref_dbg(grpc_alarm* alarm, const char* reason,
                             const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) {
+  if (grpc_trace_alarm_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "Alarm:%p  Unref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val,
@@ -103,7 +101,7 @@
   grpc_alarm* alarm = (grpc_alarm*)gpr_malloc(sizeof(grpc_alarm));
 
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) {
+  if (grpc_trace_alarm_refcount.enabled()) {
     gpr_log(GPR_DEBUG, "Alarm:%p created (ref: 1)", alarm);
   }
 #endif
diff --git a/src/core/lib/surface/alarm_internal.h b/src/core/lib/surface/alarm_internal.h
index 136b605..2ee3a31 100644
--- a/src/core/lib/surface/alarm_internal.h
+++ b/src/core/lib/surface/alarm_internal.h
@@ -22,14 +22,14 @@
 #include <grpc/support/log.h>
 #include "src/core/lib/debug/trace.h"
 
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_alarm_refcount;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #ifndef NDEBUG
 
-extern grpc_tracer_flag grpc_trace_alarm_refcount;
-
 #define GRPC_ALARM_REF(a, reason) alarm_ref_dbg(a, reason, __FILE__, __LINE__)
 #define GRPC_ALARM_UNREF(a, reason) \
   alarm_unref_dbg(a, reason, __FILE__, __LINE__)
diff --git a/src/core/lib/surface/api_trace.cc b/src/core/lib/surface/api_trace.cc
index 5697330..7ab836a 100644
--- a/src/core/lib/surface/api_trace.cc
+++ b/src/core/lib/surface/api_trace.cc
@@ -19,4 +19,4 @@
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/debug/trace.h"
 
-grpc_tracer_flag grpc_api_trace = GRPC_TRACER_INITIALIZER(false, "api");
+grpc_core::TraceFlag grpc_api_trace(false, "api");
diff --git a/src/core/lib/surface/api_trace.h b/src/core/lib/surface/api_trace.h
index 105abdf..a4e11ce 100644
--- a/src/core/lib/surface/api_trace.h
+++ b/src/core/lib/surface/api_trace.h
@@ -22,11 +22,7 @@
 #include <grpc/support/log.h>
 #include "src/core/lib/debug/trace.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern grpc_tracer_flag grpc_api_trace;
+extern grpc_core::TraceFlag grpc_api_trace;
 
 /* Provide unwrapping macros because we're in C89 and variadic macros weren't
    introduced until C99... */
@@ -47,12 +43,8 @@
 /* Due to the limitations of C89's preprocessor, the arity of the var-arg list
    'nargs' must be specified. */
 #define GRPC_API_TRACE(fmt, nargs, args)                      \
-  if (GRPC_TRACER_ON(grpc_api_trace)) {                       \
+  if (grpc_api_trace.enabled()) {                             \
     gpr_log(GPR_INFO, fmt GRPC_API_TRACE_UNWRAP##nargs args); \
   }
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* GRPC_CORE_LIB_SURFACE_API_TRACE_H */
diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc
index 3a06b0c..a83c95c 100644
--- a/src/core/lib/surface/call.cc
+++ b/src/core/lib/surface/call.cc
@@ -259,10 +259,8 @@
   gpr_atm recv_state;
 };
 
-grpc_tracer_flag grpc_call_error_trace =
-    GRPC_TRACER_INITIALIZER(false, "call_error");
-grpc_tracer_flag grpc_compression_trace =
-    GRPC_TRACER_INITIALIZER(false, "compression");
+grpc_core::TraceFlag grpc_call_error_trace(false, "call_error");
+grpc_core::TraceFlag grpc_compression_trace(false, "compression");
 
 #define CALL_STACK_FROM_CALL(call) ((grpc_call_stack*)((call) + 1))
 #define CALL_FROM_CALL_STACK(call_stack) (((grpc_call*)(call_stack)) - 1)
@@ -765,7 +763,7 @@
   for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
     status[i] = unpack_received_status(gpr_atm_acq_load(&call->status[i]));
   }
-  if (GRPC_TRACER_ON(grpc_call_error_trace)) {
+  if (grpc_call_error_trace.enabled()) {
     gpr_log(GPR_DEBUG, "get_final_status %s", call->is_client ? "CLI" : "SVR");
     for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
       if (status[i].is_set) {
@@ -1427,7 +1425,7 @@
   }
 
   if (error != GRPC_ERROR_NONE) {
-    if (GRPC_TRACER_ON(grpc_trace_operation_failures)) {
+    if (grpc_trace_operation_failures.enabled()) {
       GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error));
     }
     grpc_byte_stream_destroy(exec_ctx, call->receiving_stream);
@@ -1531,7 +1529,7 @@
     GPR_ASSERT(call->stream_encodings_accepted_by_peer != 0);
     if (!GPR_BITGET(call->stream_encodings_accepted_by_peer,
                     call->incoming_stream_compression_algorithm)) {
-      if (GRPC_TRACER_ON(grpc_compression_trace)) {
+      if (grpc_compression_trace.enabled()) {
         const char* algo_name = nullptr;
         grpc_stream_compression_algorithm_name(
             call->incoming_stream_compression_algorithm, &algo_name);
@@ -1574,7 +1572,7 @@
     GPR_ASSERT(call->encodings_accepted_by_peer != 0);
     if (!GPR_BITGET(call->encodings_accepted_by_peer,
                     call->incoming_compression_algorithm)) {
-      if (GRPC_TRACER_ON(grpc_compression_trace)) {
+      if (grpc_compression_trace.enabled()) {
         const char* algo_name = nullptr;
         grpc_compression_algorithm_name(call->incoming_compression_algorithm,
                                         &algo_name);
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
index d4e596f..07c4e48 100644
--- a/src/core/lib/surface/call.h
+++ b/src/core/lib/surface/call.h
@@ -102,8 +102,7 @@
 void* grpc_call_context_get(grpc_call* call, grpc_context_index elem);
 
 #define GRPC_CALL_LOG_BATCH(sev, call, ops, nops, tag) \
-  if (GRPC_TRACER_ON(grpc_api_trace))                  \
-  grpc_call_log_batch(sev, call, ops, nops, tag)
+  if (grpc_api_trace.enabled()) grpc_call_log_batch(sev, call, ops, nops, tag)
 
 uint8_t grpc_call_is_client(grpc_call* call);
 
@@ -112,8 +111,8 @@
 grpc_compression_algorithm grpc_call_compression_for_level(
     grpc_call* call, grpc_compression_level level);
 
-extern grpc_tracer_flag grpc_call_error_trace;
-extern grpc_tracer_flag grpc_compression_trace;
+extern grpc_core::TraceFlag grpc_call_error_trace;
+extern grpc_core::TraceFlag grpc_compression_trace;
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc
index a171f90..98d7e35 100644
--- a/src/core/lib/surface/completion_queue.cc
+++ b/src/core/lib/surface/completion_queue.cc
@@ -40,14 +40,9 @@
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/surface/event_string.h"
 
-grpc_tracer_flag grpc_trace_operation_failures =
-    GRPC_TRACER_INITIALIZER(false, "op_failure");
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_pending_tags =
-    GRPC_TRACER_INITIALIZER(false, "pending_tags");
-grpc_tracer_flag grpc_trace_cq_refcount =
-    GRPC_TRACER_INITIALIZER(false, "cq_refcount");
-#endif
+grpc_core::TraceFlag grpc_trace_operation_failures(false, "op_failure");
+grpc_core::DebugOnlyTraceFlag grpc_trace_pending_tags(false, "pending_tags");
+grpc_core::DebugOnlyTraceFlag grpc_trace_cq_refcount(false, "cq_refcount");
 
 // Specifies a cq thread local cache.
 // The first event that occurs on a thread
@@ -340,18 +335,15 @@
 #define POLLSET_FROM_CQ(cq) \
   ((grpc_pollset*)(cq->vtable->data_size + (char*)DATA_FROM_CQ(cq)))
 
-grpc_tracer_flag grpc_cq_pluck_trace =
-    GRPC_TRACER_INITIALIZER(true, "queue_pluck");
-grpc_tracer_flag grpc_cq_event_timeout_trace =
-    GRPC_TRACER_INITIALIZER(true, "queue_timeout");
+grpc_core::TraceFlag grpc_cq_pluck_trace(true, "queue_pluck");
+grpc_core::TraceFlag grpc_cq_event_timeout_trace(true, "queue_timeout");
 
-#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event)    \
-  if (GRPC_TRACER_ON(grpc_api_trace) &&                 \
-      (GRPC_TRACER_ON(grpc_cq_pluck_trace) ||           \
-       (event)->type != GRPC_QUEUE_TIMEOUT)) {          \
-    char* _ev = grpc_event_string(event);               \
-    gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \
-    gpr_free(_ev);                                      \
+#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event)                       \
+  if (grpc_api_trace.enabled() && (grpc_cq_pluck_trace.enabled() ||        \
+                                   (event)->type != GRPC_QUEUE_TIMEOUT)) { \
+    char* _ev = grpc_event_string(event);                                  \
+    gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev);                    \
+    gpr_free(_ev);                                                         \
   }
 
 static void on_pollset_shutdown_done(grpc_exec_ctx* exec_ctx, void* cq,
@@ -533,7 +525,7 @@
 #ifndef NDEBUG
 void grpc_cq_internal_ref(grpc_completion_queue* cq, const char* reason,
                           const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_cq_refcount)) {
+  if (grpc_trace_cq_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "CQ:%p   ref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val + 1,
@@ -554,7 +546,7 @@
 #ifndef NDEBUG
 void grpc_cq_internal_unref(grpc_exec_ctx* exec_ctx, grpc_completion_queue* cq,
                             const char* reason, const char* file, int line) {
-  if (GRPC_TRACER_ON(grpc_trace_cq_refcount)) {
+  if (grpc_trace_cq_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
             "CQ:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val - 1,
@@ -656,16 +648,14 @@
                                void* done_arg, grpc_cq_completion* storage) {
   GPR_TIMER_BEGIN("cq_end_op_for_next", 0);
 
-  if (GRPC_TRACER_ON(grpc_api_trace) ||
-      (GRPC_TRACER_ON(grpc_trace_operation_failures) &&
-       error != GRPC_ERROR_NONE)) {
+  if (grpc_api_trace.enabled() ||
+      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
     const char* errmsg = grpc_error_string(error);
     GRPC_API_TRACE(
         "cq_end_op_for_next(exec_ctx=%p, cq=%p, tag=%p, error=%s, "
         "done=%p, done_arg=%p, storage=%p)",
         7, (exec_ctx, cq, tag, errmsg, done, done_arg, storage));
-    if (GRPC_TRACER_ON(grpc_trace_operation_failures) &&
-        error != GRPC_ERROR_NONE) {
+    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
       gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
     }
   }
@@ -745,16 +735,14 @@
 
   GPR_TIMER_BEGIN("cq_end_op_for_pluck", 0);
 
-  if (GRPC_TRACER_ON(grpc_api_trace) ||
-      (GRPC_TRACER_ON(grpc_trace_operation_failures) &&
-       error != GRPC_ERROR_NONE)) {
+  if (grpc_api_trace.enabled() ||
+      (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) {
     const char* errmsg = grpc_error_string(error);
     GRPC_API_TRACE(
         "cq_end_op_for_pluck(exec_ctx=%p, cq=%p, tag=%p, error=%s, "
         "done=%p, done_arg=%p, storage=%p)",
         7, (exec_ctx, cq, tag, errmsg, done, done_arg, storage));
-    if (GRPC_TRACER_ON(grpc_trace_operation_failures) &&
-        error != GRPC_ERROR_NONE) {
+    if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) {
       gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg);
     }
   }
@@ -848,7 +836,7 @@
 
 #ifndef NDEBUG
 static void dump_pending_tags(grpc_completion_queue* cq) {
-  if (!GRPC_TRACER_ON(grpc_trace_pending_tags)) return;
+  if (!grpc_trace_pending_tags.enabled()) return;
 
   gpr_strvec v;
   gpr_strvec_init(&v);
@@ -1112,7 +1100,7 @@
 
   GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0);
 
-  if (GRPC_TRACER_ON(grpc_cq_pluck_trace)) {
+  if (grpc_cq_pluck_trace.enabled()) {
     GRPC_API_TRACE(
         "grpc_completion_queue_pluck("
         "cq=%p, tag=%p, "
diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h
index 0ed9875..9fdb48d 100644
--- a/src/core/lib/surface/completion_queue.h
+++ b/src/core/lib/surface/completion_queue.h
@@ -27,14 +27,11 @@
 
 /* These trace flags default to 1. The corresponding lines are only traced
    if grpc_api_trace is also truthy */
-extern grpc_tracer_flag grpc_cq_pluck_trace;
-extern grpc_tracer_flag grpc_cq_event_timeout_trace;
-extern grpc_tracer_flag grpc_trace_operation_failures;
-
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_pending_tags;
-extern grpc_tracer_flag grpc_trace_cq_refcount;
-#endif
+extern grpc_core::TraceFlag grpc_cq_pluck_trace;
+extern grpc_core::TraceFlag grpc_cq_event_timeout_trace;
+extern grpc_core::TraceFlag grpc_trace_operation_failures;
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_pending_tags;
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_cq_refcount;
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc
index 673e13c..c6d2f0a 100644
--- a/src/core/lib/surface/init.cc
+++ b/src/core/lib/surface/init.cc
@@ -126,30 +126,6 @@
     grpc_slice_intern_init();
     grpc_mdctx_global_init();
     grpc_channel_init_init();
-    grpc_register_tracer(&grpc_api_trace);
-    grpc_register_tracer(&grpc_trace_channel);
-    grpc_register_tracer(&grpc_connectivity_state_trace);
-    grpc_register_tracer(&grpc_trace_channel_stack_builder);
-    grpc_register_tracer(&grpc_http1_trace);
-    grpc_register_tracer(&grpc_cq_pluck_trace);  // default on
-    grpc_register_tracer(&grpc_call_combiner_trace);
-    grpc_register_tracer(&grpc_combiner_trace);
-    grpc_register_tracer(&grpc_server_channel_trace);
-    grpc_register_tracer(&grpc_bdp_estimator_trace);
-    grpc_register_tracer(&grpc_cq_event_timeout_trace);  // default on
-    grpc_register_tracer(&grpc_trace_operation_failures);
-    grpc_register_tracer(&grpc_resource_quota_trace);
-    grpc_register_tracer(&grpc_call_error_trace);
-#ifndef NDEBUG
-    grpc_register_tracer(&grpc_trace_pending_tags);
-    grpc_register_tracer(&grpc_trace_alarm_refcount);
-    grpc_register_tracer(&grpc_trace_cq_refcount);
-    grpc_register_tracer(&grpc_trace_closure);
-    grpc_register_tracer(&grpc_trace_error_refcount);
-    grpc_register_tracer(&grpc_trace_stream_refcount);
-    grpc_register_tracer(&grpc_trace_fd_refcount);
-    grpc_register_tracer(&grpc_trace_metadata);
-#endif
     grpc_security_pre_init();
     grpc_iomgr_init(&exec_ctx);
     gpr_timers_global_init();
diff --git a/src/core/lib/surface/init_secure.cc b/src/core/lib/surface/init_secure.cc
index deb9a8e..3eee570 100644
--- a/src/core/lib/surface/init_secure.cc
+++ b/src/core/lib/surface/init_secure.cc
@@ -24,6 +24,7 @@
 #include <string.h>
 
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/security/credentials/plugin/plugin_credentials.h"
 #include "src/core/lib/security/transport/auth_filters.h"
@@ -33,18 +34,7 @@
 #include "src/core/lib/surface/channel_init.h"
 #include "src/core/tsi/transport_security_interface.h"
 
-#ifndef NDEBUG
-#include "src/core/lib/security/context/security_context.h"
-#endif
-
-void grpc_security_pre_init(void) {
-  grpc_register_tracer(&grpc_trace_secure_endpoint);
-  grpc_register_tracer(&tsi_tracing_enabled);
-#ifndef NDEBUG
-  grpc_register_tracer(&grpc_trace_auth_context_refcount);
-  grpc_register_tracer(&grpc_trace_security_connector_refcount);
-#endif
-}
+void grpc_security_pre_init(void) {}
 
 static bool maybe_prepend_client_auth_filter(
     grpc_exec_ctx* exec_ctx, grpc_channel_stack_builder* builder, void* arg) {
@@ -85,7 +75,4 @@
                                    maybe_prepend_server_auth_filter, nullptr);
 }
 
-void grpc_security_init() {
-  grpc_security_register_handshaker_factories();
-  grpc_register_tracer(&grpc_plugin_credentials_trace);
-}
+void grpc_security_init() { grpc_security_register_handshaker_factories(); }
diff --git a/src/core/lib/surface/lame_client.cc b/src/core/lib/surface/lame_client.cc
index 7114a9f..d1cf4d7 100644
--- a/src/core/lib/surface/lame_client.cc
+++ b/src/core/lib/surface/lame_client.cc
@@ -25,7 +25,6 @@
 
 #include "src/core/lib/support/atomic.h"
 
-extern "C" {
 #include "src/core/lib/channel/channel_stack.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/api_trace.h"
@@ -33,7 +32,6 @@
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/surface/lame_client.h"
 #include "src/core/lib/transport/static_metadata.h"
-}
 
 namespace grpc_core {
 
diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc
index 2e8609e..57bb6cc 100644
--- a/src/core/lib/surface/server.cc
+++ b/src/core/lib/surface/server.cc
@@ -60,8 +60,7 @@
 
 typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type;
 
-grpc_tracer_flag grpc_server_channel_trace =
-    GRPC_TRACER_INITIALIZER(false, "server_channel");
+grpc_core::TraceFlag grpc_server_channel_trace(false, "server_channel");
 
 typedef struct requested_call {
   gpr_mpscq_node request_link; /* must be first */
@@ -428,7 +427,7 @@
   GRPC_CLOSURE_INIT(&chand->finish_destroy_channel_closure,
                     finish_destroy_channel, chand, grpc_schedule_on_exec_ctx);
 
-  if (GRPC_TRACER_ON(grpc_server_channel_trace) && error != GRPC_ERROR_NONE) {
+  if (grpc_server_channel_trace.enabled() && error != GRPC_ERROR_NONE) {
     const char* msg = grpc_error_string(error);
     gpr_log(GPR_INFO, "Disconnected client: %s", msg);
   }
diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h
index e3c43f9..d5c2b0f 100644
--- a/src/core/lib/surface/server.h
+++ b/src/core/lib/surface/server.h
@@ -31,7 +31,7 @@
 extern const grpc_channel_filter grpc_server_top_filter;
 
 /** Lightweight tracing of server channel state */
-extern grpc_tracer_flag grpc_server_channel_trace;
+extern grpc_core::TraceFlag grpc_server_channel_trace;
 
 /* Add a listener to the server: when the server starts, it will call start,
    and when it shuts down, it will call destroy */
diff --git a/src/core/lib/transport/bdp_estimator.cc b/src/core/lib/transport/bdp_estimator.cc
index e09ae8e..bb0e583 100644
--- a/src/core/lib/transport/bdp_estimator.cc
+++ b/src/core/lib/transport/bdp_estimator.cc
@@ -23,8 +23,7 @@
 
 #include <grpc/support/useful.h>
 
-grpc_tracer_flag grpc_bdp_estimator_trace =
-    GRPC_TRACER_INITIALIZER(false, "bdp_estimator");
+grpc_core::TraceFlag grpc_bdp_estimator_trace(false, "bdp_estimator");
 
 namespace grpc_core {
 
@@ -44,7 +43,7 @@
   double dt = (double)dt_ts.tv_sec + 1e-9 * (double)dt_ts.tv_nsec;
   double bw = dt > 0 ? ((double)accumulator_ / dt) : 0;
   int start_inter_ping_delay = inter_ping_delay_;
-  if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+  if (grpc_bdp_estimator_trace.enabled()) {
     gpr_log(GPR_DEBUG,
             "bdp[%s]:complete acc=%" PRId64 " est=%" PRId64
             " dt=%lf bw=%lfMbs bw_est=%lfMbs",
@@ -55,7 +54,7 @@
   if (accumulator_ > 2 * estimate_ / 3 && bw > bw_est_) {
     estimate_ = GPR_MAX(accumulator_, estimate_ * 2);
     bw_est_ = bw;
-    if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "bdp[%s]: estimate increased to %" PRId64, name_,
               estimate_);
     }
@@ -72,7 +71,7 @@
   }
   if (start_inter_ping_delay != inter_ping_delay_) {
     stable_estimate_count_ = 0;
-    if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "bdp[%s]:update_inter_time to %dms", name_,
               inter_ping_delay_);
     }
diff --git a/src/core/lib/transport/bdp_estimator.h b/src/core/lib/transport/bdp_estimator.h
index f7b94a8..df3a86c 100644
--- a/src/core/lib/transport/bdp_estimator.h
+++ b/src/core/lib/transport/bdp_estimator.h
@@ -31,7 +31,7 @@
 #include "src/core/lib/debug/trace.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 
-extern grpc_tracer_flag grpc_bdp_estimator_trace;
+extern grpc_core::TraceFlag grpc_bdp_estimator_trace;
 
 namespace grpc_core {
 
@@ -49,7 +49,7 @@
   // grpc_bdp_estimator_add_incoming_bytes once a ping has been scheduled by a
   // transport (but not necessarily started)
   void SchedulePing() {
-    if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "bdp[%s]:sched acc=%" PRId64 " est=%" PRId64, name_,
               accumulator_, estimate_);
     }
@@ -62,7 +62,7 @@
   // once
   // the ping is on the wire
   void StartPing() {
-    if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) {
+    if (grpc_bdp_estimator_trace.enabled()) {
       gpr_log(GPR_DEBUG, "bdp[%s]:start acc=%" PRId64 " est=%" PRId64, name_,
               accumulator_, estimate_);
     }
diff --git a/src/core/lib/transport/connectivity_state.cc b/src/core/lib/transport/connectivity_state.cc
index 55f4236..e7e5dbd 100644
--- a/src/core/lib/transport/connectivity_state.cc
+++ b/src/core/lib/transport/connectivity_state.cc
@@ -24,8 +24,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-grpc_tracer_flag grpc_connectivity_state_trace =
-    GRPC_TRACER_INITIALIZER(false, "connectivity_state");
+grpc_core::TraceFlag grpc_connectivity_state_trace(false, "connectivity_state");
 
 const char* grpc_connectivity_state_name(grpc_connectivity_state state) {
   switch (state) {
@@ -78,7 +77,7 @@
   grpc_connectivity_state cur =
       (grpc_connectivity_state)gpr_atm_no_barrier_load(
           &tracker->current_state_atm);
-  if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+  if (grpc_connectivity_state_trace.enabled()) {
     gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name,
             grpc_connectivity_state_name(cur));
   }
@@ -90,7 +89,7 @@
   grpc_connectivity_state cur =
       (grpc_connectivity_state)gpr_atm_no_barrier_load(
           &tracker->current_state_atm);
-  if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+  if (grpc_connectivity_state_trace.enabled()) {
     gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name,
             grpc_connectivity_state_name(cur));
   }
@@ -111,7 +110,7 @@
   grpc_connectivity_state cur =
       (grpc_connectivity_state)gpr_atm_no_barrier_load(
           &tracker->current_state_atm);
-  if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+  if (grpc_connectivity_state_trace.enabled()) {
     if (current == nullptr) {
       gpr_log(GPR_DEBUG, "CONWATCH: %p %s: unsubscribe notify=%p", tracker,
               tracker->name, notify);
@@ -165,7 +164,7 @@
       (grpc_connectivity_state)gpr_atm_no_barrier_load(
           &tracker->current_state_atm);
   grpc_connectivity_state_watcher* w;
-  if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+  if (grpc_connectivity_state_trace.enabled()) {
     const char* error_string = grpc_error_string(error);
     gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker,
             tracker->name, grpc_connectivity_state_name(cur),
@@ -192,7 +191,7 @@
   while ((w = tracker->watchers) != nullptr) {
     *w->current = state;
     tracker->watchers = w->next;
-    if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) {
+    if (grpc_connectivity_state_trace.enabled()) {
       gpr_log(GPR_DEBUG, "NOTIFY: %p %s: %p", tracker, tracker->name,
               w->notify);
     }
diff --git a/src/core/lib/transport/connectivity_state.h b/src/core/lib/transport/connectivity_state.h
index 792e27c..60d20dd 100644
--- a/src/core/lib/transport/connectivity_state.h
+++ b/src/core/lib/transport/connectivity_state.h
@@ -47,7 +47,7 @@
   char* name;
 } grpc_connectivity_state_tracker;
 
-extern grpc_tracer_flag grpc_connectivity_state_trace;
+extern grpc_core::TraceFlag grpc_connectivity_state_trace;
 
 /** enum --> string conversion */
 const char* grpc_connectivity_state_name(grpc_connectivity_state state);
diff --git a/src/core/lib/transport/metadata.cc b/src/core/lib/transport/metadata.cc
index a16f050..0f30c75 100644
--- a/src/core/lib/transport/metadata.cc
+++ b/src/core/lib/transport/metadata.cc
@@ -48,9 +48,9 @@
  * used to determine which kind of element a pointer refers to.
  */
 
+grpc_core::DebugOnlyTraceFlag grpc_trace_metadata(false, "metadata");
+
 #ifndef NDEBUG
-grpc_tracer_flag grpc_trace_metadata =
-    GRPC_TRACER_INITIALIZER(false, "metadata");
 #define DEBUG_ARGS , const char *file, int line
 #define FWD_DEBUG_ARGS , file, line
 #define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
@@ -149,7 +149,7 @@
 static void ref_md_locked(mdtab_shard* shard,
                           interned_metadata* md DEBUG_ARGS) {
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+  if (grpc_trace_metadata.enabled()) {
     char* key_str = grpc_slice_to_c_string(md->key);
     char* value_str = grpc_slice_to_c_string(md->value);
     gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
@@ -252,7 +252,7 @@
     allocated->value = grpc_slice_ref_internal(value);
     gpr_atm_rel_store(&allocated->refcnt, 1);
 #ifndef NDEBUG
-    if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+    if (grpc_trace_metadata.enabled()) {
       char* key_str = grpc_slice_to_c_string(allocated->key);
       char* value_str = grpc_slice_to_c_string(allocated->value);
       gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'",
@@ -306,7 +306,7 @@
   shard->elems[idx] = md;
   gpr_mu_init(&md->mu_user_data);
 #ifndef NDEBUG
-  if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+  if (grpc_trace_metadata.enabled()) {
     char* key_str = grpc_slice_to_c_string(md->key);
     char* value_str = grpc_slice_to_c_string(md->value);
     gpr_log(GPR_DEBUG, "ELM   NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void*)md,
@@ -373,7 +373,7 @@
     case GRPC_MDELEM_STORAGE_INTERNED: {
       interned_metadata* md = (interned_metadata*)GRPC_MDELEM_DATA(gmd);
 #ifndef NDEBUG
-      if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+      if (grpc_trace_metadata.enabled()) {
         char* key_str = grpc_slice_to_c_string(md->key);
         char* value_str = grpc_slice_to_c_string(md->value);
         gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
@@ -395,7 +395,7 @@
     case GRPC_MDELEM_STORAGE_ALLOCATED: {
       allocated_metadata* md = (allocated_metadata*)GRPC_MDELEM_DATA(gmd);
 #ifndef NDEBUG
-      if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+      if (grpc_trace_metadata.enabled()) {
         char* key_str = grpc_slice_to_c_string(md->key);
         char* value_str = grpc_slice_to_c_string(md->value);
         gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
@@ -425,7 +425,7 @@
     case GRPC_MDELEM_STORAGE_INTERNED: {
       interned_metadata* md = (interned_metadata*)GRPC_MDELEM_DATA(gmd);
 #ifndef NDEBUG
-      if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+      if (grpc_trace_metadata.enabled()) {
         char* key_str = grpc_slice_to_c_string(md->key);
         char* value_str = grpc_slice_to_c_string(md->value);
         gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
@@ -451,7 +451,7 @@
     case GRPC_MDELEM_STORAGE_ALLOCATED: {
       allocated_metadata* md = (allocated_metadata*)GRPC_MDELEM_DATA(gmd);
 #ifndef NDEBUG
-      if (GRPC_TRACER_ON(grpc_trace_metadata)) {
+      if (grpc_trace_metadata.enabled()) {
         char* key_str = grpc_slice_to_c_string(md->key);
         char* value_str = grpc_slice_to_c_string(md->value);
         gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
diff --git a/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h
index 7e7e7b4..931ba0b 100644
--- a/src/core/lib/transport/metadata.h
+++ b/src/core/lib/transport/metadata.h
@@ -25,9 +25,7 @@
 
 #include "src/core/lib/iomgr/exec_ctx.h"
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_metadata;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_metadata;
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/core/lib/transport/transport.cc b/src/core/lib/transport/transport.cc
index b39e6eb..ac99814 100644
--- a/src/core/lib/transport/transport.cc
+++ b/src/core/lib/transport/transport.cc
@@ -31,14 +31,12 @@
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/transport_impl.h"
 
-#ifndef NDEBUG
-grpc_tracer_flag grpc_trace_stream_refcount =
-    GRPC_TRACER_INITIALIZER(false, "stream_refcount");
-#endif
+grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount(false,
+                                                         "stream_refcount");
 
 #ifndef NDEBUG
 void grpc_stream_ref(grpc_stream_refcount* refcount, const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) {
+  if (grpc_trace_stream_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
     gpr_log(GPR_DEBUG, "%s %p:%p   REF %" PRIdPTR "->%" PRIdPTR " %s",
             refcount->object_type, refcount, refcount->destroy.cb_arg, val,
@@ -53,7 +51,7 @@
 #ifndef NDEBUG
 void grpc_stream_unref(grpc_exec_ctx* exec_ctx, grpc_stream_refcount* refcount,
                        const char* reason) {
-  if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) {
+  if (grpc_trace_stream_refcount.enabled()) {
     gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count);
     gpr_log(GPR_DEBUG, "%s %p:%p UNREF %" PRIdPTR "->%" PRIdPTR " %s",
             refcount->object_type, refcount, refcount->destroy.cb_arg, val,
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index 973018e..2347005 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -43,9 +43,7 @@
    for a stream. */
 typedef struct grpc_stream grpc_stream;
 
-#ifndef NDEBUG
-extern grpc_tracer_flag grpc_trace_stream_refcount;
-#endif
+extern grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount;
 
 typedef struct grpc_stream_refcount {
   gpr_refcount refs;
diff --git a/src/core/tsi/fake_transport_security.cc b/src/core/tsi/fake_transport_security.cc
index e508d9b..f2f365f 100644
--- a/src/core/tsi/fake_transport_security.cc
+++ b/src/core/tsi/fake_transport_security.cc
@@ -575,7 +575,7 @@
     if (next_message_to_send > TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
       next_message_to_send = TSI_FAKE_HANDSHAKE_MESSAGE_MAX;
     }
-    if (GRPC_TRACER_ON(tsi_tracing_enabled)) {
+    if (tsi_tracing_enabled.enabled()) {
       gpr_log(GPR_INFO, "%s prepared %s.",
               impl->is_client ? "Client" : "Server",
               tsi_fake_handshake_message_to_string(impl->next_message_to_send));
@@ -587,7 +587,7 @@
   if (!impl->is_client &&
       impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
     /* We're done. */
-    if (GRPC_TRACER_ON(tsi_tracing_enabled)) {
+    if (tsi_tracing_enabled.enabled()) {
       gpr_log(GPR_INFO, "Server is done.");
     }
     impl->result = TSI_OK;
@@ -625,7 +625,7 @@
             tsi_fake_handshake_message_to_string(received_msg),
             tsi_fake_handshake_message_to_string(expected_msg));
   }
-  if (GRPC_TRACER_ON(tsi_tracing_enabled)) {
+  if (tsi_tracing_enabled.enabled()) {
     gpr_log(GPR_INFO, "%s received %s.", impl->is_client ? "Client" : "Server",
             tsi_fake_handshake_message_to_string(received_msg));
   }
@@ -633,7 +633,7 @@
   impl->needs_incoming_message = 0;
   if (impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) {
     /* We're done. */
-    if (GRPC_TRACER_ON(tsi_tracing_enabled)) {
+    if (tsi_tracing_enabled.enabled()) {
       gpr_log(GPR_INFO, "%s is done.", impl->is_client ? "Client" : "Server");
     }
     impl->result = TSI_OK;
diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc
index f1fff04..f35caef 100644
--- a/src/core/tsi/ssl_transport_security.cc
+++ b/src/core/tsi/ssl_transport_security.cc
@@ -175,7 +175,7 @@
 /* TODO(jboeuf): Remove when we are past the debugging phase with this code. */
 static void ssl_log_where_info(const SSL* ssl, int where, int flag,
                                const char* msg) {
-  if ((where & flag) && GRPC_TRACER_ON(tsi_tracing_enabled)) {
+  if ((where & flag) && tsi_tracing_enabled.enabled()) {
     gpr_log(GPR_INFO, "%20.20s - %30.30s  - %5.10s", msg,
             SSL_state_string_long(ssl), SSL_state_string(ssl));
   }
diff --git a/src/core/tsi/transport_security.cc b/src/core/tsi/transport_security.cc
index a2232bf..5abd2f0 100644
--- a/src/core/tsi/transport_security.cc
+++ b/src/core/tsi/transport_security.cc
@@ -26,7 +26,7 @@
 
 /* --- Tracing. --- */
 
-grpc_tracer_flag tsi_tracing_enabled = GRPC_TRACER_INITIALIZER(false, "tsi");
+grpc_core::TraceFlag tsi_tracing_enabled(false, "tsi");
 
 /* --- tsi_result common implementation. --- */
 
diff --git a/src/core/tsi/transport_security.h b/src/core/tsi/transport_security.h
index d639f85..7d6dd44 100644
--- a/src/core/tsi/transport_security.h
+++ b/src/core/tsi/transport_security.h
@@ -28,7 +28,7 @@
 extern "C" {
 #endif
 
-extern grpc_tracer_flag tsi_tracing_enabled;
+extern grpc_core::TraceFlag tsi_tracing_enabled;
 
 /* Base for tsi_frame_protector implementations.
    See transport_security_interface.h for documentation. */
diff --git a/src/core/tsi/transport_security_interface.h b/src/core/tsi/transport_security_interface.h
index 54942a6..0f3d468 100644
--- a/src/core/tsi/transport_security_interface.h
+++ b/src/core/tsi/transport_security_interface.h
@@ -60,7 +60,7 @@
 
 /* --- tsi tracing --- */
 
-extern grpc_tracer_flag tsi_tracing_enabled;
+extern grpc_core::TraceFlag tsi_tracing_enabled;
 
 /* -- tsi_zero_copy_grpc_protector object --
 
diff --git a/test/core/end2end/fixtures/http_proxy_fixture.cc b/test/core/end2end/fixtures/http_proxy_fixture.cc
index 0bdd15c..ac0c953 100644
--- a/test/core/end2end/fixtures/http_proxy_fixture.cc
+++ b/test/core/end2end/fixtures/http_proxy_fixture.cc
@@ -88,9 +88,11 @@
 
   grpc_slice_buffer client_read_buffer;
   grpc_slice_buffer client_deferred_write_buffer;
+  bool client_is_writing;
   grpc_slice_buffer client_write_buffer;
   grpc_slice_buffer server_read_buffer;
   grpc_slice_buffer server_deferred_write_buffer;
+  bool server_is_writing;
   grpc_slice_buffer server_write_buffer;
 
   grpc_http_parser http_parser;
@@ -148,6 +150,7 @@
 static void on_client_write_done(grpc_exec_ctx* exec_ctx, void* arg,
                                  grpc_error* error) {
   proxy_connection* conn = (proxy_connection*)arg;
+  conn->client_is_writing = false;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, true /* is_client */,
                             "HTTP proxy client write", error);
@@ -160,6 +163,7 @@
   if (conn->client_deferred_write_buffer.length > 0) {
     grpc_slice_buffer_move_into(&conn->client_deferred_write_buffer,
                                 &conn->client_write_buffer);
+    conn->client_is_writing = true;
     grpc_endpoint_write(exec_ctx, conn->client_endpoint,
                         &conn->client_write_buffer,
                         &conn->on_client_write_done);
@@ -173,6 +177,7 @@
 static void on_server_write_done(grpc_exec_ctx* exec_ctx, void* arg,
                                  grpc_error* error) {
   proxy_connection* conn = (proxy_connection*)arg;
+  conn->server_is_writing = false;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, false /* is_client */,
                             "HTTP proxy server write", error);
@@ -185,6 +190,7 @@
   if (conn->server_deferred_write_buffer.length > 0) {
     grpc_slice_buffer_move_into(&conn->server_deferred_write_buffer,
                                 &conn->server_write_buffer);
+    conn->server_is_writing = true;
     grpc_endpoint_write(exec_ctx, conn->server_endpoint,
                         &conn->server_write_buffer,
                         &conn->on_server_write_done);
@@ -210,13 +216,14 @@
   // the current write is finished.
   //
   // Otherwise, move the read data into the write buffer and write it.
-  if (conn->server_write_buffer.length > 0) {
+  if (conn->server_is_writing) {
     grpc_slice_buffer_move_into(&conn->client_read_buffer,
                                 &conn->server_deferred_write_buffer);
   } else {
     grpc_slice_buffer_move_into(&conn->client_read_buffer,
                                 &conn->server_write_buffer);
     proxy_connection_ref(conn, "client_read");
+    conn->server_is_writing = true;
     grpc_endpoint_write(exec_ctx, conn->server_endpoint,
                         &conn->server_write_buffer,
                         &conn->on_server_write_done);
@@ -242,13 +249,14 @@
   // the current write is finished.
   //
   // Otherwise, move the read data into the write buffer and write it.
-  if (conn->client_write_buffer.length > 0) {
+  if (conn->client_is_writing) {
     grpc_slice_buffer_move_into(&conn->server_read_buffer,
                                 &conn->client_deferred_write_buffer);
   } else {
     grpc_slice_buffer_move_into(&conn->server_read_buffer,
                                 &conn->client_write_buffer);
     proxy_connection_ref(conn, "server_read");
+    conn->client_is_writing = true;
     grpc_endpoint_write(exec_ctx, conn->client_endpoint,
                         &conn->client_write_buffer,
                         &conn->on_client_write_done);
@@ -262,6 +270,7 @@
 static void on_write_response_done(grpc_exec_ctx* exec_ctx, void* arg,
                                    grpc_error* error) {
   proxy_connection* conn = (proxy_connection*)arg;
+  conn->client_is_writing = false;
   if (error != GRPC_ERROR_NONE) {
     proxy_connection_failed(exec_ctx, conn, true /* is_client */,
                             "HTTP proxy write response", error);
@@ -302,6 +311,7 @@
   grpc_slice slice =
       grpc_slice_from_copied_string("HTTP/1.0 200 connected\r\n\r\n");
   grpc_slice_buffer_add(&conn->client_write_buffer, slice);
+  conn->client_is_writing = true;
   grpc_endpoint_write(exec_ctx, conn->client_endpoint,
                       &conn->client_write_buffer,
                       &conn->on_write_response_done);
@@ -450,9 +460,11 @@
                     grpc_combiner_scheduler(conn->proxy->combiner));
   grpc_slice_buffer_init(&conn->client_read_buffer);
   grpc_slice_buffer_init(&conn->client_deferred_write_buffer);
+  conn->client_is_writing = false;
   grpc_slice_buffer_init(&conn->client_write_buffer);
   grpc_slice_buffer_init(&conn->server_read_buffer);
   grpc_slice_buffer_init(&conn->server_deferred_write_buffer);
+  conn->server_is_writing = false;
   grpc_slice_buffer_init(&conn->server_write_buffer);
   grpc_http_parser_init(&conn->http_parser, GRPC_HTTP_REQUEST,
                         &conn->http_request);
diff --git a/test/core/iomgr/timer_list_test.cc b/test/core/iomgr/timer_list_test.cc
index dc965ef..d74ea4f 100644
--- a/test/core/iomgr/timer_list_test.cc
+++ b/test/core/iomgr/timer_list_test.cc
@@ -28,11 +28,12 @@
 #include <grpc/support/log.h>
 #include "src/core/lib/debug/trace.h"
 #include "test/core/util/test_config.h"
+#include "test/core/util/tracer_util.h"
 
 #define MAX_CB 30
 
-extern "C" grpc_tracer_flag grpc_timer_trace;
-extern "C" grpc_tracer_flag grpc_timer_check_trace;
+extern grpc_core::TraceFlag grpc_timer_trace;
+extern grpc_core::TraceFlag grpc_timer_check_trace;
 
 static int cb_called[MAX_CB][2];
 
@@ -48,8 +49,8 @@
   gpr_log(GPR_INFO, "add_test");
 
   grpc_timer_list_init(&exec_ctx);
-  grpc_timer_trace.value = 1;
-  grpc_timer_check_trace.value = 1;
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_trace);
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_check_trace);
   memset(cb_called, 0, sizeof(cb_called));
 
   grpc_millis start = grpc_exec_ctx_now(&exec_ctx);
@@ -117,8 +118,8 @@
   exec_ctx.now_is_valid = true;
   exec_ctx.now = 0;
   grpc_timer_list_init(&exec_ctx);
-  grpc_timer_trace.value = 1;
-  grpc_timer_check_trace.value = 1;
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_trace);
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_timer_check_trace);
   memset(cb_called, 0, sizeof(cb_called));
 
   grpc_timer_init(
diff --git a/test/core/transport/connectivity_state_test.cc b/test/core/transport/connectivity_state_test.cc
index 7a22ec8..11046e1 100644
--- a/test/core/transport/connectivity_state_test.cc
+++ b/test/core/transport/connectivity_state_test.cc
@@ -23,6 +23,7 @@
 #include <grpc/support/log.h>
 
 #include "test/core/util/test_config.h"
+#include "test/core/util/tracer_util.h"
 
 #define THE_ARG ((void*)(size_t)0xcafebabe)
 
@@ -136,7 +137,7 @@
 
 int main(int argc, char** argv) {
   grpc_test_init(argc, argv);
-  grpc_connectivity_state_trace.value = 1;
+  grpc_core::testing::grpc_tracer_enable_flag(&grpc_connectivity_state_trace);
   test_connectivity_state_name();
   test_check();
   test_subscribe_then_unsubscribe();
diff --git a/test/core/util/BUILD b/test/core/util/BUILD
index 2437923..6443553 100644
--- a/test/core/util/BUILD
+++ b/test/core/util/BUILD
@@ -57,6 +57,7 @@
         "reconnect_server.cc",
         "slice_splitter.cc",
         "test_tcp_server.cc",
+        "tracer_util.cc",
         "trickle_endpoint.cc",
     ],
     hdrs = [
@@ -69,6 +70,7 @@
         "reconnect_server.h",
         "slice_splitter.h",
         "test_tcp_server.h",
+        "tracer_util.h",
         "trickle_endpoint.h",
     ],
     language = "C++",
diff --git a/test/core/util/tracer_util.cc b/test/core/util/tracer_util.cc
new file mode 100644
index 0000000..34a132d
--- /dev/null
+++ b/test/core/util/tracer_util.cc
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "test/core/util/test_config.h"
+
+#include "src/core/lib/debug/trace.h"
+
+namespace grpc_core {
+namespace testing {
+
+void grpc_tracer_enable_flag(grpc_core::TraceFlag* flag) {
+  flag->set_enabled(1);
+}
+
+}  // namespace testing
+}  // namespace grpc_core
diff --git a/test/core/util/tracer_util.h b/test/core/util/tracer_util.h
new file mode 100644
index 0000000..0b432ff
--- /dev/null
+++ b/test/core/util/tracer_util.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_TEST_CORE_UTIL_TRACER_UTIL_H
+#define GRPC_TEST_CORE_UTIL_TRACER_UTIL_H
+
+namespace grpc_core {
+class TraceFlag;
+
+namespace testing {
+// enables the TraceFlag passed to it. Used for testing purposes.
+void grpc_tracer_enable_flag(grpc_core::TraceFlag* flag);
+
+}  // namespace testing
+}  // namespace grpc_core
+
+#endif /* GRPC_TEST_CORE_UTIL_TRACER_UTIL_H */
diff --git a/tools/distrib/python/check_grpcio_tools.py b/tools/distrib/python/check_grpcio_tools.py
index c5afa71..b56ccae 100755
--- a/tools/distrib/python/check_grpcio_tools.py
+++ b/tools/distrib/python/check_grpcio_tools.py
@@ -14,17 +14,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import cStringIO
-
-import make_grpcio_tools as make
+import make_grpcio_tools as _make
 
 OUT_OF_DATE_MESSAGE = """file {} is out of date
 
 Have you called tools/distrib/python/make_grpcio_tools.py since upgrading protobuf?"""
 
-check_protoc_lib_deps_content = make.get_deps()
+submodule_commit_hash = _make.protobuf_submodule_commit_hash()
 
-with open(make.GRPC_PYTHON_PROTOC_LIB_DEPS, 'r') as protoc_lib_deps_file:
-  if protoc_lib_deps_file.read() != check_protoc_lib_deps_content:
-    print(OUT_OF_DATE_MESSAGE.format(make.GRPC_PYTHON_PROTOC_LIB_DEPS))
-    raise SystemExit(1)
+with open(_make.GRPC_PYTHON_PROTOC_LIB_DEPS, 'r') as _protoc_lib_deps_file:
+  content = _protoc_lib_deps_file.read().splitlines()
+
+testString = (_make.COMMIT_HASH_PREFIX +
+              submodule_commit_hash +
+              _make.COMMIT_HASH_SUFFIX)
+
+if testString not in content:
+  print(OUT_OF_DATE_MESSAGE.format(_make.GRPC_PYTHON_PROTOC_LIB_DEPS))
+  raise SystemExit(1)
diff --git a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
index 18470d5..2a42a51 100644
--- a/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
+++ b/tools/distrib/python/grpcio_tools/protoc_lib_deps.py
@@ -1,5 +1,5 @@
 
-# Copyright 2016 gRPC authors.
+# Copyright 2017 gRPC authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -19,3 +19,5 @@
 
 CC_INCLUDE='third_party/protobuf/src'
 PROTO_INCLUDE='third_party/protobuf/src'
+
+PROTOBUF_SUBMODULE_VERSION="80a37e0782d2d702d52234b62dd4b9ec74fd2c95"
diff --git a/tools/distrib/python/make_grpcio_tools.py b/tools/distrib/python/make_grpcio_tools.py
index 5bc07ff..c865f0b 100755
--- a/tools/distrib/python/make_grpcio_tools.py
+++ b/tools/distrib/python/make_grpcio_tools.py
@@ -28,7 +28,7 @@
 import uuid
 
 DEPS_FILE_CONTENT="""
-# Copyright 2016 gRPC authors.
+# Copyright 2017 gRPC authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -48,8 +48,13 @@
 
 CC_INCLUDE={cc_include}
 PROTO_INCLUDE={proto_include}
+
+{commit_hash}
 """
 
+COMMIT_HASH_PREFIX = 'PROTOBUF_SUBMODULE_VERSION="'
+COMMIT_HASH_SUFFIX = '"'
+
 # Bazel query result prefix for expected source files in protobuf.
 PROTOBUF_CC_PREFIX = '//:src/'
 PROTOBUF_PROTO_PREFIX = '//:src/'
@@ -63,6 +68,7 @@
 
 GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT = os.path.join('third_party', 'protobuf', 'src')
 GRPC_PROTOBUF = os.path.join(GRPC_ROOT, GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT)
+GRPC_PROTOBUF_SUBMODULE_ROOT = os.path.join(GRPC_ROOT, 'third_party', 'protobuf')
 GRPC_PROTOC_PLUGINS = os.path.join(GRPC_ROOT, 'src', 'compiler')
 GRPC_PYTHON_PROTOBUF = os.path.join(GRPC_PYTHON_ROOT, 'third_party', 'protobuf',
                                     'src')
@@ -78,6 +84,14 @@
 BAZEL_DEPS_PROTOC_LIB_QUERY = '//:protoc_lib'
 BAZEL_DEPS_COMMON_PROTOS_QUERY = '//:well_known_protos'
 
+def protobuf_submodule_commit_hash():
+  """Gets the commit hash for the HEAD of the protobuf submodule currently
+     checked out."""
+  cwd = os.getcwd()
+  os.chdir(GRPC_PROTOBUF_SUBMODULE_ROOT)
+  output = subprocess.check_output(['git', 'rev-parse', 'HEAD'])
+  os.chdir(cwd)
+  return output.splitlines()[0].strip()
 
 def bazel_query(query):
   output = subprocess.check_output([BAZEL_DEPS, query])
@@ -94,11 +108,13 @@
   proto_files = [
       name[len(PROTOBUF_PROTO_PREFIX):] for name in proto_files_output
       if name.endswith('.proto') and name.startswith(PROTOBUF_PROTO_PREFIX)]
+  commit_hash = protobuf_submodule_commit_hash()
   deps_file_content = DEPS_FILE_CONTENT.format(
       cc_files=cc_files,
       proto_files=proto_files,
       cc_include=repr(GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT),
-      proto_include=repr(GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT))
+      proto_include=repr(GRPC_PYTHON_PROTOBUF_RELATIVE_ROOT),
+      commit_hash=COMMIT_HASH_PREFIX + commit_hash + COMMIT_HASH_SUFFIX)
   return deps_file_content
 
 def long_path(path):
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 7e02624..a372c82 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -8902,6 +8902,7 @@
       "test/core/util/port.h", 
       "test/core/util/port_server_client.h", 
       "test/core/util/slice_splitter.h", 
+      "test/core/util/tracer_util.h", 
       "test/core/util/trickle_endpoint.h"
     ], 
     "is_filegroup": true, 
@@ -8936,6 +8937,8 @@
       "test/core/util/port_server_client.h", 
       "test/core/util/slice_splitter.cc", 
       "test/core/util/slice_splitter.h", 
+      "test/core/util/tracer_util.cc", 
+      "test/core/util/tracer_util.h", 
       "test/core/util/trickle_endpoint.cc", 
       "test/core/util/trickle_endpoint.h"
     ],