Repository: apenella/go-ansible

Issue:
Real time stdout
Hi, 
first of all, I wanted to congratulate you on the excellent library you have created.
I wanted to ask you if there are any examples of reading playbook logs in real-time.
Let me explain my need, I need to read logs generated in real-time and push them to a rabbitmq queue. Then they will be consumed asynchronously and saved to a database.
At the end of the script I send the ‘recap’ of the execution via your ParseJSONResultsStream() method.

Thank you very much
Simone

Hints from issue comments:
@apenella: Hi @simo-berto98!

Thank you so much! I'm glad to hear that the go-ansible library is proving useful for you. :)

I assume that by 'reading the playbook logs,' you are referring to accessing the console output during execution, am I correct?

If you configure your execution to use the JSON stdout callback, it won't be possible to read logs in _real-time_, as Ansible only provides the output at the end of the execution.

However, you can read the Ansible output in _real-time_ using other stdout callback methods. Keep in mind, though, that parsing the output may be more challenging in such cases.

If your goal is to send the output to a RabbitMQ queue, one approach is to create a custom driver that implements the io.Writer interface. You can then set the [DefaultExecute](https://github.com/apenella/go-ansible/blob/master/pkg/execute/defaultExecute.go#L64) to use this driver as its writer by using the [WithWrite method](https://github.com/apenella/go-ansible/blob/master/pkg/execute/defaultExecuteOptions.go#L28).
By default, the [output mechanism](https://github.com/apenella/go-ansible/blob/master/pkg/execute/defaultExecute.go#L76) writes each captured line from the Ansible playbook output to os.Stdout. Replacing the writer with your custom implementation will redirect the output to your desired destination.

I hope this helps!
Aleix@simo-berto98: Thank you for your reply.
So apart from reading the buffer directly, there is no other way to get some extra metadata?
@apenella: The go-ansible library captures the stdout of the Ansible process and allows you to handle it. You can take advantage of the [TransformerFuncs](https://github.com/apenella/go-ansible/blob/master/README.md#transformer-functions) to add metadata to each line. TransformerFuncs enables you to customize the output before sending it to the writer.

[This example](https://github.com/apenella/go-ansible/blob/master/examples/ansibleplaybook-custom-transformer/ansibleplaybook-custom-transformer.go) illustrates how to use transformers and shows how to create a custom transformer.@Grub4K: Using any other (structured) stdout callback other than `ansible.posix.jsonl` will result in all tasks being grouped together. I think using `ANSIBLE_STDOUT_CALLBACK=ansible.posix.jsonl` makes the most sense, there events get printed as they come in.

Here is an example (using `WithWrite` for simplicity):
```go
func runPlaybook(ctx context.Context, cmd *playbook.AnsiblePlaybookCmd) error {
	pipeReader, pipeWriter := io.Pipe()
	executor := execute.NewDefaultExecute(
		execute.WithCmd(cmd),
		execute.WithErrorEnrich(playbook.NewAnsiblePlaybookErrorEnrich()),
		execute.WithWrite(pipeWriter),
		execute.WithEnvVars(map[string]string{
			configuration.AnsibleStdoutCallback: "ansible.posix.jsonl",
		}),
	)

	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	result := make(chan error, 1)
	go func() {
		result <- executor.Execute(ctx)
		pipeWriter.Close()
	}()
	for data := range parseJsonl(pipeReader) {
		// data comes in as events are happening
		fmt.Println(data)
	}
	return <-result
}
```
<details><summary>Used helper functions</summary>

```go
func parseJsonl(reader io.Reader) iter.Seq[any] {
	return func(yield func(any) bool) {
		bufReader := bufio.NewReader(reader)
		decoder := json.NewDecoder(bufReader)
		for {
			var data any
			err := decoder.Decode(&data)
			if err == io.EOF {
				return
			}
			if err != nil {
				if err = consumeLine(bufReader); err != nil {
					return
				}
				decoder = json.NewDecoder(bufReader)
				continue
			}
			if !yield(data) {
				return
			}
		}
	}
}

func consumeLine(bufReader *bufio.Reader) error {
	for {
		_, more, err := bufReader.ReadLine()
		if err != nil {
			return err
		}
		if !more {
			return nil
		}
	}
}
```
</details>

Would it make sense for this library to provide functionality like that builtin?@apenella: Hi @Grub4K!
My apologies for the delay in my response. Your suggestion seems like a great idea!

Let me explore how the `jsonl` stdout callback works before providing `go-ansible` with the mechanisms to use it.

I will get back to you soon!@apenella: Hi @Grub4K!
Thank you very much for your suggestion, it shed light on this issue!
For now, I have implemented an executor to use the `ansible.posix.jsonl` stdout callback method. (#168)

I am unsure about providing a mechanism to handle the results. I need to analyse it and consider how it would fit into the library.

At the very least, I will prepare an example based on your proposal.@Grub4K: Cool, thank you for invastigating. For more info you can also see [the implementation](<https://github.com/ansible-collections/ansible.posix/blob/main/plugins/callback/jsonl.py>) which defines output types as methods and dynamic attributes, with the prefix `v2_`. The dynamic ones share a common structure and only differ by `_event`.

methods: `v2_playbook_on_play_start`, `v2_runner_on_start`, `v2_playbook_on_task_start`, `v2_playbook_on_handler_task_start`, `v2_playbook_on_stats`
dynamic: `v2_runner_on_ok`, `v2_runner_on_failed`, `v2_runner_on_unreachable`, `v2_runner_on_skipped`

If there are any things I can help with do let me know@apenella: Hi @Grub4K 
Thanks a lot for your advises! They helped me add a new struct to the library for handling event data, `AnsiblePlaybookJSONLEventResults`.
I noticed that most of the structures align with [JSONStdoutCallbackResults](https://github.com/apenella/go-ansible?tab=readme-ov-file#jsonstdoutcallbackresults-struct), so I leveraged them.

I also included an example inspired by the code you shared.@apenella: Hi @Grub4K @simo-berto98 

The [[v2.2.0 release](https://github.com/apenella/go-ansible/releases/tag/v2.2.0) is now available and includes support for handling real-time JSON output using the `ansible.posix.jsonl` stdout callback method.

Thank you both for helping me improve go-ansible!

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f98e90..245c15b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [2.2.0] (2025-04-06)
+
+## Added
+
+- Add the examples `ansibleplaybook-posix-jsonl-stdout` and `ansibleplaybook-posix-jsonl-stdout-persistence` to demostrate the usage of the `AnsiblePosixJsonlStdoutCallbackExecute` executor.
+- Include the `AnsiblePlaybookJSONLEventResults` struct into the `github.com/apenella/go-ansible/v2/pkg/execute/result/json` package that represent the `ansible.posix.jsonl` events.
+- Include the `JSONLEventStdoutCallbackResults` struct into the `github.com/apenella/go-ansible/v2/pkg/execute/result/json` as a `ResultsOutputer` to handle the `ansible.posix.jsonl` stdout callback method events.
+- Support the stdout callback plugin `ansible.posix.jsonl` by adding a stdout callback executor: `AnsiblePosixJsonlStdoutCallbackExecute`.
+
 ## [2.1.1] (2025-03-14)
 
 ## Changed
diff --git a/README.md b/README.md
index f435c1c..91e7074 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,7 @@ _**Important:** The master branch may contain unreleased or pre-released feature
         - [Result package](#result-package)
           - [ResultsOutputer interface](#resultsoutputer-interface)
           - [DefaultResults struct](#defaultresults-struct)
+          - [JSONLEventStdoutCallbackResults struct](#jsonleventstdoutcallbackresults-struct)
           - [JSONStdoutCallbackResults struct](#jsonstdoutcallbackresults-struct)
           - [Transformer functions](#transformer-functions)
         - [Stdoutcallback package](#stdoutcallback-package)
@@ -101,7 +102,7 @@ _**Important:** The master branch may contain unreleased or pre-released feature
 Use this command to fetch and install the _go-ansible_ module. You can install the release candidate version by executing the following command:
 
 ```sh
-go get github.com/apenella/go-ansible/v2@v2.1.0
+go get github.com/apenella/go-ansible/v2@v2.2.0
 ```
 
 You can also install the previous stable version by executing the following command:
@@ -605,10 +606,23 @@ exec := execute.NewDefaultExecute(
 )
 ```
 
+###### JSONLEventStdoutCallbackResults struct
+
+The `JSONLEventStdoutCallbackResults` struct, located in the `github.com/apenella/go-ansible/v2/pkg/execute/result/json` package, is designed to handle the output of command execution when using the [ansible.posix.jsonl](https://docs.ansible.com/ansible/latest/collections/ansible/posix/jsonl_callback.html) stdout callback method. It implements the [ResultsOutputer](#resultsoutputer-interface) interface, providing functionality to parse and manipulate _JSONL-formatted_ output from _Ansible_ commands.
+
+When using the `AnsiblePosixJsonlStdoutCallbackExecute` executer, the output is managed by the `JSONLEventStdoutCallbackResults` struct. The `AnsiblePosixJsonlStdoutCallbackExecute` can be also configured to update the output using a set of [transformers](#transformer-functions) to be applied to the output. These transformers enrich or update the output as needed.
+
+The `github.com/apenella/go-ansible/v2/pkg/execute/result/json` provides you with the `AnsiblePlaybookJSONLEventResults` struct, that represents a JSON event output from the `ansible.posix.jsonl`. You can use this struct to manage the events.
+
+You can refer to the [ansibleplaybook-posix-jsonl-stdout](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-posix-jsonl-stdout) example to see how to work with the `JSONLEventStdoutCallbackResults` struct.  
+For a more advanced use case, such as persisting events to a database and applying a transformer, take a look at the [ansibleplaybook-posix-jsonl-stdout-persistence](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-posix-jsonl-stdout-persistence) example.
+
 ###### JSONStdoutCallbackResults struct
 
 The `JSONStdoutCallbackResults` struct, located in the `github.com/apenella/go-ansible/v2/pkg/execute/result/json` package, is designed to handle the output of command execution when using the `JSON` stdout callback method. It implements the [ResultsOutputer](#resultsoutputer-interface) interface, providing functionality to parse and manipulate _JSON-formatted_ output from _Ansible_ commands.
 
+When using the `JSONStdoutCallbackExecute` executor, the output is managed by the `JSONStdoutCallbackResults` struct.
+
 The package also includes the `ParseJSONResultsStream` function, which decodes the `JSON` output into an `AnsiblePlaybookJSONResults` data structure. This structure can be further manipulated to format the `JSON` output according to specific requirements. The expected `JSON` schema from _Ansible_ output is defined in the [json.py](https://github.com/ansible/ansible/blob/v2.9.11/lib/ansible/plugins/callback/json.py) file within the _Ansible_ repository.
 
 When creating a new instance of `JSONStdoutCallbackResults`, you can specify a list of [transformers](#transformer-functions) to be applied to the output. These transformers enrich or update the output as needed. By default, the `JSONStdoutCallbackResults` struct applies the `IgnoreMessage` transformer to ignore any `non-JSON` lines defined in the `skipPatterns` array.
@@ -733,6 +747,7 @@ The `github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback` package provi
 
 Each stdout callback method in _Ansible_ corresponds to a specific struct in _go-ansible_, making it easy to select and configure the desired method. Here are the available structs:
 
+- AnsiblePosixJsonlStdoutCallbackExecute
 - DebugStdoutCallbackExecute
 - DefaultStdoutCallbackExecute
 - DenseStdoutCallbackExecute
@@ -1093,13 +1108,15 @@ Here you have a list of examples:
 - [ansibleplaybook-extravars](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-extravars)
 - [ansibleplaybook-json-stdout](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-json-stdout)
 - [ansibleplaybook-myexecutor](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-myexecutor)
+- [ansibleplaybook-posix-jsonl-stdout-persistence](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-posix-jsonl-stdout-persistence)
+- [ansibleplaybook-posix-jsonl-stdout](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-posix-jsonl-stdout)
 - [ansibleplaybook-signals-and-cancellation](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-signals-and-cancellation)
 - [ansibleplaybook-simple-embedfs](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-simple-embedfs)
 - [ansibleplaybook-simple-with-prompt](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-simple-with-prompt)
 - [ansibleplaybook-simple](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-simple)
 - [ansibleplaybook-skipping-failing](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-skipping-failing)
-- [ansibleplaybook-ssh](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-ssh)
 - [ansibleplaybook-ssh-become-root-with-password/](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-ssh-become-root-with-password/)
+- [ansibleplaybook-ssh](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-ssh)
 - [ansibleplaybook-time-measurement](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-time-measurement)
 - [ansibleplaybook-walk-through-json-output](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-walk-through-json-output)
 - [ansibleplaybook-with-executor-time-measurament](https://github.com/apenella/go-ansible/tree/master/examples/ansibleplaybook-with-executor-time-measurament)
@@ -1116,9 +1133,9 @@ This section provides a reference guide for developing and contributing to the _
 
 To set up a development environment for the _go-ansible_ library, you need to have the following tools installed on your system:
 
-- [Docker Compose](https://docs.docker.com/compose/). The version used for development is `docker-compose version v2.26.1`.
-- [Docker](https://docs.docker.com/engine/reference/commandline/cli/). The version used for development is `Docker version 26.0.1`.
-- [Go](https://golang.org/). The version used for development is `1.22`.
+- [Docker Compose](https://docs.docker.com/compose/). The version used for development is `docker-compose version v2.34.0`.
+- [Docker](https://docs.docker.com/engine/reference/commandline/cli/). The version used for development is `Docker version 28.0.4`.
+- [Go](https://golang.org/). The version used for development is `1.23`.
 - [make](https://www.gnu.org/software/make/) utility. The version used for development is `GNU Make 4.3`. It is used to wrap the continuous integration and development processes, such us testing or linting.
 
 #### Testing
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 5802547..d00e71a 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,12 +1,10 @@
 # Release notes
 
-## [undefined]
+## [2.2.0] (2025-04-06)
 
-## Changed
+## Added
 
-- Bump from golang 1.22 to 1.23
-
-### Fixed
-
-- Bump golang.org/x/crypto from 0.22.0 to 0.31.0
-- Bump golang.org/x/net v0.24.0 to v0.36.0
+- Add the examples `ansibleplaybook-posix-jsonl-stdout` and `ansibleplaybook-posix-jsonl-stdout-persistence` to demostrate the usage of the `AnsiblePosixJsonlStdoutCallbackExecute` executor.
+- Include the `AnsiblePlaybookJSONLEventResults` struct into the `github.com/apenella/go-ansible/v2/pkg/execute/result/json` package that represent the `ansible.posix.jsonl` events.
+- Include the `JSONLEventStdoutCallbackResults` struct into the `github.com/apenella/go-ansible/v2/pkg/execute/result/json` as a `ResultsOutputer` to handle the `ansible.posix.jsonl` stdout callback method events.
+- Support the stdout callback plugin `ansible.posix.jsonl` by adding a stdout callback executor: `AnsiblePosixJsonlStdoutCallbackExecute`.
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout-persistence/.env b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/.env
new file mode 100644
index 0000000..32d4b30
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/.env
@@ -0,0 +1 @@
+GOLANG_VERSION=1.23
\ No newline at end of file
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout-persistence/Makefile b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/Makefile
new file mode 100644
index 0000000..e250ae3
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/Makefile
@@ -0,0 +1,39 @@
+
+DOCKER_COMPOSE_BINARY := $(shell docker compose version > /dev/null 2>&1 && echo "docker compose" || (which docker-compose > /dev/null 2>&1 && echo "docker-compose" || (echo "docker compose not found. Aborting." >&2; exit 1)))
+
+PROJECT_NAME := go-ansible-$(shell basename ${PWD})
+
+# dafault target
+.DEFAULT_GOAL: help
+
+help: ## Lists available targets
+	@echo
+	@echo "Makefile usage:"
+	@grep -E '^[a-zA-Z1-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "  \033[1;32m%-20s\033[0m %s\n", $$1, $$2}'
+	@echo
+
+build: ## Build the Docker compose environment
+	@$(DOCKER_COMPOSE_BINARY) build
+
+up: ## Create and start containers
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) up --detach --build
+
+down: ## Stop and remove containers, networks, and volumes
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) down --volumes --remove-orphans --timeout 3
+
+restart: down up ## Restart the containers
+
+ps: ## List containers
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) ps
+
+logs: ## Show all logs
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) logs
+
+attach-ansible: ## Attach to the ansible container
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) exec --workdir /code/examples/$$(basename $$(pwd)) ansible /bin/sh
+
+project-name: ## Show the project name
+	@echo $(PROJECT_NAME)
+
+run: ## Run the playbook
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) run --build --rm --workdir /code/examples/$$(basename $$(pwd)) ansible go run $$(basename $$(pwd)).go
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout-persistence/ansibleplaybook-posix-jsonl-stdout-persistence.go b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/ansibleplaybook-posix-jsonl-stdout-persistence.go
new file mode 100644
index 0000000..e5330dd
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/ansibleplaybook-posix-jsonl-stdout-persistence.go
@@ -0,0 +1,213 @@
+/*
+
+	This example shows how to configure the go-ansible library executor to use a custom writer that writes events to a persistence layer, as well as how to use a transformer to update the original events returned by the ansible.posix.jsonl callback plugin.
+
+	Although the go-ansible library provides an executor that handles ansible.posix.jsonl stdout callback results, it does not allow the use of transformers to update the original events directly. In this case, you need to use the DefaultExecute executor, configure the transformer there, and then create an AnsibleWithConfigurationSettingsExecute executor with the ansible.posix.jsonl stdout callback plugin properly set.
+
+*/
+
+package main
+
+import (
+	"context"
+	"crypto/sha256"
+	"encoding/json"
+	"fmt"
+	"io"
+	"os"
+
+	"github.com/apenella/go-ansible/v2/pkg/execute"
+	"github.com/apenella/go-ansible/v2/pkg/execute/configuration"
+	jsonresults "github.com/apenella/go-ansible/v2/pkg/execute/result/json"
+	"github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback"
+	"github.com/apenella/go-ansible/v2/pkg/playbook"
+)
+
+//
+// Code for the EnrichedJSONLEvent struct. This struct is used to extend the original event data by adding a hash for enrichment purposes.
+//
+
+// EnrichedJSONLEvent is a struct that represents the enriched JSONL event
+type EnrichedJSONLEvent struct {
+	Hash string `json:"_hash"`
+	Data string `json:"data"`
+}
+
+// NewEnrichedJSONLEvent creates a new EnrichedJSONLEvent
+func NewEnrichedJSONLEvent(data string) EnrichedJSONLEvent {
+	// Generate a SHA256 hash from the data
+	hash := sha256.Sum256([]byte(data))
+
+	return EnrichedJSONLEvent{
+		Hash: fmt.Sprintf("%x", hash),
+		Data: data,
+	}
+}
+
+//
+// Code for a transformer that generates a EnrichedJSONLEvent
+//
+
+// EnrichedJSONLEventTransformer is a function that transforms a JSONL event into an enriched JSONL event. The function receives the original event as a string and returns the enriched event as a string.
+func EnrichedJSONLEventTransformer(event string) string {
+
+	enrichedEvent := NewEnrichedJSONLEvent(event)
+	jsonData, err := json.Marshal(enrichedEvent)
+	if err != nil {
+		return event
+	}
+
+	return string(jsonData)
+}
+
+//
+// Code for the JSONLEventWriter. This component is responsible for writing the events to a persistence layer.
+//
+
+// JSONLEventWriter is a custom writer that implements the io.Writer interface
+type JSONLEventWriter struct {
+	writer      io.Writer
+	persistency Persistency
+}
+
+// NewJSONLEventWriter creates a new JSONLEventWriter
+func NewJSONLEventWriter(p Persistency, w io.Writer) *JSONLEventWriter {
+	return &JSONLEventWriter{
+		persistency: p,
+		writer:      w,
+	}
+}
+
+// Write implements the io.Writer interface for JSONLEventWriter
+func (e *JSONLEventWriter) Write(data []byte) (n int, err error) {
+
+	if e.writer == nil {
+		e.writer = os.Stdout
+	}
+
+	if e.persistency == nil {
+		return len(data), fmt.Errorf("persistency is not initialized")
+	}
+	e.persistency.Add(data)
+
+	return len(data), nil
+}
+
+//
+// Code for the Persistency layer. This component is responsible for storing the events in a map.
+//
+
+// Persistency is a map that stores EnrichedJSONLEvent objects
+type Persistency map[string][]byte
+
+// NewPersistency creates a new database
+func NewPersistency() Persistency {
+	return make(Persistency)
+}
+
+// Add adds an event to the database
+func (p Persistency) Add(event []byte) {
+	hash := fmt.Sprintf("%x", sha256.Sum256(event))
+	p[hash] = event
+}
+
+// Iterate iterates over the database and applies the function f to each event
+func (p Persistency) Iterate() func(func(string, []byte) bool) {
+	return func(yield func(string, []byte) bool) {
+		for hash, event := range p {
+			if !yield(hash, event) {
+				return
+			}
+		}
+	}
+}
+
+//
+// main function
+//
+
+func main() {
+	var err error
+
+	persistency := NewPersistency()
+
+	ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
+		Connection: "local",
+		Inventory:  "127.0.0.1,",
+	}
+
+	playbooksList := []string{"site1.yml", "site2.yml", "site3.yml"}
+
+	playbookCmd := playbook.NewAnsiblePlaybookCmd(
+		playbook.WithPlaybooks(playbooksList...),
+		playbook.WithPlaybookOptions(ansiblePlaybookOptions),
+	)
+
+	// Create a new DefaultExecute executor using the playbook command. Set the writer to a custom JSONLEventWriter that persists events to a storage layer.
+	baseExec := execute.NewDefaultExecute(
+		execute.WithCmd(playbookCmd),
+		execute.WithErrorEnrich(playbook.NewAnsiblePlaybookErrorEnrich()),
+		execute.WithWrite(NewJSONLEventWriter(persistency, os.Stdout)),
+	)
+	baseExec.Quiet()
+	// Configure the DefaultExecute output to use JSONLEventStdoutCallbackResults and assign the EnrichedJSONLEventTransformer to enrich the event data.
+	baseExec.WithOutput(
+		jsonresults.NewJSONLEventStdoutCallbackResults(
+			jsonresults.WithJSONLEventTransformers(
+				EnrichedJSONLEventTransformer,
+			),
+		),
+	)
+
+	// Finally, create a new AnsibleWithConfigurationSettingsExecute executor using the previously defined base executor. This step manually sets the ansible.posix.jsonl stdout callback method.
+	exec := configuration.NewAnsibleWithConfigurationSettingsExecute(baseExec,
+		configuration.WithAnsibleStdoutCallback(stdoutcallback.AnsiblePosixJsonlStdoutCallback),
+	)
+
+	/*
+
+		   If you don't need to apply a transformer to the original event, you can use the AnsiblePosixJsonlStdoutCallbackExecute executor directly, as is show below in that comment. Keep in mind that this will change the behavior of the example: when reading events from persistence, they will be the original events returned by the ansible.posix.jsonl callback plugin, rather than EnrichedJSONLEvent objects.
+
+				  exec := stdoutcallback.NewAnsiblePosixJsonlStdoutCallbackExecute(
+						execute.NewDefaultExecute(
+							execute.WithCmd(playbookCmd),
+							execute.WithErrorEnrich(playbook.NewAnsiblePlaybookErrorEnrich()),
+							execute.WithWrite(NewJSONLEventWriter(persistency, os.Stdout)),
+						),
+				  )
+
+	*/
+
+	ctx, cancel := context.WithCancel(context.TODO())
+	defer cancel()
+
+	err = exec.Execute(ctx)
+	if err != nil {
+		fmt.Println(err)
+	}
+
+	for _, event := range persistency {
+
+		enrichedEvent := new(EnrichedJSONLEvent)
+		eventOriginal := new(jsonresults.AnsiblePlaybookJSONLEventResults)
+
+		err = json.Unmarshal(event, &enrichedEvent)
+		if err != nil {
+			fmt.Printf("Error unmarshaling EnrichedJSONLEvent: %v. Received event data: %s\n", err, string(event))
+			continue
+		}
+
+		if enrichedEvent.Data == "" {
+			fmt.Printf("EnrichedJSONLEvent data is empty. Received event data: %s\n", string(event))
+			continue
+		}
+
+		err = json.Unmarshal([]byte(enrichedEvent.Data), &eventOriginal)
+		if err != nil {
+			fmt.Printf("Error unmarshaling original event: %v. Received event data: %s\n", err, string(event))
+			continue
+		}
+
+		fmt.Printf("(%s)\n%s\n", enrichedEvent.Hash, eventOriginal.String())
+	}
+}
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout-persistence/docker-compose.yaml b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/docker-compose.yaml
new file mode 100644
index 0000000..e0f9dc4
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/docker-compose.yaml
@@ -0,0 +1,15 @@
+---
+
+services:
+  ansible:
+    build: 
+      context: docker/ansible
+      args:
+        - golang_version=${GOLANG_VERSION}
+    command: ["tail", "-f", "/dev/null"]
+    # command: ["ansible-playbook", "--help"]
+    volumes:
+      - ../..:/code
+    working_dir: /code
+    ## Set the init flag to true lets the process 1 to reap all the zombie processes
+    init: true
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout-persistence/docker/ansible/Dockerfile b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/docker/ansible/Dockerfile
new file mode 100644
index 0000000..6126b8e
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/docker/ansible/Dockerfile
@@ -0,0 +1,27 @@
+ARG golang_version=1.23
+
+FROM golang:${golang_version}-bookworm AS golang
+
+FROM python:3.12-alpine3.19
+
+RUN apk add --update --no-cache \
+        openssh-client \
+        git \
+    && rm -rf /var/cache/apk/*
+
+RUN pip3 install -U pip setuptools \
+    && pip3 install --no-cache-dir \
+        setuptools-rust \
+        cryptography \
+        # Required library to execute ansible community.general.dig plugin
+        dnspython \
+        ansible \
+    && ln /usr/local/bin/ansible-playbook /usr/bin/ansible-playbook
+
+COPY --from=golang /usr/local/go /usr/local/go
+
+# Configure Go
+ENV GOROOT /usr/local/go
+ENV PATH /usr/local/go/bin:/go/bin:$PATH
+
+RUN mkdir -p ${GOPATH}/src ${GOPATH}/bin
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout-persistence/site1.yml b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/site1.yml
new file mode 100644
index 0000000..8cef933
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/site1.yml
@@ -0,0 +1,29 @@
+---
+
+- hosts: all
+  gather_facts: false
+  vars:
+    array:
+      - Your are running
+      - 'ansibleplaybook-posix-jsonl-stdout'
+      - first example
+    map:
+      intro: Your are running
+      what: ansibleplaybook-posix-jsonl-stdout
+      outro: first example
+
+  tasks:
+    - name: ansibleplaybook-posix-jsonl-stdout
+      debug:
+        msg: |
+          Your are running
+          'ansibleplaybook-posix-jsonl-stdout'
+          first example
+
+    - name: ansibleplaybook-posix-jsonl-stdout from array
+      debug:
+        msg: "{{ array }}"
+
+    - name: ansibleplaybook-posix-jsonl-stdout from map
+      debug:
+        msg: "{{ map }}"
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout-persistence/site2.yml b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/site2.yml
new file mode 100644
index 0000000..d8217f6
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/site2.yml
@@ -0,0 +1,29 @@
+---
+
+- hosts: all
+  gather_facts: false
+  vars:
+    array:
+      - Your are running
+      - 'ansibleplaybook-posix-jsonl-stdout'
+      - second example
+    map:
+      intro: Your are running
+      what: ansibleplaybook-posix-jsonl-stdout
+      outro: second example
+
+  tasks:
+    - name: ansibleplaybook-posix-jsonl-stdout
+      debug:
+        msg: |
+          Your are running
+          'ansibleplaybook-posix-jsonl-stdout'
+          second example
+
+    - name: ansibleplaybook-posix-jsonl-stdout from array
+      debug:
+        msg: "{{ array }}"
+
+    - name: ansibleplaybook-posix-jsonl-stdout from map
+      debug:
+        msg: "{{ map }}"
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout-persistence/site3.yml b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/site3.yml
new file mode 100644
index 0000000..cf2c54d
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout-persistence/site3.yml
@@ -0,0 +1,29 @@
+---
+
+- hosts: all
+  gather_facts: false
+  vars:
+    array:
+      - Your are running
+      - 'ansibleplaybook-posix-jsonl-stdout'
+      - third example
+    map:
+      intro: Your are running
+      what: ansibleplaybook-posix-jsonl-stdout
+      outro: third example
+
+  tasks:
+    - name: ansibleplaybook-posix-jsonl-stdout
+      debug:
+        msg: |
+          Your are running
+          'ansibleplaybook-posix-jsonl-stdout'
+          third example
+
+    - name: ansibleplaybook-posix-jsonl-stdout from array
+      debug:
+        msg: "{{ array }}"
+
+    - name: ansibleplaybook-posix-jsonl-stdout from map
+      debug:
+        msg: "{{ map }}"
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout/.env b/examples/ansibleplaybook-posix-jsonl-stdout/.env
new file mode 100644
index 0000000..32d4b30
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout/.env
@@ -0,0 +1 @@
+GOLANG_VERSION=1.23
\ No newline at end of file
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout/Makefile b/examples/ansibleplaybook-posix-jsonl-stdout/Makefile
new file mode 100644
index 0000000..e250ae3
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout/Makefile
@@ -0,0 +1,39 @@
+
+DOCKER_COMPOSE_BINARY := $(shell docker compose version > /dev/null 2>&1 && echo "docker compose" || (which docker-compose > /dev/null 2>&1 && echo "docker-compose" || (echo "docker compose not found. Aborting." >&2; exit 1)))
+
+PROJECT_NAME := go-ansible-$(shell basename ${PWD})
+
+# dafault target
+.DEFAULT_GOAL: help
+
+help: ## Lists available targets
+	@echo
+	@echo "Makefile usage:"
+	@grep -E '^[a-zA-Z1-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "  \033[1;32m%-20s\033[0m %s\n", $$1, $$2}'
+	@echo
+
+build: ## Build the Docker compose environment
+	@$(DOCKER_COMPOSE_BINARY) build
+
+up: ## Create and start containers
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) up --detach --build
+
+down: ## Stop and remove containers, networks, and volumes
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) down --volumes --remove-orphans --timeout 3
+
+restart: down up ## Restart the containers
+
+ps: ## List containers
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) ps
+
+logs: ## Show all logs
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) logs
+
+attach-ansible: ## Attach to the ansible container
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) exec --workdir /code/examples/$$(basename $$(pwd)) ansible /bin/sh
+
+project-name: ## Show the project name
+	@echo $(PROJECT_NAME)
+
+run: ## Run the playbook
+	@$(DOCKER_COMPOSE_BINARY) --project-name $(PROJECT_NAME) run --build --rm --workdir /code/examples/$$(basename $$(pwd)) ansible go run $$(basename $$(pwd)).go
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout/ansibleplaybook-posix-jsonl-stdout.go b/examples/ansibleplaybook-posix-jsonl-stdout/ansibleplaybook-posix-jsonl-stdout.go
new file mode 100644
index 0000000..49475a8
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout/ansibleplaybook-posix-jsonl-stdout.go
@@ -0,0 +1,77 @@
+package main
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"os"
+
+	"github.com/apenella/go-ansible/v2/pkg/execute"
+	jsonresults "github.com/apenella/go-ansible/v2/pkg/execute/result/json"
+	"github.com/apenella/go-ansible/v2/pkg/execute/stdoutcallback"
+	"github.com/apenella/go-ansible/v2/pkg/playbook"
+)
+
+func main() {
+	var err error
+
+	ansiblePlaybookOptions := &playbook.AnsiblePlaybookOptions{
+		Connection: "local",
+		Inventory:  "127.0.0.1,",
+	}
+
+	playbooksList := []string{"site1.yml", "site2.yml", "site3.yml"}
+
+	playbookCmd := playbook.NewAnsiblePlaybookCmd(
+		playbook.WithPlaybooks(playbooksList...),
+		playbook.WithPlaybookOptions(ansiblePlaybookOptions),
+	)
+
+	exec := stdoutcallback.NewAnsiblePosixJsonlStdoutCallbackExecute(
+		execute.NewDefaultExecute(
+			execute.WithCmd(playbookCmd),
+			execute.WithErrorEnrich(playbook.NewAnsiblePlaybookErrorEnrich()),
+			execute.WithWrite(NewJSONLEventWriter(os.Stdout)),
+		),
+	)
+
+	ctx, cancel := context.WithCancel(context.TODO())
+	defer cancel()
+
+	err = exec.Execute(ctx)
+	if err != nil {
+		fmt.Println(err)
+	}
+}
+
+// JSONLEventWriter is a custom writer that implements the io.Writer interface
+type JSONLEventWriter struct {
+	writer io.Writer
+}
+
+// NewJSONLEventWriter creates a new JSONLEventWriter
+func NewJSONLEventWriter(w io.Writer) *JSONLEventWriter {
+	return &JSONLEventWriter{
+		writer: w,
+	}
+}
+
+// Write implements the io.Writer interface for JSONLEventWriter
+func (e *JSONLEventWriter) Write(p []byte) (n int, err error) {
+
+	var event jsonresults.AnsiblePlaybookJSONLEventResults
+
+	if e.writer == nil {
+		e.writer = os.Stdout
+	}
+
+	err = json.Unmarshal(p, &event)
+	if err != nil {
+		return len(p), fmt.Errorf("error unmarshaling JSON: %w", err)
+	}
+
+	fmt.Fprintf(e.writer, "%s\n", event)
+
+	return len(p), nil
+}
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout/docker-compose.yaml b/examples/ansibleplaybook-posix-jsonl-stdout/docker-compose.yaml
new file mode 100644
index 0000000..e0f9dc4
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout/docker-compose.yaml
@@ -0,0 +1,15 @@
+---
+
+services:
+  ansible:
+    build: 
+      context: docker/ansible
+      args:
+        - golang_version=${GOLANG_VERSION}
+    command: ["tail", "-f", "/dev/null"]
+    # command: ["ansible-playbook", "--help"]
+    volumes:
+      - ../..:/code
+    working_dir: /code
+    ## Set the init flag to true lets the process 1 to reap all the zombie processes
+    init: true
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout/docker/ansible/Dockerfile b/examples/ansibleplaybook-posix-jsonl-stdout/docker/ansible/Dockerfile
new file mode 100644
index 0000000..6126b8e
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout/docker/ansible/Dockerfile
@@ -0,0 +1,27 @@
+ARG golang_version=1.23
+
+FROM golang:${golang_version}-bookworm AS golang
+
+FROM python:3.12-alpine3.19
+
+RUN apk add --update --no-cache \
+        openssh-client \
+        git \
+    && rm -rf /var/cache/apk/*
+
+RUN pip3 install -U pip setuptools \
+    && pip3 install --no-cache-dir \
+        setuptools-rust \
+        cryptography \
+        # Required library to execute ansible community.general.dig plugin
+        dnspython \
+        ansible \
+    && ln /usr/local/bin/ansible-playbook /usr/bin/ansible-playbook
+
+COPY --from=golang /usr/local/go /usr/local/go
+
+# Configure Go
+ENV GOROOT /usr/local/go
+ENV PATH /usr/local/go/bin:/go/bin:$PATH
+
+RUN mkdir -p ${GOPATH}/src ${GOPATH}/bin
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout/site1.yml b/examples/ansibleplaybook-posix-jsonl-stdout/site1.yml
new file mode 100644
index 0000000..8cef933
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout/site1.yml
@@ -0,0 +1,29 @@
+---
+
+- hosts: all
+  gather_facts: false
+  vars:
+    array:
+      - Your are running
+      - 'ansibleplaybook-posix-jsonl-stdout'
+      - first example
+    map:
+      intro: Your are running
+      what: ansibleplaybook-posix-jsonl-stdout
+      outro: first example
+
+  tasks:
+    - name: ansibleplaybook-posix-jsonl-stdout
+      debug:
+        msg: |
+          Your are running
+          'ansibleplaybook-posix-jsonl-stdout'
+          first example
+
+    - name: ansibleplaybook-posix-jsonl-stdout from array
+      debug:
+        msg: "{{ array }}"
+
+    - name: ansibleplaybook-posix-jsonl-stdout from map
+      debug:
+        msg: "{{ map }}"
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout/site2.yml b/examples/ansibleplaybook-posix-jsonl-stdout/site2.yml
new file mode 100644
index 0000000..d8217f6
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout/site2.yml
@@ -0,0 +1,29 @@
+---
+
+- hosts: all
+  gather_facts: false
+  vars:
+    array:
+      - Your are running
+      - 'ansibleplaybook-posix-jsonl-stdout'
+      - second example
+    map:
+      intro: Your are running
+      what: ansibleplaybook-posix-jsonl-stdout
+      outro: second example
+
+  tasks:
+    - name: ansibleplaybook-posix-jsonl-stdout
+      debug:
+        msg: |
+          Your are running
+          'ansibleplaybook-posix-jsonl-stdout'
+          second example
+
+    - name: ansibleplaybook-posix-jsonl-stdout from array
+      debug:
+        msg: "{{ array }}"
+
+    - name: ansibleplaybook-posix-jsonl-stdout from map
+      debug:
+        msg: "{{ map }}"
diff --git a/examples/ansibleplaybook-posix-jsonl-stdout/site3.yml b/examples/ansibleplaybook-posix-jsonl-stdout/site3.yml
new file mode 100644
index 0000000..cf2c54d
--- /dev/null
+++ b/examples/ansibleplaybook-posix-jsonl-stdout/site3.yml
@@ -0,0 +1,29 @@
+---
+
+- hosts: all
+  gather_facts: false
+  vars:
+    array:
+      - Your are running
+      - 'ansibleplaybook-posix-jsonl-stdout'
+      - third example
+    map:
+      intro: Your are running
+      what: ansibleplaybook-posix-jsonl-stdout
+      outro: third example
+
+  tasks:
+    - name: ansibleplaybook-posix-jsonl-stdout
+      debug:
+        msg: |
+          Your are running
+          'ansibleplaybook-posix-jsonl-stdout'
+          third example
+
+    - name: ansibleplaybook-posix-jsonl-stdout from array
+      debug:
+        msg: "{{ array }}"
+
+    - name: ansibleplaybook-posix-jsonl-stdout from map
+      debug:
+        msg: "{{ map }}"
diff --git a/go.mod b/go.mod
index 8781417..1658c69 100644
--- a/go.mod
+++ b/go.mod
@@ -2,8 +2,6 @@ module github.com/apenella/go-ansible/v2
 
 go 1.23.0
 
-toolchain go1.23.7
-
 require (
 	github.com/PuerkitoBio/goquery v1.9.1
 	github.com/apenella/go-common-utils/data v0.0.0-20220913191136-86daaa87e7df
diff --git a/pkg/execute/result/json/JSONLEventStdoutCallbackResults.go b/pkg/execute/result/json/JSONLEventStdoutCallbackResults.go
new file mode 100644
index 0000000..93371ed
--- /dev/null
+++ b/pkg/execute/result/json/JSONLEventStdoutCallbackResults.go
@@ -0,0 +1,148 @@
+package json
+
+import (
+	"bufio"
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"iter"
+
+	"github.com/apenella/go-ansible/v2/pkg/execute/result"
+	"github.com/apenella/go-ansible/v2/pkg/execute/result/transformer"
+	errors "github.com/apenella/go-common-utils/error"
+)
+
+// JSONLEventStdoutCallbackResults handles the ansible.posix.jsonl callback plugin output
+type JSONLEventStdoutCallbackResults struct {
+	trans []transformer.TransformerFunc
+}
+
+// NewJSONLEventStdoutCallbackResults creates a new JSONLEventStdoutCallbackResults instance
+func NewJSONLEventStdoutCallbackResults(options ...result.OptionsFunc) *JSONLEventStdoutCallbackResults {
+	results := &JSONLEventStdoutCallbackResults{}
+	results.Options(options...)
+	return results
+}
+
+// WithJSONLEventTransformers sets a transformers list to JSONLEventStdoutCallbackResults
+func WithJSONLEventTransformers(trans ...transformer.TransformerFunc) result.OptionsFunc {
+	return func(r result.ResultsOutputer) {
+		r.(*JSONLEventStdoutCallbackResults).trans = append(r.(*JSONLEventStdoutCallbackResults).trans, trans...)
+	}
+}
+
+// Options executes the options functions received as a parameters to set the JSONLEventStdoutCallbackResults attributes
+func (r *JSONLEventStdoutCallbackResults) Options(options ...result.OptionsFunc) {
+	for _, opt := range options {
+		opt(r)
+	}
+}
+
+// Print handles the ansible.posix.jsonl callback plugin output
+func (r *JSONLEventStdoutCallbackResults) Print(ctx context.Context, reader io.Reader, writer io.Writer, options ...result.OptionsFunc) error {
+	printChan := make(chan []byte)
+	errChan := make(chan error)
+	done := make(chan struct{})
+
+	errContext := "(result::json::JSONLEventStdoutCallbackResults::Print)"
+
+	if reader == nil {
+		return errors.New(errContext, "JSONLEventStdoutCallbackResults requires a reader to print the output of the execution")
+	}
+
+	if writer == nil {
+		return errors.New(errContext, "JSONLEventStdoutCallbackResults requires a writer to print the output of the execution")
+	}
+
+	r.Options(options...)
+
+	go func() {
+		defer close(printChan)
+		defer close(errChan)
+		defer close(done)
+
+		errs := []error{}
+
+		for data, err := range readResultsStream(reader) {
+			if err != nil {
+				errs = append(errs, err)
+				continue
+			}
+
+			// TransformerFunc expects and returns a string so we need to convert the byte array to a string and back
+			if len(r.trans) > 0 {
+				dataString := string(data)
+				for _, t := range r.trans {
+					dataString = t(dataString)
+				}
+				data = []byte(dataString)
+			}
+
+			printChan <- data
+		}
+
+		if len(errs) > 0 {
+			errChan <- errors.New(errContext, "Error processing the execution output", errs...)
+		}
+
+		done <- struct{}{}
+	}()
+
+	for {
+		select {
+		case data := <-printChan:
+			_, err := writer.Write(data)
+			if err != nil {
+				return errors.New(errContext, "Error writing to writer", err)
+			}
+		case err := <-errChan:
+			if err != nil {
+				return errors.New(errContext, "Error reading the results stream", err)
+			}
+		case <-done:
+			return nil
+		case <-ctx.Done():
+			return nil
+		}
+	}
+}
+
+func readResultsStream(reader io.Reader) iter.Seq2[[]byte, error] {
+	return func(yield func([]byte, error) bool) {
+		scanner := bufio.NewScanner(reader)
+
+		for scanner.Scan() {
+			line := scanner.Text()
+
+			// Validate if the line is a properly formed JSON object
+			var event any
+			err := json.Unmarshal([]byte(line), &event)
+			if err != nil {
+				if !yield(nil, fmt.Errorf("error decoding JSON: %w", err)) {
+					return
+				}
+				continue
+			}
+
+			// Convert the JSON object back to a byte array
+			eventByteArray, err := json.Marshal(event)
+			if err != nil {
+				if !yield(nil, fmt.Errorf("error converting event to string: %w", err)) {
+					return
+				}
+				continue
+			}
+
+			// Yield the valid JSON byte array
+			if !yield(eventByteArray, nil) {
+				return
+			}
+		}
+
+		// Handle any errors that occurred during scanning
+		if err := scanner.Err(); err != nil {
+			yield(nil, fmt.Errorf("error reading input: %w", err))
+		}
+	}
+}
diff --git a/pkg/execute/result/json/JSONResults.go b/pkg/execute/result/json/JSONStdoutCallbackResults.go
similarity index 97%
rename from pkg/execute/result/json/JSONResults.go
rename to pkg/execute/result/json/JSONStdoutCallbackResults.go
index 8001cde..9bc3d37 100644
--- a/pkg/execute/result/json/JSONResults.go
+++ b/pkg/execute/result/json/JSONStdoutCallbackResults.go
@@ -41,7 +41,7 @@ func (r *JSONStdoutCallbackResults) Options(options ...result.OptionsFunc) {
 	}
 }
 
-// JSONStdoutCallbackResults method manges the ansible' JSON stdout callback and print the result stats
+// Print method manges the ansible' JSON stdout callback and print the result stats
 func (r *JSONStdoutCallbackResults) Print(ctx context.Context, reader io.Reader, writer io.Writer, options ...result.OptionsFunc) error {
 	var transformers []transformer.TransformerFunc
 
diff --git a/pkg/execute/result/json/ansiblePlaybookJSONLEventResults.go b/pkg/execute/result/json/ansiblePlaybookJSONLEventResults.go
new file mode 100644
index 0000000..28981a1
--- /dev/null
+++ b/pkg/execute/result/json/ansiblePlaybookJSONLEventResults.go
@@ -0,0 +1,65 @@
+package json
+
+import "fmt"
+
+// AnsiblePlaybookJSONLEventResults represents the structure of the JSON lines generated by an Ansible playbook execution using the ansible.posix.jsonl callback plugin
+type AnsiblePlaybookJSONLEventResults struct {
+	Event     string `json:"_event"`
+	Timestamp string `json:"_timestamp"`
+
+	CustomStats       interface{}                                             `json:"custom_stats"`
+	GlobalCustomStats interface{}                                             `json:"global_custom_stats"`
+	Hosts             map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem `json:"hosts,omitempty"`
+	Play              *AnsiblePlaybookJSONResultsPlaysPlay                    `json:"play,omitempty"`
+	Stats             map[string]*AnsiblePlaybookJSONResultsStats             `json:"stats"`
+	Task              *AnsiblePlaybookJSONResultsPlayTaskItem                 `json:"task,omitempty"`
+	Tasks             []AnsiblePlaybookJSONResultsPlayTask                    `json:"tasks,omitempty"`
+}
+
+// String returns a string representation of AnsiblePlaybookJSONLEventResults
+func (a AnsiblePlaybookJSONLEventResults) String() string {
+
+	str := fmt.Sprintf("Event: %s\nTimestamp: %s\n", a.Event, a.Timestamp)
+
+	if a.Play != nil {
+		str = fmt.Sprintf("%sPlay: %+v\n", str, a.Play)
+	}
+
+	if a.Task != nil {
+		str = fmt.Sprintf("%sTask: %+v\n", str, a.Task)
+	}
+
+	if len(a.Hosts) > 0 {
+
+		str = fmt.Sprintf("%sHosts:\n", str)
+		for k, v := range a.Hosts {
+			str = fmt.Sprintf("%s  %s:\n    %s\n", str, k, fmt.Sprintf("%+v", v))
+		}
+	}
+
+	if len(a.Tasks) > 0 {
+		str = fmt.Sprintf("%sTasks:\n", str)
+
+		for _, task := range a.Tasks {
+			str = fmt.Sprintf("%s  %+v\n", str, task)
+		}
+	}
+
+	if len(a.Stats) > 0 {
+		str = fmt.Sprintf("%sStats:\n", str)
+		for k, v := range a.Stats {
+			str = fmt.Sprintf("%s  %s:\n    %s\n", str, k, fmt.Sprintf("%+v", v))
+		}
+	}
+
+	if a.CustomStats != nil {
+		str = fmt.Sprintf("%sCustomStats: %+v\n", str, a.CustomStats)
+	}
+
+	if a.GlobalCustomStats != nil {
+		str = fmt.Sprintf("%sGlobalCustomStats: %+v\n", str, a.GlobalCustomStats)
+	}
+
+	return str
+
+}
diff --git a/pkg/execute/result/json/ansiblePlaybookJSONResults.go b/pkg/execute/result/json/ansiblePlaybookJSONResults.go
index 8ef18d5..a0853a5 100644
--- a/pkg/execute/result/json/ansiblePlaybookJSONResults.go
+++ b/pkg/execute/result/json/ansiblePlaybookJSONResults.go
@@ -9,7 +9,7 @@ import (
 	errors "github.com/apenella/go-common-utils/error"
 )
 
-// AnsiblePlaybookJSONResults
+// AnsiblePlaybookJSONResults represents the structure of the JSON generated by an Ansible playbook execution using the JSON callback plugin
 type AnsiblePlaybookJSONResults struct {
 	Playbook          string                                      `json:"-"`
 	CustomStats       interface{}                                 `json:"custom_stats"`
@@ -18,6 +18,7 @@ type AnsiblePlaybookJSONResults struct {
 	Stats             map[string]*AnsiblePlaybookJSONResultsStats `json:"stats"`
 }
 
+// String return a string representation of the AnsiblePlaybookJSONResults
 func (r *AnsiblePlaybookJSONResults) String() string {
 
 	str := ""
@@ -58,29 +59,27 @@ func (r *AnsiblePlaybookJSONResults) CheckStats() error {
 	return nil
 }
 
-/*
-https://github.com/ansible-collections/ansible.posix/blob/main/plugins/callback/json.py#L80
-
-	{
-		'play': {
-			'name': play.get_name(),
-			'id': to_text(play._uuid),
-			'path': to_text(play.get_path()),
-			'duration': {
-				'start': current_time()
-			}
-		},
-		'tasks': []
-	}
-
-	AnsiblePlaybookJSONResultsPlay
-*/
+// AnsiblePlaybookJSONResultsPlay represents the structure of the JSON generated by an Ansible playbook execution using the JSON callback plugin
+//
+// https://github.com/ansible-collections/ansible.posix/blob/main/plugins/callback/json.py#L80
+//
+//	{
+//		'play': {
+//			'name': play.get_name(),
+//			'id': to_text(play._uuid),
+//			'path': to_text(play.get_path()),
+//			'duration': {
+//				'start': current_time()
+//			}
+//		},
+//		'tasks': []
+//	}
 type AnsiblePlaybookJSONResultsPlay struct {
 	Play  *AnsiblePlaybookJSONResultsPlaysPlay `json:"play"`
 	Tasks []AnsiblePlaybookJSONResultsPlayTask `json:"tasks"`
 }
 
-// AnsiblePlaybookJSONResultsPlaysPlay
+// AnsiblePlaybookJSONResultsPlaysPlay represents the structure of the JSON generated by an Ansible playbook execution using the JSON callback plugin
 type AnsiblePlaybookJSONResultsPlaysPlay struct {
 	Duration *AnsiblePlaybookJSONResultsPlayDuration `json:"duration"`
 	Id       string                                  `json:"id"`
@@ -88,29 +87,28 @@ type AnsiblePlaybookJSONResultsPlaysPlay struct {
 	Path     string                                  `json:"path"`
 }
 
-/*
-https://github.com/ansible-collections/ansible.posix/blob/main/plugins/callback/json.py#L94
-
-	{
-		'task': {
-			'name': task.get_name(),
-			'id': to_text(task._uuid),
-			'path': to_text(task.get_path()),
-			'duration': {
-				'start': current_time()
-			}
-		},
-
-		'hosts': {}
-	}
-
-	AnsiblePlaybookJSONResultsPlayTask
-*/
+// AnsiblePlaybookJSONResultsPlayTask represents the structure of the JSON generated by an Ansible playbook execution using the JSON callback plugin.
+//
+// For more details, see: https://github.com/ansible-collections/ansible.posix/blob/main/plugins/callback/json.py#L94
+//
+//	{
+//		'task': {
+//			'name': task.get_name(),
+//			'id': to_text(task._uuid),
+//			'path': to_text(task.get_path()),
+//			'duration': {
+//				'start': current_time()
+//			}
+//		},
+//
+//		'hosts': {}
+//	}
 type AnsiblePlaybookJSONResultsPlayTask struct {
 	Hosts map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem `json:"hosts"`
 	Task  *AnsiblePlaybookJSONResultsPlayTaskItem                 `json:"task"`
 }
 
+// AnsiblePlaybookJSONResultsPlayTaskHostsItem represents the structure of the JSON generated by an Ansible playbook execution using the JSON callback plugin
 type AnsiblePlaybookJSONResultsPlayTaskHostsItem struct {
 	Action           string                 `json:"action"`
 	Changed          bool                   `json:"changed"`
@@ -128,6 +126,7 @@ type AnsiblePlaybookJSONResultsPlayTaskHostsItem struct {
 	Unreachable      bool                   `json:"unreachable"`
 }
 
+// AnsiblePlaybookJSONResultsPlayTaskItem represents the structure of the JSON generated by an Ansible playbook execution using the JSON callback plugin
 type AnsiblePlaybookJSONResultsPlayTaskItem struct {
 	Duration *AnsiblePlaybookJSONResultsPlayTaskItemDuration `json:"duration"`
 	Id       string                                          `json:"id"`
@@ -135,18 +134,29 @@ type AnsiblePlaybookJSONResultsPlayTaskItem struct {
 	Path     string                                          `json:"path"`
 }
 
+// AnsiblePlaybookJSONResultsPlayTaskItemDuration represents the structure of the JSON generated by an Ansible playbook execution using the JSON callback plugin
 type AnsiblePlaybookJSONResultsPlayTaskItemDuration struct {
 	Start string `json:"start"`
 	End   string `json:"end"`
 }
 
-// AnsiblePlaybookJSONResultsPlayDuration
+// String return a string representation of the AnsiblePlaybookJSONResultsPlayTaskItem
+func (t *AnsiblePlaybookJSONResultsPlayTaskItemDuration) String() string {
+	return fmt.Sprintf("[%s - %s]", t.Start, t.End)
+}
+
+// AnsiblePlaybookJSONResultsPlayDuration represents the structure of the JSON generated by an Ansible playbook execution using the JSON callback plugin
 type AnsiblePlaybookJSONResultsPlayDuration struct {
 	Start string `json:"start"`
 	End   string `json:"end"`
 }
 
-// AnsiblePlaybookJSONResultsStats
+// String return a string representation of the AnsiblePlaybookJSONResultsPlayDuration
+func (d *AnsiblePlaybookJSONResultsPlayDuration) String() string {
+	return fmt.Sprintf("[%s - %s]", d.Start, d.End)
+}
+
+// AnsiblePlaybookJSONResultsStats represents the structure of the JSON generated by an Ansible playbook execution using the JSON callback plugin
 type AnsiblePlaybookJSONResultsStats struct {
 	Changed     int `json:"changed"`
 	Failures    int `json:"failures"`
@@ -157,6 +167,7 @@ type AnsiblePlaybookJSONResultsStats struct {
 	Unreachable int `json:"unreachable"`
 }
 
+// String return a string representation of the AnsiblePlaybookJSONResultsStats
 func (s *AnsiblePlaybookJSONResultsStats) String() string {
 	str := fmt.Sprintf(" Changed: %s", strconv.Itoa(s.Changed))
 	str = fmt.Sprintf("%s Failures: %s", str, strconv.Itoa(s.Failures))
@@ -169,7 +180,7 @@ func (s *AnsiblePlaybookJSONResultsStats) String() string {
 	return str
 }
 
-// JSONParse return an AnsiblePlaybookJSONResults from
+// JSONParse return an AnsiblePlaybookJSONResults from a JSON data
 func JSONParse(data []byte) (*AnsiblePlaybookJSONResults, error) {
 
 	result := &AnsiblePlaybookJSONResults{}
diff --git a/pkg/execute/stdoutcallback/ansiblePosixJsonl.go b/pkg/execute/stdoutcallback/ansiblePosixJsonl.go
new file mode 100644
index 0000000..269e482
--- /dev/null
+++ b/pkg/execute/stdoutcallback/ansiblePosixJsonl.go
@@ -0,0 +1,45 @@
+package stdoutcallback
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/apenella/go-ansible/v2/pkg/execute/configuration"
+	jsonresults "github.com/apenella/go-ansible/v2/pkg/execute/result/json"
+)
+
+const (
+	// AnsiblePosixJsonlStdoutCallback ansible posix jsonl stdout output
+	AnsiblePosixJsonlStdoutCallback = "ansible.posix.jsonl"
+)
+
+// AnsiblePosixJsonlStdoutCallbackExecute defines an executor to run an ansible command with a ansible posix jsonl stdout callback
+type AnsiblePosixJsonlStdoutCallbackExecute struct {
+	executor ExecutorQuietStdoutCallbackSetter
+}
+
+// NewAnsiblePosixJsonlStdoutCallbackExecute creates a AnsiblePosixJsonlStdoutCallbackExecute
+func NewAnsiblePosixJsonlStdoutCallbackExecute(executor ExecutorQuietStdoutCallbackSetter) *AnsiblePosixJsonlStdoutCallbackExecute {
+	return &AnsiblePosixJsonlStdoutCallbackExecute{executor: executor}
+}
+
+// WithExecutor sets the executor for the AnsiblePosixJsonlStdoutCallbackExecute
+func (e *AnsiblePosixJsonlStdoutCallbackExecute) WithExecutor(exec ExecutorQuietStdoutCallbackSetter) *AnsiblePosixJsonlStdoutCallbackExecute {
+	e.executor = exec
+	return e
+}
+
+// Execute takes a command and args and runs it, streaming output to stdout
+func (e *AnsiblePosixJsonlStdoutCallbackExecute) Execute(ctx context.Context) error {
+
+	if e.executor == nil {
+		return fmt.Errorf("AnsiblePosixJsonlStdoutCallbackExecute executor requires an executor")
+	}
+
+	e.executor.Quiet()
+	e.executor.WithOutput(jsonresults.NewJSONLEventStdoutCallbackResults())
+
+	return configuration.NewAnsibleWithConfigurationSettingsExecute(e.executor,
+		configuration.WithAnsibleStdoutCallback(AnsiblePosixJsonlStdoutCallback),
+	).Execute(ctx)
+}
diff --git a/pkg/execute/stdoutcallback/debug.go b/pkg/execute/stdoutcallback/debug.go
index b3b627d..3bc3a14 100644
--- a/pkg/execute/stdoutcallback/debug.go
+++ b/pkg/execute/stdoutcallback/debug.go
@@ -13,14 +13,17 @@ const (
 	DebugStdoutCallback = "debug"
 )
 
+// DebugStdoutCallbackExecute defines an executor to run an ansible command with a debug stdout callback
 type DebugStdoutCallbackExecute struct {
 	executor ExecutorStdoutCallbackSetter
 }
 
+// NewDebugStdoutCallbackExecute creates a DebugStdoutCallbackExecute
 func NewDebugStdoutCallbackExecute(executor ExecutorStdoutCallbackSetter) *DebugStdoutCallbackExecute {
 	return &DebugStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the DebugStdoutCallbackExecute
 func (e *DebugStdoutCallbackExecute) WithExecutor(exec ExecutorStdoutCallbackSetter) *DebugStdoutCallbackExecute {
 	e.executor = exec
 	return e
diff --git a/pkg/execute/stdoutcallback/default.go b/pkg/execute/stdoutcallback/default.go
index 677c050..ae9b14b 100644
--- a/pkg/execute/stdoutcallback/default.go
+++ b/pkg/execute/stdoutcallback/default.go
@@ -13,14 +13,17 @@ const (
 	DefaultStdoutCallback = "default"
 )
 
+// DefaultStdoutCallbackExecute defines an executor to run an ansible command with a default stdout callback
 type DefaultStdoutCallbackExecute struct {
 	executor ExecutorStdoutCallbackSetter
 }
 
+// NewDefaultStdoutCallbackExecute creates a DefaultStdoutCallbackExecute
 func NewDefaultStdoutCallbackExecute(executor ExecutorStdoutCallbackSetter) *DefaultStdoutCallbackExecute {
 	return &DefaultStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the DefaultStdoutCallbackExecute
 func (e *DefaultStdoutCallbackExecute) WithExecutor(exec ExecutorStdoutCallbackSetter) *DefaultStdoutCallbackExecute {
 	e.executor = exec
 	return e
diff --git a/pkg/execute/stdoutcallback/dense.go b/pkg/execute/stdoutcallback/dense.go
index ff97b65..da17f59 100644
--- a/pkg/execute/stdoutcallback/dense.go
+++ b/pkg/execute/stdoutcallback/dense.go
@@ -13,14 +13,17 @@ const (
 	DenseStdoutCallback = "dense"
 )
 
+// DenseStdoutCallbackExecute defines an executor to run an ansible command with a dense stdout callback
 type DenseStdoutCallbackExecute struct {
 	executor ExecutorStdoutCallbackSetter
 }
 
+// NewDenseStdoutCallbackExecute creates a DenseStdoutCallbackExecute
 func NewDenseStdoutCallbackExecute(executor ExecutorStdoutCallbackSetter) *DenseStdoutCallbackExecute {
 	return &DenseStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the DenseStdoutCallbackExecute
 func (e *DenseStdoutCallbackExecute) WithExecutor(exec ExecutorStdoutCallbackSetter) *DenseStdoutCallbackExecute {
 	e.executor = exec
 	return e
diff --git a/pkg/execute/stdoutcallback/json.go b/pkg/execute/stdoutcallback/json.go
index e259f4d..e050471 100644
--- a/pkg/execute/stdoutcallback/json.go
+++ b/pkg/execute/stdoutcallback/json.go
@@ -13,14 +13,17 @@ const (
 	JSONStdoutCallback = "json"
 )
 
+// JSONStdoutCallbackExecute defines an executor to run an ansible command with a json stdout callback
 type JSONStdoutCallbackExecute struct {
 	executor ExecutorQuietStdoutCallbackSetter
 }
 
+// NewJSONStdoutCallbackExecute creates a JSONStdoutCallbackExecute
 func NewJSONStdoutCallbackExecute(executor ExecutorQuietStdoutCallbackSetter) *JSONStdoutCallbackExecute {
 	return &JSONStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the JSONStdoutCallbackExecute
 func (e *JSONStdoutCallbackExecute) WithExecutor(exec ExecutorQuietStdoutCallbackSetter) *JSONStdoutCallbackExecute {
 	e.executor = exec
 	return e
diff --git a/pkg/execute/stdoutcallback/minimal.go b/pkg/execute/stdoutcallback/minimal.go
index 438ea0b..148dbbd 100644
--- a/pkg/execute/stdoutcallback/minimal.go
+++ b/pkg/execute/stdoutcallback/minimal.go
@@ -13,14 +13,17 @@ const (
 	MinimalStdoutCallback = "minimal"
 )
 
+// MinimalStdoutCallbackExecute defines an executor to run an ansible command with a minimal stdout callback
 type MinimalStdoutCallbackExecute struct {
 	executor ExecutorStdoutCallbackSetter
 }
 
+// NewMinimalStdoutCallbackExecute creates a MinimalStdoutCallbackExecute
 func NewMinimalStdoutCallbackExecute(executor ExecutorStdoutCallbackSetter) *MinimalStdoutCallbackExecute {
 	return &MinimalStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the MinimalStdoutCallbackExecute
 func (e *MinimalStdoutCallbackExecute) WithExecutor(exec ExecutorStdoutCallbackSetter) *MinimalStdoutCallbackExecute {
 	e.executor = exec
 	return e
diff --git a/pkg/execute/stdoutcallback/null.go b/pkg/execute/stdoutcallback/null.go
index ff975d0..ba4b8cf 100644
--- a/pkg/execute/stdoutcallback/null.go
+++ b/pkg/execute/stdoutcallback/null.go
@@ -13,14 +13,17 @@ const (
 	NullStdoutCallback = "null"
 )
 
+// NullStdoutCallbackExecute defines an executor to run an ansible command with a null stdout callback
 type NullStdoutCallbackExecute struct {
 	executor ExecutorStdoutCallbackSetter
 }
 
+// NewNullStdoutCallbackExecute creates a NullStdoutCallbackExecute
 func NewNullStdoutCallbackExecute(executor ExecutorStdoutCallbackSetter) *NullStdoutCallbackExecute {
 	return &NullStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the NullStdoutCallbackExecute
 func (e *NullStdoutCallbackExecute) WithExecutor(exec ExecutorStdoutCallbackSetter) *NullStdoutCallbackExecute {
 	e.executor = exec
 	return e
diff --git a/pkg/execute/stdoutcallback/oneline.go b/pkg/execute/stdoutcallback/oneline.go
index 19109ea..3efc25f 100644
--- a/pkg/execute/stdoutcallback/oneline.go
+++ b/pkg/execute/stdoutcallback/oneline.go
@@ -13,14 +13,17 @@ const (
 	OnelineStdoutCallback = "oneline"
 )
 
+// OnelineStdoutCallbackExecute defines an executor to run an ansible command with a oneline stdout callback
 type OnelineStdoutCallbackExecute struct {
 	executor ExecutorStdoutCallbackSetter
 }
 
+// NewOnelineStdoutCallbackExecute creates a OnelineStdoutCallbackExecute
 func NewOnelineStdoutCallbackExecute(executor ExecutorStdoutCallbackSetter) *OnelineStdoutCallbackExecute {
 	return &OnelineStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the OnelineStdoutCallbackExecute
 func (e *OnelineStdoutCallbackExecute) WithExecutor(exec ExecutorStdoutCallbackSetter) *OnelineStdoutCallbackExecute {
 	e.executor = exec
 	return e
diff --git a/pkg/execute/stdoutcallback/stderr.go b/pkg/execute/stdoutcallback/stderr.go
index 17d9380..f475270 100644
--- a/pkg/execute/stdoutcallback/stderr.go
+++ b/pkg/execute/stdoutcallback/stderr.go
@@ -13,14 +13,17 @@ const (
 	StderrStdoutCallback = "stderr"
 )
 
+// StderrStdoutCallbackExecute defines an executor to run an ansible command with a stderr stdout callback
 type StderrStdoutCallbackExecute struct {
 	executor ExecutorStdoutCallbackSetter
 }
 
+// NewStderrStdoutCallbackExecute creates a StderrStdoutCallbackExecute
 func NewStderrStdoutCallbackExecute(executor ExecutorStdoutCallbackSetter) *StderrStdoutCallbackExecute {
 	return &StderrStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the StderrStdoutCallbackExecute
 func (e *StderrStdoutCallbackExecute) WithExecutor(exec ExecutorStdoutCallbackSetter) *StderrStdoutCallbackExecute {
 	e.executor = exec
 	return e
diff --git a/pkg/execute/stdoutcallback/timer.go b/pkg/execute/stdoutcallback/timer.go
index 567c54f..b20a15d 100644
--- a/pkg/execute/stdoutcallback/timer.go
+++ b/pkg/execute/stdoutcallback/timer.go
@@ -13,14 +13,17 @@ const (
 	TimerStdoutCallback = "timer"
 )
 
+// TimerStdoutCallbackExecute defines an executor to run an ansible command with a timer stdout callback
 type TimerStdoutCallbackExecute struct {
 	executor ExecutorStdoutCallbackSetter
 }
 
+// NewTimerStdoutCallbackExecute creates a TimerStdoutCallbackExecute
 func NewTimerStdoutCallbackExecute(executor ExecutorStdoutCallbackSetter) *TimerStdoutCallbackExecute {
 	return &TimerStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the TimerStdoutCallbackExecute
 func (e *TimerStdoutCallbackExecute) WithExecutor(exec ExecutorStdoutCallbackSetter) *TimerStdoutCallbackExecute {
 	e.executor = exec
 	return e
diff --git a/pkg/execute/stdoutcallback/yaml.go b/pkg/execute/stdoutcallback/yaml.go
index c53c0d5..62966f1 100644
--- a/pkg/execute/stdoutcallback/yaml.go
+++ b/pkg/execute/stdoutcallback/yaml.go
@@ -13,14 +13,17 @@ const (
 	YAMLStdoutCallback = "yaml"
 )
 
+// YAMLStdoutCallbackExecute defines an executor to run an ansible command with a yaml stdout callback
 type YAMLStdoutCallbackExecute struct {
 	executor ExecutorStdoutCallbackSetter
 }
 
+// NewYAMLStdoutCallbackExecute creates a YAMLStdoutCallbackExecute
 func NewYAMLStdoutCallbackExecute(executor ExecutorStdoutCallbackSetter) *YAMLStdoutCallbackExecute {
 	return &YAMLStdoutCallbackExecute{executor: executor}
 }
 
+// WithExecutor sets the executor for the YAMLStdoutCallbackExecute
 func (e *YAMLStdoutCallbackExecute) WithExecutor(exec ExecutorStdoutCallbackSetter) *YAMLStdoutCallbackExecute {
 	e.executor = exec
 	return e



Test patch:
diff --git a/pkg/execute/result/json/JSONLEventStdoutCallbackResults_test.go b/pkg/execute/result/json/JSONLEventStdoutCallbackResults_test.go
new file mode 100644
index 0000000..37c31b3
--- /dev/null
+++ b/pkg/execute/result/json/JSONLEventStdoutCallbackResults_test.go
@@ -0,0 +1,117 @@
+package json
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"strings"
+	"testing"
+
+	errors "github.com/apenella/go-common-utils/error"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+// MockWriter mocks an io.Writer
+type MockWriter struct {
+	mock.Mock
+}
+
+func (w *MockWriter) Write(p []byte) (n int, err error) {
+	args := w.Called(p)
+	return args.Int(0), args.Error(1)
+}
+
+var events = `{"_event":"v2_playbook_on_play_start","_timestamp":"2025-04-01T05:17:36.646328Z","play":{"duration":{"start":"2025-04-01T05:17:36.646322Z"},"id":"cdeaff0f-de61-3a76-e91c-000000000002","name":"all","path":"site.yml:3"},"tasks":[]}`
+var invalidEvent = `{"_event":"v2_playbook_on_play_start",`
+
+func TestJSONLEventStdoutCallbackResults_Print(t *testing.T) {
+
+	tests := []struct {
+		desc        string
+		context     context.Context
+		writer      io.Writer
+		reader      io.Reader
+		results     *JSONLEventStdoutCallbackResults
+		err         error
+		arrangeFunc func(t *testing.T, w *MockWriter)
+		assertFunc  func(t *testing.T, w *MockWriter)
+	}{
+		{
+			desc:    "Testing JSONLEventStdoutCallbackResults Print method handle the data as expected",
+			context: context.TODO(),
+			writer:  &MockWriter{},
+			reader:  strings.NewReader(events),
+			results: NewJSONLEventStdoutCallbackResults(),
+			arrangeFunc: func(t *testing.T, w *MockWriter) {
+				w.On("Write", []byte(events)).Return(len([]byte("data transformed")), nil)
+			},
+			assertFunc: func(t *testing.T, w *MockWriter) {
+				w.AssertExpectations(t)
+			},
+			err: nil,
+		},
+		{
+			desc:    "Testing error in JSONLEventStdoutCallbackResults Print method when there is an error writing data to the writer",
+			context: context.TODO(),
+			writer:  &MockWriter{},
+			reader:  strings.NewReader(events),
+			results: NewJSONLEventStdoutCallbackResults(),
+			arrangeFunc: func(t *testing.T, w *MockWriter) {
+				w.On("Write", []byte(events)).Return(0, fmt.Errorf("error from writer"))
+			},
+			assertFunc: func(t *testing.T, w *MockWriter) {
+				w.AssertExpectations(t)
+			},
+			err: errors.New("(result::json::JSONLEventStdoutCallbackResults::Print)", "Error writing to writer", fmt.Errorf("error from writer")),
+		},
+		{
+			desc:    "Testing error in JSONLEventStdoutCallbackResults Print method when received data is not a JSON",
+			context: context.TODO(),
+			writer:  &MockWriter{},
+			reader:  strings.NewReader(invalidEvent),
+			results: NewJSONLEventStdoutCallbackResults(),
+			err:     fmt.Errorf("Error reading the results stream\n\tError processing the execution output\n\terror decoding JSON: unexpected end of JSON input"),
+		},
+		{
+			desc:    "Testing error in JSONLEventStdoutCallbackResults Print using a transformer",
+			context: context.TODO(),
+			writer:  &MockWriter{},
+			reader:  strings.NewReader(events),
+			results: NewJSONLEventStdoutCallbackResults(
+				WithJSONLEventTransformers(func(data string) string {
+					return "data transformed"
+				}),
+			),
+			arrangeFunc: func(t *testing.T, w *MockWriter) {
+				w.On("Write", []byte("data transformed")).Return(len([]byte("data transformed")), nil)
+			},
+			assertFunc: func(t *testing.T, w *MockWriter) {
+				w.AssertExpectations(t)
+			},
+			err: nil,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.desc, func(t *testing.T) {
+			t.Log(test.desc)
+
+			if test.arrangeFunc != nil {
+				test.arrangeFunc(t, test.writer.(*MockWriter))
+			}
+
+			err := test.results.Print(test.context, test.reader, test.writer)
+			if err != nil && test.err != nil {
+				assert.Equal(t, test.err.Error(), err.Error())
+			} else {
+				assert.Nil(t, err, "unexpected error")
+				assert.Nil(t, test.err, "expected error not reproduced")
+
+				if test.assertFunc != nil {
+					test.assertFunc(t, test.writer.(*MockWriter))
+				}
+			}
+		})
+	}
+}
diff --git a/pkg/execute/result/json/JSONResults_test.go b/pkg/execute/result/json/JSONStdoutCallbackResults_test.go
similarity index 99%
rename from pkg/execute/result/json/JSONResults_test.go
rename to pkg/execute/result/json/JSONStdoutCallbackResults_test.go
index 1e29fe1..c3a3ab6 100644
--- a/pkg/execute/result/json/JSONResults_test.go
+++ b/pkg/execute/result/json/JSONStdoutCallbackResults_test.go
@@ -17,7 +17,7 @@ import (
 
 const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
-func TestPrint(t *testing.T) {
+func TestJSONStdoutCallbackResults_Print(t *testing.T) {
 
 	longMessageLine := randStringBytes(512_000)
 	_ = longMessageLine
diff --git a/pkg/execute/result/json/ansiblePlaybookJSONLEventResults_test.go b/pkg/execute/result/json/ansiblePlaybookJSONLEventResults_test.go
new file mode 100644
index 0000000..18172d0
--- /dev/null
+++ b/pkg/execute/result/json/ansiblePlaybookJSONLEventResults_test.go
@@ -0,0 +1,576 @@
+package json
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestString(t *testing.T) {
+
+	tests := []struct {
+		desc     string
+		input    *AnsiblePlaybookJSONLEventResults
+		expected string
+	}{
+		{
+			desc: "Testing String method with a v2_playbook_on_play_start event",
+			input: &AnsiblePlaybookJSONLEventResults{
+				Event:     "v2_playbook_on_play_start",
+				Timestamp: "2025-03-28T20:00:00.000000",
+				Play: &AnsiblePlaybookJSONResultsPlaysPlay{
+					Id:   "play-id",
+					Name: "play-name",
+					Duration: &AnsiblePlaybookJSONResultsPlayDuration{
+						Start: "2025-03-28T20:00:00.000000",
+						End:   "2025-03-28T20:00:00.000000",
+					},
+					Path: "play-path",
+				},
+			},
+			expected: "Event: v2_playbook_on_play_start\nTimestamp: 2025-03-28T20:00:00.000000\nPlay: &{Duration:[2025-03-28T20:00:00.000000 - 2025-03-28T20:00:00.000000] Id:play-id Name:play-name Path:play-path}\n",
+		},
+		{
+			desc: "Testing String method with a v2_runner_on_start event",
+			input: &AnsiblePlaybookJSONLEventResults{
+				Event:     "v2_runner_on_start",
+				Timestamp: "2025-03-28T20:00:00.000000",
+				Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+					Id:   "task-id",
+					Name: "task-name",
+					Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+						Start: "2025-03-28T20:00:00.000000",
+						End:   "2025-03-28T20:00:00.000000",
+					},
+					Path: "task-path",
+				},
+			},
+			expected: "Event: v2_runner_on_start\nTimestamp: 2025-03-28T20:00:00.000000\nTask: &{Duration:[2025-03-28T20:00:00.000000 - 2025-03-28T20:00:00.000000] Id:task-id Name:task-name Path:task-path}\n",
+		},
+		{
+			desc: "Testing String method with a v2_playbook_on_task_start event",
+			input: &AnsiblePlaybookJSONLEventResults{
+				Event:     "v2_playbook_on_task_start",
+				Timestamp: "2025-03-28T20:00:00.000000",
+				Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+					Id:   "task-id",
+					Name: "task-name",
+					Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+						Start: "2025-03-28T20:00:00.000000",
+						End:   "2025-03-28T20:00:00.000000",
+					},
+					Path: "task-path",
+				},
+			},
+			expected: "Event: v2_playbook_on_task_start\nTimestamp: 2025-03-28T20:00:00.000000\nTask: &{Duration:[2025-03-28T20:00:00.000000 - 2025-03-28T20:00:00.000000] Id:task-id Name:task-name Path:task-path}\n",
+		},
+		{
+			desc: "Testing String method with a v2_playbook_on_handler_task_start event",
+			input: &AnsiblePlaybookJSONLEventResults{
+				Event:     "v2_playbook_on_handler_task_start",
+				Timestamp: "2025-03-28T20:00:00.000000",
+				Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+					Id:   "task-id",
+					Name: "task-name",
+					Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+						Start: "2025-03-28T20:00:00.000000",
+						End:   "2025-03-28T20:00:00.000000",
+					},
+					Path: "task-path",
+				},
+			},
+			expected: "Event: v2_playbook_on_handler_task_start\nTimestamp: 2025-03-28T20:00:00.000000\nTask: &{Duration:[2025-03-28T20:00:00.000000 - 2025-03-28T20:00:00.000000] Id:task-id Name:task-name Path:task-path}\n",
+		},
+		{
+			desc: "Testing String method with a v2_playbook_on_stats event",
+			input: &AnsiblePlaybookJSONLEventResults{
+				Event:     "v2_playbook_on_stats",
+				Timestamp: "2025-03-28T20:00:00.000000",
+				Stats: map[string]*AnsiblePlaybookJSONResultsStats{
+					"host1": {
+						Changed:     1,
+						Failures:    2,
+						Ignored:     3,
+						Ok:          4,
+						Rescued:     5,
+						Skipped:     6,
+						Unreachable: 7,
+					},
+				},
+				CustomStats: map[string]int{
+					"host1": 1,
+				},
+				GlobalCustomStats: map[string]string{
+					"total_runtime": "1s",
+				},
+			},
+			expected: "Event: v2_playbook_on_stats\nTimestamp: 2025-03-28T20:00:00.000000\nStats:\n  host1:\n     Changed: 1 Failures: 2 Ignored: 3 Ok: 4 Rescued: 5 Skipped: 6 Unreachable: 7\nCustomStats: map[host1:1]\nGlobalCustomStats: map[total_runtime:1s]\n",
+		},
+		{
+			desc: "Testing String method with a v2_runner_on_ok event",
+			input: &AnsiblePlaybookJSONLEventResults{
+				Event:     "v2_runner_on_ok",
+				Timestamp: "2025-03-28T20:00:00.000000",
+				Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+					Id:   "task-id",
+					Name: "task-name",
+					Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+						Start: "2025-03-28T20:00:00.000000",
+						End:   "2025-03-28T20:00:00.000000",
+					},
+					Path: "task-path",
+				},
+				Hosts: map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem{
+					"host1": {
+						Action:           "action",
+						Changed:          true,
+						Msg:              "msg",
+						AnsibleFacts:     map[string]interface{}{"fact1": "value1"},
+						Stdout:           "stdout",
+						StdoutLines:      []interface{}{"stdout-line1", "stdout-line2"},
+						Stderr:           "stderr",
+						StderrLines:      []interface{}{"stderr-line1", "stderr-line2"},
+						Cmd:              "cmd",
+						Failed:           true,
+						FailedWhenResult: true,
+						Skipped:          true,
+						SkipReason:       "skip-reason",
+						Unreachable:      true,
+					},
+				},
+			},
+			expected: "Event: v2_runner_on_ok\nTimestamp: 2025-03-28T20:00:00.000000\nTask: &{Duration:[2025-03-28T20:00:00.000000 - 2025-03-28T20:00:00.000000] Id:task-id Name:task-name Path:task-path}\nHosts:\n  host1:\n    &{Action:action Changed:true Msg:msg AnsibleFacts:map[fact1:value1] Stdout:stdout StdoutLines:[stdout-line1 stdout-line2] Stderr:stderr StderrLines:[stderr-line1 stderr-line2] Cmd:cmd Failed:true FailedWhenResult:true Skipped:true SkipReason:skip-reason Unreachable:true}\n",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.desc, func(t *testing.T) {
+			t.Parallel()
+			t.Log(test.desc)
+
+			res := test.input.String()
+			assert.Equal(t, test.expected, res)
+		})
+	}
+}
+
+func TestUnmarshalJSONV2PlaybookOnPlayStart(t *testing.T) {
+	t.Log("Testing unmarshal JSON with v2_playbook_on_play_start event")
+	t.Parallel()
+
+	input := []byte(`{
+		"_event": "v2_playbook_on_play_start",
+		"_timestamp": "2025-03-24T12:34:56Z",
+		"play": {
+			"name": "Example Play",
+			"id": "12345-uuid-play",
+			"path": "/path/to/playbook.yml",
+			"duration": {
+				"start": "2025-03-24T12:34:56Z"
+			}
+		},
+		"tasks": []
+	}`)
+
+	var result AnsiblePlaybookJSONLEventResults
+	expected := AnsiblePlaybookJSONLEventResults{
+		Event:     "v2_playbook_on_play_start",
+		Timestamp: "2025-03-24T12:34:56Z",
+		Play: &AnsiblePlaybookJSONResultsPlaysPlay{
+			Name: "Example Play",
+			Id:   "12345-uuid-play",
+			Path: "/path/to/playbook.yml",
+			Duration: &AnsiblePlaybookJSONResultsPlayDuration{
+				Start: "2025-03-24T12:34:56Z",
+			},
+		},
+		Tasks: []AnsiblePlaybookJSONResultsPlayTask{},
+	}
+
+	err := json.Unmarshal(input, &result)
+	assert.Nil(t, err)
+	assert.Equal(t, expected, result)
+
+}
+
+func TestUnmarshalJSONV2OnRunnerStart(t *testing.T) {
+	t.Log("Testing unmarshal JSON with v2_runner_on_start event")
+	t.Parallel()
+
+	input := []byte(`{
+		"_event": "v2_runner_on_start",
+		"_timestamp": "2025-03-24T12:35:00Z",
+		"task": {
+			"name": "Example Task",
+			"id": "67890-uuid-task",
+			"path": "/path/to/playbook.yml",
+			"duration": {
+				"start": "2025-03-24T12:35:00Z"
+			}
+		},
+		"hosts": {}
+	}`)
+	var result AnsiblePlaybookJSONLEventResults
+	expected := AnsiblePlaybookJSONLEventResults{
+		Event:     "v2_runner_on_start",
+		Timestamp: "2025-03-24T12:35:00Z",
+		Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+			Name: "Example Task",
+			Id:   "67890-uuid-task",
+			Path: "/path/to/playbook.yml",
+			Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+				Start: "2025-03-24T12:35:00Z",
+			},
+		},
+		Hosts: map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem{},
+	}
+
+	err := json.Unmarshal(input, &result)
+	assert.Nil(t, err)
+	assert.Equal(t, expected, result)
+}
+
+func TestUnmarshalJSONV2PlaybookOnTaskStart(t *testing.T) {
+	t.Log("Testing unmarshal JSON with v2_playbook_on_task_start event")
+	t.Parallel()
+
+	input := []byte(`{
+	"_event": "v2_playbook_on_task_start",
+	"_timestamp": "2025-03-24T12:36:00Z",
+	"task": {
+		"name": "Start Example Task",
+		"id": "67890-uuid-task",
+		"path": "/path/to/playbook.yml",
+		"duration": {
+			"start": "2025-03-24T12:36:00Z"
+		}
+	},
+	"hosts": {}
+}`)
+	var result AnsiblePlaybookJSONLEventResults
+	expected := AnsiblePlaybookJSONLEventResults{
+		Event:     "v2_playbook_on_task_start",
+		Timestamp: "2025-03-24T12:36:00Z",
+		Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+			Name: "Start Example Task",
+			Id:   "67890-uuid-task",
+			Path: "/path/to/playbook.yml",
+			Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+				Start: "2025-03-24T12:36:00Z",
+			},
+		},
+		Hosts: map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem{},
+	}
+
+	err := json.Unmarshal(input, &result)
+	assert.Nil(t, err)
+	assert.Equal(t, expected, result)
+}
+
+func TestUnmarshalJSONV2OnHandlerTaskStart(t *testing.T) {
+	t.Log("Testing unmarshal JSON with v2_playbook_on_handler_task_start event")
+	t.Parallel()
+
+	input := []byte(`{
+		"_event": "v2_playbook_on_handler_task_start",
+		"_timestamp": "2025-03-24T12:37:00Z",
+		"task": {
+			"name": "Example Handler Task",
+			"id": "abcdef-handler-task",
+			"path": "/path/to/handlers.yml",
+			"duration": {
+				"start": "2025-03-24T12:37:00Z"
+			}
+		},
+		"hosts": {}
+	}`)
+	var result AnsiblePlaybookJSONLEventResults
+	expected := AnsiblePlaybookJSONLEventResults{
+		Event:     "v2_playbook_on_handler_task_start",
+		Timestamp: "2025-03-24T12:37:00Z",
+		Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+			Name: "Example Handler Task",
+			Id:   "abcdef-handler-task",
+			Path: "/path/to/handlers.yml",
+			Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+				Start: "2025-03-24T12:37:00Z",
+			},
+		},
+		Hosts: map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem{},
+	}
+
+	err := json.Unmarshal(input, &result)
+	assert.Nil(t, err)
+	assert.Equal(t, expected, result)
+}
+
+func TestUnmarshalJSONV2PlaybookOnStats(t *testing.T) {
+	t.Log("Testing unmarshal JSON with v2_playbook_on_stats event")
+	t.Parallel()
+
+	input := []byte(`{
+		"_event": "v2_playbook_on_stats",
+		"_timestamp": "2025-03-24T12:40:00Z",
+		"stats": {
+			"host1": {
+				"ok": 5,
+				"changed": 2,
+				"failed": 0,
+				"skipped": 1,
+				"unreachable": 0
+			},
+			"host2": {
+				"ok": 3,
+				"changed": 1,
+				"failed": 1,
+				"skipped": 0,
+				"unreachable": 0
+			}
+		},
+		"custom_stats": {
+			"host1": {
+				"custom_metric": "100"
+			}
+		},
+		"global_custom_stats": {
+			"total_runtime": "30s"
+		}
+	}`)
+
+	var result AnsiblePlaybookJSONLEventResults
+	expected := AnsiblePlaybookJSONLEventResults{
+		Event:     "v2_playbook_on_stats",
+		Timestamp: "2025-03-24T12:40:00Z",
+		Stats: map[string]*AnsiblePlaybookJSONResultsStats{
+			"host1": {
+				Ok:          5,
+				Changed:     2,
+				Failures:    0,
+				Skipped:     1,
+				Unreachable: 0,
+			},
+			"host2": {
+				Ok:          3,
+				Changed:     1,
+				Failures:    0,
+				Skipped:     0,
+				Unreachable: 0,
+			},
+		},
+		CustomStats: map[string]interface{}{
+			"host1": map[string]interface{}{
+				"custom_metric": "100",
+			},
+		},
+		GlobalCustomStats: map[string]interface{}{
+			"total_runtime": "30s",
+		},
+	}
+
+	err := json.Unmarshal(input, &result)
+	assert.Nil(t, err)
+	assert.Equal(t, expected, result)
+}
+
+func TestUnmarshalJSONV2RunnerOnOk(t *testing.T) {
+	t.Log("Testing unmarshal JSON with v2_runner_on_ok event")
+	t.Parallel()
+
+	input := []byte(`{
+		"_event": "v2_runner_on_ok",
+		"_timestamp": "2025-03-24T12:38:00Z",
+		"task": {
+			"name": "Example Task",
+			"id": "67890-uuid-task",
+			"path": "/path/to/playbook.yml",
+			"duration": {
+				"start": "2025-03-24T12:35:00Z",
+				"end": "2025-03-24T12:38:00Z"
+			}
+		},
+		"hosts": {
+			"host1": {
+				"action": "shell",
+				"stdout": "Task executed successfully"
+			}
+		}
+	}`)
+	var result AnsiblePlaybookJSONLEventResults
+	expected := AnsiblePlaybookJSONLEventResults{
+		Event:     "v2_runner_on_ok",
+		Timestamp: "2025-03-24T12:38:00Z",
+		Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+			Name: "Example Task",
+			Id:   "67890-uuid-task",
+			Path: "/path/to/playbook.yml",
+			Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+				Start: "2025-03-24T12:35:00Z",
+				End:   "2025-03-24T12:38:00Z",
+			},
+		},
+		Hosts: map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem{
+			"host1": {
+				Action: "shell",
+				Stdout: "Task executed successfully",
+			},
+		},
+	}
+
+	err := json.Unmarshal(input, &result)
+	assert.Nil(t, err)
+	assert.Equal(t, expected, result)
+}
+
+func TestUnmarshalJSONV2RunnerOnFailed(t *testing.T) {
+	t.Log("Testing unmarshal JSON with v2_runner_on_failed event")
+	t.Parallel()
+
+	input := []byte(`{
+    "_event": "v2_runner_on_failed",
+    "_timestamp": "2025-03-24T12:39:00Z",
+    "task": {
+      "name": "Failing Task",
+      "id": "abcdef-failed-task",
+      "path": "/path/to/playbook.yml",
+      "duration": {
+        "start": "2025-03-24T12:38:30Z",
+        "end": "2025-03-24T12:39:00Z"
+      }
+    },
+    "hosts": {
+      "host2": {
+        "action": "command",
+        "stderr": "Error executing command",
+        "failed": true
+      }
+    }
+}`)
+	var result AnsiblePlaybookJSONLEventResults
+	expected := AnsiblePlaybookJSONLEventResults{
+		Event:     "v2_runner_on_failed",
+		Timestamp: "2025-03-24T12:39:00Z",
+		Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+			Name: "Failing Task",
+			Id:   "abcdef-failed-task",
+			Path: "/path/to/playbook.yml",
+			Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+				Start: "2025-03-24T12:38:30Z",
+				End:   "2025-03-24T12:39:00Z",
+			},
+		},
+		Hosts: map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem{
+			"host2": {
+				Action: "command",
+				Stderr: "Error executing command",
+				Failed: true,
+			},
+		},
+	}
+
+	err := json.Unmarshal(input, &result)
+	assert.Nil(t, err)
+	assert.Equal(t, expected, result)
+}
+
+func TestUnmarshalJSONV2RunnerOnUnreachable(t *testing.T) {
+	t.Log("Testing unmarshal JSON with v2_runner_on_unreachable event")
+	t.Parallel()
+
+	input := []byte(`{
+		"_event": "v2_runner_on_unreachable",
+		"_timestamp": "2025-03-24T12:39:30Z",
+		"task": {
+			"name": "Unreachable Task",
+			"id": "ghijkl-unreachable-task",
+			"path": "/path/to/playbook.yml",
+			"duration": {
+				"start": "2025-03-24T12:39:00Z",
+				"end": "2025-03-24T12:39:30Z"
+			}
+		},
+		"hosts": {
+			"host3": {
+				"action": "ping",
+				"msg": "Host unreachable",
+				"unreachable": true
+			}
+		}
+	}`)
+	var result AnsiblePlaybookJSONLEventResults
+	expected := AnsiblePlaybookJSONLEventResults{
+		Event:     "v2_runner_on_unreachable",
+		Timestamp: "2025-03-24T12:39:30Z",
+		Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+			Name: "Unreachable Task",
+			Id:   "ghijkl-unreachable-task",
+			Path: "/path/to/playbook.yml",
+			Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+				Start: "2025-03-24T12:39:00Z",
+				End:   "2025-03-24T12:39:30Z",
+			},
+		},
+		Hosts: map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem{
+			"host3": {
+				Action:      "ping",
+				Msg:         "Host unreachable",
+				Unreachable: true,
+			},
+		},
+	}
+
+	err := json.Unmarshal(input, &result)
+	assert.Nil(t, err)
+	assert.Equal(t, expected, result)
+}
+
+func TestUnmarshalJSONV2RunnerOnSkipped(t *testing.T) {
+	t.Log("Testing unmarshal JSON with v2_runner_on_skipped event")
+	t.Parallel()
+
+	input := []byte(`{
+		"_event": "v2_runner_on_skipped",
+		"_timestamp": "2025-03-24T12:40:00Z",
+		"task": {
+			"name": "Skipped Task",
+			"id": "mnopqr-skipped-task",
+			"path": "/path/to/playbook.yml",
+			"duration": {
+				"start": "2025-03-24T12:39:50Z",
+				"end": "2025-03-24T12:40:00Z"
+			}
+		},
+		"hosts": {
+			"host4": {
+				"action": "copy",
+				"msg": "Task skipped",
+				"skipped": true
+			}
+		}
+	}`)
+	var result AnsiblePlaybookJSONLEventResults
+	expected := AnsiblePlaybookJSONLEventResults{
+		Event:     "v2_runner_on_skipped",
+		Timestamp: "2025-03-24T12:40:00Z",
+		Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+			Name: "Skipped Task",
+			Id:   "mnopqr-skipped-task",
+			Path: "/path/to/playbook.yml",
+			Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+				Start: "2025-03-24T12:39:50Z",
+				End:   "2025-03-24T12:40:00Z",
+			},
+		},
+		Hosts: map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem{
+			"host4": {
+				Action:  "copy",
+				Msg:     "Task skipped",
+				Skipped: true,
+			},
+		},
+	}
+
+	err := json.Unmarshal(input, &result)
+	assert.Nil(t, err)
+	assert.Equal(t, expected, result)
+}
diff --git a/pkg/execute/result/json/ansiblePlaybookJSONResults_test.go b/pkg/execute/result/json/ansiblePlaybookJSONResults_test.go
index cf5dc48..1be3e27 100644
--- a/pkg/execute/result/json/ansiblePlaybookJSONResults_test.go
+++ b/pkg/execute/result/json/ansiblePlaybookJSONResults_test.go
@@ -4,6 +4,7 @@ import (
 	"strings"
 	"testing"
 
+	errors "github.com/apenella/go-common-utils/error"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -1030,3 +1031,118 @@ func TestParseJSONResultsStream(t *testing.T) {
 		})
 	}
 }
+
+func TestCheckStats(t *testing.T) {
+
+	tests := []struct {
+		desc    string
+		results *AnsiblePlaybookJSONResults
+		err     error
+	}{
+		{
+			desc:    "Testing check stats when stats is not defined",
+			results: &AnsiblePlaybookJSONResults{},
+			err:     nil,
+		},
+		{
+			desc: "Testing check stats when there are failures on the stats",
+			results: &AnsiblePlaybookJSONResults{
+				Stats: map[string]*AnsiblePlaybookJSONResultsStats{
+					"host1": {
+						Failures: 1,
+					},
+				},
+			},
+			err: errors.New(
+				"(results::JSONStdoutCallbackResults)",
+				"Host host1 finished with 1 failures",
+			),
+		},
+		{
+			desc: "Testing check stats when there are unreachable on the stats",
+			results: &AnsiblePlaybookJSONResults{
+				Stats: map[string]*AnsiblePlaybookJSONResultsStats{
+					"host1": {
+						Unreachable: 1,
+					},
+				},
+			},
+			err: errors.New(
+				"(results::JSONStdoutCallbackResults)",
+				"Host host1 finished with 1 unrecheable hosts",
+			),
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.desc, func(t *testing.T) {
+			t.Log(test.desc)
+
+			err := test.results.CheckStats()
+			assert.Equal(t, test.err, err, "Unexpected error")
+		})
+	}
+}
+
+func TestAnsiblePlaybookJSONResults_String(t *testing.T) {
+	tests := []struct {
+		desc     string
+		results  *AnsiblePlaybookJSONResults
+		expected string
+	}{
+		{
+			desc: "Testing string representation of the results",
+			results: &AnsiblePlaybookJSONResults{
+				Plays: []AnsiblePlaybookJSONResultsPlay{
+					{
+						Play: &AnsiblePlaybookJSONResultsPlaysPlay{
+							Duration: &AnsiblePlaybookJSONResultsPlayDuration{
+								End:   "2025-03-28T06:55:29.890926Z",
+								Start: "2025-03-28T06:55:29.881536Z",
+							},
+							Id:   "play-id",
+							Name: "play-name",
+							Path: "play-path",
+						},
+						Tasks: []AnsiblePlaybookJSONResultsPlayTask{
+							{
+								Hosts: map[string]*AnsiblePlaybookJSONResultsPlayTaskHostsItem{
+									"host1": {
+										Action:  "debug",
+										Changed: false,
+										Msg:     "Your are running\n'json-stdout-ansibleplaybook'\nfirst example\n",
+									},
+								},
+								Task: &AnsiblePlaybookJSONResultsPlayTaskItem{
+									Duration: &AnsiblePlaybookJSONResultsPlayTaskItemDuration{
+										End:   "2025-03-28T06:55:29.890926Z",
+										Start: "2025-03-28T06:55:29.881536Z",
+									},
+									Id:   "task-id",
+									Name: "task-name",
+									Path: "task-path",
+								},
+							},
+						},
+					},
+				},
+				Stats: map[string]*AnsiblePlaybookJSONResultsStats{
+					"host1": {
+						Ok: 1,
+					},
+				},
+			},
+			expected: "[host1] (task-name)\tYour are running\n'json-stdout-ansibleplaybook'\nfirst example\n\n\nHost: host1\n Changed: 0 Failures: 0 Ignored: 0 Ok: 1 Rescued: 0 Skipped: 0 Unreachable: 0\n",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.desc, func(t *testing.T) {
+			t.Log(test.desc)
+
+			res := test.results.String()
+
+			assert.Equal(t, test.expected, res, "Unexpected result")
+		})
+	}
+}
diff --git a/pkg/execute/stdoutcallback/ansiblePosixJsonl_test.go b/pkg/execute/stdoutcallback/ansiblePosixJsonl_test.go
new file mode 100644
index 0000000..581e109
--- /dev/null
+++ b/pkg/execute/stdoutcallback/ansiblePosixJsonl_test.go
@@ -0,0 +1,51 @@
+package stdoutcallback
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/apenella/go-ansible/v2/pkg/execute"
+	"github.com/apenella/go-ansible/v2/pkg/execute/configuration"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func TestAnsiblePosixJsonlStdoutCallbackExecute(t *testing.T) {
+	t.Parallel()
+	t.Run("Testing AnsiblePosixJsonl stdout callback execution", func(t *testing.T) {
+		exec := execute.NewMockExecute()
+
+		exec.On("Quiet")
+		exec.On("WithOutput", mock.Anything).Return(exec)
+		exec.On("AddEnvVar", configuration.AnsibleStdoutCallback, AnsiblePosixJsonlStdoutCallback)
+		exec.On("Execute", mock.Anything).Return(nil)
+
+		e := NewAnsiblePosixJsonlStdoutCallbackExecute(nil).WithExecutor(exec)
+		err := e.Execute(context.TODO())
+
+		assert.Nil(t, err)
+		exec.AssertExpectations(t)
+	})
+
+	t.Run("Testing error on AnsiblePosixJsonl stdout callback when execute function returns an error", func(t *testing.T) {
+		exec := execute.NewMockExecute()
+
+		exec.On("Quiet")
+		exec.On("WithOutput", mock.Anything).Return(exec)
+		exec.On("AddEnvVar", configuration.AnsibleStdoutCallback, AnsiblePosixJsonlStdoutCallback)
+		exec.On("Execute", mock.Anything).Return(errors.New("some error"))
+
+		e := NewAnsiblePosixJsonlStdoutCallbackExecute(exec)
+		err := e.Execute(context.TODO())
+
+		assert.ErrorContains(t, err, "some error")
+	})
+
+	t.Run("Testing error on AnsiblePosixJsonl stdout callback when executor is not provided", func(t *testing.T) {
+		e := NewAnsiblePosixJsonlStdoutCallbackExecute(nil)
+		err := e.Execute(context.TODO())
+
+		assert.ErrorContains(t, err, "AnsiblePosixJsonlStdoutCallbackExecute executor requires an executor")
+	})
+}