diff --git a/src/java/org/jastadd/Configuration.java b/src/java/org/jastadd/Configuration.java
index e2e4147d735fb88fc129e0479fbfe466d0ea2031..38c112f417012ca2252ed2cfc80fafd599d52f84 100644
--- a/src/java/org/jastadd/Configuration.java
+++ b/src/java/org/jastadd/Configuration.java
@@ -250,6 +250,7 @@ public class Configuration {
       .addAcceptedValue("copy", "trace node copy operations")
       .addAcceptedValue("flush", "trace flush operations")
       .addAcceptedValue("coll", "trace collection attribute contributions")
+      .addAcceptedValue("token", "trace token reads")
       .additionalDescription("All events are collected by default.\n"
           + "Listen to events by calling ASTState.Trace.setReceiver()");
 
@@ -536,6 +537,7 @@ public class Configuration {
     tt.bind("TraceCopy", traceCopy());
     tt.bind("TraceFlush", traceFlush());
     tt.bind("TraceColl", traceColl());
+    tt.bind("TraceToken", traceToken());
 
     // Set template variables to accommodate deprecated options
     // (the deprecated options may alter the value of the template variable).
@@ -824,13 +826,20 @@ public class Configuration {
     return traceAll() || tracingOption.hasValue("flush");
   }
 
-/**
+  /**
    * @return {@code true} if collection attributes should be traced
    */
   public boolean traceColl() {
     return traceAll() || tracingOption.hasValue("coll");
   }
 
+  /**
+   * @return {@code true} if token reads should be traced
+   */
+  public boolean traceToken() {
+    return traceAll() || tracingOption.hasValue("token");
+  }
+
 
   /**
    * @return ASTNode type name
diff --git a/src/template/ast/components/TokenComponent.tt b/src/template/ast/components/TokenComponent.tt
index 5c7fa2a6eaebd5497ecf2897b980061ad46407c6..e9830c61bd8f7697c59a6005c7d33a036f519d0f 100644
--- a/src/template/ast/components/TokenComponent.tt
+++ b/src/template/ast/components/TokenComponent.tt
@@ -88,6 +88,7 @@ $if(!#isNTA)
    */
   $include(TokenComponent.generatedAnnotations)
   $Modifier $Type $Host.get$Id() {
+    $include(TokenComponent.traceTokenRead)
     $include(TokenComponent.incHookGetToken)
     return token$(TypeInSignature)_$Id != null ? token$(TypeInSignature)_$Id : "";
   }
@@ -99,6 +100,7 @@ $if(!#isNTA)
    */
   $include(TokenComponent.generatedAnnotations)
   $Modifier $Type $Host.get$Id() {
+    $include(TokenComponent.traceTokenRead)
     $include(TokenComponent.incHookGetToken)
     return token$(TypeInSignature)_$Id;
   }
diff --git a/src/template/trace/TraceHooks.tt b/src/template/trace/TraceHooks.tt
index e23ae7abc93e416ccfbeea99dc783da2b7464923..46bd522c0f287824ea46cb025f1c7cb2b510bf40 100644
--- a/src/template/trace/TraceHooks.tt
+++ b/src/template/trace/TraceHooks.tt
@@ -344,3 +344,9 @@ state().trace().contributionCheckMatch(this, "#collectionId", "#escapeConditionF
 $endif
 $endif
 ]]
+
+TokenComponent.traceTokenRead [[
+$if (TraceToken)
+state().trace().tokenRead(this, "$Id", token$(TypeInSignature)_$Id);
+$endif
+]]
diff --git a/src/template/trace/Tracer.tt b/src/template/trace/Tracer.tt
index 8c352470b634648230d7ab929c86d001bd53452b..0a90a599f74e7775c12397c6386d681cac74b15c 100644
--- a/src/template/trace/Tracer.tt
+++ b/src/template/trace/Tracer.tt
@@ -84,7 +84,10 @@ public static class Trace {
     // Flag: --tracing=coll
     CONTRIBUTION_CHECK_BEGIN,
     CONTRIBUTION_CHECK_MATCH,
-    CONTRIBUTION_CHECK_END;
+    CONTRIBUTION_CHECK_END,
+    
+    // Flag: --tracing=token
+    TOKEN_READ;
   }
 
   /**
@@ -359,6 +362,13 @@ public static class Trace {
     receiver.accept($StateClass.Trace.Event.CONTRIBUTION_CHECK_MATCH, node, attr, check, value);
   }
 
+  /**
+   * Trace that a token was read.
+   */
+  public void tokenRead($ASTNode node, String token, Object value) {
+    receiver.accept($StateClass.Trace.Event.TOKEN_READ, node, token, "", value);
+  }
+
 }
 $endif
 ]]