EmbeddedJavaExecutionLogger.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Carsten Hammer - initial API and implementation
*******************************************************************************/
package org.sandbox.jdt.triggerpattern.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Structured logger for embedded Java guard/fix function execution.
*
* <p>Captures execution traces when hint rules with embedded Java code are
* evaluated during cleanup/quickfix operations. Each trace records:</p>
* <ul>
* <li>Which guard/fix function was called</li>
* <li>Input bindings (placeholder → value mappings)</li>
* <li>Return value or exception</li>
* <li>Execution time in milliseconds</li>
* </ul>
*
* <p>Backed by {@link java.util.logging.Logger} for consistency with the
* existing {@link HintFileParser} logging.</p>
*
* @since 1.5.0
*/
public final class EmbeddedJavaExecutionLogger {
private static final Logger LOGGER = Logger.getLogger(EmbeddedJavaExecutionLogger.class.getName());
private final List<ExecutionTrace> traces = new ArrayList<>();
/**
* An execution trace record capturing details of a single guard/fix invocation.
*
* @param ruleId the hint rule ID
* @param functionName the guard/fix function name
* @param bindings input placeholder bindings (may be empty)
* @param result the return value (for guards: Boolean; for fix: null)
* @param exception the exception if execution failed, or {@code null}
* @param durationMs execution time in milliseconds
*/
public record ExecutionTrace(
String ruleId,
String functionName,
Map<String, String> bindings,
Object result,
Throwable exception,
long durationMs) {
/**
* Returns {@code true} if the execution succeeded without exception.
*
* @return {@code true} if successful
*/
public boolean isSuccess() {
return exception == null;
}
}
/**
* Logs a successful guard function execution.
*
* @param ruleId the hint rule ID
* @param functionName the guard function name
* @param bindings input placeholder bindings
* @param result the boolean result of the guard
* @param durationMs execution time in milliseconds
*/
public void logGuardExecution(String ruleId, String functionName,
Map<String, String> bindings, boolean result, long durationMs) {
ExecutionTrace trace = new ExecutionTrace(
ruleId, functionName, bindings, result, null, durationMs);
traces.add(trace);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE,
"Guard {0}::{1} returned {2} in {3}ms (bindings: {4})", //$NON-NLS-1$
new Object[] { ruleId, functionName, result, durationMs, bindings });
}
}
/**
* Logs a failed guard/fix function execution.
*
* @param ruleId the hint rule ID
* @param functionName the function name
* @param bindings input placeholder bindings
* @param exception the exception that occurred
* @param durationMs execution time in milliseconds
*/
public void logExecutionFailure(String ruleId, String functionName,
Map<String, String> bindings, Throwable exception, long durationMs) {
ExecutionTrace trace = new ExecutionTrace(
ruleId, functionName, bindings, null, exception, durationMs);
traces.add(trace);
LOGGER.log(Level.WARNING,
"Guard {0}::{1} failed after {2}ms: {3}", //$NON-NLS-1$
new Object[] { ruleId, functionName, durationMs, exception.getMessage() });
}
/**
* Logs a successful fix function execution.
*
* @param ruleId the hint rule ID
* @param functionName the fix function name
* @param bindings input placeholder bindings
* @param durationMs execution time in milliseconds
*/
public void logFixExecution(String ruleId, String functionName,
Map<String, String> bindings, long durationMs) {
ExecutionTrace trace = new ExecutionTrace(
ruleId, functionName, bindings, null, null, durationMs);
traces.add(trace);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE,
"Fix {0}::{1} executed in {2}ms (bindings: {3})", //$NON-NLS-1$
new Object[] { ruleId, functionName, durationMs, bindings });
}
}
/**
* Returns all recorded execution traces.
*
* @return unmodifiable list of traces
*/
public List<ExecutionTrace> getTraces() {
return Collections.unmodifiableList(traces);
}
/**
* Returns the number of successful guard executions.
*
* @return the count of successful guard traces
*/
public long getSuccessCount() {
return traces.stream().filter(ExecutionTrace::isSuccess).count();
}
/**
* Returns the number of failed executions.
*
* @return the count of failed traces
*/
public long getFailureCount() {
return traces.stream().filter(t -> !t.isSuccess()).count();
}
/**
* Clears all recorded traces.
*/
public void clear() {
traces.clear();
}
}