@@ -208,6 +208,9 @@ import (
208208 "fmt"
209209 "math/big"
210210 "net/http"
211+ "os"
212+ "path"
213+ "slices"
211214 "strconv"
212215 "strings"
213216 "sync"
@@ -217,9 +220,11 @@ import (
217220 "github.com/avast/retry-go/v4"
218221 "github.com/ethereum/go-ethereum/accounts/abi/bind"
219222 "github.com/ethereum/go-ethereum/crypto"
223+ "github.com/rs/zerolog"
220224 chainsel "github.com/smartcontractkit/chain-selectors"
221225 "github.com/smartcontractkit/chainlink-testing-framework/framework"
222226 "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
227+ ctfdocker "github.com/smartcontractkit/chainlink-testing-framework/lib/docker"
223228 "github.com/smartcontractkit/freeport"
224229 "github.com/testcontainers/testcontainers-go"
225230
@@ -516,7 +521,18 @@ func (p *CTFAnvilChainProvider) GetNodeHTTPURL() string {
516521// Returns an error if the container termination fails.
517522func (p * CTFAnvilChainProvider ) Cleanup (ctx context.Context ) error {
518523 if p .container != nil {
519- err := p .container .Terminate (ctx )
524+ shouldSave , path , err := shouldSaveCtfContainerLogs (os .Getenv (saveContainerLogsEnvVar ))
525+ if err != nil {
526+ return fmt .Errorf ("failed to check if we should save the container logs: %w" , err )
527+ }
528+
529+ if shouldSave {
530+ zlogger := zerolog .New (os .Stdout )
531+ ctfdocker .WriteAllContainersLogs (zlogger , path )
532+ zlogger .Info ().Msgf ("container logs saved to %q" , path )
533+ }
534+
535+ err = p .container .Terminate (ctx )
520536 if err != nil {
521537 return fmt .Errorf ("failed to terminate Anvil container: %w" , err )
522538 }
@@ -714,3 +730,41 @@ func (p *CTFAnvilChainProvider) waitForAnvilReady(ctx context.Context, httpURL s
714730 retry .DelayType (retry .FixedDelay ),
715731 )
716732}
733+
734+ const saveContainerLogsEnvVar = "CLDF_CONTAINER_LOGS"
735+
736+ func shouldSaveCtfContainerLogs (envVarValue string ) (bool , string , error ) {
737+ if envVarValue == "" {
738+ return false , "" , nil
739+ }
740+
741+ if path .IsAbs (envVarValue ) {
742+ if isDirectory (envVarValue ) {
743+ return true , envVarValue , nil
744+ }
745+
746+ err := os .MkdirAll (envVarValue , 0o700 )
747+ if err != nil {
748+ return false , "" , fmt .Errorf ("failed to create container logs dir: %w" , err )
749+ }
750+
751+ return true , envVarValue , nil
752+ }
753+
754+ trueValues := []string {"1" , "true" , "on" , "enabled" }
755+ if slices .Contains (trueValues , strings .ToLower (envVarValue )) {
756+ tmpDirPath , err := os .MkdirTemp ("" , "cldf-container-logs-*" )
757+ if err != nil {
758+ return false , "" , fmt .Errorf ("failed to create temporary dir: %w" , err )
759+ }
760+
761+ return true , tmpDirPath , nil
762+ }
763+
764+ return false , "" , fmt .Errorf ("invalid value for %s variable: %v" , saveContainerLogsEnvVar , envVarValue )
765+ }
766+
767+ func isDirectory (path string ) bool {
768+ fileInfo , err := os .Stat (path )
769+ return err == nil && fileInfo .IsDir ()
770+ }
0 commit comments