Part 5: Trace Observers¶
In Part 1, we saw that plugins can provide many types of extensions. So far we've implemented custom functions. This part explores trace observers, which let you hook into workflow lifecycle events.
Starting from here?
If you're joining at this part, copy the solution from Part 4 to use as your starting point:
1. Understanding the existing trace observer¶
Remember the "Pipeline is starting!" message when you ran the pipeline?
That came from the GreetingObserver class in your plugin.
Look at the observer code:
This observer hooks into workflow lifecycle events. Trace observers can respond to many events:
| Method | When it's called |
|---|---|
onFlowCreate |
Workflow starts |
onFlowComplete |
Workflow finishes |
onProcessStart |
A task begins execution |
onProcessComplete |
A task finishes |
onProcessCached |
A cached task is reused |
onFilePublish |
A file is published |
This enables powerful use cases like custom reports, Slack notifications, or metrics collection.
2. Try it: Add a task counter observer¶
Rather than modifying the existing observer, create a new one that counts completed tasks. We'll build it up progressively: first a minimal version, then add features.
2.1. Create a minimal observer¶
Create a new file:
Start with the simplest possible observer. Just print a message when any task completes:
This is the minimum needed:
- Import the required classes (
TraceObserver,TaskHandler,TraceRecord) - Create a class that
implements TraceObserver - Override
onProcessCompleteto do something when a task finishes
2.2. Register the observer¶
The GreetingFactory creates observers.
Take a look at it:
@CompileStatic
class GreetingFactory implements TraceObserverFactory {
@Override
Collection<TraceObserver> create(Session session) {
return List.<TraceObserver>of(new GreetingObserver())
}
}
Edit GreetingFactory.groovy to add our new observer:
Groovy list syntax
We've replaced the Java-style List.<TraceObserver>of(...) with Groovy's simpler list literal [...].
Both return a Collection, but the Groovy syntax is more readable when adding multiple items.
2.3. Build, install, and test¶
You should see "✓ Task completed!" printed five times (once per task):
...
[be/bd8e72] Submitted process > SAY_HELLO (2)
✓ Task completed!
[5b/d24c2b] Submitted process > SAY_HELLO (1)
✓ Task completed!
...
Our observer is responding to task completion events. The next step makes it more useful.
2.4. Add counting logic¶
Update TaskCounterObserver.groovy to track a count and report a summary:
The key additions:
- Line 14: A private instance variable
taskCountpersists across method calls - Lines 18-19: Increment the counter and print the running total
- Lines 22-24:
onFlowCompleteis called once when the workflow finishes, perfect for a summary
Rebuild and test:
N E X T F L O W ~ version 25.10.2
Launching `main.nf` [pensive_engelbart] DSL2 - revision: 85fefd90d0
Pipeline is starting! 🚀
Reversed: olleH
Reversed: ruojnoB
Reversed: àloH
Reversed: oaiC
Reversed: ollaH
[be/bd8e72] Submitted process > SAY_HELLO (2)
[5b/d24c2b] Submitted process > SAY_HELLO (1)
[14/1f9dbe] Submitted process > SAY_HELLO (3)
Decorated: *** Bonjour ***
Decorated: *** Hello ***
[85/a6b3ad] Submitted process > SAY_HELLO (4)
📊 Tasks completed so far: 1
📊 Tasks completed so far: 2
Decorated: *** Holà ***
📊 Tasks completed so far: 3
Decorated: *** Ciao ***
[3c/be6686] Submitted process > SAY_HELLO (5)
📊 Tasks completed so far: 4
Decorated: *** Hallo ***
📊 Tasks completed so far: 5
Pipeline complete! 👋
📈 Final task count: 5
Why -ansi-log false?
By default, Nextflow's ANSI progress display overwrites previous lines to show a clean, updating view of progress. This means you'd only see the final task count, not the intermediate "Tasks completed so far" messages. They'd be overwritten as new output arrives.
Using -ansi-log false disables this behavior and shows all output sequentially, which is essential when testing observers that print messages during execution.
Without this flag, you might think your observer isn't working when it actually is.
The output is just being overwritten.
Takeaway¶
You learned that:
- Trace observers hook into workflow lifecycle events like
onFlowCreate,onProcessComplete, andonFlowComplete - Create observers by implementing
TraceObserverand registering them in a Factory - Observers are useful for custom logging, metrics collection, notifications, and reporting
What's next?¶
The next section shows how plugins can read configuration from nextflow.config.