Skip to content

09 - Logging Strategy

This document specifies the logging approach used throughout AgentEnsemble.

  • SLF4J API (slf4j-api) for the logging facade
  • Users provide their own SLF4J implementation (Logback, Log4j2, etc.)
  • The agentensemble-examples module includes Logback for demonstration

Each class uses its own SLF4J logger:

private static final Logger log = LoggerFactory.getLogger(ClassName.class);

This follows standard Java logging conventions and allows fine-grained log level control per class.

Logger: net.agentensemble.Ensemble

INFO : "Ensemble run started | Workflow: {workflow} | Tasks: {taskCount} | Agents: {agentCount}"
DEBUG : "Input variables: {inputs}"
DEBUG : "Task list: [{task1Description}, {task2Description}, ...]"
DEBUG : "Agent list: [{agent1Role}, {agent2Role}, ...]"
INFO : "Ensemble run completed | Duration: {totalDuration} | Tasks: {taskCount} | Tool calls: {totalToolCalls}"
WARN : "Agent '{role}' is registered but not assigned to any task"
ERROR : "Ensemble run failed: {exceptionMessage}"

Logger: net.agentensemble.workflow.SequentialWorkflowExecutor

INFO : "Task {index}/{total} starting | Description: {truncated80} | Agent: {role}"
INFO : "Task {index}/{total} completed | Duration: {duration} | Tool calls: {toolCallCount}"
DEBUG : "Task {index}/{total} context: {contextCount} prior outputs"
DEBUG : "Task {index}/{total} output preview: {truncated200}"
ERROR : "Task {index}/{total} failed: {exceptionMessage}"

Logger: net.agentensemble.agent.AgentExecutor

INFO : "Agent '{role}' executing task | Tools: {toolCount}"
INFO : "Tool call: {toolName}({truncatedInput200}) -> {truncatedOutput200} [{durationMs}ms]"
DEBUG : "System prompt ({charCount} chars):\n{systemPrompt}"
DEBUG : "User prompt ({charCount} chars):\n{userPrompt}"
DEBUG : "Agent '{role}' completed | Tool calls: {count} | Duration: {duration}"
WARN : "Tool error: {toolName}({truncatedInput200}) -> {errorMessage}"
WARN : "Agent '{role}' returned empty response for task '{truncatedDescription}'"
WARN : "Agent '{role}' exceeded max iterations ({max}). Stop message sent ({stopCount}/3)."
WARN : "Context from task '{description}' is {length} characters (>10000). Consider breaking into smaller tasks."
TRACE : "Full LLM response:\n{fullResponse}"

Logger: net.agentensemble.agent.AgentPromptBuilder

DEBUG : "Built system prompt ({charCount} chars) for agent '{role}'"
DEBUG : "Built user prompt ({charCount} chars) for task '{truncatedDescription}'"

Logger: net.agentensemble.config.TemplateResolver

DEBUG : "Resolving template ({charCount} chars) with {inputCount} input variables"
DEBUG : "Resolved {variableCount} variables in template"

Logger: net.agentensemble.tool.LangChain4jToolAdapter

DEBUG : "Adapted AgentTool '{name}' to LangChain4j ToolSpecification"
WARN : "AgentTool '{name}' threw exception during execution: {message}"

When verbose mode is active (ensemble.verbose = true OR agent.verbose = true), certain log statements are elevated from DEBUG/TRACE to INFO level:

Normal LevelElevated ToContent
DEBUGINFOSystem prompt text
DEBUGINFOUser prompt text
TRACEINFOFull LLM response text
DEBUGINFOTask output preview

Resolution: effectiveVerbose = ensemble.verbose || task.agent().verbose()

if (effectiveVerbose) {
log.info("System prompt:\n{}", systemPrompt);
} else {
log.debug("System prompt ({} chars):\n{}", systemPrompt.length(), systemPrompt);
}

Verbose mode is intended for development and debugging. In production, users should rely on standard log level configuration.

MDC values are set at the start of each task execution and cleared after completion (including on failure via try/finally).

KeyValueExample
ensemble.idUUID generated per run() call"a1b2c3d4-e5f6-7890-abcd-ef1234567890"
task.indexCurrent task position"2/5"
agent.roleCurrent agent’s role"Senior Research Analyst"

Users can include MDC values in their logging configuration:

Logback example:

<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level [%X{ensemble.id:-}] [%X{task.index:-}] [%X{agent.role:-}] %logger{36} - %msg%n</pattern>

Example output:

14:23:01.456 [main] INFO [a1b2c3d4] [1/3] [Researcher] i.a.w.SequentialWorkflowExecutor - Task 1/3 starting | Description: Research AI trends | Agent: Researcher
14:23:05.789 [main] INFO [a1b2c3d4] [1/3] [Researcher] i.a.a.AgentExecutor - Tool call: web_search(AI agent frameworks 2026) -> Top results: 1. LangChain... [1234ms]
14:23:12.321 [main] INFO [a1b2c3d4] [1/3] [Researcher] i.a.w.SequentialWorkflowExecutor - Task 1/3 completed | Duration: PT10.865S | Tool calls: 2
ensemble.run():
MDC.put("ensemble.id", UUID.randomUUID().toString())
try:
for each task:
MDC.put("task.index", ...)
MDC.put("agent.role", ...)
try:
execute task
finally:
MDC.remove("task.index")
MDC.remove("agent.role")
finally:
MDC.remove("ensemble.id")

To keep logs readable, certain values are truncated:

ContextMax LengthConstant Name
Task description in MDC80 charsMDC_DESCRIPTION_MAX_LENGTH
Tool input/output in INFO logs200 charsLOG_TRUNCATE_LENGTH
Output preview in verbose logs200 charsLOG_TRUNCATE_LENGTH
Template in error messages100 charsERROR_TEMPLATE_MAX_LENGTH

Truncation appends "..." when text is cut.

Full untruncated values are always available at DEBUG or TRACE level.

Logger: net.agentensemble.trace.export.JsonTraceExporter

DEBUG : "Execution trace written to {path}"
WARN : "Failed to export execution trace to {path}: {message}"

Logger: net.agentensemble.trace.CaptureMode

INFO : "CaptureMode active: {STANDARD|FULL}" (logged in Ensemble.runWithInputs)
DEBUG : "CaptureMode activated via system property agentensemble.captureMode={value}"
DEBUG : "CaptureMode activated via environment variable AGENTENSEMBLE_CAPTURE_MODE={value}"
DEBUG : "CaptureMode.FULL: auto-registering JsonTraceExporter at ./traces/"
WARN : "Unrecognised CaptureMode value '{value}' in {source} -- ignoring. Valid values: OFF, STANDARD, FULL"

The agentensemble-core module depends only on slf4j-api (the facade). It does NOT include any SLF4J implementation. Users must provide their own:

  • Logback: ch.qos.logback:logback-classic
  • Log4j2: org.apache.logging.log4j:log4j-slf4j2-impl
  • JUL bridge: org.slf4j:slf4j-jdk14
  • Simple: org.slf4j:slf4j-simple (for quick testing)

If no implementation is provided, SLF4J outputs a warning and discards all log messages.