Repository: GoogleContainerTools/skaffold
Pull request #7193 (2022-03-15 12:21:10)

refactor: Use new logrus.Logger instead of default
Use a new `logrus.Logger` instance instead of the default logrus singleton.

Relying on the default singleton is problematic when dependencies (or transitive dependencies) also use logrus. When code paths in these dependencies modify the default singleton, it also impacts Skaffold logging.

I ran into this issue while trying to upgrade the `ko` dependency to v0.10, and there was unwanted error-level log output messages from `amazon-ecr-credential-helper`.

Context:
- https://github.com/google/ko/pull/586
- https://github.com/awslabs/amazon-ecr-credential-helper/pull/309

This change also moves us closer to not leaking the logger implementation dependency outside the `output/log` package.

Tracking: #7131


codecov[bot]: # [Codecov](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) Report
> Merging [#7193](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (c0b8c5d) into [main](https://codecov.io/gh/GoogleContainerTools/skaffold/commit/290280e8c6c19231e9ed5b5c027f2c01d22f75be?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) (290280e) will **decrease** coverage by `2.06%`.
> The diff coverage is `56.96%`.

```diff
@@            Coverage Diff             @@
##             main    #7193      +/-   ##
==========================================
- Coverage   70.48%   68.42%   -2.07%     
==========================================
  Files         515      560      +45     
  Lines       23150    26485    +3335     
==========================================
+ Hits        16317    18122    +1805     
- Misses       5776     7105    +1329     
- Partials     1057     1258     +201     
```


| [Impacted Files](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | Coverage Δ | |
|---|---|---|
| [cmd/skaffold/app/cmd/deploy.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGVwbG95Lmdv) | `52.00% <ø> (-1.85%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/dev.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZGV2Lmdv) | `84.61% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/render.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvcmVuZGVyLmdv) | `36.66% <0.00%> (-4.72%)` | :arrow_down: |
| [cmd/skaffold/skaffold.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL3NrYWZmb2xkLmdv) | `0.00% <0.00%> (ø)` | |
| [cmd/skaffold/app/cmd/inspect\_tests.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvaW5zcGVjdF90ZXN0cy5nbw==) | `62.50% <14.28%> (-1.14%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/lsp.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvbHNwLmdv) | `28.12% <28.12%> (ø)` | |
| [cmd/skaffold/app/cmd/fix.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZml4Lmdv) | `68.85% <40.00%> (-7.62%)` | :arrow_down: |
| [cmd/skaffold/app/cmd/lint.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvbGludC5nbw==) | `42.85% <42.85%> (ø)` | |
| [cmd/skaffold/app/cmd/find\_configs.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9jbWQvZmluZF9jb25maWdzLmdv) | `48.88% <50.00%> (+0.24%)` | :arrow_up: |
| [cmd/skaffold/app/skaffold.go](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools#diff-Y21kL3NrYWZmb2xkL2FwcC9za2FmZm9sZC5nbw==) | `76.19% <70.00%> (-8.43%)` | :arrow_down: |
| ... and [221 more](https://codecov.io/gh/GoogleContainerTools/skaffold/pull/7193/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools) | |

:mega: Codecov can now indicate which changes are the most critical in Pull Requests. [Learn more](https://about.codecov.io/product/feature/runtime-insights/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleContainerTools)

briandealwis: Adding @MarlonGamez as he's our logging expert

Patch:
diff --git a/docs/static/javascripts/tabs.js b/docs/static/javascripts/tabs.js
index 065602226..95dd67085 100644
--- a/docs/static/javascripts/tabs.js
+++ b/docs/static/javascripts/tabs.js
@@ -1,6 +1,6 @@
 function initTabs() {
-    $('.tab-content').find('.tab-pane').each(function(idx, item) {
-      var navTabs = $(this).closest('.code-tabs').find('.nav-tabs'),
+    $('.tab-content').children('.tab-pane').each(function(idx, item) {
+      var navTabs = $(this).closest('.code-tabs').children('.nav-tabs'),
           title = $(this).attr('title');
       navTabs.append('<li class="nav-tab"><a href="#" class="nav-tab">'+title+'</a></li');
     });
@@ -18,9 +18,10 @@ function initTabs() {
       var tab = $(this).parent(),
           tabIndex = tab.index(),
           tabPanel = $(this).closest('.code-tabs'),
-          tabPane = tabPanel.find('.tab-pane').eq(tabIndex);
-      tabPanel.find('.active').removeClass('active');
+          tabPane = tabPanel.find('.tab-content:first').children('.tab-pane').eq(tabIndex);
+      tab.siblings().removeClass('active');
+      tabPane.siblings().removeClass('active');
       tab.addClass('active');
       tabPane.addClass('active');
     });
-  }
\ No newline at end of file
+  }
diff --git a/integration/binpack/binpack.go b/integration/binpack/binpack.go
index c6499e6c6..9a0ed6006 100644
--- a/integration/binpack/binpack.go
+++ b/integration/binpack/binpack.go
@@ -17,10 +17,11 @@ limitations under the License.
 package binpack
 
 import (
+	"context"
 	"fmt"
 	"sort"
 
-	"github.com/sirupsen/logrus"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 )
 
 type timing struct {
@@ -124,10 +125,11 @@ func Partitions() (map[string]int, int) {
 			result[timing.name] = len(bins) - 1
 		}
 	}
-	if logrus.GetLevel() == logrus.TraceLevel {
-		logrus.Trace("Partitions: ")
+	if log.IsTraceLevelEnabled() {
+		ctx := context.TODO()
+		log.Entry(ctx).Trace("Partitions: ")
 		for i, b := range bins {
-			logrus.Tracef("P%d %s\n", i, b.String())
+			log.Entry(ctx).Tracef("P%d %s\n", i, b.String())
 		}
 	}
 	return result, len(bins) - 1
diff --git a/integration/skaffold/helper.go b/integration/skaffold/helper.go
index 5db7e6b0f..40c8f98f6 100644
--- a/integration/skaffold/helper.go
+++ b/integration/skaffold/helper.go
@@ -27,7 +27,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
 
 	"github.com/GoogleContainerTools/skaffold/cmd/skaffold/app/cmd"
@@ -229,7 +228,7 @@ func (b *RunBuilder) runForked(t *testing.T, out io.Writer) {
 
 	cmd := b.cmd(ctx)
 	cmd.Stdout = out
-	logrus.Infof("Running %s in %s", cmd.Args, cmd.Dir)
+	t.Logf("Running %s in %s", cmd.Args, cmd.Dir)
 
 	start := time.Now()
 	if err := cmd.Start(); err != nil {
@@ -238,7 +237,7 @@ func (b *RunBuilder) runForked(t *testing.T, out io.Writer) {
 
 	go func() {
 		cmd.Wait()
-		logrus.Infof("Ran %s in %v", cmd.Args, timeutil.Humanize(time.Since(start)))
+		t.Logf("Ran %s in %v", cmd.Args, timeutil.Humanize(time.Since(start)))
 	}()
 
 	waitAndTriggerStacktrace(ctx, t, cmd.Process)
@@ -260,7 +259,7 @@ func (b *RunBuilder) Run(t *testing.T) error {
 	t.Helper()
 
 	cmd := b.cmd(context.Background())
-	logrus.Infof("Running %s in %s", cmd.Args, cmd.Dir)
+	t.Logf("Running %s in %s", cmd.Args, cmd.Dir)
 
 	if err := cmd.Run(); err != nil {
 		return fmt.Errorf("skaffold %q: %w", b.command, err)
@@ -273,7 +272,7 @@ func (b *RunBuilder) StartWithProcess(t *testing.T) (*os.Process, error) {
 	t.Helper()
 
 	cmd := b.cmd(context.Background())
-	logrus.Infof("Running %s in %s", cmd.Args, cmd.Dir)
+	t.Logf("Running %s in %s", cmd.Args, cmd.Dir)
 
 	if err := cmd.Start(); err != nil {
 		return nil, fmt.Errorf("skaffold %q: %w", b.command, err)
@@ -287,14 +286,14 @@ func (b *RunBuilder) RunWithCombinedOutput(t *testing.T) ([]byte, error) {
 
 	cmd := b.cmd(context.Background())
 	cmd.Stdout, cmd.Stderr = nil, nil
-	logrus.Infof("Running %s in %s", cmd.Args, cmd.Dir)
+	t.Logf("Running %s in %s", cmd.Args, cmd.Dir)
 
 	start := time.Now()
 	out, err := cmd.CombinedOutput()
 	if err != nil {
 		return out, fmt.Errorf("skaffold %q: %w", b.command, err)
 	}
-	logrus.Infof("Ran %s in %v", cmd.Args, timeutil.Humanize(time.Since(start)))
+	t.Logf("Ran %s in %v", cmd.Args, timeutil.Humanize(time.Since(start)))
 	return out, nil
 }
 
@@ -306,7 +305,7 @@ func (b *RunBuilder) RunOrFailOutput(t *testing.T) []byte {
 
 	cmd := b.cmd(context.Background())
 	cmd.Stdout, cmd.Stderr = nil, nil
-	logrus.Infof("Running %s in %s", cmd.Args, cmd.Dir)
+	t.Logf("Running %s in %s", cmd.Args, cmd.Dir)
 
 	start := time.Now()
 	out, err := cmd.Output()
@@ -316,7 +315,7 @@ func (b *RunBuilder) RunOrFailOutput(t *testing.T) []byte {
 		}
 		t.Fatalf("skaffold %s: %v, %s", b.command, err, out)
 	}
-	logrus.Infof("Ran %s in %v", cmd.Args, timeutil.Humanize(time.Since(start)))
+	t.Logf("Ran %s in %v", cmd.Args, timeutil.Humanize(time.Since(start)))
 	return out
 }
 
diff --git a/integration/util.go b/integration/util.go
index c560570df..8bd5ad55b 100644
--- a/integration/util.go
+++ b/integration/util.go
@@ -28,7 +28,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/sirupsen/logrus"
 	appsv1 "k8s.io/api/apps/v1"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -41,6 +40,7 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
 	kubernetesclient "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/client"
 	kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 	k8s "github.com/GoogleContainerTools/skaffold/pkg/webhook/kubernetes"
 )
 
@@ -115,7 +115,8 @@ func SetupNamespace(t *testing.T) (*v1.Namespace, *NSKubernetesClient) {
 		t.Fatalf("creating namespace: %s", err)
 	}
 
-	logrus.Infoln("Namespace:", ns.Name)
+	ctx := context.Background()
+	log.Entry(ctx).Infoln("Namespace:", ns.Name)
 
 	nsClient := &NSKubernetesClient{
 		t:      t,
@@ -124,7 +125,7 @@ func SetupNamespace(t *testing.T) (*v1.Namespace, *NSKubernetesClient) {
 	}
 
 	t.Cleanup(func() {
-		client.CoreV1().Namespaces().Delete(context.Background(), ns.Name, metav1.DeleteOptions{})
+		client.CoreV1().Namespaces().Delete(ctx, ns.Name, metav1.DeleteOptions{})
 	})
 
 	return ns, nsClient
@@ -197,7 +198,7 @@ func (k *NSKubernetesClient) WaitForPodsReady(podNames ...string) {
 		return false
 	}
 	result := k.waitForPods(f, podNames...)
-	logrus.Infof("Pods marked as ready: %v", result)
+	log.Entry(context.Background()).Infof("Pods marked as ready: %v", result)
 }
 
 // WaitForPodsInPhase waits for a list of pods to reach the given phase.
@@ -206,7 +207,7 @@ func (k *NSKubernetesClient) WaitForPodsInPhase(expectedPhase v1.PodPhase, podNa
 		return pod.Status.Phase == expectedPhase
 	}
 	result := k.waitForPods(f, podNames...)
-	logrus.Infof("Pods in phase %q: %v", expectedPhase, result)
+	log.Entry(context.Background()).Infof("Pods in phase %q: %v", expectedPhase, result)
 }
 
 // waitForPods waits for a list of pods to become ready.
@@ -223,9 +224,9 @@ func (k *NSKubernetesClient) waitForPods(podReady func(*v1.Pod) bool, podNames .
 
 	waitForAllPods := len(podNames) == 0
 	if waitForAllPods {
-		logrus.Infof("Waiting for all pods in namespace %q to be ready", k.ns)
+		log.Entry(ctx).Infof("Waiting for all pods in namespace %q to be ready", k.ns)
 	} else {
-		logrus.Infoln("Waiting for pods", podNames, "to be ready")
+		log.Entry(ctx).Infoln("Waiting for pods", podNames, "to be ready")
 	}
 
 	podsReady = map[string]bool{}
@@ -264,11 +265,11 @@ func (k *NSKubernetesClient) waitForPods(podReady func(*v1.Pod) bool, podNames .
 				}
 			}
 			if len(waiting) > 0 {
-				logrus.Infof("Still waiting for pods %v", waiting)
+				log.Entry(ctx).Infof("Still waiting for pods %v", waiting)
 				break waitLoop
 			} else if l := len(w.ResultChan()); l > 0 {
 				// carry on when there are pending messages in case a new pod has been created
-				logrus.Infof("%d pending pod update messages", l)
+				log.Entry(ctx).Infof("%d pending pod update messages", l)
 				break waitLoop
 			}
 			return
@@ -312,11 +313,11 @@ func (k *NSKubernetesClient) waitForDeploymentsToStabilizeWithTimeout(timeout ti
 		return
 	}
 
-	logrus.Infoln("Waiting for deployments", depNames, "to stabilize")
-
 	ctx, cancelTimeout := context.WithTimeout(context.Background(), timeout)
 	defer cancelTimeout()
 
+	log.Entry(ctx).Infoln("Waiting for deployments", depNames, "to stabilize")
+
 	w, err := k.Deployments().Watch(ctx, metav1.ListOptions{})
 	if err != nil {
 		k.t.Fatalf("Unable to watch deployments: %v", err)
@@ -338,7 +339,7 @@ func (k *NSKubernetesClient) waitForDeploymentsToStabilizeWithTimeout(timeout ti
 		case event := <-w.ResultChan():
 			dp := event.Object.(*appsv1.Deployment)
 			desiredReplicas := *(dp.Spec.Replicas)
-			logrus.Infof("Deployment %s: Generation %d/%d, Replicas %d/%d, Available %d/%d",
+			log.Entry(ctx).Infof("Deployment %s: Generation %d/%d, Replicas %d/%d, Available %d/%d",
 				dp.Name,
 				dp.Status.ObservedGeneration, dp.Generation,
 				dp.Status.Replicas, desiredReplicas,
@@ -352,7 +353,7 @@ func (k *NSKubernetesClient) waitForDeploymentsToStabilizeWithTimeout(timeout ti
 				}
 			}
 
-			logrus.Infoln("Deployments", depNames, "are stable")
+			log.Entry(ctx).Infoln("Deployments", depNames, "are stable")
 			return
 		}
 	}
@@ -361,14 +362,14 @@ func (k *NSKubernetesClient) waitForDeploymentsToStabilizeWithTimeout(timeout ti
 // debug is used to print all the details about pods or deployments
 func (k *NSKubernetesClient) debug(entities string) {
 	cmd := exec.Command("kubectl", "-n", k.ns, "get", entities, "-oyaml")
-	logrus.Warnln(cmd.Args)
+	log.Entry(context.Background()).Warnln(cmd.Args)
 	out, _ := cmd.CombinedOutput()
 	fmt.Println(string(out)) // Use fmt.Println, not logrus, for prettier output
 }
 
 func (k *NSKubernetesClient) printDiskFreeSpace() {
 	cmd := exec.Command("df", "-h")
-	logrus.Warnln(cmd.Args)
+	log.Entry(context.Background()).Warnln(cmd.Args)
 	out, _ := cmd.CombinedOutput()
 	fmt.Println(string(out))
 }
@@ -377,7 +378,7 @@ func (k *NSKubernetesClient) printDiskFreeSpace() {
 func (k *NSKubernetesClient) logs(entity string, names []string) {
 	for _, n := range names {
 		cmd := exec.Command("kubectl", "-n", k.ns, "logs", entity+"/"+n)
-		logrus.Warnln(cmd.Args)
+		log.Entry(context.Background()).Warnln(cmd.Args)
 		out, _ := cmd.CombinedOutput()
 		fmt.Println(string(out)) // Use fmt.Println, not logrus, for prettier output
 	}
diff --git a/pkg/skaffold/build/build.go b/pkg/skaffold/build/build.go
index 414ed06fb..676f0400a 100644
--- a/pkg/skaffold/build/build.go
+++ b/pkg/skaffold/build/build.go
@@ -51,7 +51,7 @@ type PipelineBuilder interface {
 	PostBuild(ctx context.Context, out io.Writer) error
 
 	// Concurrency specifies the max number of builds that can run at any one time. If concurrency is 0, then all builds can run in parallel.
-	Concurrency() int
+	Concurrency() *int
 
 	// Prune removes images built in this pipeline
 	Prune(context.Context, io.Writer) error
diff --git a/pkg/skaffold/build/builder_mux.go b/pkg/skaffold/build/builder_mux.go
index ab7853e20..c079acaa1 100644
--- a/pkg/skaffold/build/builder_mux.go
+++ b/pkg/skaffold/build/builder_mux.go
@@ -22,6 +22,7 @@ import (
 	"io"
 	"reflect"
 
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/hooks"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
@@ -51,38 +52,19 @@ type Config interface {
 func NewBuilderMux(cfg Config, store ArtifactStore, builder func(p latestV1.Pipeline) (PipelineBuilder, error)) (*BuilderMux, error) {
 	pipelines := cfg.GetPipelines()
 	m := make(map[string]PipelineBuilder)
-	var pb []PipelineBuilder
-	minConcurrency := -1
-	for i, p := range pipelines {
+	var pbs []PipelineBuilder
+	for _, p := range pipelines {
 		b, err := builder(p)
 		if err != nil {
 			return nil, fmt.Errorf("creating builder: %w", err)
 		}
-		pb = append(pb, b)
+		pbs = append(pbs, b)
 		for _, a := range p.Build.Artifacts {
 			m[a.ImageName] = b
 		}
-
-		if cfg.BuildConcurrency() >= 0 {
-			minConcurrency = cfg.BuildConcurrency()
-		} else {
-			concurrency := b.Concurrency()
-			// set mux concurrency to be the minimum of all builders' concurrency. (concurrency = 0 means unlimited)
-			switch {
-			case minConcurrency < 0:
-				minConcurrency = concurrency
-				log.Entry(context.TODO()).Infof("build concurrency first set to %d parsed from %s[%d]", minConcurrency, reflect.TypeOf(b).String(), i)
-			case concurrency > 0 && (minConcurrency == 0 || concurrency < minConcurrency):
-				minConcurrency = concurrency
-				log.Entry(context.TODO()).Infof("build concurrency updated to %d parsed from %s[%d]", minConcurrency, reflect.TypeOf(b).String(), i)
-			default:
-				log.Entry(context.TODO()).Infof("build concurrency value %d parsed from %s[%d] is ignored since it's not less than previously set value %d", concurrency, reflect.TypeOf(b).String(), i, minConcurrency)
-			}
-		}
 	}
-	log.Entry(context.TODO()).Infof("final build concurrency value is %d", minConcurrency)
-
-	return &BuilderMux{builders: pb, byImageName: m, store: store, concurrency: minConcurrency}, nil
+	concurrency := getConcurrency(pbs, cfg.BuildConcurrency())
+	return &BuilderMux{builders: pbs, byImageName: m, store: store, concurrency: concurrency}, nil
 }
 
 // Build executes the specific image builder for each artifact in the given artifact slice.
@@ -98,7 +80,7 @@ func (b *BuilderMux) Build(ctx context.Context, out io.Writer, tags tag.ImageTag
 		}
 	}
 
-	builder := func(ctx context.Context, out io.Writer, artifact *latestV1.Artifact, tag string, platforms platform.Matcher) (string, error) {
+	builderF := func(ctx context.Context, out io.Writer, artifact *latestV1.Artifact, tag string, platforms platform.Matcher) (string, error) {
 		p := b.byImageName[artifact.ImageName]
 		pl, err := filterBuildEnvSupportedPlatforms(p.SupportedPlatforms(), platforms)
 		if err != nil {
@@ -124,14 +106,14 @@ func (b *BuilderMux) Build(ctx context.Context, out io.Writer, tags tag.ImageTag
 		}
 		return built, nil
 	}
-	ar, err := InOrder(ctx, out, tags, resolver, artifacts, builder, b.concurrency, b.store)
+	ar, err := InOrder(ctx, out, tags, resolver, artifacts, builderF, b.concurrency, b.store)
 	if err != nil {
 		return nil, err
 	}
 
 	for builder := range m {
-		if err := builder.PostBuild(ctx, out); err != nil {
-			return nil, err
+		if errB := builder.PostBuild(ctx, out); errB != nil {
+			return nil, errB
 		}
 	}
 
@@ -159,3 +141,36 @@ func filterBuildEnvSupportedPlatforms(supported platform.Matcher, target platfor
 	}
 	return pl, nil
 }
+
+func getConcurrency(pbs []PipelineBuilder, cliConcurrency int) int {
+	ctx := context.TODO()
+	if cliConcurrency >= 0 {
+		log.Entry(ctx).Infof("build concurrency set to cli concurrency %d", cliConcurrency)
+		return cliConcurrency
+	}
+	minConcurrency := -1
+	for i, b := range pbs {
+		concurrency := 1
+		if b.Concurrency() != nil {
+			concurrency = *b.Concurrency()
+		}
+		// set mux concurrency to be the minimum of all builders' concurrency. (concurrency = 0 means unlimited)
+		switch {
+		case minConcurrency < 0:
+			minConcurrency = concurrency
+			log.Entry(ctx).Infof("build concurrency first set to %d parsed from %s[%d]", minConcurrency, reflect.TypeOf(b).String(), i)
+		case concurrency > 0 && (minConcurrency == 0 || concurrency < minConcurrency):
+			minConcurrency = concurrency
+			log.Entry(ctx).Infof("build concurrency updated to %d parsed from %s[%d]", minConcurrency, reflect.TypeOf(b).String(), i)
+		default:
+			log.Entry(ctx).Infof("build concurrency value %d parsed from %s[%d] is ignored since it's not less than previously set value %d", concurrency, reflect.TypeOf(b).String(), i, minConcurrency)
+		}
+	}
+	if minConcurrency < 0 {
+		log.Entry(ctx).Infof("build concurrency set to default value of %d", minConcurrency)
+		// set default concurrency to 1 for local builder. For GCB and Cluster build the default value is 0
+		return constants.DefaultLocalConcurrency
+	}
+	log.Entry(ctx).Infof("final build concurrency value is %d", minConcurrency)
+	return minConcurrency
+}
diff --git a/pkg/skaffold/build/cluster/cluster.go b/pkg/skaffold/build/cluster/cluster.go
index c265307d8..242abf2ab 100644
--- a/pkg/skaffold/build/cluster/cluster.go
+++ b/pkg/skaffold/build/cluster/cluster.go
@@ -70,8 +70,8 @@ func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, artifact *la
 	return build.TagWithDigest(tag, digest), nil
 }
 
-func (b *Builder) Concurrency() int {
-	return b.ClusterDetails.Concurrency
+func (b *Builder) Concurrency() *int {
+	return util.IntPtr(b.ClusterDetails.Concurrency)
 }
 
 func (b *Builder) runBuildForArtifact(ctx context.Context, out io.Writer, a *latestV1.Artifact, tag string, platforms platform.Matcher) (string, error) {
diff --git a/pkg/skaffold/build/gcb/cloud_build.go b/pkg/skaffold/build/gcb/cloud_build.go
index 383dbd920..ce69b616e 100644
--- a/pkg/skaffold/build/gcb/cloud_build.go
+++ b/pkg/skaffold/build/gcb/cloud_build.go
@@ -44,6 +44,7 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/sources"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
 	"github.com/GoogleContainerTools/skaffold/proto/v1"
 )
 
@@ -65,8 +66,8 @@ func (b *Builder) PostBuild(_ context.Context, _ io.Writer) error {
 	return nil
 }
 
-func (b *Builder) Concurrency() int {
-	return b.GoogleCloudBuild.Concurrency
+func (b *Builder) Concurrency() *int {
+	return util.IntPtr(b.GoogleCloudBuild.Concurrency)
 }
 
 func (b *Builder) buildArtifactWithCloudBuild(ctx context.Context, out io.Writer, artifact *latestV1.Artifact, tag string, platform platform.Matcher) (string, error) {
diff --git a/pkg/skaffold/build/local/local.go b/pkg/skaffold/build/local/local.go
index df70a42ab..bbf40f327 100644
--- a/pkg/skaffold/build/local/local.go
+++ b/pkg/skaffold/build/local/local.go
@@ -58,12 +58,7 @@ func (b *Builder) PostBuild(ctx context.Context, _ io.Writer) error {
 	return nil
 }
 
-func (b *Builder) Concurrency() int {
-	if b.local.Concurrency == nil {
-		return 0
-	}
-	return *b.local.Concurrency
-}
+func (b *Builder) Concurrency() *int { return b.local.Concurrency }
 
 func (b *Builder) PushImages() bool {
 	return b.pushImages
diff --git a/pkg/skaffold/deploy/util/util.go b/pkg/skaffold/deploy/util/util.go
index a7ef02b7b..5c5a95fff 100644
--- a/pkg/skaffold/deploy/util/util.go
+++ b/pkg/skaffold/deploy/util/util.go
@@ -30,6 +30,7 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
@@ -123,11 +124,13 @@ func ConsolidateTransformConfiguration(cfg types.Config) (map[schema.GroupKind]l
 
 	// add user schema values, override defaults
 	for _, rf := range cfg.TransformAllowList() {
+		instrumentation.AddResourceFilter("schema", "allow")
 		groupKind := schema.ParseGroupKind(rf.GroupKind)
 		transformableAllowlist[groupKind] = convertJSONPathIndex(rf)
 		delete(transformableDenylist, groupKind)
 	}
 	for _, rf := range cfg.TransformDenyList() {
+		instrumentation.AddResourceFilter("schema", "deny")
 		groupKind := schema.ParseGroupKind(rf.GroupKind)
 		transformableDenylist[groupKind] = convertJSONPathIndex(rf)
 		delete(transformableAllowlist, groupKind)
@@ -146,12 +149,14 @@ func ConsolidateTransformConfiguration(cfg types.Config) (map[schema.GroupKind]l
 			return nil, nil, err
 		}
 		for _, rf := range rsc.Allow {
+			instrumentation.AddResourceFilter("cli-flag", "allow")
 			groupKind := schema.ParseGroupKind(rf.GroupKind)
 			transformableAllowlist[groupKind] = convertJSONPathIndex(rf)
 			delete(transformableDenylist, groupKind)
 		}
 
 		for _, rf := range rsc.Deny {
+			instrumentation.AddResourceFilter("cli-flag", "deny")
 			groupKind := schema.ParseGroupKind(rf.GroupKind)
 			transformableDenylist[groupKind] = convertJSONPathIndex(rf)
 			delete(transformableAllowlist, groupKind)
diff --git a/pkg/skaffold/instrumentation/export.go b/pkg/skaffold/instrumentation/export.go
index d9c313478..c86b11fb6 100644
--- a/pkg/skaffold/instrumentation/export.go
+++ b/pkg/skaffold/instrumentation/export.go
@@ -204,6 +204,9 @@ func createMetrics(ctx context.Context, meter skaffoldMeter) {
 		if doesDeploy.Contains(meter.Command) {
 			deployerMetrics(ctx, meter, m, sharedLabels...)
 		}
+		if doesDeploy.Contains(meter.Command) || meter.Command == "render" {
+			resourceSelectorMetrics(ctx, meter, m, sharedLabels...)
+		}
 	}
 
 	if meter.ErrorCode != 0 {
@@ -267,6 +270,15 @@ func deployerMetrics(ctx context.Context, meter skaffoldMeter, m metric.Meter, l
 	}
 }
 
+func resourceSelectorMetrics(ctx context.Context, meter skaffoldMeter, m metric.Meter, labels ...attribute.KeyValue) {
+	if len(meter.ResourceFilters) > 0 {
+		resourceFilters := metric.Must(m).NewInt64ValueRecorder("resource-filters", metric.WithDescription("The resource filters defined for rendering and/or deployment"))
+		for _, resourceFilter := range meter.ResourceFilters {
+			resourceFilters.Record(ctx, 1, append(labels, attribute.String("source", resourceFilter.Source), attribute.String("type", resourceFilter.Type))...)
+		}
+	}
+}
+
 func builderMetrics(ctx context.Context, meter skaffoldMeter, m metric.Meter, labels ...attribute.KeyValue) {
 	builderCounter := metric.Must(m).NewInt64ValueRecorder("builders", metric.WithDescription("Builders used"))
 	artifactCounter := metric.Must(m).NewInt64ValueRecorder("artifacts", metric.WithDescription("Number of artifacts used"))
diff --git a/pkg/skaffold/instrumentation/meter.go b/pkg/skaffold/instrumentation/meter.go
index 2a114161c..fe8e74ffa 100644
--- a/pkg/skaffold/instrumentation/meter.go
+++ b/pkg/skaffold/instrumentation/meter.go
@@ -143,6 +143,10 @@ func AddDevIterationErr(i int, errorCode proto.StatusCode) {
 	meter.DevIterations[i].ErrorCode = errorCode
 }
 
+func AddResourceFilter(source string, typ string) {
+	meter.ResourceFilters = append(meter.ResourceFilters, resourceFilter{Source: source, Type: typ})
+}
+
 func AddFlag(flag *flag.Flag) {
 	if flag.Changed {
 		meter.EnumFlags[flag.Name] = flag.Value.String()
diff --git a/pkg/skaffold/instrumentation/types.go b/pkg/skaffold/instrumentation/types.go
index 507066ccf..bb80fa7bc 100644
--- a/pkg/skaffold/instrumentation/types.go
+++ b/pkg/skaffold/instrumentation/types.go
@@ -83,6 +83,9 @@ type skaffoldMeter struct {
 	// reasons they were triggered. The triggers can be one of sync, build, or deploy.
 	DevIterations []devIteration
 
+	// ResourceFilters represents user defined resource filters including 'source' and 'type' information
+	ResourceFilters []resourceFilter
+
 	// StartTime Start time of the Skaffold program, used to track how long Skaffold took to finish executing.
 	StartTime time.Time
 
@@ -115,6 +118,15 @@ type devIteration struct {
 	ErrorCode proto.StatusCode
 }
 
+// resourceFilter describes a user defined resource filter.
+type resourceFilter struct {
+	// Source is the source configuration of the resource filter (cli-flag, schema)
+	Source string
+
+	// Type is the defined type of the resource filter (allow, deny)
+	Type string
+}
+
 // creds contains the Google Cloud project ID.
 type creds struct {
 	// ProjectID is the ID of the Google Cloud project to upload metrics to.
diff --git a/pkg/skaffold/kubernetes/portforward/port_forward_integration.go b/pkg/skaffold/kubernetes/portforward/port_forward_integration.go
index 8e10d7973..de8ff1f18 100644
--- a/pkg/skaffold/kubernetes/portforward/port_forward_integration.go
+++ b/pkg/skaffold/kubernetes/portforward/port_forward_integration.go
@@ -32,7 +32,8 @@ import (
 )
 
 // SimulateDevCycle is used for testing a port forward + stop + restart in a simulated dev cycle
-func SimulateDevCycle(t *testing.T, kubectlCLI *kubectl.CLI, namespace string) {
+func SimulateDevCycle(t *testing.T, kubectlCLI *kubectl.CLI, namespace string, level log.Level) {
+	log.SetLevel(level)
 	em := NewEntryManager(NewKubectlForwarder(kubectlCLI))
 	portForwardEventHandler := portForwardEvent
 	defer func() { portForwardEvent = portForwardEventHandler }()
diff --git a/pkg/skaffold/output/log/log.go b/pkg/skaffold/output/log/log.go
index 49f7ee57e..e838b68f1 100644
--- a/pkg/skaffold/output/log/log.go
+++ b/pkg/skaffold/output/log/log.go
@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+// Includes code from github.com/sirupsen/logrus (MIT License)
+
 package log
 
 import (
@@ -28,25 +30,85 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
 )
 
-// DefaultLogLevel is logrus warn level
-const DefaultLogLevel = logrus.WarnLevel
+// Logging levels. Defining our own so we can encapsulate the underlying logger implementation.
+const (
+	// PanicLevel level, highest level of severity. Logs and then calls panic with the
+	// message passed to Debug, Info, ...
+	PanicLevel Level = iota
+	// FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
+	// logging level is set to Panic.
+	FatalLevel
+	// ErrorLevel level. Logs. Used for errors that should definitely be noted.
+	// Commonly used for hooks to send errors to an error tracking service.
+	ErrorLevel
+	// WarnLevel level. Non-critical entries that deserve eyes.
+	WarnLevel
+	// InfoLevel level. General operational entries about what's going on inside the
+	// application.
+	InfoLevel
+	// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
+	DebugLevel
+	// TraceLevel level. Designates finer-grained informational events than the Debug.
+	TraceLevel
+)
+
+// Level type for logging levels
+type Level uint32
+
+// AllLevels exposes all logging levels
+var AllLevels = []Level{
+	PanicLevel,
+	FatalLevel,
+	ErrorLevel,
+	WarnLevel,
+	InfoLevel,
+	DebugLevel,
+	TraceLevel,
+}
+
+// DefaultLogLevel for the global Skaffold logger
+const DefaultLogLevel = WarnLevel
 
 type contextKey struct{}
 
-var AllLevels = logrus.AllLevels
 var ContextKey = contextKey{}
 
+// logger is the global logrus.Logger for Skaffold
+// TODO: Make this not global.
+var logger = New()
+
 type EventContext struct {
 	Task    constants.Phase
 	Subtask string
 }
 
+// String converts the Level to a string. E.g. PanicLevel becomes "panic".
+func (level Level) String() string {
+	switch level {
+	case TraceLevel:
+		return "trace"
+	case DebugLevel:
+		return "debug"
+	case InfoLevel:
+		return "info"
+	case WarnLevel:
+		return "warning"
+	case ErrorLevel:
+		return "error"
+	case FatalLevel:
+		return "fatal"
+	case PanicLevel:
+		return "panic"
+	}
+	return "unknown"
+}
+
 // Entry takes an context.Context and constructs a logrus.Entry from it, adding
 // fields for task and subtask information
 func Entry(ctx context.Context) *logrus.Entry {
 	val := ctx.Value(ContextKey)
 	if eventContext, ok := val.(EventContext); ok {
-		return logrus.WithFields(logrus.Fields{
+		return logger.WithFields(logrus.Fields{
 			"task":    eventContext.Task,
 			"subtask": eventContext.Subtask,
 		})
@@ -54,7 +116,7 @@ func Entry(ctx context.Context) *logrus.Entry {
 
 	// Use constants.DevLoop as the default task, as it's the highest level task we
 	// can default to if one isn't specified.
-	return logrus.WithFields(logrus.Fields{
+	return logger.WithFields(logrus.Fields{
 		"task":    constants.DevLoop,
 		"subtask": constants.SubtaskIDNone,
 	})
@@ -62,66 +124,76 @@ func Entry(ctx context.Context) *logrus.Entry {
 
 // IsDebugLevelEnabled returns true if debug level log is enabled.
 func IsDebugLevelEnabled() bool {
-	return logrus.IsLevelEnabled(logrus.DebugLevel)
+	return logger.IsLevelEnabled(logrus.DebugLevel)
 }
 
 // IsTraceLevelEnabled returns true if trace level log is enabled.
 func IsTraceLevelEnabled() bool {
-	return logrus.IsLevelEnabled(logrus.TraceLevel)
+	return logger.IsLevelEnabled(logrus.TraceLevel)
 }
 
+// New returns a new logrus.Logger.
+// We use a new instance instead of the default logrus singleton to avoid clashes with dependencies that also use logrus.
 func New() *logrus.Logger {
 	return logrus.New()
 }
 
 // KanikoLogLevel makes sure kaniko logs at least at Info level and at most Debug level (trace doesn't work with Kaniko)
 func KanikoLogLevel() logrus.Level {
-	level := logrus.GetLevel()
-	if level < logrus.InfoLevel {
+	if GetLevel() <= InfoLevel {
 		return logrus.InfoLevel
 	}
-	if level > logrus.DebugLevel {
-		return logrus.DebugLevel
-	}
-	return level
+	return logrus.DebugLevel
 }
 
 // SetupLogs sets up logrus logger for skaffold command line
 func SetupLogs(stdErr io.Writer, level string, timestamp bool, hook logrus.Hook) error {
-	logrus.SetOutput(stdErr)
+	logger.SetOutput(stdErr)
 	lvl, err := logrus.ParseLevel(level)
 	if err != nil {
 		return fmt.Errorf("parsing log level: %w", err)
 	}
-	logrus.SetLevel(lvl)
-	logrus.SetFormatter(&logrus.TextFormatter{
+	logger.SetLevel(lvl)
+	logger.SetFormatter(&logrus.TextFormatter{
 		FullTimestamp: timestamp,
 	})
-	logrus.AddHook(hook)
-	setupStdLog(logrus.StandardLogger(), lvl)
-	setupGGCRLogging(logrus.StandardLogger(), lvl)
+	logger.AddHook(hook)
+	setupStdLog(logger, lvl, stdlog.Default())
+	setupGGCRLogging(logger, lvl)
 	return nil
 }
 
+// AddHook adds a hook to the global Skaffold logger.
+func AddHook(hook logrus.Hook) {
+	logger.AddHook(hook)
+}
+
+// SetLevel sets the global Skaffold logger level.
+func SetLevel(level Level) {
+	logger.SetLevel(logrus.AllLevels[level])
+}
+
+// GetLevel returns the global Skaffold logger level.
+func GetLevel() Level {
+	return AllLevels[logger.GetLevel()]
+}
+
 // setupStdLog writes Go's standard library `log` messages to logrus at Info level.
 //
 // This function uses SetFlags() to standardize the output format.
-//
-// TODO(halvards) Add *stdlog.Logger argument (from stdlog.Default) when the build moves to Go >= 1.16.
-// We can't unit test this function in isolation when we're modifying the singleton stdlog instance.
-func setupStdLog(logger *logrus.Logger, lvl logrus.Level) {
-	stdlog.SetFlags(0)
+func setupStdLog(logger *logrus.Logger, lvl logrus.Level, stdlogger *stdlog.Logger) {
+	stdlogger.SetFlags(0)
 	if lvl >= logrus.InfoLevel {
-		stdlog.SetOutput(logger.WriterLevel(logrus.InfoLevel))
+		stdlogger.SetOutput(logger.WriterLevel(logrus.InfoLevel))
 	}
 }
 
-// setupGGCRLogging enables go-containerregistry logging, mapping its levels to logrus.
+// setupGGCRLogging enables go-containerregistry logging, mapping its levels to our levels.
 //
 // The mapping is:
-// - ggcr Warn -> logrus Error
-// - ggcr Progress -> logrus Info
-// - ggcr Debug -> logrus Trace
+// - ggcr Warn -> Skaffold Error
+// - ggcr Progress -> Skaffold Info
+// - ggcr Debug -> Skaffold Trace
 //
 // The reasons for this mapping are:
 // - `ggcr` defines `Warn` as "non-fatal errors": https://github.com/google/go-containerregistry/blob/main/pkg/logs/logs.go#L24
diff --git a/pkg/skaffold/schema/defaults/defaults.go b/pkg/skaffold/schema/defaults/defaults.go
index c14aa8337..672e04f8d 100644
--- a/pkg/skaffold/schema/defaults/defaults.go
+++ b/pkg/skaffold/schema/defaults/defaults.go
@@ -76,13 +76,6 @@ func Set(c *latestV1.SkaffoldConfig) error {
 		}
 	}
 
-	withLocalBuild(c, func(lb *latestV1.LocalBuild) {
-		// don't set build concurrency if there are no artifacts in the current config
-		if len(c.Build.Artifacts) > 0 {
-			setDefaultConcurrency(lb)
-		}
-	})
-
 	withCloudBuildConfig(c,
 		setDefaultCloudBuildDockerImage,
 		setDefaultCloudBuildMavenImage,
@@ -136,20 +129,6 @@ func defaultToKubectlDeploy(c *latestV1.SkaffoldConfig) {
 	c.Deploy.DeployType.KubectlDeploy = &latestV1.KubectlDeploy{}
 }
 
-func withLocalBuild(c *latestV1.SkaffoldConfig, operations ...func(*latestV1.LocalBuild)) {
-	if local := c.Build.LocalBuild; local != nil {
-		for _, operation := range operations {
-			operation(local)
-		}
-	}
-}
-
-func setDefaultConcurrency(local *latestV1.LocalBuild) {
-	if local.Concurrency == nil {
-		local.Concurrency = &constants.DefaultLocalConcurrency
-	}
-}
-
 func withCloudBuildConfig(c *latestV1.SkaffoldConfig, operations ...func(*latestV1.GoogleCloudBuild)) {
 	if gcb := c.Build.GoogleCloudBuild; gcb != nil {
 		for _, operation := range operations {
diff --git a/pkg/skaffold/util/wrapper_windows.go b/pkg/skaffold/util/wrapper_windows.go
index 27f580736..c7e654fa7 100644
--- a/pkg/skaffold/util/wrapper_windows.go
+++ b/pkg/skaffold/util/wrapper_windows.go
@@ -20,7 +20,7 @@ import (
 	"context"
 	"os/exec"
 
-	"github.com/sirupsen/logrus"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 )
 
 // CreateCommand creates an `exec.Cmd` that is configured to call the
@@ -33,7 +33,7 @@ func (cw CommandWrapper) CreateCommand(ctx context.Context, workingDir string, a
 		for _, extension := range []string{".cmd", ".bat"} {
 			wrapper := cw.Wrapper + extension
 			if wrapperExecutable, err := AbsFile(workingDir, wrapper); err == nil {
-				logrus.Debugf("Using wrapper %s for %s", wrapper, cw.Executable)
+				log.Entry(ctx).Debugf("Using wrapper %s for %s", wrapper, cw.Executable)
 				executable = "cmd"
 				args = append([]string{"/c", wrapperExecutable}, args...)
 				break


Test patch:
diff --git a/integration/binpack/binpack_test.go b/integration/binpack/binpack_test.go
index ca08eae44..9f61bfbe3 100644
--- a/integration/binpack/binpack_test.go
+++ b/integration/binpack/binpack_test.go
@@ -20,15 +20,14 @@ import (
 	"fmt"
 	"testing"
 
-	"github.com/sirupsen/logrus"
-
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 	"github.com/GoogleContainerTools/skaffold/testutil"
 )
 
 func TestPartitions(t *testing.T) {
-	level := logrus.GetLevel()
-	defer logrus.SetLevel(level)
-	logrus.SetLevel(logrus.TraceLevel)
+	level := log.GetLevel()
+	defer log.SetLevel(level)
+	log.SetLevel(log.TraceLevel)
 	partitions, lastPartition := Partitions()
 	testutil.CheckDeepEqual(t, len(partitions), len(timings))
 	var bins []bin
diff --git a/integration/build_dependencies_test.go b/integration/build_dependencies_test.go
index 80a16c0d4..c767aa7dd 100644
--- a/integration/build_dependencies_test.go
+++ b/integration/build_dependencies_test.go
@@ -22,7 +22,6 @@ import (
 	"testing"
 
 	"github.com/google/uuid"
-	"github.com/sirupsen/logrus"
 
 	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
 )
@@ -85,7 +84,7 @@ func TestBuildDependenciesOrder(t *testing.T) {
 				if out, err := skaffold.Build(test.args...).InDir("testdata/build-dependencies").RunWithCombinedOutput(t); err == nil {
 					t.Fatal("expected build to fail")
 				} else if !strings.Contains(string(out), test.failure) {
-					logrus.Info("build output: ", string(out))
+					t.Log("build output: ", string(out))
 					t.Fatalf("build failed but for wrong reason")
 				}
 			}
@@ -152,12 +151,12 @@ func TestBuildDependenciesCache(t *testing.T) {
 
 			for i := 1; i <= 4; i++ {
 				if !contains(test.rebuilt, i) && !strings.Contains(log, fmt.Sprintf("image%d: Found Locally", i)) {
-					logrus.Info("build output: ", string(out))
+					t.Log("build output: ", string(out))
 					t.Fatalf("expected image%d to be cached", i)
 				}
 
 				if contains(test.rebuilt, i) && !strings.Contains(log, fmt.Sprintf("image%d: Not found. Building", i)) {
-					logrus.Info("build output: ", string(out))
+					t.Log("build output: ", string(out))
 					t.Fatalf("expected image%d to be rebuilt", i)
 				}
 			}
diff --git a/integration/build_test.go b/integration/build_test.go
index 81bc292d3..22e315cf7 100644
--- a/integration/build_test.go
+++ b/integration/build_test.go
@@ -25,7 +25,6 @@ import (
 	"time"
 
 	"4d63.com/tz"
-	"github.com/sirupsen/logrus"
 
 	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/jib"
@@ -136,7 +135,7 @@ func TestExpectedBuildFailures(t *testing.T) {
 			if out, err := skaffold.Build(test.args...).InDir(test.dir).RunWithCombinedOutput(t); err == nil {
 				t.Fatal("expected build to fail")
 			} else if !strings.Contains(string(out), test.expected) {
-				logrus.Info("build output: ", string(out))
+				t.Log("build output: ", string(out))
 				t.Fatalf("build failed but for wrong reason")
 			}
 		})
diff --git a/integration/dev_dependencies_test.go b/integration/dev_dependencies_test.go
index c9452babb..a3f7c21f2 100644
--- a/integration/dev_dependencies_test.go
+++ b/integration/dev_dependencies_test.go
@@ -20,7 +20,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/sirupsen/logrus"
 	"k8s.io/apimachinery/pkg/util/wait"
 
 	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
@@ -51,10 +50,10 @@ func TestDev_WithDependencies(t *testing.T) {
 			newDep2 := client.GetDeployment("app2")
 			newDep3 := client.GetDeployment("app3")
 			newDep4 := client.GetDeployment("app4")
-			logrus.Infof("app1 - old gen: %d, new gen: %d", dep1.GetGeneration(), newDep1.GetGeneration())
-			logrus.Infof("app2 - old gen: %d, new gen: %d", dep2.GetGeneration(), newDep2.GetGeneration())
-			logrus.Infof("app3 - old gen: %d, new gen: %d", dep3.GetGeneration(), newDep3.GetGeneration())
-			logrus.Infof("app4 - old gen: %d, new gen: %d", dep4.GetGeneration(), newDep4.GetGeneration())
+			t.Logf("app1 - old gen: %d, new gen: %d", dep1.GetGeneration(), newDep1.GetGeneration())
+			t.Logf("app2 - old gen: %d, new gen: %d", dep2.GetGeneration(), newDep2.GetGeneration())
+			t.Logf("app3 - old gen: %d, new gen: %d", dep3.GetGeneration(), newDep3.GetGeneration())
+			t.Logf("app4 - old gen: %d, new gen: %d", dep4.GetGeneration(), newDep4.GetGeneration())
 			return dep1.GetGeneration() != newDep1.GetGeneration() &&
 				dep2.GetGeneration() != newDep2.GetGeneration() &&
 				dep3.GetGeneration() != newDep3.GetGeneration() &&
diff --git a/integration/dev_test.go b/integration/dev_test.go
index be339f96a..52a7e9a72 100644
--- a/integration/dev_test.go
+++ b/integration/dev_test.go
@@ -28,7 +28,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/sirupsen/logrus"
 	appsv1 "k8s.io/api/apps/v1"
 	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/tools/clientcmd"
@@ -76,7 +75,7 @@ func TestDevNotification(t *testing.T) {
 			// Make sure the old Deployment and the new Deployment are different
 			err := wait.PollImmediate(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
 				newDep := client.GetDeployment(testDev)
-				logrus.Infof("old gen: %d, new gen: %d", dep.GetGeneration(), newDep.GetGeneration())
+				t.Logf("old gen: %d, new gen: %d", dep.GetGeneration(), newDep.GetGeneration())
 				return dep.GetGeneration() != newDep.GetGeneration(), nil
 			})
 			failNowIfError(t, err)
@@ -248,7 +247,7 @@ func verifyDeployment(t *testing.T, entries chan *proto.LogEntry, client *NSKube
 	// Make sure the old Deployment and the new Deployment are different
 	err = wait.Poll(time.Millisecond*500, 1*time.Minute, func() (bool, error) {
 		newDep := client.GetDeployment(testDev)
-		logrus.Infof("old gen: %d, new gen: %d", dep.GetGeneration(), newDep.GetGeneration())
+		t.Logf("old gen: %d, new gen: %d", dep.GetGeneration(), newDep.GetGeneration())
 		return dep.GetGeneration() != newDep.GetGeneration(), nil
 	})
 	failNowIfError(t, err)
diff --git a/integration/modules_test.go b/integration/modules_test.go
index f48763347..760650c56 100644
--- a/integration/modules_test.go
+++ b/integration/modules_test.go
@@ -20,7 +20,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/sirupsen/logrus"
 	"k8s.io/apimachinery/pkg/util/wait"
 
 	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
@@ -49,9 +48,9 @@ func TestModules_BuildDependency(t *testing.T) {
 			newDep1 := client.GetDeployment("app1")
 			newDep2 := client.GetDeployment("app2")
 			newDep3 := client.GetDeployment("app3")
-			logrus.Infof("app1 - old gen: %d, new gen: %d", dep1.GetGeneration(), newDep1.GetGeneration())
-			logrus.Infof("app2 - old gen: %d, new gen: %d", dep2.GetGeneration(), newDep2.GetGeneration())
-			logrus.Infof("app3 - old gen: %d, new gen: %d", dep3.GetGeneration(), newDep3.GetGeneration())
+			t.Logf("app1 - old gen: %d, new gen: %d", dep1.GetGeneration(), newDep1.GetGeneration())
+			t.Logf("app2 - old gen: %d, new gen: %d", dep2.GetGeneration(), newDep2.GetGeneration())
+			t.Logf("app3 - old gen: %d, new gen: %d", dep3.GetGeneration(), newDep3.GetGeneration())
 			return dep1.GetGeneration() != newDep1.GetGeneration() &&
 				dep2.GetGeneration() != newDep2.GetGeneration() &&
 				dep3.GetGeneration() != newDep3.GetGeneration(), nil
diff --git a/integration/port_forward_test.go b/integration/port_forward_test.go
index 54b961cff..e346ed335 100644
--- a/integration/port_forward_test.go
+++ b/integration/port_forward_test.go
@@ -19,14 +19,13 @@ package integration
 import (
 	"testing"
 
-	"github.com/sirupsen/logrus"
-
 	"github.com/GoogleContainerTools/skaffold/integration/skaffold"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl"
 	kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/portforward"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
 )
 
@@ -53,8 +52,7 @@ func TestPortForward(t *testing.T) {
 			},
 		}, "")
 
-		logrus.SetLevel(logrus.TraceLevel)
-		portforward.SimulateDevCycle(t, kubectlCLI, ns.Name)
+		portforward.SimulateDevCycle(t, kubectlCLI, ns.Name, log.TraceLevel)
 	}
 }
 
diff --git a/integration/testdata/diagnose/multi-config/diagnose.tmpl b/integration/testdata/diagnose/multi-config/diagnose.tmpl
index d6e64e61f..b3d020563 100644
--- a/integration/testdata/diagnose/multi-config/diagnose.tmpl
+++ b/integration/testdata/diagnose/multi-config/diagnose.tmpl
@@ -10,8 +10,7 @@ build:
       dockerfile: Dockerfile
   tagPolicy:
     gitCommit: {}
-  local:
-    concurrency: 1
+  local: {}
 deploy:
   kubectl:
     manifests:
@@ -31,8 +30,7 @@ build:
       dockerfile: Dockerfile
   tagPolicy:
     gitCommit: {}
-  local:
-    concurrency: 1
+  local: {}
 deploy:
   kubectl:
     manifests:
@@ -50,8 +48,7 @@ build:
       dockerfile: Dockerfile
   tagPolicy:
     gitCommit: {}
-  local:
-    concurrency: 1
+  local: {}
 deploy:
   kubectl:
     manifests:
diff --git a/integration/testdata/diagnose/temp-config/diagnose.tmpl b/integration/testdata/diagnose/temp-config/diagnose.tmpl
index 5dbba2eb2..278c618cb 100644
--- a/integration/testdata/diagnose/temp-config/diagnose.tmpl
+++ b/integration/testdata/diagnose/temp-config/diagnose.tmpl
@@ -8,8 +8,7 @@ build:
       dockerfile: Dockerfile
   tagPolicy:
     gitCommit: {}
-  local:
-    concurrency: 1
+  local: {}
 deploy:
   kubectl:
     manifests:
diff --git a/pkg/skaffold/build/builder_mux_test.go b/pkg/skaffold/build/builder_mux_test.go
index 49516ff8c..f79b17d98 100644
--- a/pkg/skaffold/build/builder_mux_test.go
+++ b/pkg/skaffold/build/builder_mux_test.go
@@ -92,6 +92,93 @@ func TestNewBuilderMux(t *testing.T) {
 	}
 }
 
+func TestGetConcurrency(t *testing.T) {
+	tests := []struct {
+		description         string
+		pbs                 []PipelineBuilder
+		cliConcurrency      int
+		expectedConcurrency int
+	}{
+		{
+			description: "default concurrency - builder and cli concurrency unset.",
+			pbs: []PipelineBuilder{
+				&mockPipelineBuilder{concurrency: nil, builderType: "local"},
+				&mockPipelineBuilder{concurrency: nil, builderType: "gcb"},
+			},
+			cliConcurrency:      -1,
+			expectedConcurrency: 1,
+		},
+		{
+			description: "builder concurrency set to less than cli concurrency",
+			pbs: []PipelineBuilder{
+				&mockPipelineBuilder{concurrency: util.IntPtr(1), builderType: "local"},
+				&mockPipelineBuilder{concurrency: util.IntPtr(1), builderType: "local"},
+				&mockPipelineBuilder{concurrency: nil, builderType: "gcb"},
+			},
+			cliConcurrency:      2,
+			expectedConcurrency: 2,
+		},
+		{
+			description: "builder concurrency set",
+			pbs: []PipelineBuilder{
+				&mockPipelineBuilder{concurrency: util.IntPtr(2), builderType: "local"},
+				&mockPipelineBuilder{concurrency: util.IntPtr(2), builderType: "local"},
+				// As per docs https://github.com/GoogleContainerTools/skaffold/blob/dbd18994955f5805e80c6354ed0fd424ec4d987b/pkg/skaffold/schema/v2beta26/config.go#L287
+				// nil concurrency defaults to 1
+				&mockPipelineBuilder{concurrency: nil, builderType: "local"},
+			},
+			cliConcurrency:      -1,
+			expectedConcurrency: 1,
+		},
+		{
+			description: "builder concurrency set to 0 and cli concurrency set to 1",
+			pbs: []PipelineBuilder{
+				// build all in parallel
+				&mockPipelineBuilder{concurrency: util.IntPtr(0), builderType: "local"},
+				&mockPipelineBuilder{concurrency: util.IntPtr(0), builderType: "gcb"},
+			},
+			cliConcurrency:      1,
+			expectedConcurrency: 1,
+		},
+		{
+			description: "builder concurrency set to 0 and cli concurrency unset",
+			pbs: []PipelineBuilder{
+				// build all in parallel
+				&mockPipelineBuilder{concurrency: util.IntPtr(0), builderType: "local"},
+				&mockPipelineBuilder{concurrency: util.IntPtr(0), builderType: "gcb"},
+			},
+			cliConcurrency:      -1,
+			expectedConcurrency: 0,
+		},
+		{
+			description: "builder concurrency set to default 0 for gcb",
+			pbs: []PipelineBuilder{
+				// build all in parallel
+				&mockPipelineBuilder{concurrency: util.IntPtr(0), builderType: "gcb"},
+				&mockPipelineBuilder{concurrency: util.IntPtr(0), builderType: "gcb"},
+			},
+			cliConcurrency:      -1,
+			expectedConcurrency: 0,
+		},
+		{
+			description: "min non-zero concurrency",
+			pbs: []PipelineBuilder{
+				&mockPipelineBuilder{concurrency: util.IntPtr(0), builderType: "local"},
+				&mockPipelineBuilder{concurrency: util.IntPtr(3), builderType: "gcb"},
+				&mockPipelineBuilder{concurrency: util.IntPtr(2), builderType: "gcb"},
+			},
+			cliConcurrency:      -1,
+			expectedConcurrency: 2,
+		},
+	}
+	for _, test := range tests {
+		testutil.Run(t, test.description, func(t *testutil.T) {
+			actual := getConcurrency(test.pbs, test.cliConcurrency)
+			t.CheckDeepEqual(test.expectedConcurrency, actual)
+		})
+	}
+}
+
 type mockConfig struct {
 	pipelines []latestV1.Pipeline
 	optRepo   string
@@ -109,7 +196,7 @@ func (m *mockConfig) MultiLevelRepo() *bool { return nil }
 func (m *mockConfig) BuildConcurrency() int { return -1 }
 
 type mockPipelineBuilder struct {
-	concurrency int
+	concurrency *int
 	builderType string
 }
 
@@ -121,7 +208,7 @@ func (m *mockPipelineBuilder) Build(ctx context.Context, out io.Writer, artifact
 
 func (m *mockPipelineBuilder) PostBuild(ctx context.Context, out io.Writer) error { return nil }
 
-func (m *mockPipelineBuilder) Concurrency() int { return m.concurrency }
+func (m *mockPipelineBuilder) Concurrency() *int { return m.concurrency }
 
 func (m *mockPipelineBuilder) Prune(context.Context, io.Writer) error { return nil }
 
@@ -132,15 +219,11 @@ func (m *mockPipelineBuilder) SupportedPlatforms() platform.Matcher { return pla
 func newMockPipelineBuilder(p latestV1.Pipeline) (PipelineBuilder, error) {
 	switch {
 	case p.Build.BuildType.LocalBuild != nil:
-		c := 0
-		if p.Build.LocalBuild.Concurrency != nil {
-			c = *p.Build.LocalBuild.Concurrency
-		}
-		return &mockPipelineBuilder{builderType: "local", concurrency: c}, nil
+		return &mockPipelineBuilder{builderType: "local", concurrency: p.Build.LocalBuild.Concurrency}, nil
 	case p.Build.BuildType.Cluster != nil:
-		return &mockPipelineBuilder{builderType: "cluster", concurrency: p.Build.Cluster.Concurrency}, nil
+		return &mockPipelineBuilder{builderType: "cluster", concurrency: util.IntPtr(p.Build.Cluster.Concurrency)}, nil
 	case p.Build.BuildType.GoogleCloudBuild != nil:
-		return &mockPipelineBuilder{builderType: "gcb", concurrency: p.Build.GoogleCloudBuild.Concurrency}, nil
+		return &mockPipelineBuilder{builderType: "gcb", concurrency: util.IntPtr(p.Build.GoogleCloudBuild.Concurrency)}, nil
 	default:
 		return nil, errors.New("invalid config")
 	}
diff --git a/pkg/skaffold/instrumentation/export_test.go b/pkg/skaffold/instrumentation/export_test.go
index 3a32eca37..b90677820 100644
--- a/pkg/skaffold/instrumentation/export_test.go
+++ b/pkg/skaffold/instrumentation/export_test.go
@@ -81,6 +81,7 @@ func TestExportMetrics(t *testing.T) {
 		ResolvedBuildTargetPlatforms: []string{"linux/amd64"},
 		HelmReleasesCount:            2,
 		DevIterations:                []devIteration{{"sync", 0}, {"build", 400}, {"build", 0}, {"sync", 200}, {"deploy", 0}},
+		ResourceFilters:              []resourceFilter{{"schema", "allow"}, {"schema", "deny"}, {"cli-flag", "allow"}, {"cli-flag", "deny"}},
 		ErrorCode:                    proto.StatusCode_BUILD_CANCELLED,
 		StartTime:                    startTime.Add(time.Hour * 24 * 30),
 		Duration:                     time.Minute * 2,
@@ -404,6 +405,7 @@ func checkOutput(t *testutil.T, meters []skaffoldMeter, b []byte) {
 	buildDeps := make(map[interface{}]int)
 	helmReleases := make(map[interface{}]int)
 	devIterations := make(map[interface{}]int)
+	resourceFilters := make(map[interface{}]int)
 	deployers := make(map[interface{}]int)
 	enumFlags := make(map[interface{}]int)
 	platform := make(map[interface{}]int)
@@ -412,7 +414,7 @@ func checkOutput(t *testutil.T, meters []skaffoldMeter, b []byte) {
 	nodePlatforms := make(map[interface{}]int)
 
 	testMaps := []map[interface{}]int{
-		platform, osCount, versionCount, archCount, durationCount, commandCount, errorCount, builders, devIterations, deployers}
+		platform, osCount, versionCount, archCount, durationCount, commandCount, errorCount, builders, devIterations, resourceFilters, deployers}
 
 	for _, meter := range meters {
 		osCount[meter.OS]++
@@ -449,6 +451,11 @@ func checkOutput(t *testutil.T, meters []skaffoldMeter, b []byte) {
 				deployers[d]++
 			}
 		}
+		if doesDeploy.Contains(meter.Command) || meter.Command == "render" {
+			for _, filterI := range meter.ResourceFilters {
+				resourceFilters[filterI]++
+			}
+		}
 	}
 
 	var lines []*line
@@ -481,6 +488,8 @@ func checkOutput(t *testutil.T, meters []skaffoldMeter, b []byte) {
 		case "dev/iterations", "debug/iterations":
 			e := l.Labels["error"]
 			devIterations[devIteration{l.Labels["intent"], proto.StatusCode(proto.StatusCode_value[e])}]--
+		case "resource-filters":
+			resourceFilters[resourceFilter{l.Labels["source"], l.Labels["type"]}]--
 		case "errors":
 			e := l.Labels["error"]
 			errorCount[e]--
diff --git a/pkg/skaffold/kubernetes/context/context_test.go b/pkg/skaffold/kubernetes/context/context_test.go
index c8c05ea04..75b26e96d 100644
--- a/pkg/skaffold/kubernetes/context/context_test.go
+++ b/pkg/skaffold/kubernetes/context/context_test.go
@@ -21,8 +21,7 @@ import (
 	"sync"
 	"testing"
 
-	"github.com/sirupsen/logrus"
-
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 	"github.com/GoogleContainerTools/skaffold/testutil"
 )
 
@@ -149,7 +148,7 @@ func TestGetRestClientConfig(t *testing.T) {
 	})
 
 	testutil.Run(t, "kube-config immutability", func(t *testutil.T) {
-		logrus.SetLevel(logrus.InfoLevel)
+		log.SetLevel(log.InfoLevel)
 		kubeConfig := t.TempFile("config", []byte(validKubeConfig))
 		kubeContext = clusterBarContext
 		t.SetEnvs(map[string]string{"KUBECONFIG": kubeConfig})
diff --git a/pkg/skaffold/output/log/log_test.go b/pkg/skaffold/output/log/log_test.go
index a8bd3bd6c..909c487f6 100644
--- a/pkg/skaffold/output/log/log_test.go
+++ b/pkg/skaffold/output/log/log_test.go
@@ -82,8 +82,8 @@ func TestKanikoLogLevel(t *testing.T) {
 		{logrusLevel: logrus.PanicLevel, expected: logrus.InfoLevel},
 	}
 	for _, test := range tests {
-		defer func(l logrus.Level) { logrus.SetLevel(l) }(logrus.GetLevel())
-		logrus.SetLevel(test.logrusLevel)
+		defer func(l logrus.Level) { logger.SetLevel(l) }(logger.GetLevel())
+		logger.SetLevel(test.logrusLevel)
 
 		kanikoLevel := KanikoLogLevel()
 
diff --git a/pkg/skaffold/parser/config_test.go b/pkg/skaffold/parser/config_test.go
index bd9c33854..913a723cb 100644
--- a/pkg/skaffold/parser/config_test.go
+++ b/pkg/skaffold/parser/config_test.go
@@ -70,16 +70,11 @@ func createCfg(name string, imageName string, workspace string, requires []lates
 			Artifacts: []*latestV1.Artifact{{ImageName: imageName, ArtifactType: latestV1.ArtifactType{
 				DockerArtifact: &latestV1.DockerArtifact{DockerfilePath: "Dockerfile"}}, Workspace: workspace}}, TagPolicy: latestV1.TagPolicy{
 				GitTagger: &latestV1.GitTagger{}}, BuildType: latestV1.BuildType{
-				LocalBuild: &latestV1.LocalBuild{Concurrency: concurrency()},
+				LocalBuild: &latestV1.LocalBuild{},
 			}}, Deploy: latestV1.DeployConfig{Logs: latestV1.LogsConfig{Prefix: "container"}}},
 	}
 }
 
-func concurrency() *int {
-	c := 1
-	return &c
-}
-
 type document struct {
 	path    string
 	configs []mockCfg
diff --git a/pkg/skaffold/runner/timings_test.go b/pkg/skaffold/runner/timings_test.go
index 3d050c02a..7fe62409a 100644
--- a/pkg/skaffold/runner/timings_test.go
+++ b/pkg/skaffold/runner/timings_test.go
@@ -29,6 +29,7 @@ import (
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
+	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
 	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
 	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/tag"
@@ -107,7 +108,8 @@ func TestTimingsBuild(t *testing.T) {
 	}
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
-			hook := logrustest.NewGlobal()
+			hook := &logrustest.Hook{}
+			log.AddHook(hook)
 
 			b := &mockBuilder{err: test.shouldErr}
 			builder, _, _ := WithTimings(b, nil, nil, false)
@@ -143,7 +145,8 @@ func TestTimingsPrune(t *testing.T) {
 	}
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
-			hook := logrustest.NewGlobal()
+			hook := &logrustest.Hook{}
+			log.AddHook(hook)
 
 			b := &mockBuilder{err: test.shouldErr}
 			builder, _, _ := WithTimings(b, nil, nil, false)
@@ -179,7 +182,8 @@ func TestTimingsTest(t *testing.T) {
 	}
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
-			hook := logrustest.NewGlobal()
+			hook := &logrustest.Hook{}
+			log.AddHook(hook)
 
 			tt := &mockTester{err: test.shouldErr}
 			_, tester, _ := WithTimings(nil, tt, nil, false)
@@ -215,7 +219,8 @@ func TestTimingsDeploy(t *testing.T) {
 	}
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
-			hook := logrustest.NewGlobal()
+			hook := &logrustest.Hook{}
+			log.AddHook(hook)
 
 			d := &mockDeployer{err: test.shouldErr}
 			_, _, deployer := WithTimings(nil, nil, d, false)
@@ -251,7 +256,8 @@ func TestTimingsCleanup(t *testing.T) {
 	}
 	for _, test := range tests {
 		testutil.Run(t, test.description, func(t *testutil.T) {
-			hook := logrustest.NewGlobal()
+			hook := &logrustest.Hook{}
+			log.AddHook(hook)
 
 			d := &mockDeployer{err: test.shouldErr}
 			_, _, deployer := WithTimings(nil, nil, d, false)
diff --git a/pkg/skaffold/schema/defaults/defaults_test.go b/pkg/skaffold/schema/defaults/defaults_test.go
index a34d178be..94e85f425 100644
--- a/pkg/skaffold/schema/defaults/defaults_test.go
+++ b/pkg/skaffold/schema/defaults/defaults_test.go
@@ -319,7 +319,6 @@ func TestSetDefaultsOnLocalBuild(t *testing.T) {
 	err = Set(cfg2)
 	testutil.CheckError(t, false, err)
 	SetDefaultDeployer(cfg2)
-	testutil.CheckDeepEqual(t, 1, *cfg2.Build.LocalBuild.Concurrency)
 }
 
 func TestSetPortForwardLocalPort(t *testing.T) {
diff --git a/pkg/skaffold/schema/versions_test.go b/pkg/skaffold/schema/versions_test.go
index 03a2a5485..ce03cc9dd 100644
--- a/pkg/skaffold/schema/versions_test.go
+++ b/pkg/skaffold/schema/versions_test.go
@@ -493,9 +493,6 @@ func withLocalBuild(ops ...func(*latestV1.BuildConfig)) func(*latestV1.SkaffoldC
 		for _, op := range ops {
 			op(&b)
 		}
-		if len(b.Artifacts) > 0 {
-			b.LocalBuild.Concurrency = &constants.DefaultLocalConcurrency
-		}
 		cfg.Build = b
 	}
 }
diff --git a/pkg/skaffold/util/port_test.go b/pkg/skaffold/util/port_test.go
index 34519390d..1f75bc075 100644
--- a/pkg/skaffold/util/port_test.go
+++ b/pkg/skaffold/util/port_test.go
@@ -72,6 +72,6 @@ func TestGetAvailablePort(t *testing.T) {
 	wg.Wait()
 
 	for port, err := range errors {
-		t.Errorf("available port (%d) couldn't be used: %w", port, err)
+		t.Errorf("available port (%d) couldn't be used: %v", port, err)
 	}
 }