diff --git a/ragconnect.base/src/main/jastadd/Intermediate.jadd b/ragconnect.base/src/main/jastadd/Intermediate.jadd
index 69ff5d8932cd33a97e44b2fa3562b13cd52617f2..f7a12a6635641baf5adfb1d2b36fc02bd3f832af 100644
--- a/ragconnect.base/src/main/jastadd/Intermediate.jadd
+++ b/ragconnect.base/src/main/jastadd/Intermediate.jadd
@@ -15,6 +15,10 @@ aspect SharedMustache {
 
   syn boolean RagConnect.configExperimentalJastAdd329() = getConfiguration().getExperimentalJastAdd329();
 
+  syn boolean RagConnect.configEvaluationCounter() = getConfiguration().getEvaluationCounter();
+
+  syn String RagConnect.evaluationCounterVariable() = internalRagConnectPrefix() + "evaluationCounter";
+
   syn String RagConnect.internalRagConnectPrefix() = "_ragconnect_";
 
   syn String RagConnect.logDebug() = logStatement("debug");
@@ -99,6 +103,10 @@ aspect MustacheHandler {
   // === RagConnect ===
   syn String RagConnect.closeMethodName() = "ragconnectCloseConnections";
 
+  syn String RagConnect.evaluationCounterInnerClass() = internalRagConnectPrefix() + "Counter";
+
+  syn String RagConnect.evaluationCounterSummaryMethodName() = "ragconnectEvaluationCounterSummary";
+
   syn boolean RagConnect.hasRootTypeComponents() = !rootTypeComponents().isEmpty();
 
   syn List<TypeComponent> RagConnect.rootTypeComponents() {
diff --git a/ragconnect.base/src/main/jastadd/Mappings.jrag b/ragconnect.base/src/main/jastadd/Mappings.jrag
index 9fd55aa4cad7eb22f75913f7942b547ca91ddec2..e92fc4fa27569ee353b0c16798e2ce3b7eb07a29 100644
--- a/ragconnect.base/src/main/jastadd/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/Mappings.jrag
@@ -243,7 +243,7 @@ aspect Mappings {
           return getEndpointTarget().isTypeEndpointTarget() && typeIsList() && !getIndexBasedListAccess() ? ragconnect().defaultBytesToListMapping(typeDecl.getName()) : ragconnect().defaultBytesToTreeMapping(typeDecl.getName());
         } catch (Exception ignore) {
         }
-        System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this);
+        System.err.println("Could not find suitable default receive mapping for " + targetTypeName() + " on " + this);
         return null;
     }
   }
@@ -287,7 +287,7 @@ aspect Mappings {
         } catch (Exception ignore) {
           // exception should be logged to debug/fine
         }
-        System.err.println("Could not find suitable default mapping for " + targetTypeName() + " on " + this);
+        System.err.println("Could not find suitable default send mapping for " + targetTypeName() + " on " + this);
         return null;
     }
   }
diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast
index cf12d73202b144240ed929ba499d99a8e797aa89..df50e3dab1dde22e38bca0588396de647b49c8cb 100644
--- a/ragconnect.base/src/main/jastadd/RagConnect.relast
+++ b/ragconnect.base/src/main/jastadd/RagConnect.relast
@@ -40,5 +40,6 @@ Configuration ::=
 <JastAddOpt:String>
 <IncrementalOptionActive:boolean>
 <CacheAllOptionActive:boolean>
+<EvaluationCounter:boolean>
 <ExperimentalJastAdd329:boolean>;
 rel Configuration.RootNode -> TypeDecl ;
diff --git a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
index e255b3b49c8a308661cf248a3f00e8ca41b96869..772cebba17f57474352176a38a06a1b19b039973 100644
--- a/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
+++ b/ragconnect.base/src/main/java/org/jastadd/ragconnect/compiler/Compiler.java
@@ -31,6 +31,7 @@ public class Compiler extends AbstractCompiler {
   private BooleanOption optionLogIncremental;
   private ValueOption optionLogTarget;
   private BooleanOption optionExperimentalJastAdd329;
+  private BooleanOption optionEvaluationCounter;
 
   private static final String OPTION_LOGGING_TARGET_CONSOLE = "console";
   private static final String OPTION_LOGGING_TARGET_SLF4J = "slf4j";
@@ -206,6 +207,9 @@ public class Compiler extends AbstractCompiler {
     optionExperimentalJastAdd329 = addOption(
         new BooleanOption("experimental-jastadd-329", "Use trace events INC_FLUSH_START and INC_FLUSH_END (JastAdd issue #329).")
             .defaultValue(false));
+    optionEvaluationCounter = addOption(
+            new BooleanOption("evaluationCounter", "Enable counters for evaluation.")
+                    .defaultValue(false));
   }
 
   private RagConnect parseProgram(Collection<String> files) throws CompilerException {
@@ -301,13 +305,14 @@ public class Compiler extends AbstractCompiler {
    * Set all configuration values.
    * @param ragConnect the RagConnect instance to set configuration values
    */
-  private void setConfiguration(RagConnect ragConnect) {
+  private void setConfiguration(RagConnect ragConnect) throws CompilerException {
     ragConnect.setConfiguration(new Configuration());
     ragConnect.getConfiguration().setLoggingEnabledForReads(optionLogReads.value());
     ragConnect.getConfiguration().setLoggingEnabledForWrites(optionLogWrites.value());
     ragConnect.getConfiguration().setLoggingEnabledForIncremental(optionLogIncremental.value());
     ragConnect.getConfiguration().setLoggingTarget(optionLogTarget.value());
     ragConnect.getConfiguration().setExperimentalJastAdd329(optionExperimentalJastAdd329.value());
+    ragConnect.getConfiguration().setEvaluationCounter(optionEvaluationCounter.value());
 
     // reuse "--incremental" and "--trace=flush" options of JastAdd
     boolean incrementalOptionActive = this.getConfiguration().incremental() && this.getConfiguration().traceFlush();
@@ -328,7 +333,7 @@ public class Compiler extends AbstractCompiler {
       rootNode = ragConnect.getProgram().resolveTypeDecl(optionRootNode.value());
     } catch (RuntimeException re) {
       // root node was not found
-      throw new RuntimeException("Could not resolve root node '" + optionRootNode.value() + "'!", re);
+      throw new CompilerException("Could not resolve root node '" + optionRootNode.value() + "'!", re);
     }
     ragConnect.getConfiguration().setRootNode(rootNode);
 
diff --git a/ragconnect.base/src/main/resources/handleUri.mustache b/ragconnect.base/src/main/resources/handleUri.mustache
index 2312cd6983822c0e0b2987459047b7ca2a277fc2..efb4aacfbf1e003dcb540ba77bdaca68a8aed6cd 100644
--- a/ragconnect.base/src/main/resources/handleUri.mustache
+++ b/ragconnect.base/src/main/resources/handleUri.mustache
@@ -10,14 +10,14 @@ try {
   return false;
 }
 if (scheme == null || scheme.isBlank()) {
-  {{logError}}("Missing or empty scheme in " + uri);
+  {{logError}}("Missing or empty scheme in {{log_}}", uri);
   return false;
 }
 if (host == null || host.isBlank()) {
-  {{logError}}("Missing or empty host in " + uri);
+  {{logError}}("Missing or empty host in {{log_}}", uri);
   return false;
 }
 if (path == null || path.isBlank()) {
-  {{logError}}("Missing or empty path in " + uri);
+  {{logError}}("Missing or empty path in {{log_}}", uri);
   return false;
 }
diff --git a/ragconnect.base/src/main/resources/mappingApplication.mustache b/ragconnect.base/src/main/resources/mappingApplication.mustache
index 482d54783782ada82cacd9ce75b81d30c1e21e68..efd17af4e8947232f44b93c3749d86b1e637dd46 100644
--- a/ragconnect.base/src/main/resources/mappingApplication.mustache
+++ b/ragconnect.base/src/main/resources/mappingApplication.mustache
@@ -1,6 +1,12 @@
+{{#configEvaluationCounter}}
+  {{evaluationCounterVariable}}.incrementCall("{{parentTypeName}}", "{{entityName}}");
+{{/configEvaluationCounter}}
 {{#Send}}
 {{^PrimitiveType}}
 if ({{firstInputVarName}} == null) {
+  {{#configEvaluationCounter}}
+    {{evaluationCounterVariable}}.incrementFirstNull("{{parentTypeName}}", "{{entityName}}");
+  {{/configEvaluationCounter}}
   {{preemptiveReturn}}
 }
 {{/PrimitiveType}}
@@ -12,13 +18,22 @@ try {
   {{/innerMappingDefinitions}}
 } catch (RagConnectRejectMappingException e) {
   // do not print message in case of rejection
+  {{#configEvaluationCounter}}
+    {{evaluationCounterVariable}}.incrementReject("{{parentTypeName}}", "{{entityName}}");
+  {{/configEvaluationCounter}}
   {{preemptiveReturn}}
 } catch (Exception e) {
   e.printStackTrace();
+  {{#configEvaluationCounter}}
+    {{evaluationCounterVariable}}.incrementException("{{parentTypeName}}", "{{entityName}}");
+  {{/configEvaluationCounter}}
   {{preemptiveReturn}}
 }
 {{^AlwaysApply}}
 if ({{{condition}}}) {
+  {{#configEvaluationCounter}}
+    {{evaluationCounterVariable}}.incrementSkip("{{parentTypeName}}", "{{entityName}}");
+  {{/configEvaluationCounter}}
   {{preemptiveReturn}}
 }
 {{/AlwaysApply}}
diff --git a/ragconnect.base/src/main/resources/mappingDefinition.mustache b/ragconnect.base/src/main/resources/mappingDefinition.mustache
index 5be93e4bdc91ca2eaa54f364eb8e5f7c069da2a5..b6a035eb41b9d3f877b731293203de399399e6c0 100644
--- a/ragconnect.base/src/main/resources/mappingDefinition.mustache
+++ b/ragconnect.base/src/main/resources/mappingDefinition.mustache
@@ -1,3 +1,3 @@
-protected static {{{toType}}} ASTNode.{{methodName}}({{{fromType}}} {{FromVariableName}}) throws Exception {
+protected {{{toType}}} ASTNode.{{methodName}}({{{fromType}}} {{FromVariableName}}) throws Exception {
   {{{Content}}}
 }
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index ac7b906067f5f9ecd4a2bf0c51a368797f55dfbc..7e054a4927b527e6d32a30e32a6bffa71d86b97f 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -285,3 +285,98 @@ aspect RagConnectObserver {
   }
 }
 {{/configIncrementalOptionActive}}
+
+aspect EvaluationCounter {
+  public String ASTNode.{{evaluationCounterSummaryMethodName}}() {
+  {{#configEvaluationCounter}}
+    return {{evaluationCounterVariable}}.summary();
+  {{/configEvaluationCounter}}
+  {{^configEvaluationCounter}}
+    String message = "Option --evaluationCounter was not set. No Summary available";
+    {{logWarn}}(message);
+    return message;
+  {{/configEvaluationCounter}}
+  }
+{{#configEvaluationCounter}}
+  static EvaluationCounter ASTNode.{{evaluationCounterVariable}} = new EvaluationCounter();
+
+  public class EvaluationCounter {
+    private java.util.Map<String, java.util.Map<String, {{evaluationCounterInnerClass}}>> counters = new java.util.HashMap<>();
+    private final java.util.function.Function<? super String, ? extends java.util.Map<String, {{evaluationCounterInnerClass}}>> parentAbsent = key -> {
+      return new java.util.HashMap<>();
+    };
+    private final java.util.function.Function<? super String, ? extends {{evaluationCounterInnerClass}}> entityAbsent = key -> {
+      return new {{evaluationCounterInnerClass}}();
+    };
+
+    public void incrementReceive(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).receive += 1;
+    }
+
+    public void incrementSend(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).send += 1;
+    }
+
+    public void incrementCall(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).call += 1;
+    }
+
+    public void incrementFirstNull(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).firstNull += 1;
+    }
+
+    public void incrementSkip(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).skip += 1;
+    }
+
+    public void incrementException(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).exception += 1;
+    }
+
+    public void incrementReject(String parentTypeName, String entityName) {
+      getCounter(parentTypeName, entityName).reject += 1;
+    }
+
+    public String summary() {
+      StringBuilder sb = new StringBuilder();
+      // header
+      sb.append("parentTypeName,entityName,receive,send,call,firstNull,skip,exception,reject").append("\n");
+      // values
+      java.util.Set<String> sortedParentTypes = new java.util.TreeSet<>(counters.keySet());
+      for (String parentType : sortedParentTypes) {
+        java.util.Set<String> sortedEntityNames = new java.util.TreeSet<>(counters.get(parentType).keySet());
+        for (String entityName : sortedEntityNames) {
+          {{evaluationCounterInnerClass}} count = getCounter(parentType, entityName);
+          java.util.StringJoiner sj = new java.util.StringJoiner(",", "", "\n");
+          sj.add(parentType)
+             .add(entityName)
+             .add(Integer.toString(count.receive))
+             .add(Integer.toString(count.send))
+             .add(Integer.toString(count.call))
+             .add(Integer.toString(count.firstNull))
+             .add(Integer.toString(count.skip))
+             .add(Integer.toString(count.exception))
+             .add(Integer.toString(count.reject))
+          ;
+          sb.append(sj);
+        }
+      }
+      return sb.toString();
+    }
+
+    private {{evaluationCounterInnerClass}} getCounter(String parentTypeName, String entityName) {
+      return counters.computeIfAbsent(parentTypeName, parentAbsent).computeIfAbsent(entityName, entityAbsent);
+    }
+  }
+
+  class {{evaluationCounterInnerClass}} {
+    int receive = 0;
+    int send = 0;
+    int call = 0;
+    int firstNull = 0;
+    int skip = 0;
+    int exception = 0;
+    int reject = 0;
+  }
+{{/configEvaluationCounter}}
+}
diff --git a/ragconnect.base/src/main/resources/receiveDefinition.mustache b/ragconnect.base/src/main/resources/receiveDefinition.mustache
index 28a9bb5feae6e8305744ab3bfdd224f7daecc31b..81b0ecbbd96aa40155ec8f396549a4af2160b64e 100644
--- a/ragconnect.base/src/main/resources/receiveDefinition.mustache
+++ b/ragconnect.base/src/main/resources/receiveDefinition.mustache
@@ -25,6 +25,9 @@ private int {{parentTypeName}}.{{resolveInListMethodName}}(String topic) {
  */
 public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}{{#typeIsList}}{{#IndexBasedListAccess}}{{^WithAdd}}, int index{{/WithAdd}}{{/IndexBasedListAccess}}{{/typeIsList}}) throws java.io.IOException {
   java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> {
+    {{#configEvaluationCounter}}
+      {{evaluationCounterVariable}}.incrementReceive("{{parentTypeName}}", "{{entityName}}");
+    {{/configEvaluationCounter}}
     {{> mappingApplication}}
 {{#configLoggingEnabledForReads}}
     {{logDebug}}("[Receive] {{log_}} -> {{entityName}} = {{log_}}", {{connectParameterName}}, {{lastResult}});
@@ -70,6 +73,9 @@ public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParamete
 */
 public boolean {{parentTypeName}}.{{connectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
   java.util.function.BiConsumer<String, byte[]> consumer = (topic, message) -> {
+    {{#configEvaluationCounter}}
+      {{evaluationCounterVariable}}.incrementReceive("{{parentTypeName}}", "{{entityName}}");
+    {{/configEvaluationCounter}}
     int index = {{resolveInListMethodName}}(topic);
     {{> mappingApplication}}
 {{#configLoggingEnabledForReads}}
diff --git a/ragconnect.base/src/main/resources/sendDefinition.mustache b/ragconnect.base/src/main/resources/sendDefinition.mustache
index 082997361ba3ec4c08a28f31eee827677454115d..abcbdffe2d4f7aaff5dfce8700cb11e72ec8d83d 100644
--- a/ragconnect.base/src/main/resources/sendDefinition.mustache
+++ b/ragconnect.base/src/main/resources/sendDefinition.mustache
@@ -134,14 +134,25 @@ protected boolean {{parentTypeName}}.{{updateMethodName}}({{#IndexBasedListAcces
   {{> mappingApplication}}
   {{lastValueSetter}}({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}{{lastResult}});
   // normally we would return true here. unless no connect method was called so far to initialize {{senderName}} yet
+  {{#configEvaluationCounter}}
+    if ({{senderName}} == null) {
+      {{evaluationCounterVariable}}.incrementSkip("{{parentTypeName}}", "{{entityName}}");
+    }
+  {{/configEvaluationCounter}}
   return {{senderName}} != null;
 }
 
 protected void {{parentTypeName}}.{{writeMethodName}}({{#IndexBasedListAccess}}int index{{/IndexBasedListAccess}}) {
+  {{#configEvaluationCounter}}
+    {{evaluationCounterVariable}}.incrementSend("{{parentTypeName}}", "{{entityName}}");
+  {{/configEvaluationCounter}}
   {{senderName}}.run({{#IndexBasedListAccess}}index{{/IndexBasedListAccess}});
 }
 
 protected void {{parentTypeName}}.{{writeMethodName}}({{#IndexBasedListAccess}}int index, {{/IndexBasedListAccess}}RagConnectToken token) {
+  {{#configEvaluationCounter}}
+    {{evaluationCounterVariable}}.incrementSend("{{parentTypeName}}", "{{entityName}}");
+  {{/configEvaluationCounter}}
   {{senderName}}.run({{#IndexBasedListAccess}}index, {{/IndexBasedListAccess}}token);
 }
 
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index 719b55dfb92eb09ebfae7fce2aa51b7be543487e..42c2cf660f7893a7990792b8e8c89f12d96533a1 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -651,7 +651,7 @@ task compileRelationIncremental(type: RagConnectTest) {
         inputFiles = [file('src/test/01-input/relation/Test.relast'),
                       file('src/test/01-input/relation/Test.connect')]
         rootNode = 'Root'
-        extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329'])
+        extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329', '--evaluationCounter'])
     }
     relast {
         useJastAddNames = true
@@ -677,7 +677,7 @@ task compileJavaIncremental(type: RagConnectTest) {
         logWrites = true
         logIncremental = true
         protocols = ['java']
-        extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329'])
+        extraOptions = defaultRagConnectOptionsAnd(['--experimental-jastadd-329', '--evaluationCounter'])
     }
     relast {
         useJastAddNames = true
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java
index 5d6e49248e8e025c3f4ab383637dedb335fd8e7a..fa4319cccf9844dabbb8068b365661a36e7cbd11 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/JavaTest.java
@@ -106,6 +106,8 @@ public class JavaTest {
 
     handler.push(TOPIC_RECEIVE_TOKEN, ExposingASTNode.INSTANCE.stringToBytes("7"));
     checker.put(TOPIC_RECEIVE_TOKEN, "7").check();
+
+    System.out.println(model.ragconnectEvaluationCounterSummary());
   }
 
   @Test
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java
index d9c4979c0d6c206c642ca8aa9dbcf780e3fbf370..a245387637d4a61f0e6ff38ab1389e340f4a7093 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/relation/RelationTest.java
@@ -616,6 +616,7 @@ public class RelationTest extends AbstractMqttTest {
     biB(3).getInner().setInnerValue("inner-bi-b3");
     checker.check();
 
+    System.out.println(model.ragconnectEvaluationCounterSummary());
   }
 
   private void assertNullOrA(String expectedValue, A actual, String alias) {