Repository: kubernetes-sigs/cluster-api-provider-aws
Pull request #3776 (2022-10-13 12:47:40)

Unify the logger interface in CAPA
**What type of PR is this?**

/kind feature

**What this PR does / why we need it**:

Create a unified Logger interface which developers can use to log debug and trace information. It's just a soft wrapper around logr.Logger.

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues/3152

**Special notes for your reviewer**:

**Checklist**:
<!-- Put an "X" character inside the brackets of each completed task. Some may be optional depending on the PR in which case these can be deleted -->

- [x] squashed commits
- [ ] includes documentation
- [ ] adds unit tests
- [ ] adds or updates e2e tests


k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please assign sedefsavas for approval by writing `/assign @sedefsavas` in a comment. For more information see:[The Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fcluster-api-provider-aws).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/cluster-api-provider-aws/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["sedefsavas"]} -->
k8s-ci-robot: @Skarlso: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-verify | 028074686b7aecfb1207854074e5b05d8974d00c | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-verify/1580540785563013120) | true | `/test pull-cluster-api-provider-aws-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @Skarlso: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-verify | 028074686b7aecfb1207854074e5b05d8974d00c | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-verify/1580540785563013120) | true | `/test pull-cluster-api-provider-aws-verify`
pull-cluster-api-provider-aws-test | 028074686b7aecfb1207854074e5b05d8974d00c | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-test/1580540785420406784) | true | `/test pull-cluster-api-provider-aws-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @Skarlso: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-verify | 028074686b7aecfb1207854074e5b05d8974d00c | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-verify/1580540785563013120) | true | `/test pull-cluster-api-provider-aws-verify`
pull-cluster-api-provider-aws-test | 3d3a29cf362388e8cbf0e194d7099401c76e1b69 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-test/1580541661065252864) | true | `/test pull-cluster-api-provider-aws-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @Skarlso: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-verify | 028074686b7aecfb1207854074e5b05d8974d00c | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-verify/1580540785563013120) | true | `/test pull-cluster-api-provider-aws-verify`
pull-cluster-api-provider-aws-test | 8790b322390d9cdf200a6e6ebf2b5914610b5590 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-test/1580543821714821120) | true | `/test pull-cluster-api-provider-aws-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @Skarlso: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-test | 8790b322390d9cdf200a6e6ebf2b5914610b5590 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-test/1580543821714821120) | true | `/test pull-cluster-api-provider-aws-test`
pull-cluster-api-provider-aws-verify | 8790b322390d9cdf200a6e6ebf2b5914610b5590 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-verify/1580543821849038848) | true | `/test pull-cluster-api-provider-aws-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @Skarlso: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-verify | 8790b322390d9cdf200a6e6ebf2b5914610b5590 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-verify/1580543821849038848) | true | `/test pull-cluster-api-provider-aws-verify`
pull-cluster-api-provider-aws-test | f7aa27c518546ed8fc8c1765ad2aaa63aee68a85 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-test/1580547990852997120) | true | `/test pull-cluster-api-provider-aws-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
Skarlso: @richardcase When you review this, please take note of the Debug vs Trace messages. I wasn't sure which belonged to what category at times. :D so please take a look at them and see if they are at the correct level. :) Thanks! 👍 
Skarlso: Some output with this logging interface:

```
Tags:map[]} {ID:subnet-01f36f8d65e926fd6 CidrBlock:10.0.64.0/18 IPv6CidrBlock: AvailabilityZone:eu-central-1a IsPublic:false IsIPv6:false RouteTableID:<nil> NatGatewayID:<nil> Tags:map[]} {ID:subnet-0711ca7fb292bc564 CidrBlock:10.0.16.0/20 IPv6CidrBlock: AvailabilityZone:eu-central-1b IsPublic:true IsIPv6:false RouteTableID:<nil> NatGatewayID:<nil> Tags:map[]} {ID:subnet-090dc1ec75941ec5b CidrBlock:10.0.128.0/18 IPv6CidrBlock: AvailabilityZone:eu-central-1b IsPublic:false IsIPv6:false RouteTableID:<nil> NatGatewayID:<nil> Tags:map[]} {ID:subnet-06560f282e7a2c07b CidrBlock:10.0.32.0/20 IPv6CidrBlock: AvailabilityZone:eu-central-1c IsPublic:true IsIPv6:false RouteTableID:<nil> NatGatewayID:<nil> Tags:map[]} {ID:subnet-0b65deb3c514aa670 CidrBlock:10.0.192.0/18 IPv6CidrBlock: AvailabilityZone:eu-central-1c IsPublic:false IsIPv6:false RouteTableID:<nil> NatGatewayID:<nil> Tags:map[]}]
I1016 07:58:15.420742      12 logger.go:56] "Reconciling internet gateways"
I1016 07:58:15.636314      12 logger.go:52] "Created Internet gateway for VPC" internet-gateway-id="igw-049f01e0f647b8455" vpc-id="vpc-0fcc6fc7ce974c4a0"
I1016 07:58:15.636453      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulCreateInternetGateway" message="Created new managed Internet Gateway \"igw-049f01e0f647b8455\""
I1016 07:58:15.636796      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulCreateInternetGateway" message="Created new managed Internet Gateway \"igw-049f01e0f647b8455\""
I1016 07:58:15.790775      12 logger.go:56] "attached internet gateway to VPC" internet-gateway-id="igw-049f01e0f647b8455" vpc-id="vpc-0fcc6fc7ce974c4a0"
I1016 07:58:15.790840      12 logger.go:56] "Reconciling NAT gateways"
I1016 07:58:15.791063      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulAttachInternetGateway" message="Internet Gateway \"igw-049f01e0f647b8455\" attached to VPC \"vpc-0fcc6fc7ce974c4a0\""
I1016 07:58:15.791110      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulAttachInternetGateway" message="Internet Gateway \"igw-049f01e0f647b8455\" attached to VPC \"vpc-0fcc6fc7ce974c4a0\""
I1016 07:58:15.915308      12 http.go:96] "controller-runtime/webhook/webhooks: received request" webhook="/mutate-controlplane-cluster-x-k8s-io-v1beta2-awsmanagedcontrolplane" UID=fd327d65-2309-4a55-b453-017184219a56 kind="controlplane.cluster.x-k8s.io/v1beta2, Kind=AWSManagedControlPlane" resource={Group:controlplane.cluster.x-k8s.io Version:v1beta2 Resource:awsmanagedcontrolplanes}
I1016 07:58:15.915686      12 logr.go:261] "awsmanagedcontrolplane-resource: AWSManagedControlPlane setting defaults" control-plane="default/managed-cluster-control-plane"
I1016 07:58:15.916635      12 http.go:143] "controller-runtime/webhook/webhooks: wrote response" webhook="/mutate-controlplane-cluster-x-k8s-io-v1beta2-awsmanagedcontrolplane" code=200 reason= UID=fd327d65-2309-4a55-b453-017184219a56 allowed=true
I1016 07:58:15.922013      12 http.go:96] "controller-runtime/webhook/webhooks: received request" webhook="/validate-controlplane-cluster-x-k8s-io-v1beta2-awsmanagedcontrolplane" UID=0459f7e1-83ef-44fe-9f82-979afb5947fb kind="controlplane.cluster.x-k8s.io/v1beta2, Kind=AWSManagedControlPlane" resource={Group:controlplane.cluster.x-k8s.io Version:v1beta2 Resource:awsmanagedcontrolplanes}
I1016 07:58:15.922540      12 logr.go:261] "awsmanagedcontrolplane-resource: AWSManagedControlPlane validate update" control-plane="default/managed-cluster-control-plane"
I1016 07:58:15.922703      12 http.go:143] "controller-runtime/webhook/webhooks: wrote response" webhook="/validate-controlplane-cluster-x-k8s-io-v1beta2-awsmanagedcontrolplane" code=200 reason= UID=0459f7e1-83ef-44fe-9f82-979afb5947fb allowed=true
I1016 07:58:17.366588      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulCreateNATGateway" message="Created new NAT Gateway \"nat-0ccac7e239d4d3b01\""
I1016 07:58:17.366589      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulCreateNATGateway" message="Created new NAT Gateway \"nat-0ccac7e239d4d3b01\""
I1016 07:58:17.470873      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulCreateNATGateway" message="Created new NAT Gateway \"nat-0711abc19de6ef624\""
I1016 07:58:17.470893      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulCreateNATGateway" message="Created new NAT Gateway \"nat-0711abc19de6ef624\""
I1016 07:58:17.550041      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulCreateNATGateway" message="Created new NAT Gateway \"nat-0dab5ddae2b8a8e57\""
I1016 07:58:17.550132      12 recorder.go:103] "events: Normal" object={Kind:AWSManagedControlPlane Namespace:default Name:managed-cluster-control-plane UID:c6bc345c-a26b-4d5d-8dc4-fb4f5568e515 APIVersion:controlplane.cluster.x-k8s.io/v1beta2 ResourceVersion:1322 FieldPath:} reason="SuccessfulCreateNATGateway" message="Created new NAT Gateway \"nat-0dab5ddae2b8a8e57\""
```


k8s-ci-robot: @Skarlso: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please ask for approval from skarlso by writing `/assign @skarlso` in a comment. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fcluster-api-provider-aws).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/cluster-api-provider-aws/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["skarlso"]} -->
k8s-ci-robot: @Skarlso: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-build | c5f2f3354345e387b71c9e60514cfa2aa377cf16 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-build/1582992840672153600) | true | `/test pull-cluster-api-provider-aws-build`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
Skarlso: Damn.......:D
k8s-ci-robot: @Skarlso: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-build | f9fb1c55b528a1be3df8eb230db25803ee1740eb | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-build/1583796903584206848) | true | `/test pull-cluster-api-provider-aws-build`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @Skarlso: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-build | f9fb1c55b528a1be3df8eb230db25803ee1740eb | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-build/1583796903584206848) | true | `/test pull-cluster-api-provider-aws-build`
pull-cluster-api-provider-aws-test | f9fb1c55b528a1be3df8eb230db25803ee1740eb | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-test/1583796903500320768) | true | `/test pull-cluster-api-provider-aws-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @Skarlso: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cluster-api-provider-aws-build | f9fb1c55b528a1be3df8eb230db25803ee1740eb | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-build/1583796903584206848) | true | `/test pull-cluster-api-provider-aws-build`
pull-cluster-api-provider-aws-test | f9fb1c55b528a1be3df8eb230db25803ee1740eb | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-test/1583796903500320768) | true | `/test pull-cluster-api-provider-aws-test`
pull-cluster-api-provider-aws-verify | f9fb1c55b528a1be3df8eb230db25803ee1740eb | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cluster-api-provider-aws/3776/pull-cluster-api-provider-aws-verify/1583796903621955584) | true | `/test pull-cluster-api-provider-aws-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cluster-api-provider-aws&pr=3776). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ASkarlso). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cluster-api-provider-aws/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/bootstrap/eks/controllers/eksconfig_controller.go b/bootstrap/eks/controllers/eksconfig_controller.go
index e05b17a9c..893742669 100644
--- a/bootstrap/eks/controllers/eksconfig_controller.go
+++ b/bootstrap/eks/controllers/eksconfig_controller.go
@@ -37,6 +37,7 @@ import (
 	eksbootstrapv1 "sigs.k8s.io/cluster-api-provider-aws/v2/bootstrap/eks/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/bootstrap/eks/internal/userdata"
 	ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/eks/api/v1beta2"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	bsutil "sigs.k8s.io/cluster-api/bootstrap/util"
 	expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
@@ -63,7 +64,7 @@ type EKSConfigReconciler struct {
 // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete;
 
 func (r *EKSConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, rerr error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	// get EKSConfig
 	config := &eksbootstrapv1.EKSConfig{}
@@ -142,7 +143,7 @@ func (r *EKSConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
 }
 
 func (r *EKSConfigReconciler) joinWorker(ctx context.Context, cluster *clusterv1.Cluster, config *eksbootstrapv1.EKSConfig) (ctrl.Result, error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	if config.Status.DataSecretName != nil {
 		secretKey := client.ObjectKey{Namespace: config.Namespace, Name: *config.Status.DataSecretName}
@@ -238,7 +239,7 @@ func (r *EKSConfigReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Man
 	b := ctrl.NewControllerManagedBy(mgr).
 		For(&eksbootstrapv1.EKSConfig{}).
 		WithOptions(option).
-		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
+		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(logger.FromContext(ctx).GetLogger(), r.WatchFilterValue)).
 		Watches(
 			&source.Kind{Type: &clusterv1.Machine{}},
 			handler.EnqueueRequestsFromMapFunc(r.MachineToBootstrapMapFunc),
@@ -259,7 +260,7 @@ func (r *EKSConfigReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Man
 	err = c.Watch(
 		&source.Kind{Type: &clusterv1.Cluster{}},
 		handler.EnqueueRequestsFromMapFunc((r.ClusterToEKSConfigs)),
-		predicates.ClusterUnpausedAndInfrastructureReady(ctrl.LoggerFrom(ctx)),
+		predicates.ClusterUnpausedAndInfrastructureReady(logger.FromContext(ctx).GetLogger()),
 	)
 	if err != nil {
 		return errors.Wrap(err, "failed adding watch for Clusters to controller manager")
@@ -271,7 +272,7 @@ func (r *EKSConfigReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Man
 // storeBootstrapData creates a new secret with the data passed in as input,
 // sets the reference in the configuration status and ready to true.
 func (r *EKSConfigReconciler) storeBootstrapData(ctx context.Context, cluster *clusterv1.Cluster, config *eksbootstrapv1.EKSConfig, data []byte) error {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	// as secret creation and scope.Config status patch are not atomic operations
 	// it is possible that secret creation happens but the config.Status patches are not applied
@@ -296,7 +297,7 @@ func (r *EKSConfigReconciler) storeBootstrapData(ctx context.Context, cluster *c
 		if updated {
 			log.Info("updated bootstrap data secret for EKSConfig", "secret", klog.KObj(secret))
 		} else {
-			log.V(4).Info("no change in bootstrap data secret for EKSConfig", "secret", klog.KObj(secret))
+			log.Trace("no change in bootstrap data secret for EKSConfig", "secret", klog.KObj(secret))
 		}
 	}
 
diff --git a/controllers/awscluster_controller.go b/controllers/awscluster_controller.go
index 7ab86c9da..b68eb8fb0 100644
--- a/controllers/awscluster_controller.go
+++ b/controllers/awscluster_controller.go
@@ -22,7 +22,6 @@ import (
 	"net"
 	"time"
 
-	"github.com/go-logr/logr"
 	"github.com/google/go-cmp/cmp"
 	"github.com/pkg/errors"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -50,6 +49,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/network"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/s3"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/securitygroup"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	infrautilconditions "sigs.k8s.io/cluster-api-provider-aws/v2/util/conditions"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util"
@@ -130,7 +130,7 @@ func (r *AWSClusterReconciler) getSecurityGroupService(scope scope.ClusterScope)
 // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsclustercontrolleridentities,verbs=get;list;watch;create;
 
 func (r *AWSClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	// Fetch the AWSCluster instance
 	awsCluster := &infrav1.AWSCluster{}
@@ -180,7 +180,7 @@ func (r *AWSClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
 	// Create the scope.
 	clusterScope, err := scope.NewClusterScope(scope.ClusterScopeParams{
 		Client:         r.Client,
-		Logger:         &log,
+		Logger:         log,
 		Cluster:        cluster,
 		AWSCluster:     awsCluster,
 		ControllerName: "awscluster",
@@ -356,11 +356,11 @@ func (r *AWSClusterReconciler) reconcileNormal(clusterScope *scope.ClusterScope)
 }
 
 func (r *AWSClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 	controller, err := ctrl.NewControllerManagedBy(mgr).
 		WithOptions(options).
 		For(&infrav1.AWSCluster{}).
-		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log, r.WatchFilterValue)).
+		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log.GetLogger(), r.WatchFilterValue)).
 		WithEventFilter(
 			predicate.Funcs{
 				// Avoid reconciling if the event triggering the reconciliation is related to incremental status updates
@@ -383,7 +383,7 @@ func (r *AWSClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma
 				},
 			},
 		).
-		WithEventFilter(predicates.ResourceIsNotExternallyManaged(log)).
+		WithEventFilter(predicates.ResourceIsNotExternallyManaged(log.GetLogger())).
 		Build(r)
 	if err != nil {
 		return errors.Wrap(err, "error creating controller")
@@ -392,11 +392,11 @@ func (r *AWSClusterReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma
 	return controller.Watch(
 		&source.Kind{Type: &clusterv1.Cluster{}},
 		handler.EnqueueRequestsFromMapFunc(r.requeueAWSClusterForUnpausedCluster(ctx, log)),
-		predicates.ClusterUnpaused(log),
+		predicates.ClusterUnpaused(log.GetLogger()),
 	)
 }
 
-func (r *AWSClusterReconciler) requeueAWSClusterForUnpausedCluster(ctx context.Context, log logr.Logger) handler.MapFunc {
+func (r *AWSClusterReconciler) requeueAWSClusterForUnpausedCluster(ctx context.Context, log logger.Wrapper) handler.MapFunc {
 	return func(o client.Object) []ctrl.Request {
 		c, ok := o.(*clusterv1.Cluster)
 		if !ok {
@@ -407,18 +407,18 @@ func (r *AWSClusterReconciler) requeueAWSClusterForUnpausedCluster(ctx context.C
 
 		// Don't handle deleted clusters
 		if !c.ObjectMeta.DeletionTimestamp.IsZero() {
-			log.V(4).Info("Cluster has a deletion timestamp, skipping mapping.")
+			log.Trace("Cluster has a deletion timestamp, skipping mapping.")
 			return nil
 		}
 
 		// Make sure the ref is set
 		if c.Spec.InfrastructureRef == nil {
-			log.V(4).Info("Cluster does not have an InfrastructureRef, skipping mapping.")
+			log.Trace("Cluster does not have an InfrastructureRef, skipping mapping.")
 			return nil
 		}
 
 		if c.Spec.InfrastructureRef.GroupVersionKind().Kind != "AWSCluster" {
-			log.V(4).Info("Cluster has an InfrastructureRef for a different type, skipping mapping.")
+			log.Trace("Cluster has an InfrastructureRef for a different type, skipping mapping.")
 			return nil
 		}
 
@@ -426,16 +426,16 @@ func (r *AWSClusterReconciler) requeueAWSClusterForUnpausedCluster(ctx context.C
 		key := types.NamespacedName{Namespace: c.Spec.InfrastructureRef.Namespace, Name: c.Spec.InfrastructureRef.Name}
 
 		if err := r.Get(ctx, key, awsCluster); err != nil {
-			log.V(4).Error(err, "Failed to get AWS cluster")
+			log.Error(err, "Failed to get AWS cluster")
 			return nil
 		}
 
 		if capiannotations.IsExternallyManaged(awsCluster) {
-			log.V(4).Info("AWSCluster is externally managed, skipping mapping.")
+			log.Trace("AWSCluster is externally managed, skipping mapping.")
 			return nil
 		}
 
-		log.V(4).Info("Adding request.", "awsCluster", c.Spec.InfrastructureRef.Name)
+		log.Trace("Adding request.", "awsCluster", c.Spec.InfrastructureRef.Name)
 		return []ctrl.Request{
 			{
 				NamespacedName: client.ObjectKey{Namespace: c.Namespace, Name: c.Spec.InfrastructureRef.Name},
diff --git a/controllers/awsmachine_controller.go b/controllers/awsmachine_controller.go
index 93f80551c..b293eedbb 100644
--- a/controllers/awsmachine_controller.go
+++ b/controllers/awsmachine_controller.go
@@ -53,6 +53,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/secretsmanager"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/ssm"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/userdata"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/controllers/noderefutil"
 	capierrors "sigs.k8s.io/cluster-api/errors"
@@ -140,7 +141,7 @@ func (r *AWSMachineReconciler) getObjectStoreService(scope scope.S3Scope) servic
 // +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch
 
 func (r *AWSMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	// Fetch the AWSMachine instance.
 	awsMachine := &infrav1.AWSMachine{}
@@ -226,7 +227,7 @@ func (r *AWSMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request)
 }
 
 func (r *AWSMachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 	AWSClusterToAWSMachines := r.AWSClusterToAWSMachines(log)
 
 	controller, err := ctrl.NewControllerManagedBy(mgr).
@@ -240,7 +241,7 @@ func (r *AWSMachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma
 			&source.Kind{Type: &infrav1.AWSCluster{}},
 			handler.EnqueueRequestsFromMapFunc(AWSClusterToAWSMachines),
 		).
-		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log, r.WatchFilterValue)).
+		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log.GetLogger(), r.WatchFilterValue)).
 		WithEventFilter(
 			predicate.Funcs{
 				// Avoid reconciling if the event triggering the reconciliation is related to incremental status updates
@@ -280,7 +281,7 @@ func (r *AWSMachineReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Ma
 	return controller.Watch(
 		&source.Kind{Type: &clusterv1.Cluster{}},
 		handler.EnqueueRequestsFromMapFunc(requeueAWSMachinesForUnpausedCluster),
-		predicates.ClusterUnpausedAndInfrastructureReady(log),
+		predicates.ClusterUnpausedAndInfrastructureReady(log.GetLogger()),
 	)
 }
 
@@ -308,13 +309,13 @@ func (r *AWSMachineReconciler) reconcileDelete(machineScope *scope.MachineScope,
 		// and AWSMachine
 		// 3. Issue a delete
 		// 4. Scale controller deployment to 1
-		machineScope.V(2).Info("Unable to locate EC2 instance by ID or tags")
+		machineScope.Debug("Unable to locate EC2 instance by ID or tags")
 		r.Recorder.Eventf(machineScope.AWSMachine, corev1.EventTypeWarning, "NoInstanceFound", "Unable to find matching EC2 instance")
 		controllerutil.RemoveFinalizer(machineScope.AWSMachine, infrav1.MachineFinalizer)
 		return ctrl.Result{}, nil
 	}
 
-	machineScope.V(3).Info("EC2 instance found matching deleted AWSMachine", "instance-id", instance.ID)
+	machineScope.Debug("EC2 instance found matching deleted AWSMachine", "instance-id", instance.ID)
 
 	if err := r.reconcileLBAttachment(machineScope, elbScope, instance); err != nil {
 		// We are tolerating AccessDenied error, so this won't block for users with older version of IAM;
@@ -367,7 +368,7 @@ func (r *AWSMachineReconciler) reconcileDelete(machineScope *scope.MachineScope,
 				return ctrl.Result{}, err
 			}
 
-			machineScope.V(3).Info(
+			machineScope.Debug(
 				"Detaching security groups from provided network interface",
 				"groups", core,
 				"instanceID", instance.ID,
@@ -430,7 +431,7 @@ func (r *AWSMachineReconciler) findInstance(scope *scope.MachineScope, ec2svc se
 }
 
 func (r *AWSMachineReconciler) reconcileNormal(_ context.Context, machineScope *scope.MachineScope, clusterScope cloud.ClusterScoper, ec2Scope scope.EC2Scope, elbScope scope.ELBScope, objectStoreScope scope.S3Scope) (ctrl.Result, error) {
-	machineScope.V(4).Info("Reconciling AWSMachine")
+	machineScope.Trace("Reconciling AWSMachine")
 
 	// If the AWSMachine is in an error state, return early.
 	if machineScope.HasFailed() {
@@ -837,7 +838,7 @@ func (r *AWSMachineReconciler) reconcileLBAttachment(machineScope *scope.Machine
 
 // AWSClusterToAWSMachines is a handler.ToRequestsFunc to be used to enqeue requests for reconciliation
 // of AWSMachines.
-func (r *AWSMachineReconciler) AWSClusterToAWSMachines(log logr.Logger) handler.MapFunc {
+func (r *AWSMachineReconciler) AWSClusterToAWSMachines(log logger.Wrapper) handler.MapFunc {
 	return func(o client.Object) []ctrl.Request {
 		c, ok := o.(*infrav1.AWSCluster)
 		if !ok {
@@ -848,14 +849,14 @@ func (r *AWSMachineReconciler) AWSClusterToAWSMachines(log logr.Logger) handler.
 
 		// Don't handle deleted AWSClusters
 		if !c.ObjectMeta.DeletionTimestamp.IsZero() {
-			log.V(4).Info("AWSCluster has a deletion timestamp, skipping mapping.")
+			log.Trace("AWSCluster has a deletion timestamp, skipping mapping.")
 			return nil
 		}
 
 		cluster, err := util.GetOwnerCluster(context.TODO(), r.Client, c.ObjectMeta)
 		switch {
 		case apierrors.IsNotFound(err) || cluster == nil:
-			log.V(4).Info("Cluster for AWSCluster not found, skipping mapping.")
+			log.Trace("Cluster for AWSCluster not found, skipping mapping.")
 			return nil
 		case err != nil:
 			log.Error(err, "Failed to get owning cluster, skipping mapping.")
@@ -866,7 +867,7 @@ func (r *AWSMachineReconciler) AWSClusterToAWSMachines(log logr.Logger) handler.
 	}
 }
 
-func (r *AWSMachineReconciler) requeueAWSMachinesForUnpausedCluster(log logr.Logger) handler.MapFunc {
+func (r *AWSMachineReconciler) requeueAWSMachinesForUnpausedCluster(log logger.Wrapper) handler.MapFunc {
 	return func(o client.Object) []ctrl.Request {
 		c, ok := o.(*clusterv1.Cluster)
 		if !ok {
@@ -877,7 +878,7 @@ func (r *AWSMachineReconciler) requeueAWSMachinesForUnpausedCluster(log logr.Log
 
 		// Don't handle deleted clusters
 		if !c.ObjectMeta.DeletionTimestamp.IsZero() {
-			log.V(4).Info("Cluster has a deletion timestamp, skipping mapping.")
+			log.Trace("Cluster has a deletion timestamp, skipping mapping.")
 			return nil
 		}
 
@@ -885,7 +886,7 @@ func (r *AWSMachineReconciler) requeueAWSMachinesForUnpausedCluster(log logr.Log
 	}
 }
 
-func (r *AWSMachineReconciler) requestsForCluster(log logr.Logger, namespace, name string) []ctrl.Request {
+func (r *AWSMachineReconciler) requestsForCluster(log logger.Wrapper, namespace, name string) []ctrl.Request {
 	labels := map[string]string{clusterv1.ClusterLabelName: name}
 	machineList := &clusterv1.MachineList{}
 	if err := r.Client.List(context.TODO(), machineList, client.InNamespace(namespace), client.MatchingLabels(labels)); err != nil {
@@ -898,21 +899,21 @@ func (r *AWSMachineReconciler) requestsForCluster(log logr.Logger, namespace, na
 		m := m
 		log.WithValues("machine", klog.KObj(&m))
 		if m.Spec.InfrastructureRef.GroupVersionKind().Kind != "AWSMachine" {
-			log.V(4).Info("Machine has an InfrastructureRef for a different type, will not add to reconciliation request.")
+			log.Trace("Machine has an InfrastructureRef for a different type, will not add to reconciliation request.")
 			continue
 		}
 		if m.Spec.InfrastructureRef.Name == "" {
-			log.V(4).Info("Machine has an InfrastructureRef with an empty name, will not add to reconciliation request.")
+			log.Trace("Machine has an InfrastructureRef with an empty name, will not add to reconciliation request.")
 			continue
 		}
 		log.WithValues("awsMachine", klog.KRef(m.Spec.InfrastructureRef.Namespace, m.Spec.InfrastructureRef.Name))
-		log.V(4).Info("Adding AWSMachine to reconciliation request.")
+		log.Trace("Adding AWSMachine to reconciliation request.")
 		result = append(result, ctrl.Request{NamespacedName: client.ObjectKey{Namespace: m.Namespace, Name: m.Spec.InfrastructureRef.Name}})
 	}
 	return result
 }
 
-func (r *AWSMachineReconciler) getInfraCluster(ctx context.Context, log logr.Logger, cluster *clusterv1.Cluster, awsMachine *infrav1.AWSMachine) (scope.EC2Scope, error) {
+func (r *AWSMachineReconciler) getInfraCluster(ctx context.Context, log *logger.Logger, cluster *clusterv1.Cluster, awsMachine *infrav1.AWSMachine) (scope.EC2Scope, error) {
 	var clusterScope *scope.ClusterScope
 	var managedControlPlaneScope *scope.ManagedControlPlaneScope
 	var err error
@@ -931,7 +932,7 @@ func (r *AWSMachineReconciler) getInfraCluster(ctx context.Context, log logr.Log
 
 		managedControlPlaneScope, err = scope.NewManagedControlPlaneScope(scope.ManagedControlPlaneScopeParams{
 			Client:         r.Client,
-			Logger:         &log,
+			Logger:         log,
 			Cluster:        cluster,
 			ControlPlane:   controlPlane,
 			ControllerName: "awsManagedControlPlane",
@@ -959,7 +960,7 @@ func (r *AWSMachineReconciler) getInfraCluster(ctx context.Context, log logr.Log
 	// Create the cluster scope
 	clusterScope, err = scope.NewClusterScope(scope.ClusterScopeParams{
 		Client:         r.Client,
-		Logger:         &log,
+		Logger:         log,
 		Cluster:        cluster,
 		AWSCluster:     awsCluster,
 		ControllerName: "awsmachine",
diff --git a/controlplane/eks/controllers/awsmanagedcontrolplane_controller.go b/controlplane/eks/controllers/awsmanagedcontrolplane_controller.go
index 25852dbd6..d236e2abf 100644
--- a/controlplane/eks/controllers/awsmanagedcontrolplane_controller.go
+++ b/controlplane/eks/controllers/awsmanagedcontrolplane_controller.go
@@ -46,6 +46,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/kubeproxy"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/network"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/securitygroup"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util"
 	capiannotations "sigs.k8s.io/cluster-api/util/annotations"
@@ -89,13 +90,13 @@ type AWSManagedControlPlaneReconciler struct {
 
 // SetupWithManager is used to setup the controller.
 func (r *AWSManagedControlPlaneReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	awsManagedControlPlane := &ekscontrolplanev1.AWSManagedControlPlane{}
 	c, err := ctrl.NewControllerManagedBy(mgr).
 		For(awsManagedControlPlane).
 		WithOptions(options).
-		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log, r.WatchFilterValue)).
+		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log.GetLogger(), r.WatchFilterValue)).
 		Build(r)
 
 	if err != nil {
@@ -105,7 +106,7 @@ func (r *AWSManagedControlPlaneReconciler) SetupWithManager(ctx context.Context,
 	if err = c.Watch(
 		&source.Kind{Type: &clusterv1.Cluster{}},
 		handler.EnqueueRequestsFromMapFunc(util.ClusterToInfrastructureMapFunc(ctx, awsManagedControlPlane.GroupVersionKind(), mgr.GetClient(), &ekscontrolplanev1.AWSManagedControlPlane{})),
-		predicates.ClusterUnpausedAndInfrastructureReady(log),
+		predicates.ClusterUnpausedAndInfrastructureReady(log.GetLogger()),
 	); err != nil {
 		return fmt.Errorf("failed adding a watch for ready clusters: %w", err)
 	}
@@ -126,7 +127,7 @@ func (r *AWSManagedControlPlaneReconciler) SetupWithManager(ctx context.Context,
 
 // Reconcile will reconcile AWSManagedControlPlane Resources.
 func (r *AWSManagedControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl.Result, reterr error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	// Get the control plane instance
 	awsControlPlane := &ekscontrolplanev1.AWSManagedControlPlane{}
@@ -269,7 +270,7 @@ func (r *AWSManagedControlPlaneReconciler) reconcileNormal(ctx context.Context,
 }
 
 func (r *AWSManagedControlPlaneReconciler) reconcileDelete(ctx context.Context, managedScope *scope.ManagedControlPlaneScope) (_ ctrl.Result, reterr error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	managedScope.Info("Reconciling AWSManagedControlPlane delete")
 
@@ -344,7 +345,7 @@ func (r *AWSManagedControlPlaneReconciler) ClusterToAWSManagedControlPlane(o cli
 }
 
 func (r *AWSManagedControlPlaneReconciler) dependencyCount(ctx context.Context, managedScope *scope.ManagedControlPlaneScope) (int, error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	clusterName := managedScope.Name()
 	namespace := managedScope.Namespace()
@@ -361,7 +362,7 @@ func (r *AWSManagedControlPlaneReconciler) dependencyCount(ctx context.Context,
 	if err := r.Client.List(ctx, machines, listOptions...); err != nil {
 		return dependencies, fmt.Errorf("failed to list machines for cluster %s/%s: %w", namespace, clusterName, err)
 	}
-	log.V(2).Info("tested for AWSMachine dependencies", "count", len(machines.Items))
+	log.Debug("tested for AWSMachine dependencies", "count", len(machines.Items))
 	dependencies += len(machines.Items)
 
 	if feature.Gates.Enabled(feature.MachinePool) {
@@ -369,14 +370,14 @@ func (r *AWSManagedControlPlaneReconciler) dependencyCount(ctx context.Context,
 		if err := r.Client.List(ctx, managedMachinePools, listOptions...); err != nil {
 			return dependencies, fmt.Errorf("failed to list managed machine pools for cluster %s/%s: %w", namespace, clusterName, err)
 		}
-		log.V(2).Info("tested for AWSManagedMachinePool dependencies", "count", len(managedMachinePools.Items))
+		log.Debug("tested for AWSManagedMachinePool dependencies", "count", len(managedMachinePools.Items))
 		dependencies += len(managedMachinePools.Items)
 
 		machinePools := &expinfrav1.AWSMachinePoolList{}
 		if err := r.Client.List(ctx, machinePools, listOptions...); err != nil {
 			return dependencies, fmt.Errorf("failed to list machine pools for cluster %s/%s: %w", namespace, clusterName, err)
 		}
-		log.V(2).Info("tested for AWSMachinePool dependencies", "count", len(machinePools.Items))
+		log.Debug("tested for AWSMachinePool dependencies", "count", len(machinePools.Items))
 		dependencies += len(machinePools.Items)
 	}
 
diff --git a/docs/book/src/topics/bring-your-own-aws-infrastructure.md b/docs/book/src/topics/bring-your-own-aws-infrastructure.md
index d4c4369c9..cae557ad2 100644
--- a/docs/book/src/topics/bring-your-own-aws-infrastructure.md
+++ b/docs/book/src/topics/bring-your-own-aws-infrastructure.md
@@ -184,7 +184,7 @@ c, err := ctrl.NewControllerManagedBy(mgr).
         For(&providerv1.InfraCluster{}).
         Watches(...).
         WithOptions(options).
-        WithEventFilter(predicates.ResourceIsNotExternallyManaged(ctrl.LoggerFrom(ctx))).
+        WithEventFilter(predicates.ResourceIsNotExternallyManaged(logger.FromContext(ctx))).
         Build(r)
 if err != nil {
 	return errors.Wrap(err, "failed setting up with a controller manager")
diff --git a/exp/controlleridentitycreator/awscontrolleridentity_controller.go b/exp/controlleridentitycreator/awscontrolleridentity_controller.go
index 31b6ddd86..78d31f84f 100644
--- a/exp/controlleridentitycreator/awscontrolleridentity_controller.go
+++ b/exp/controlleridentitycreator/awscontrolleridentity_controller.go
@@ -36,6 +36,7 @@ import (
 	ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/eks/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/feature"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	"sigs.k8s.io/cluster-api/util/predicates"
 )
 
@@ -50,7 +51,7 @@ type AWSControllerIdentityReconciler struct {
 // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsclustercontrolleridentities,verbs=get;list;watch;create
 
 func (r *AWSControllerIdentityReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	var identityRef *infrav1.AWSIdentityReference
 
@@ -62,10 +63,10 @@ func (r *AWSControllerIdentityReconciler) Reconcile(ctx context.Context, req ctr
 		if !apierrors.IsNotFound(err) {
 			return reconcile.Result{}, err
 		}
-		log.V(4).Info("AWSCluster not found, trying AWSManagedControlPlane")
+		log.Trace("AWSCluster not found, trying AWSManagedControlPlane")
 		clusterFound = false
 	} else {
-		log.V(4).Info("Found identityRef on AWSCluster")
+		log.Trace("Found identityRef on AWSCluster")
 		identityRef = awsCluster.Spec.IdentityRef
 	}
 
@@ -74,12 +75,12 @@ func (r *AWSControllerIdentityReconciler) Reconcile(ctx context.Context, req ctr
 		awsControlPlane := &ekscontrolplanev1.AWSManagedControlPlane{}
 		if err := r.Client.Get(ctx, req.NamespacedName, awsControlPlane); err != nil {
 			if apierrors.IsNotFound(err) {
-				log.V(4).Info("AWSManagedMachinePool not found, no identityRef so no action taken")
+				log.Trace("AWSManagedMachinePool not found, no identityRef so no action taken")
 				return ctrl.Result{}, nil
 			}
 			return reconcile.Result{}, err
 		}
-		log.V(4).Info("Found identityRef on AWSManagedControlPlane")
+		log.Trace("Found identityRef on AWSManagedControlPlane")
 		identityRef = awsControlPlane.Spec.IdentityRef
 	}
 
@@ -92,7 +93,7 @@ func (r *AWSControllerIdentityReconciler) Reconcile(ctx context.Context, req ctr
 	// If identity type is not AWSClusterControllerIdentity, then no need to create AWSClusterControllerIdentity singleton.
 	if identityRef.Kind == infrav1.ClusterRoleIdentityKind ||
 		identityRef.Kind == infrav1.ClusterStaticIdentityKind {
-		log.V(4).Info("Cluster does not use AWSClusterControllerIdentity as identityRef, skipping new instance creation")
+		log.Trace("Cluster does not use AWSClusterControllerIdentity as identityRef, skipping new instance creation")
 		return ctrl.Result{}, nil
 	}
 
@@ -135,7 +136,7 @@ func (r *AWSControllerIdentityReconciler) SetupWithManager(ctx context.Context,
 	controller := ctrl.NewControllerManagedBy(mgr).
 		For(&infrav1.AWSCluster{}).
 		WithOptions(options).
-		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue))
+		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(logger.FromContext(ctx).GetLogger(), r.WatchFilterValue))
 
 	if feature.Gates.Enabled(feature.EKS) {
 		controller.Watches(
diff --git a/exp/controllers/awsfargatepool_controller.go b/exp/controllers/awsfargatepool_controller.go
index acaf0dec6..90f503d6b 100644
--- a/exp/controllers/awsfargatepool_controller.go
+++ b/exp/controllers/awsfargatepool_controller.go
@@ -20,7 +20,6 @@ import (
 	"context"
 	"fmt"
 
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/client-go/tools/record"
@@ -37,6 +36,7 @@ import (
 	expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/eks"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util"
 	"sigs.k8s.io/cluster-api/util/conditions"
@@ -54,11 +54,11 @@ type AWSFargateProfileReconciler struct {
 
 // SetupWithManager is used to setup the controller.
 func (r *AWSFargateProfileReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
-	managedControlPlaneToFargateProfileMap := managedControlPlaneToFargateProfileMapFunc(r.Client, ctrl.LoggerFrom(ctx))
+	managedControlPlaneToFargateProfileMap := managedControlPlaneToFargateProfileMapFunc(r.Client, logger.FromContext(ctx))
 	return ctrl.NewControllerManagedBy(mgr).
 		For(&expinfrav1.AWSFargateProfile{}).
 		WithOptions(options).
-		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
+		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(logger.FromContext(ctx).GetLogger(), r.WatchFilterValue)).
 		Watches(
 			&source.Kind{Type: &ekscontrolplanev1.AWSManagedControlPlane{}},
 			handler.EnqueueRequestsFromMapFunc(managedControlPlaneToFargateProfileMap),
@@ -74,7 +74,7 @@ func (r *AWSFargateProfileReconciler) SetupWithManager(ctx context.Context, mgr
 
 // Reconcile reconciles AWSFargateProfiles.
 func (r *AWSFargateProfileReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	fargateProfile := &expinfrav1.AWSFargateProfile{}
 	if err := r.Get(ctx, req.NamespacedName, fargateProfile); err != nil {
@@ -182,7 +182,7 @@ func (r *AWSFargateProfileReconciler) reconcileDelete(
 	return res, nil
 }
 
-func managedControlPlaneToFargateProfileMapFunc(c client.Client, log logr.Logger) handler.MapFunc {
+func managedControlPlaneToFargateProfileMapFunc(c client.Client, log logger.Wrapper) handler.MapFunc {
 	return func(o client.Object) []ctrl.Request {
 		ctx := context.Background()
 
diff --git a/exp/controllers/awsmachinepool_controller.go b/exp/controllers/awsmachinepool_controller.go
index 348e6657b..379bd2210 100644
--- a/exp/controllers/awsmachinepool_controller.go
+++ b/exp/controllers/awsmachinepool_controller.go
@@ -20,7 +20,6 @@ import (
 	"context"
 	"fmt"
 
-	"github.com/go-logr/logr"
 	"github.com/google/go-cmp/cmp"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
@@ -46,6 +45,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services"
 	asg "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/autoscaling"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/ec2"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util"
@@ -86,7 +86,7 @@ func (r *AWSMachinePoolReconciler) getEC2Service(scope scope.EC2Scope) services.
 
 // Reconcile is the reconciliation loop for AWSMachinePool.
 func (r *AWSMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	// Fetch the AWSMachinePool .
 	awsMachinePool := &expinfrav1.AWSMachinePool{}
@@ -185,7 +185,7 @@ func (r *AWSMachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctr
 			&source.Kind{Type: &expclusterv1.MachinePool{}},
 			handler.EnqueueRequestsFromMapFunc(machinePoolToInfrastructureMapFunc(expinfrav1.GroupVersion.WithKind("AWSMachinePool"))),
 		).
-		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
+		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(logger.FromContext(ctx).GetLogger(), r.WatchFilterValue)).
 		Complete(r)
 }
 
@@ -234,7 +234,7 @@ func (r *AWSMachinePoolReconciler) reconcileNormal(ctx context.Context, machineP
 	runPostLaunchTemplateUpdateOperation := func() error {
 		// skip instance refresh if explicitly disabled
 		if machinePoolScope.AWSMachinePool.Spec.RefreshPreferences != nil && machinePoolScope.AWSMachinePool.Spec.RefreshPreferences.Disable {
-			machinePoolScope.V(4).Info("instance refresh disabled, skipping instance refresh")
+			machinePoolScope.Debug("instance refresh disabled, skipping instance refresh")
 			return nil
 		}
 		// After creating a new version of launch template, instance refresh is required
@@ -331,7 +331,7 @@ func (r *AWSMachinePoolReconciler) reconcileDelete(machinePoolScope *scope.Machi
 	}
 
 	if asg == nil {
-		machinePoolScope.V(2).Info("Unable to locate ASG")
+		machinePoolScope.Debug("Unable to locate ASG")
 		r.Recorder.Eventf(machinePoolScope.AWSMachinePool, corev1.EventTypeNormal, "NoASGFound", "Unable to find matching ASG")
 	} else {
 		machinePoolScope.SetASGStatus(asg.Status)
@@ -358,7 +358,7 @@ func (r *AWSMachinePoolReconciler) reconcileDelete(machinePoolScope *scope.Machi
 	}
 
 	if launchTemplate == nil {
-		machinePoolScope.V(2).Info("Unable to locate launch template")
+		machinePoolScope.Debug("Unable to locate launch template")
 		r.Recorder.Eventf(machinePoolScope.AWSMachinePool, corev1.EventTypeNormal, "NoASGFound", "Unable to find matching ASG")
 		controllerutil.RemoveFinalizer(machinePoolScope.AWSMachinePool, expinfrav1.MachinePoolFinalizer)
 		return ctrl.Result{}, nil
@@ -552,7 +552,7 @@ func machinePoolToInfrastructureMapFunc(gvk schema.GroupVersionKind) handler.Map
 	}
 }
 
-func (r *AWSMachinePoolReconciler) getInfraCluster(ctx context.Context, log logr.Logger, cluster *clusterv1.Cluster, awsMachinePool *expinfrav1.AWSMachinePool) (scope.EC2Scope, error) {
+func (r *AWSMachinePoolReconciler) getInfraCluster(ctx context.Context, log *logger.Logger, cluster *clusterv1.Cluster, awsMachinePool *expinfrav1.AWSMachinePool) (scope.EC2Scope, error) {
 	var clusterScope *scope.ClusterScope
 	var managedControlPlaneScope *scope.ManagedControlPlaneScope
 	var err error
@@ -571,7 +571,7 @@ func (r *AWSMachinePoolReconciler) getInfraCluster(ctx context.Context, log logr
 
 		managedControlPlaneScope, err = scope.NewManagedControlPlaneScope(scope.ManagedControlPlaneScopeParams{
 			Client:         r.Client,
-			Logger:         &log,
+			Logger:         log,
 			Cluster:        cluster,
 			ControlPlane:   controlPlane,
 			ControllerName: "awsManagedControlPlane",
@@ -598,7 +598,7 @@ func (r *AWSMachinePoolReconciler) getInfraCluster(ctx context.Context, log logr
 	// Create the cluster scope
 	clusterScope, err = scope.NewClusterScope(scope.ClusterScopeParams{
 		Client:         r.Client,
-		Logger:         &log,
+		Logger:         log,
 		Cluster:        cluster,
 		AWSCluster:     awsCluster,
 		ControllerName: "awsmachine",
diff --git a/exp/controllers/awsmanagedmachinepool_controller.go b/exp/controllers/awsmanagedmachinepool_controller.go
index cdb1dce29..957a05fa9 100644
--- a/exp/controllers/awsmanagedmachinepool_controller.go
+++ b/exp/controllers/awsmanagedmachinepool_controller.go
@@ -20,7 +20,6 @@ import (
 	"context"
 	"fmt"
 
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -43,6 +42,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/ec2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/eks"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util"
@@ -63,7 +63,7 @@ type AWSManagedMachinePoolReconciler struct {
 
 // SetupWithManager is used to setup the controller.
 func (r *AWSManagedMachinePoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	gvk, err := apiutil.GVKForObject(new(expinfrav1.AWSManagedMachinePool), mgr.GetScheme())
 	if err != nil {
@@ -73,7 +73,7 @@ func (r *AWSManagedMachinePoolReconciler) SetupWithManager(ctx context.Context,
 	return ctrl.NewControllerManagedBy(mgr).
 		For(&expinfrav1.AWSManagedMachinePool{}).
 		WithOptions(options).
-		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log, r.WatchFilterValue)).
+		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(log.GetLogger(), r.WatchFilterValue)).
 		Watches(
 			&source.Kind{Type: &expclusterv1.MachinePool{}},
 			handler.EnqueueRequestsFromMapFunc(machinePoolToInfrastructureMapFunc(gvk)),
@@ -93,7 +93,7 @@ func (r *AWSManagedMachinePoolReconciler) SetupWithManager(ctx context.Context,
 
 // Reconcile reconciles AWSManagedMachinePools.
 func (r *AWSManagedMachinePoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
-	log := ctrl.LoggerFrom(ctx)
+	log := logger.FromContext(ctx)
 
 	awsPool := &expinfrav1.AWSManagedMachinePool{}
 	if err := r.Get(ctx, req.NamespacedName, awsPool); err != nil {
@@ -140,7 +140,7 @@ func (r *AWSManagedMachinePoolReconciler) Reconcile(ctx context.Context, req ctr
 
 	managedControlPlaneScope, err := scope.NewManagedControlPlaneScope(scope.ManagedControlPlaneScopeParams{
 		Client:         r.Client,
-		Logger:         &log,
+		Logger:         log,
 		Cluster:        cluster,
 		ControlPlane:   controlPlane,
 		ControllerName: "awsManagedControlPlane",
@@ -263,7 +263,7 @@ func (r *AWSManagedMachinePoolReconciler) reconcileDelete(
 		}
 
 		if launchTemplate == nil {
-			machinePoolScope.V(2).Info("Unable to find matching launch template")
+			machinePoolScope.Debug("Unable to find matching launch template")
 			r.Recorder.Eventf(machinePoolScope.ManagedMachinePool, corev1.EventTypeNormal, "NoLaunchTemplateFound", "Unable to find matching launch template")
 			controllerutil.RemoveFinalizer(machinePoolScope.ManagedMachinePool, expinfrav1.ManagedMachinePoolFinalizer)
 			return ctrl.Result{}, nil
@@ -303,7 +303,7 @@ func GetOwnerClusterKey(obj metav1.ObjectMeta) (*client.ObjectKey, error) {
 	return nil, nil
 }
 
-func managedControlPlaneToManagedMachinePoolMapFunc(c client.Client, gvk schema.GroupVersionKind, log logr.Logger) handler.MapFunc {
+func managedControlPlaneToManagedMachinePoolMapFunc(c client.Client, gvk schema.GroupVersionKind, log logger.Wrapper) handler.MapFunc {
 	return func(o client.Object) []reconcile.Request {
 		ctx := context.Background()
 		awsControlPlane, ok := o.(*ekscontrolplanev1.AWSManagedControlPlane)
diff --git a/exp/instancestate/awsinstancestate_controller.go b/exp/instancestate/awsinstancestate_controller.go
index e888bce9b..c04f8687c 100644
--- a/exp/instancestate/awsinstancestate_controller.go
+++ b/exp/instancestate/awsinstancestate_controller.go
@@ -38,6 +38,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/controllers"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/instancestate"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	"sigs.k8s.io/cluster-api/util/patch"
 	"sigs.k8s.io/cluster-api/util/predicates"
 )
@@ -116,7 +117,7 @@ func (r *AwsInstanceStateReconciler) SetupWithManager(ctx context.Context, mgr c
 	return ctrl.NewControllerManagedBy(mgr).
 		For(&infrav1.AWSCluster{}).
 		WithOptions(options).
-		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
+		WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(logger.FromContext(ctx).GetLogger(), r.WatchFilterValue)).
 		Complete(r)
 }
 
diff --git a/main.go b/main.go
index c4137a389..ea2689d0d 100644
--- a/main.go
+++ b/main.go
@@ -56,6 +56,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/feature"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/endpoints"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/record"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/version"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -64,7 +65,7 @@ import (
 
 var (
 	scheme   = runtime.NewScheme()
-	setupLog = ctrl.Log.WithName("setup")
+	setupLog = logger.NewLogger(ctrl.Log.WithName("setup"))
 )
 
 func init() {
@@ -170,7 +171,7 @@ func main() {
 	// Initialize event recorder.
 	record.InitFromRecorder(mgr.GetEventRecorderFor("aws-controller"))
 
-	setupLog.V(1).Info(fmt.Sprintf("feature gates: %+v\n", feature.Gates))
+	setupLog.Info(fmt.Sprintf("feature gates: %+v\n", feature.Gates))
 
 	externalResourceGC := false
 	if feature.Gates.Enabled(feature.ExternalResourceGC) {
@@ -239,7 +240,7 @@ func setupReconcilersAndWebhooks(ctx context.Context, mgr ctrl.Manager, awsServi
 	}
 
 	if feature.Gates.Enabled(feature.MachinePool) {
-		setupLog.V(2).Info("enabling machine pool controller and webhook")
+		setupLog.Debug("enabling machine pool controller and webhook")
 		if err := (&expcontrollers.AWSMachinePoolReconciler{
 			Client:           mgr.GetClient(),
 			Recorder:         mgr.GetEventRecorderFor("awsmachinepool-controller"),
@@ -323,14 +324,14 @@ func setupEKSReconcilersAndWebhooks(ctx context.Context, mgr ctrl.Manager, awsSe
 
 	enableIAM := feature.Gates.Enabled(feature.EKSEnableIAM)
 	allowAddRoles := feature.Gates.Enabled(feature.EKSAllowAddRoles)
-	setupLog.V(2).Info("EKS IAM role creation", "enabled", enableIAM)
-	setupLog.V(2).Info("EKS IAM additional roles", "enabled", allowAddRoles)
+	setupLog.Debug("EKS IAM role creation", "enabled", enableIAM)
+	setupLog.Debug("EKS IAM additional roles", "enabled", allowAddRoles)
 	if allowAddRoles && !enableIAM {
 		setupLog.Error(errEKSInvalidFlags, "cannot use EKSAllowAddRoles flag without EKSEnableIAM")
 		os.Exit(1)
 	}
 
-	setupLog.V(2).Info("enabling EKS control plane controller")
+	setupLog.Debug("enabling EKS control plane controller")
 	if err := (&ekscontrolplanecontrollers.AWSManagedControlPlaneReconciler{
 		Client:               mgr.GetClient(),
 		EnableIAM:            enableIAM,
@@ -343,7 +344,7 @@ func setupEKSReconcilersAndWebhooks(ctx context.Context, mgr ctrl.Manager, awsSe
 		os.Exit(1)
 	}
 
-	setupLog.V(2).Info("enabling EKS bootstrap controller")
+	setupLog.Debug("enabling EKS bootstrap controller")
 	if err := (&eksbootstrapcontrollers.EKSConfigReconciler{
 		Client:           mgr.GetClient(),
 		WatchFilterValue: watchFilterValue,
@@ -353,7 +354,7 @@ func setupEKSReconcilersAndWebhooks(ctx context.Context, mgr ctrl.Manager, awsSe
 	}
 
 	if feature.Gates.Enabled(feature.EKSFargate) {
-		setupLog.V(2).Info("enabling EKS fargate profile controller")
+		setupLog.Debug("enabling EKS fargate profile controller")
 		if err := (&expcontrollers.AWSFargateProfileReconciler{
 			Client:           mgr.GetClient(),
 			Recorder:         mgr.GetEventRecorderFor("awsfargateprofile-reconciler"),
@@ -371,7 +372,7 @@ func setupEKSReconcilersAndWebhooks(ctx context.Context, mgr ctrl.Manager, awsSe
 	}
 
 	if feature.Gates.Enabled(feature.MachinePool) {
-		setupLog.V(2).Info("enabling EKS managed machine pool controller")
+		setupLog.Debug("enabling EKS managed machine pool controller")
 		if err := (&expcontrollers.AWSManagedMachinePoolReconciler{
 			AllowAdditionalRoles: allowAddRoles,
 			Client:               mgr.GetClient(),
diff --git a/pkg/cloud/identity/identity.go b/pkg/cloud/identity/identity.go
index 2f9ffc3b8..c14a667e2 100644
--- a/pkg/cloud/identity/identity.go
+++ b/pkg/cloud/identity/identity.go
@@ -27,10 +27,10 @@ import (
 	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/sts/stsiface"
-	"github.com/go-logr/logr"
 	corev1 "k8s.io/api/core/v1"
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 )
 
 // AWSPrincipalTypeProvider defines the interface for AWS Principal Type Provider.
@@ -79,7 +79,7 @@ func GetAssumeRoleCredentials(roleIdentityProvider *AWSRolePrincipalTypeProvider
 }
 
 // NewAWSRolePrincipalTypeProvider will create a new AWSRolePrincipalTypeProvider from an AWSClusterRoleIdentity.
-func NewAWSRolePrincipalTypeProvider(identity *infrav1.AWSClusterRoleIdentity, sourceProvider *AWSPrincipalTypeProvider, log logr.Logger) *AWSRolePrincipalTypeProvider {
+func NewAWSRolePrincipalTypeProvider(identity *infrav1.AWSClusterRoleIdentity, sourceProvider *AWSPrincipalTypeProvider, log logger.Wrapper) *AWSRolePrincipalTypeProvider {
 	return &AWSRolePrincipalTypeProvider{
 		credentials:    nil,
 		stsClient:      nil,
@@ -130,7 +130,7 @@ type AWSRolePrincipalTypeProvider struct {
 	Principal      *infrav1.AWSClusterRoleIdentity
 	credentials    *credentials.Credentials
 	sourceProvider *AWSPrincipalTypeProvider
-	log            logr.Logger
+	log            logger.Wrapper
 	stsClient      stsiface.STSAPI
 }
 
diff --git a/pkg/cloud/interfaces.go b/pkg/cloud/interfaces.go
index 2012e6e7b..a615ece6f 100644
--- a/pkg/cloud/interfaces.go
+++ b/pkg/cloud/interfaces.go
@@ -18,11 +18,11 @@ package cloud
 
 import (
 	awsclient "github.com/aws/aws-sdk-go/aws/client"
-	"github.com/go-logr/logr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/throttle"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util/conditions"
 )
@@ -44,52 +44,9 @@ type ClusterObject interface {
 	conditions.Setter
 }
 
-// Logger represents the ability to log messages, both errors and not.
-type Logger interface {
-	// Enabled tests whether this Logger is enabled.  For example, commandline
-	// flags might be used to set the logging verbosity and disable some info
-	// logs.
-	Enabled() bool
-
-	// Info logs a non-error message with the given key/value pairs as context.
-	//
-	// The msg argument should be used to add some constant description to
-	// the log line.  The key/value pairs can then be used to add additional
-	// variable information.  The key/value pairs should alternate string
-	// keys and arbitrary values.
-	Info(msg string, keysAndValues ...interface{})
-
-	// Error logs an error, with the given message and key/value pairs as context.
-	// It functions similarly to calling Info with the "error" named value, but may
-	// have unique behavior, and should be preferred for logging errors (see the
-	// package documentations for more information).
-	//
-	// The msg field should be used to add context to any underlying error,
-	// while the err field should be used to attach the actual error that
-	// triggered this log line, if present.
-	Error(err error, msg string, keysAndValues ...interface{})
-
-	// V returns a Logger value for a specific verbosity level, relative to
-	// this Logger.  In other words, V values are additive.  V higher verbosity
-	// level means a log message is less important.  It's illegal to pass a log
-	// level less than zero.
-	V(level int) logr.Logger
-
-	// WithValues adds some key-value pairs of context to a logger.
-	// See Info for documentation on how key/value pairs work.
-	WithValues(keysAndValues ...interface{}) logr.Logger
-
-	// WithName adds a new element to the logger's name.
-	// Successive calls with WithName continue to append
-	// suffixes to the logger's name.  It's strongly recommended
-	// that name segments contain only letters, digits, and hyphens
-	// (see the package documentation for more information).
-	WithName(name string) logr.Logger
-}
-
 // ClusterScoper is the interface for a cluster scope.
 type ClusterScoper interface {
-	Logger
+	logger.Wrapper
 	Session
 	ScopeUsage
 
@@ -97,7 +54,7 @@ type ClusterScoper interface {
 	Name() string
 	// Namespace returns the cluster namespace.
 	Namespace() string
-	// AWSClusterName returns the AWS cluster name.
+	// InfraClusterName returns the AWS infrastructure cluster name.
 	InfraClusterName() string
 	// Region returns the cluster region.
 	Region() string
diff --git a/pkg/cloud/logs/logs.go b/pkg/cloud/logs/logs.go
index a32a3fce9..431ede6a8 100644
--- a/pkg/cloud/logs/logs.go
+++ b/pkg/cloud/logs/logs.go
@@ -18,8 +18,7 @@ package logs
 
 import (
 	"github.com/aws/aws-sdk-go/aws"
-
-	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
+	"github.com/go-logr/logr"
 )
 
 const (
@@ -28,7 +27,7 @@ const (
 )
 
 // GetAWSLogLevel will return the log level of an AWS Logger.
-func GetAWSLogLevel(logger cloud.Logger) aws.LogLevelType {
+func GetAWSLogLevel(logger logr.Logger) aws.LogLevelType {
 	if logger.V(logWithHTTPBody).Enabled() {
 		return aws.LogDebugWithHTTPBody
 	}
@@ -41,14 +40,14 @@ func GetAWSLogLevel(logger cloud.Logger) aws.LogLevelType {
 }
 
 // NewWrapLogr will create an AWS Logger wrapper.
-func NewWrapLogr(logger cloud.Logger) aws.Logger {
+func NewWrapLogr(logger logr.Logger) aws.Logger {
 	return &logrWrapper{
 		log: logger,
 	}
 }
 
 type logrWrapper struct {
-	log cloud.Logger
+	log logr.Logger
 }
 
 func (l *logrWrapper) Log(msgs ...interface{}) {
@@ -58,6 +57,9 @@ func (l *logrWrapper) Log(msgs ...interface{}) {
 	case 1:
 		l.log.Info(msgs[0].(string))
 	default:
+		// Even the previous implementation had this problem but because it wasn't a direct
+		// logr.Logger thing, it didn't say this...
+		//nolint: logrlint
 		l.log.Info(msgs[0].(string), msgs[:1]...)
 	}
 }
diff --git a/pkg/cloud/scope/clients.go b/pkg/cloud/scope/clients.go
index 9b150b6dd..bd0fa05ed 100644
--- a/pkg/cloud/scope/clients.go
+++ b/pkg/cloud/scope/clients.go
@@ -51,13 +51,14 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
 	awslogs "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/logs"
 	awsmetrics "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/metrics"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/record"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/version"
 )
 
 // NewASGClient creates a new ASG API client for a given session.
-func NewASGClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) autoscalingiface.AutoScalingAPI {
-	asgClient := autoscaling.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewASGClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) autoscalingiface.AutoScalingAPI {
+	asgClient := autoscaling.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	asgClient.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	asgClient.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
 	asgClient.Handlers.Complete.PushBack(recordAWSPermissionsIssue(target))
@@ -66,8 +67,8 @@ func NewASGClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger clou
 }
 
 // NewEC2Client creates a new EC2 API client for a given session.
-func NewEC2Client(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) ec2iface.EC2API {
-	ec2Client := ec2.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewEC2Client(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) ec2iface.EC2API {
+	ec2Client := ec2.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	ec2Client.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	if session.ServiceLimiter(ec2.ServiceID) != nil {
 		ec2Client.Handlers.Sign.PushFront(session.ServiceLimiter(ec2.ServiceID).LimitRequest)
@@ -82,8 +83,8 @@ func NewEC2Client(scopeUser cloud.ScopeUsage, session cloud.Session, logger clou
 }
 
 // NewELBClient creates a new ELB API client for a given session.
-func NewELBClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) elbiface.ELBAPI {
-	elbClient := elb.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewELBClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) elbiface.ELBAPI {
+	elbClient := elb.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	elbClient.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	elbClient.Handlers.Sign.PushFront(session.ServiceLimiter(elb.ServiceID).LimitRequest)
 	elbClient.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
@@ -94,8 +95,8 @@ func NewELBClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger clou
 }
 
 // NewELBv2Client creates a new ELB v2 API client for a given session.
-func NewELBv2Client(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) elbv2iface.ELBV2API {
-	elbClient := elbv2.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewELBv2Client(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) elbv2iface.ELBV2API {
+	elbClient := elbv2.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	elbClient.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	elbClient.Handlers.Sign.PushFront(session.ServiceLimiter(elbv2.ServiceID).LimitRequest)
 	elbClient.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
@@ -135,8 +136,8 @@ func NewGlobalSQSClient(scopeUser cloud.ScopeUsage, session cloud.Session) sqsif
 }
 
 // NewResourgeTaggingClient creates a new Resource Tagging API client for a given session.
-func NewResourgeTaggingClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI {
-	resourceTagging := resourcegroupstaggingapi.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewResourgeTaggingClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI {
+	resourceTagging := resourcegroupstaggingapi.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	resourceTagging.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	resourceTagging.Handlers.Sign.PushFront(session.ServiceLimiter(resourceTagging.ServiceID).LimitRequest)
 	resourceTagging.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
@@ -147,8 +148,8 @@ func NewResourgeTaggingClient(scopeUser cloud.ScopeUsage, session cloud.Session,
 }
 
 // NewSecretsManagerClient creates a new Secrets API client for a given session..
-func NewSecretsManagerClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) secretsmanageriface.SecretsManagerAPI {
-	secretsClient := secretsmanager.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewSecretsManagerClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) secretsmanageriface.SecretsManagerAPI {
+	secretsClient := secretsmanager.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	secretsClient.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	secretsClient.Handlers.Sign.PushFront(session.ServiceLimiter(secretsClient.ServiceID).LimitRequest)
 	secretsClient.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
@@ -159,8 +160,8 @@ func NewSecretsManagerClient(scopeUser cloud.ScopeUsage, session cloud.Session,
 }
 
 // NewEKSClient creates a new EKS API client for a given session.
-func NewEKSClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) eksiface.EKSAPI {
-	eksClient := eks.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewEKSClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) eksiface.EKSAPI {
+	eksClient := eks.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	eksClient.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	eksClient.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
 	eksClient.Handlers.Complete.PushBack(recordAWSPermissionsIssue(target))
@@ -169,8 +170,8 @@ func NewEKSClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger clou
 }
 
 // NewIAMClient creates a new IAM API client for a given session.
-func NewIAMClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) iamiface.IAMAPI {
-	iamClient := iam.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewIAMClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) iamiface.IAMAPI {
+	iamClient := iam.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	iamClient.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	iamClient.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
 	iamClient.Handlers.Complete.PushBack(recordAWSPermissionsIssue(target))
@@ -179,8 +180,8 @@ func NewIAMClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger clou
 }
 
 // NewSTSClient creates a new STS API client for a given session.
-func NewSTSClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) stsiface.STSAPI {
-	stsClient := sts.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewSTSClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) stsiface.STSAPI {
+	stsClient := sts.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	stsClient.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	stsClient.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
 	stsClient.Handlers.Complete.PushBack(recordAWSPermissionsIssue(target))
@@ -189,8 +190,8 @@ func NewSTSClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger clou
 }
 
 // NewSSMClient creates a new Secrets API client for a given session.
-func NewSSMClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) ssmiface.SSMAPI {
-	ssmClient := ssm.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewSSMClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) ssmiface.SSMAPI {
+	ssmClient := ssm.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	ssmClient.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	ssmClient.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
 	ssmClient.Handlers.Complete.PushBack(recordAWSPermissionsIssue(target))
@@ -199,8 +200,8 @@ func NewSSMClient(scopeUser cloud.ScopeUsage, session cloud.Session, logger clou
 }
 
 // NewS3Client creates a new S3 API client for a given session.
-func NewS3Client(scopeUser cloud.ScopeUsage, session cloud.Session, logger cloud.Logger, target runtime.Object) s3iface.S3API {
-	s3Client := s3.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger)).WithLogger(awslogs.NewWrapLogr(logger)))
+func NewS3Client(scopeUser cloud.ScopeUsage, session cloud.Session, logger logger.Wrapper, target runtime.Object) s3iface.S3API {
+	s3Client := s3.New(session.Session(), aws.NewConfig().WithLogLevel(awslogs.GetAWSLogLevel(logger.GetLogger())).WithLogger(awslogs.NewWrapLogr(logger.GetLogger())))
 	s3Client.Handlers.Build.PushFrontNamed(getUserAgentHandler())
 	s3Client.Handlers.CompleteAttempt.PushFront(awsmetrics.CaptureRequestMetrics(scopeUser.ControllerName()))
 	s3Client.Handlers.Complete.PushBack(recordAWSPermissionsIssue(target))
diff --git a/pkg/cloud/scope/cluster.go b/pkg/cloud/scope/cluster.go
index 10c755750..dd376e015 100644
--- a/pkg/cloud/scope/cluster.go
+++ b/pkg/cloud/scope/cluster.go
@@ -21,7 +21,6 @@ import (
 	"fmt"
 
 	awsclient "github.com/aws/aws-sdk-go/aws/client"
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	"k8s.io/klog/v2"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -29,6 +28,7 @@ import (
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/throttle"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util/conditions"
 	"sigs.k8s.io/cluster-api/util/patch"
@@ -37,7 +37,7 @@ import (
 // ClusterScopeParams defines the input parameters used to create a new Scope.
 type ClusterScopeParams struct {
 	Client         client.Client
-	Logger         *logr.Logger
+	Logger         *logger.Logger
 	Cluster        *clusterv1.Cluster
 	AWSCluster     *infrav1.AWSCluster
 	ControllerName string
@@ -57,7 +57,7 @@ func NewClusterScope(params ClusterScopeParams) (*ClusterScope, error) {
 
 	if params.Logger == nil {
 		log := klog.Background()
-		params.Logger = &log
+		params.Logger = logger.NewLogger(log)
 	}
 
 	clusterScope := &ClusterScope{
@@ -68,7 +68,7 @@ func NewClusterScope(params ClusterScopeParams) (*ClusterScope, error) {
 		controllerName: params.ControllerName,
 	}
 
-	session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, clusterScope, params.AWSCluster.Spec.Region, params.Endpoints, *params.Logger)
+	session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, clusterScope, params.AWSCluster.Spec.Region, params.Endpoints, params.Logger)
 	if err != nil {
 		return nil, errors.Errorf("failed to create aws session: %v", err)
 	}
@@ -87,7 +87,7 @@ func NewClusterScope(params ClusterScopeParams) (*ClusterScope, error) {
 
 // ClusterScope defines the basic context for an actuator to operate upon.
 type ClusterScope struct {
-	logr.Logger
+	logger.Logger
 	client      client.Client
 	patchHelper *patch.Helper
 
diff --git a/pkg/cloud/scope/fargate.go b/pkg/cloud/scope/fargate.go
index d9a32279a..e35dc7885 100644
--- a/pkg/cloud/scope/fargate.go
+++ b/pkg/cloud/scope/fargate.go
@@ -20,7 +20,6 @@ import (
 	"context"
 
 	awsclient "github.com/aws/aws-sdk-go/aws/client"
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	"k8s.io/klog/v2"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -30,6 +29,7 @@ import (
 	expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/throttle"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util/conditions"
 	"sigs.k8s.io/cluster-api/util/patch"
@@ -38,7 +38,7 @@ import (
 // FargateProfileScopeParams defines the input parameters used to create a new Scope.
 type FargateProfileScopeParams struct {
 	Client         client.Client
-	Logger         *logr.Logger
+	Logger         *logger.Logger
 	Cluster        *clusterv1.Cluster
 	ControlPlane   *ekscontrolplanev1.AWSManagedControlPlane
 	FargateProfile *expinfrav1.AWSFargateProfile
@@ -57,7 +57,7 @@ func NewFargateProfileScope(params FargateProfileScopeParams) (*FargateProfileSc
 	}
 	if params.Logger == nil {
 		log := klog.Background()
-		params.Logger = &log
+		params.Logger = logger.NewLogger(log)
 	}
 
 	managedScope := &ManagedControlPlaneScope{
@@ -68,7 +68,7 @@ func NewFargateProfileScope(params FargateProfileScopeParams) (*FargateProfileSc
 		controllerName: params.ControllerName,
 	}
 
-	session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, managedScope, params.ControlPlane.Spec.Region, params.Endpoints, *params.Logger)
+	session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, managedScope, params.ControlPlane.Spec.Region, params.Endpoints, params.Logger)
 	if err != nil {
 		return nil, errors.Errorf("failed to create aws session: %v", err)
 	}
@@ -94,7 +94,7 @@ func NewFargateProfileScope(params FargateProfileScopeParams) (*FargateProfileSc
 
 // FargateProfileScope defines the basic context for an actuator to operate upon.
 type FargateProfileScope struct {
-	logr.Logger
+	logger.Logger
 	Client      client.Client
 	patchHelper *patch.Helper
 
diff --git a/pkg/cloud/scope/launchtemplate.go b/pkg/cloud/scope/launchtemplate.go
index c504a11d3..2281c660d 100644
--- a/pkg/cloud/scope/launchtemplate.go
+++ b/pkg/cloud/scope/launchtemplate.go
@@ -22,7 +22,7 @@ import (
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
-	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util/conditions"
 )
@@ -47,7 +47,7 @@ type LaunchTemplateScope interface {
 	GetEC2Scope() EC2Scope
 
 	client.Client
-	cloud.Logger
+	logger.Wrapper
 }
 
 type ResourceServiceToUpdate struct {
diff --git a/pkg/cloud/scope/machine.go b/pkg/cloud/scope/machine.go
index 696f61e01..a14686cc8 100644
--- a/pkg/cloud/scope/machine.go
+++ b/pkg/cloud/scope/machine.go
@@ -21,7 +21,6 @@ import (
 	"encoding/base64"
 	"fmt"
 
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
@@ -31,6 +30,7 @@ import (
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/eks/api/v1beta2"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/controllers/noderefutil"
 	capierrors "sigs.k8s.io/cluster-api/errors"
@@ -43,7 +43,7 @@ import (
 // MachineScopeParams defines the input parameters used to create a new MachineScope.
 type MachineScopeParams struct {
 	Client       client.Client
-	Logger       *logr.Logger
+	Logger       *logger.Logger
 	Cluster      *clusterv1.Cluster
 	Machine      *clusterv1.Machine
 	InfraCluster EC2Scope
@@ -71,7 +71,7 @@ func NewMachineScope(params MachineScopeParams) (*MachineScope, error) {
 
 	if params.Logger == nil {
 		log := klog.Background()
-		params.Logger = &log
+		params.Logger = logger.NewLogger(log)
 	}
 
 	helper, err := patch.NewHelper(params.AWSMachine, params.Client)
@@ -92,7 +92,7 @@ func NewMachineScope(params MachineScopeParams) (*MachineScope, error) {
 
 // MachineScope defines a scope defined around a machine and its cluster.
 type MachineScope struct {
-	logr.Logger
+	logger.Logger
 	client      client.Client
 	patchHelper *patch.Helper
 
diff --git a/pkg/cloud/scope/machinepool.go b/pkg/cloud/scope/machinepool.go
index fa75e8699..66beb5261 100644
--- a/pkg/cloud/scope/machinepool.go
+++ b/pkg/cloud/scope/machinepool.go
@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -34,6 +33,7 @@ import (
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/eks/api/v1beta2"
 	expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/controllers/remote"
 	capierrors "sigs.k8s.io/cluster-api/errors"
@@ -45,7 +45,7 @@ import (
 
 // MachinePoolScope defines a scope defined around a machine and its cluster.
 type MachinePoolScope struct {
-	logr.Logger
+	logger.Logger
 	client.Client
 	patchHelper *patch.Helper
 
@@ -58,7 +58,7 @@ type MachinePoolScope struct {
 // MachinePoolScopeParams defines a scope defined around a machine and its cluster.
 type MachinePoolScopeParams struct {
 	client.Client
-	Logger *logr.Logger
+	Logger *logger.Logger
 
 	Cluster        *clusterv1.Cluster
 	MachinePool    *expclusterv1.MachinePool
@@ -95,7 +95,7 @@ func NewMachinePoolScope(params MachinePoolScopeParams) (*MachinePoolScope, erro
 
 	if params.Logger == nil {
 		log := klog.Background()
-		params.Logger = &log
+		params.Logger = logger.NewLogger(log)
 	}
 
 	helper, err := patch.NewHelper(params.AWSMachinePool, params.Client)
diff --git a/pkg/cloud/scope/managedcontrolplane.go b/pkg/cloud/scope/managedcontrolplane.go
index 27eb6c2cf..2ac37ab3c 100644
--- a/pkg/cloud/scope/managedcontrolplane.go
+++ b/pkg/cloud/scope/managedcontrolplane.go
@@ -23,7 +23,6 @@ import (
 
 	amazoncni "github.com/aws/amazon-vpc-cni-k8s/pkg/apis/crd/v1alpha1"
 	awsclient "github.com/aws/aws-sdk-go/aws/client"
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
@@ -36,6 +35,7 @@ import (
 	ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/eks/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/throttle"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/controllers/remote"
 	"sigs.k8s.io/cluster-api/util/patch"
@@ -55,7 +55,7 @@ func init() {
 // ManagedControlPlaneScopeParams defines the input parameters used to create a new Scope.
 type ManagedControlPlaneScopeParams struct {
 	Client         client.Client
-	Logger         *logr.Logger
+	Logger         *logger.Logger
 	Cluster        *clusterv1.Cluster
 	ControlPlane   *ekscontrolplanev1.AWSManagedControlPlane
 	ControllerName string
@@ -77,7 +77,7 @@ func NewManagedControlPlaneScope(params ManagedControlPlaneScopeParams) (*Manage
 	}
 	if params.Logger == nil {
 		log := klog.Background()
-		params.Logger = &log
+		params.Logger = logger.NewLogger(log)
 	}
 
 	managedScope := &ManagedControlPlaneScope{
@@ -92,7 +92,7 @@ func NewManagedControlPlaneScope(params ManagedControlPlaneScopeParams) (*Manage
 		allowAdditionalRoles: params.AllowAdditionalRoles,
 		enableIAM:            params.EnableIAM,
 	}
-	session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, managedScope, params.ControlPlane.Spec.Region, params.Endpoints, *params.Logger)
+	session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, managedScope, params.ControlPlane.Spec.Region, params.Endpoints, params.Logger)
 	if err != nil {
 		return nil, errors.Errorf("failed to create aws session: %v", err)
 	}
@@ -111,7 +111,7 @@ func NewManagedControlPlaneScope(params ManagedControlPlaneScopeParams) (*Manage
 
 // ManagedControlPlaneScope defines the basic context for an actuator to operate upon.
 type ManagedControlPlaneScope struct {
-	logr.Logger
+	logger.Logger
 	Client      client.Client
 	patchHelper *patch.Helper
 
diff --git a/pkg/cloud/scope/managednodegroup.go b/pkg/cloud/scope/managednodegroup.go
index b7687889b..1f32bb0c4 100644
--- a/pkg/cloud/scope/managednodegroup.go
+++ b/pkg/cloud/scope/managednodegroup.go
@@ -21,7 +21,6 @@ import (
 	"fmt"
 
 	awsclient "github.com/aws/aws-sdk-go/aws/client"
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -35,6 +34,7 @@ import (
 	expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/exp/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/throttle"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util/conditions"
@@ -44,7 +44,7 @@ import (
 // ManagedMachinePoolScopeParams defines the input parameters used to create a new Scope.
 type ManagedMachinePoolScopeParams struct {
 	Client             client.Client
-	Logger             *logr.Logger
+	Logger             *logger.Logger
 	Cluster            *clusterv1.Cluster
 	ControlPlane       *ekscontrolplanev1.AWSManagedControlPlane
 	ManagedMachinePool *expinfrav1.AWSManagedMachinePool
@@ -73,7 +73,7 @@ func NewManagedMachinePoolScope(params ManagedMachinePoolScopeParams) (*ManagedM
 	}
 	if params.Logger == nil {
 		log := klog.Background()
-		params.Logger = &log
+		params.Logger = logger.NewLogger(log)
 	}
 
 	managedScope := &ManagedControlPlaneScope{
@@ -83,7 +83,7 @@ func NewManagedMachinePoolScope(params ManagedMachinePoolScopeParams) (*ManagedM
 		ControlPlane:   params.ControlPlane,
 		controllerName: params.ControllerName,
 	}
-	session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, managedScope, params.ControlPlane.Spec.Region, params.Endpoints, *params.Logger)
+	session, serviceLimiters, err := sessionForClusterWithRegion(params.Client, managedScope, params.ControlPlane.Spec.Region, params.Endpoints, params.Logger)
 	if err != nil {
 		return nil, errors.Errorf("failed to create aws session: %v", err)
 	}
@@ -112,7 +112,7 @@ func NewManagedMachinePoolScope(params ManagedMachinePoolScopeParams) (*ManagedM
 
 // ManagedMachinePoolScope defines the basic context for an actuator to operate upon.
 type ManagedMachinePoolScope struct {
-	logr.Logger
+	logger.Logger
 	client.Client
 	patchHelper *patch.Helper
 
diff --git a/pkg/cloud/scope/session.go b/pkg/cloud/scope/session.go
index 269efa493..f2338a260 100644
--- a/pkg/cloud/scope/session.go
+++ b/pkg/cloud/scope/session.go
@@ -30,7 +30,6 @@ import (
 	"github.com/aws/aws-sdk-go/service/elbv2"
 	"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
 	"github.com/aws/aws-sdk-go/service/secretsmanager"
-	"github.com/go-logr/logr"
 	"github.com/google/go-cmp/cmp"
 	"github.com/pkg/errors"
 	corev1 "k8s.io/api/core/v1"
@@ -41,6 +40,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/identity"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/throttle"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/util/system"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util"
@@ -104,9 +104,9 @@ func sessionForRegion(region string, endpoint []ServiceEndpoint) (*session.Sessi
 	return ns, sl, nil
 }
 
-func sessionForClusterWithRegion(k8sClient client.Client, clusterScoper cloud.ClusterScoper, region string, endpoint []ServiceEndpoint, logger logr.Logger) (*session.Session, throttle.ServiceLimiters, error) {
-	log := logger.WithName("identity")
-	log.V(4).Info("Creating an AWS Session")
+func sessionForClusterWithRegion(k8sClient client.Client, clusterScoper cloud.ClusterScoper, region string, endpoint []ServiceEndpoint, log logger.Wrapper) (*session.Session, throttle.ServiceLimiters, error) {
+	log = log.WithName("identity")
+	log.Trace("Creating an AWS Session")
 
 	resolver := func(service, region string, optFns ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
 		for _, s := range endpoint {
@@ -140,7 +140,7 @@ func sessionForClusterWithRegion(k8sClient client.Client, clusterScoper cloud.Cl
 			provider = cachedProvider.(identity.AWSPrincipalTypeProvider)
 		} else {
 			isChanged = true
-			// add this providers to the cache
+			// add this provider to the cache
 			providerCache.Store(providerHash, provider)
 		}
 		awsProviders[i] = provider.(credentials.Provider)
@@ -252,16 +252,16 @@ func buildProvidersForRef(
 	k8sClient client.Client,
 	clusterScoper cloud.ClusterScoper,
 	ref *infrav1.AWSIdentityReference,
-	log logr.Logger) ([]identity.AWSPrincipalTypeProvider, error) {
+	log logger.Wrapper) ([]identity.AWSPrincipalTypeProvider, error) {
 	if ref == nil {
-		log.V(4).Info("AWSCluster does not have a IdentityRef specified")
+		log.Trace("AWSCluster does not have a IdentityRef specified")
 		return providers, nil
 	}
 
 	var provider identity.AWSPrincipalTypeProvider
 	identityObjectKey := client.ObjectKey{Name: ref.Name}
 	log = log.WithValues("identityKey", identityObjectKey)
-	log.V(4).Info("Getting identity")
+	log.Trace("Getting identity")
 
 	switch ref.Kind {
 	case infrav1.ControllerIdentityKind:
@@ -283,7 +283,7 @@ func buildProvidersForRef(
 		if err != nil {
 			return providers, err
 		}
-		log.V(4).Info("Principal retrieved")
+		log.Trace("Principal retrieved")
 		canUse, err := isClusterPermittedToUsePrincipal(k8sClient, roleIdentity.Spec.AllowedNamespaces, clusterScoper.Namespace())
 		if err != nil {
 			return providers, err
@@ -404,7 +404,7 @@ func buildAWSClusterControllerIdentity(ctx context.Context, identityObjectKey cl
 	return nil
 }
 
-func getProvidersForCluster(ctx context.Context, k8sClient client.Client, clusterScoper cloud.ClusterScoper, log logr.Logger) ([]identity.AWSPrincipalTypeProvider, error) {
+func getProvidersForCluster(ctx context.Context, k8sClient client.Client, clusterScoper cloud.ClusterScoper, log logger.Wrapper) ([]identity.AWSPrincipalTypeProvider, error) {
 	providers := make([]identity.AWSPrincipalTypeProvider, 0)
 	providers, err := buildProvidersForRef(ctx, providers, k8sClient, clusterScoper, clusterScoper.IdentityRef(), log)
 	if err != nil {
diff --git a/pkg/cloud/scope/shared.go b/pkg/cloud/scope/shared.go
index d2e512e3c..2a472c779 100644
--- a/pkg/cloud/scope/shared.go
+++ b/pkg/cloud/scope/shared.go
@@ -19,10 +19,10 @@ package scope
 import (
 	"fmt"
 
-	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 )
 
 var (
@@ -46,19 +46,19 @@ type subnetsPlacementStratgey interface {
 	Place(input *placementInput) ([]string, error)
 }
 
-func newDefaultSubnetPlacementStrategy(logger *logr.Logger) (subnetsPlacementStratgey, error) {
+func newDefaultSubnetPlacementStrategy(logger logger.Wrapper) (subnetsPlacementStratgey, error) {
 	if logger == nil {
 		return nil, ErrLoggerRequired
 	}
 
 	return &defaultSubnetPlacementStrategy{
-		logger: *logger,
+		logger: logger,
 	}, nil
 }
 
 // defaultSubnetPlacementStrategy is the default strategy for subnet placement.
 type defaultSubnetPlacementStrategy struct {
-	logger logr.Logger
+	logger logger.Wrapper
 }
 
 // Place works out the subnet placement based on the following precedence:
@@ -69,12 +69,12 @@ type defaultSubnetPlacementStrategy struct {
 // In Cluster API Availability Zone can also be referred to by the name `Failure Domain`.
 func (p *defaultSubnetPlacementStrategy) Place(input *placementInput) ([]string, error) {
 	if len(input.SpecSubnetIDs) > 0 {
-		p.logger.V(2).Info("using subnets from the spec")
+		p.logger.Debug("using subnets from the spec")
 		return input.SpecSubnetIDs, nil
 	}
 
 	if len(input.SpecAvailabilityZones) > 0 {
-		p.logger.V(2).Info("determining subnets to use from the spec availability zones")
+		p.logger.Debug("determining subnets to use from the spec availability zones")
 		subnetIDs, err := p.getSubnetsForAZs(input.SpecAvailabilityZones, input.ControlplaneSubnets)
 		if err != nil {
 			return nil, fmt.Errorf("getting subnets for spec azs: %w", err)
@@ -84,7 +84,7 @@ func (p *defaultSubnetPlacementStrategy) Place(input *placementInput) ([]string,
 	}
 
 	if len(input.ParentAvailabilityZones) > 0 {
-		p.logger.V(2).Info("determining subnets to use from the parents availability zones")
+		p.logger.Debug("determining subnets to use from the parents availability zones")
 		subnetIDs, err := p.getSubnetsForAZs(input.ParentAvailabilityZones, input.ControlplaneSubnets)
 		if err != nil {
 			return nil, fmt.Errorf("getting subnets for parent azs: %w", err)
@@ -95,7 +95,7 @@ func (p *defaultSubnetPlacementStrategy) Place(input *placementInput) ([]string,
 
 	controlPlaneSubnetIDs := input.ControlplaneSubnets.FilterPrivate().IDs()
 	if len(controlPlaneSubnetIDs) > 0 {
-		p.logger.V(2).Info("using all the private subnets from the control plane")
+		p.logger.Debug("using all the private subnets from the control plane")
 		return controlPlaneSubnetIDs, nil
 	}
 
diff --git a/pkg/cloud/services/autoscaling/autoscalinggroup.go b/pkg/cloud/services/autoscaling/autoscalinggroup.go
index 44320f3fa..18957ffee 100644
--- a/pkg/cloud/services/autoscaling/autoscalinggroup.go
+++ b/pkg/cloud/services/autoscaling/autoscalinggroup.go
@@ -129,7 +129,7 @@ func (s *Service) ASGIfExists(name *string) (*expinfrav1.AutoScalingGroup, error
 
 // GetASGByName returns the existing ASG or nothing if it doesn't exist.
 func (s *Service) GetASGByName(scope *scope.MachinePoolScope) (*expinfrav1.AutoScalingGroup, error) {
-	s.scope.V(2).Info("Looking for existing AutoScalingGroup by name")
+	s.scope.Debug("Looking for existing AutoScalingGroup by name")
 
 	input := &autoscaling.DescribeAutoScalingGroupsInput{
 		AutoScalingGroupNames: []*string{
@@ -245,7 +245,7 @@ func (s *Service) DeleteASGAndWait(name string) error {
 		return err
 	}
 
-	s.scope.V(2).Info("Waiting for ASG to be deleted", "name", name)
+	s.scope.Debug("Waiting for ASG to be deleted", "name", name)
 
 	input := &autoscaling.DescribeAutoScalingGroupsInput{
 		AutoScalingGroupNames: aws.StringSlice([]string{name}),
@@ -260,7 +260,7 @@ func (s *Service) DeleteASGAndWait(name string) error {
 
 // DeleteASG will delete the ASG of a service.
 func (s *Service) DeleteASG(name string) error {
-	s.scope.V(2).Info("Attempting to delete ASG", "name", name)
+	s.scope.Debug("Attempting to delete ASG", "name", name)
 
 	input := &autoscaling.DeleteAutoScalingGroupInput{
 		AutoScalingGroupName: aws.String(name),
@@ -271,7 +271,7 @@ func (s *Service) DeleteASG(name string) error {
 		return errors.Wrapf(err, "failed to delete ASG %q", name)
 	}
 
-	s.scope.V(2).Info("Deleted ASG", "name", name)
+	s.scope.Debug("Deleted ASG", "name", name)
 	return nil
 }
 
@@ -419,12 +419,12 @@ func BuildTagsFromMap(asgName string, inTags map[string]string) []*autoscaling.T
 // We may not always have to perform each action, so we check what we're
 // receiving to avoid calling AWS if we don't need to.
 func (s *Service) UpdateResourceTags(resourceID *string, create, remove map[string]string) error {
-	s.scope.V(2).Info("Attempting to update tags on resource", "resource-id", *resourceID)
+	s.scope.Debug("Attempting to update tags on resource", "resource-id", *resourceID)
 	s.scope.Info("updating tags on resource", "resource-id", *resourceID, "create", create, "remove", remove)
 
 	// If we have anything to create or update
 	if len(create) > 0 {
-		s.scope.V(2).Info("Attempting to create tags on resource", "resource-id", *resourceID)
+		s.scope.Debug("Attempting to create tags on resource", "resource-id", *resourceID)
 
 		createOrUpdateTagsInput := &autoscaling.CreateOrUpdateTagsInput{}
 
@@ -437,7 +437,7 @@ func (s *Service) UpdateResourceTags(resourceID *string, create, remove map[stri
 
 	// If we have anything to remove
 	if len(remove) > 0 {
-		s.scope.V(2).Info("Attempting to delete tags on resource", "resource-id", *resourceID)
+		s.scope.Debug("Attempting to delete tags on resource", "resource-id", *resourceID)
 
 		// Convert our remove map into an array of *ec2.Tag
 		removeTagsInput := mapToTags(remove, resourceID)
diff --git a/pkg/cloud/services/awsnode/cni.go b/pkg/cloud/services/awsnode/cni.go
index cc4d760a3..5bfd7e74e 100644
--- a/pkg/cloud/services/awsnode/cni.go
+++ b/pkg/cloud/services/awsnode/cni.go
@@ -296,7 +296,7 @@ func (s *Service) deleteResource(ctx context.Context, remoteClient client.Client
 		if !apierrors.IsNotFound(err) {
 			return fmt.Errorf("deleting resource %s: %w", key, err)
 		}
-		s.scope.V(2).Info(fmt.Sprintf("resource %s was not found, no action", key))
+		s.scope.Debug(fmt.Sprintf("resource %s was not found, no action", key))
 	} else {
 		// resource found, delete if no label or not managed by helm
 		if val, ok := obj.GetLabels()[konfig.ManagedbyLabelKey]; !ok || val != "Helm" {
@@ -304,13 +304,13 @@ func (s *Service) deleteResource(ctx context.Context, remoteClient client.Client
 				if !apierrors.IsNotFound(err) {
 					return fmt.Errorf("deleting %s: %w", key, err)
 				}
-				s.scope.V(2).Info(fmt.Sprintf(
+				s.scope.Debug(fmt.Sprintf(
 					"resource %s was not found, not deleted", key))
 			} else {
-				s.scope.V(2).Info(fmt.Sprintf("resource %s was deleted", key))
+				s.scope.Debug(fmt.Sprintf("resource %s was deleted", key))
 			}
 		} else {
-			s.scope.V(2).Info(fmt.Sprintf("resource %s is managed by helm, not deleted", key))
+			s.scope.Debug(fmt.Sprintf("resource %s is managed by helm, not deleted", key))
 		}
 	}
 
diff --git a/pkg/cloud/services/ec2/ami.go b/pkg/cloud/services/ec2/ami.go
index b5d760e33..8f46c97dd 100644
--- a/pkg/cloud/services/ec2/ami.go
+++ b/pkg/cloud/services/ec2/ami.go
@@ -158,7 +158,7 @@ func (s *Service) defaultAMIIDLookup(amiNameFormat, ownerID, baseOS, kubernetesV
 		return "", errors.Wrapf(err, "failed to find ami")
 	}
 
-	s.scope.V(2).Info("Found and using an existing AMI", "ami-id", aws.StringValue(latestImage.ImageId))
+	s.scope.Debug("Found and using an existing AMI", "ami-id", aws.StringValue(latestImage.ImageId))
 	return aws.StringValue(latestImage.ImageId), nil
 }
 
diff --git a/pkg/cloud/services/ec2/bastion.go b/pkg/cloud/services/ec2/bastion.go
index 5b38fc0e6..37fa6f3fd 100644
--- a/pkg/cloud/services/ec2/bastion.go
+++ b/pkg/cloud/services/ec2/bastion.go
@@ -46,7 +46,7 @@ var (
 // ReconcileBastion ensures a bastion is created for the cluster.
 func (s *Service) ReconcileBastion() error {
 	if !s.scope.Bastion().Enabled {
-		s.scope.V(4).Info("Skipping bastion reconcile")
+		s.scope.Trace("Skipping bastion reconcile")
 		_, err := s.describeBastionInstance()
 		if err != nil {
 			if awserrors.IsNotFound(err) {
@@ -57,11 +57,11 @@ func (s *Service) ReconcileBastion() error {
 		return s.DeleteBastion()
 	}
 
-	s.scope.V(2).Info("Reconciling bastion host")
+	s.scope.Debug("Reconciling bastion host")
 
 	subnets := s.scope.Subnets()
 	if len(subnets.FilterPrivate()) == 0 {
-		s.scope.V(2).Info("No private subnets available, skipping bastion host")
+		s.scope.Debug("No private subnets available, skipping bastion host")
 		return nil
 	} else if len(subnets.FilterPublic()) == 0 {
 		return errors.New("failed to reconcile bastion host, no public subnets are available")
@@ -97,7 +97,7 @@ func (s *Service) ReconcileBastion() error {
 
 	s.scope.SetBastionInstance(instance.DeepCopy())
 	conditions.MarkTrue(s.scope.InfraCluster(), infrav1.BastionHostReadyCondition)
-	s.scope.V(2).Info("Reconcile bastion completed successfully")
+	s.scope.Debug("Reconcile bastion completed successfully")
 
 	return nil
 }
@@ -107,7 +107,7 @@ func (s *Service) DeleteBastion() error {
 	instance, err := s.describeBastionInstance()
 	if err != nil {
 		if awserrors.IsNotFound(err) {
-			s.scope.V(4).Info("bastion instance does not exist")
+			s.scope.Trace("bastion instance does not exist")
 			return nil
 		}
 		return errors.Wrap(err, "unable to describe bastion instance")
diff --git a/pkg/cloud/services/ec2/instances.go b/pkg/cloud/services/ec2/instances.go
index 949202b63..536cd7d19 100644
--- a/pkg/cloud/services/ec2/instances.go
+++ b/pkg/cloud/services/ec2/instances.go
@@ -44,7 +44,7 @@ import (
 
 // GetRunningInstanceByTags returns the existing instance or nothing if it doesn't exist.
 func (s *Service) GetRunningInstanceByTags(scope *scope.MachineScope) (*infrav1.Instance, error) {
-	s.scope.V(2).Info("Looking for existing machine instance by tags")
+	s.scope.Debug("Looking for existing machine instance by tags")
 
 	input := &ec2.DescribeInstancesInput{
 		Filters: []*ec2.Filter{
@@ -84,7 +84,7 @@ func (s *Service) InstanceIfExists(id *string) (*infrav1.Instance, error) {
 		return nil, nil
 	}
 
-	s.scope.V(2).Info("Looking for instance by id", "instance-id", *id)
+	s.scope.Debug("Looking for instance by id", "instance-id", *id)
 
 	input := &ec2.DescribeInstancesInput{
 		InstanceIds: []*string{id},
@@ -111,7 +111,7 @@ func (s *Service) InstanceIfExists(id *string) (*infrav1.Instance, error) {
 
 // CreateInstance runs an ec2 instance.
 func (s *Service) CreateInstance(scope *scope.MachineScope, userData []byte, userDataFormat string) (*infrav1.Instance, error) {
-	s.scope.V(2).Info("Creating an instance for a machine")
+	s.scope.Debug("Creating an instance for a machine")
 
 	input := &infrav1.Instance{
 		Type:              scope.AWSMachine.Spec.InstanceType,
@@ -229,7 +229,7 @@ func (s *Service) CreateInstance(scope *scope.MachineScope, userData []byte, use
 
 	input.Tenancy = scope.AWSMachine.Spec.Tenancy
 
-	s.scope.V(2).Info("Running instance", "machine-role", scope.Role())
+	s.scope.Debug("Running instance", "machine-role", scope.Role())
 	out, err := s.runInstance(scope.Role(), input)
 	if err != nil {
 		// Only record the failure event if the error is not related to failed dependencies.
@@ -242,7 +242,7 @@ func (s *Service) CreateInstance(scope *scope.MachineScope, userData []byte, use
 
 	if len(input.NetworkInterfaces) > 0 {
 		for _, id := range input.NetworkInterfaces {
-			s.scope.V(2).Info("Attaching security groups to provided network interface", "groups", input.SecurityGroupIDs, "interface", id)
+			s.scope.Debug("Attaching security groups to provided network interface", "groups", input.SecurityGroupIDs, "interface", id)
 			if err := s.attachSecurityGroupsToNetworkInterface(input.SecurityGroupIDs, id); err != nil {
 				return nil, err
 			}
@@ -436,7 +436,7 @@ func (s *Service) GetCoreNodeSecurityGroups(scope scope.LaunchTemplateScope) ([]
 // TerminateInstance terminates an EC2 instance.
 // Returns nil on success, error in all other cases.
 func (s *Service) TerminateInstance(instanceID string) error {
-	s.scope.V(2).Info("Attempting to terminate instance", "instance-id", instanceID)
+	s.scope.Debug("Attempting to terminate instance", "instance-id", instanceID)
 
 	input := &ec2.TerminateInstancesInput{
 		InstanceIds: aws.StringSlice([]string{instanceID}),
@@ -446,7 +446,7 @@ func (s *Service) TerminateInstance(instanceID string) error {
 		return errors.Wrapf(err, "failed to terminate instance with id %q", instanceID)
 	}
 
-	s.scope.V(2).Info("Terminated instance", "instance-id", instanceID)
+	s.scope.Debug("Terminated instance", "instance-id", instanceID)
 	return nil
 }
 
@@ -457,7 +457,7 @@ func (s *Service) TerminateInstanceAndWait(instanceID string) error {
 		return err
 	}
 
-	s.scope.V(2).Info("Waiting for EC2 instance to terminate", "instance-id", instanceID)
+	s.scope.Debug("Waiting for EC2 instance to terminate", "instance-id", instanceID)
 
 	input := &ec2.DescribeInstancesInput{
 		InstanceIds: aws.StringSlice([]string{instanceID}),
@@ -481,7 +481,7 @@ func (s *Service) runInstance(role string, i *infrav1.Instance) (*infrav1.Instan
 		UserData:     i.UserData,
 	}
 
-	s.scope.V(2).Info("userData size", "bytes", len(*i.UserData), "role", role)
+	s.scope.Debug("userData size", "bytes", len(*i.UserData), "role", role)
 
 	if len(i.NetworkInterfaces) > 0 {
 		netInterfaces := make([]*ec2.InstanceNetworkInterfaceSpecification, 0, len(i.NetworkInterfaces))
@@ -572,16 +572,16 @@ func (s *Service) runInstance(role string, i *infrav1.Instance) (*infrav1.Instan
 	}
 
 	waitTimeout := 1 * time.Minute
-	s.scope.V(2).Info("Waiting for instance to be in running state", "instance-id", *out.Instances[0].InstanceId, "timeout", waitTimeout.String())
+	s.scope.Debug("Waiting for instance to be in running state", "instance-id", *out.Instances[0].InstanceId, "timeout", waitTimeout.String())
 	ctx, cancel := context.WithTimeout(aws.BackgroundContext(), waitTimeout)
 	defer cancel()
 
 	if err := s.EC2Client.WaitUntilInstanceRunningWithContext(
 		ctx,
 		&ec2.DescribeInstancesInput{InstanceIds: []*string{out.Instances[0].InstanceId}},
-		request.WithWaiterLogger(awslogs.NewWrapLogr(s.scope)),
+		request.WithWaiterLogger(awslogs.NewWrapLogr(s.scope.GetLogger())),
 	); err != nil {
-		s.scope.V(2).Info("Could not determine if Machine is running. Machine state might be unavailable until next renconciliation.")
+		s.scope.Debug("Could not determine if Machine is running. Machine state might be unavailable until next renconciliation.")
 	}
 
 	return s.SDKToInstance(out.Instances[0])
@@ -639,14 +639,14 @@ func (s *Service) GetInstanceSecurityGroups(instanceID string) (map[string][]str
 // UpdateInstanceSecurityGroups modifies the security groups of the given
 // EC2 instance.
 func (s *Service) UpdateInstanceSecurityGroups(instanceID string, ids []string) error {
-	s.scope.V(2).Info("Attempting to update security groups on instance", "instance-id", instanceID)
+	s.scope.Debug("Attempting to update security groups on instance", "instance-id", instanceID)
 
 	enis, err := s.getInstanceENIs(instanceID)
 	if err != nil {
 		return errors.Wrapf(err, "failed to get ENIs for instance %q", instanceID)
 	}
 
-	s.scope.V(3).Info("Found ENIs on instance", "number-of-enis", len(enis), "instance-id", instanceID)
+	s.scope.Debug("Found ENIs on instance", "number-of-enis", len(enis), "instance-id", instanceID)
 
 	for _, eni := range enis {
 		if err := s.attachSecurityGroupsToNetworkInterface(ids, aws.StringValue(eni.NetworkInterfaceId)); err != nil {
@@ -662,11 +662,11 @@ func (s *Service) UpdateInstanceSecurityGroups(instanceID string, ids []string)
 // We may not always have to perform each action, so we check what we're
 // receiving to avoid calling AWS if we don't need to.
 func (s *Service) UpdateResourceTags(resourceID *string, create, remove map[string]string) error {
-	s.scope.V(2).Info("Attempting to update tags on resource", "resource-id", *resourceID)
+	s.scope.Debug("Attempting to update tags on resource", "resource-id", *resourceID)
 
 	// If we have anything to create or update
 	if len(create) > 0 {
-		s.scope.V(2).Info("Attempting to create tags on resource", "resource-id", *resourceID)
+		s.scope.Debug("Attempting to create tags on resource", "resource-id", *resourceID)
 
 		// Convert our create map into an array of *ec2.Tag
 		createTagsInput := converters.MapToTags(create)
@@ -685,7 +685,7 @@ func (s *Service) UpdateResourceTags(resourceID *string, create, remove map[stri
 
 	// If we have anything to remove
 	if len(remove) > 0 {
-		s.scope.V(2).Info("Attempting to delete tags on resource", "resource-id", *resourceID)
+		s.scope.Debug("Attempting to delete tags on resource", "resource-id", *resourceID)
 
 		// Convert our remove map into an array of *ec2.Tag
 		removeTagsInput := converters.MapToTags(remove)
diff --git a/pkg/cloud/services/ec2/launchtemplate.go b/pkg/cloud/services/ec2/launchtemplate.go
index 519c78d53..74877132f 100644
--- a/pkg/cloud/services/ec2/launchtemplate.go
+++ b/pkg/cloud/services/ec2/launchtemplate.go
@@ -327,7 +327,7 @@ func (s *Service) GetLaunchTemplate(launchTemplateName string) (*expinfrav1.AWSL
 		return nil, "", nil
 	}
 
-	s.scope.V(2).Info("Looking for existing LaunchTemplates")
+	s.scope.Debug("Looking for existing LaunchTemplates")
 
 	input := &ec2.DescribeLaunchTemplateVersionsInput{
 		LaunchTemplateName: aws.String(launchTemplateName),
@@ -422,7 +422,7 @@ func (s *Service) CreateLaunchTemplate(scope scope.LaunchTemplateScope, imageID
 
 // CreateLaunchTemplateVersion will create a launch template.
 func (s *Service) CreateLaunchTemplateVersion(id string, scope scope.LaunchTemplateScope, imageID *string, userData []byte) error {
-	s.scope.V(2).Info("creating new launch template version", "machine-pool", scope.LaunchTemplateName())
+	s.scope.Debug("creating new launch template version", "machine-pool", scope.LaunchTemplateName())
 
 	launchTemplateData, err := s.createLaunchTemplateData(scope, imageID, userData)
 	if err != nil {
@@ -547,7 +547,7 @@ func volumeToLaunchTemplateBlockDeviceMappingRequest(v *infrav1.Volume) *ec2.Lau
 
 // DeleteLaunchTemplate delete a launch template.
 func (s *Service) DeleteLaunchTemplate(id string) error {
-	s.scope.V(2).Info("Deleting launch template", "id", id)
+	s.scope.Debug("Deleting launch template", "id", id)
 
 	input := &ec2.DeleteLaunchTemplateInput{
 		LaunchTemplateId: aws.String(id),
@@ -557,7 +557,7 @@ func (s *Service) DeleteLaunchTemplate(id string) error {
 		return errors.Wrapf(err, "failed to delete launch template %q", id)
 	}
 
-	s.scope.V(2).Info("Deleted launch template", "id", id)
+	s.scope.Debug("Deleted launch template", "id", id)
 	return nil
 }
 
@@ -616,7 +616,7 @@ func (s *Service) GetLaunchTemplateLatestVersion(id string) (string, error) {
 }
 
 func (s *Service) deleteLaunchTemplateVersion(id string, version *int64) error {
-	s.scope.V(2).Info("Deleting launch template version", "id", id)
+	s.scope.Debug("Deleting launch template version", "id", id)
 
 	if version == nil {
 		return errors.New("version is a nil pointer")
@@ -633,7 +633,7 @@ func (s *Service) deleteLaunchTemplateVersion(id string, version *int64) error {
 		return err
 	}
 
-	s.scope.V(2).Info("Deleted launch template", "id", id, "version", *version)
+	s.scope.Debug("Deleted launch template", "id", id, "version", *version)
 	return nil
 }
 
diff --git a/pkg/cloud/services/eks/addons.go b/pkg/cloud/services/eks/addons.go
index c2300b6cb..afe7614d9 100644
--- a/pkg/cloud/services/eks/addons.go
+++ b/pkg/cloud/services/eks/addons.go
@@ -43,7 +43,7 @@ func (s *Service) reconcileAddons(ctx context.Context) error {
 	}
 
 	// Get installed addons for the cluster
-	s.scope.V(2).Info("getting installed eks addons", "cluster", eksClusterName)
+	s.scope.Debug("getting installed eks addons", "cluster", eksClusterName)
 	installed, err := s.getClusterAddonsInstalled(eksClusterName, addonNames)
 	if err != nil {
 		return fmt.Errorf("getting installed eks addons: %w", err)
@@ -59,18 +59,18 @@ func (s *Service) reconcileAddons(ctx context.Context) error {
 	}
 
 	//  Compute operations to move installed to desired
-	s.scope.V(2).Info("creating eks addons plan", "cluster", eksClusterName, "numdesired", len(desiredAddons), "numinstalled", len(installed))
+	s.scope.Debug("creating eks addons plan", "cluster", eksClusterName, "numdesired", len(desiredAddons), "numinstalled", len(installed))
 	addonsPlan := eksaddons.NewPlan(eksClusterName, desiredAddons, installed, s.EKSClient)
 	procedures, err := addonsPlan.Create(ctx)
 	if err != nil {
 		s.scope.Error(err, "failed creating eks addons plane")
 		return fmt.Errorf("creating eks addons plan: %w", err)
 	}
-	s.scope.V(2).Info("computed EKS addons plan", "numprocs", len(procedures))
+	s.scope.Debug("computed EKS addons plan", "numprocs", len(procedures))
 
 	// Perform required operations
 	for _, procedure := range procedures {
-		s.scope.V(2).Info("Executing addon procedure", "name", procedure.Name())
+		s.scope.Debug("Executing addon procedure", "name", procedure.Name())
 		if err := procedure.Do(ctx); err != nil {
 			s.scope.Error(err, "failed executing addon procedure", "name", procedure.Name())
 			return fmt.Errorf("%s: %w", procedure.Name(), err)
@@ -80,7 +80,7 @@ func (s *Service) reconcileAddons(ctx context.Context) error {
 	// Update status with addons installed details
 	// Note: we are not relying on the computed state from the operations as we still want
 	// to update the state even if there are no operations to capture things like status changes
-	s.scope.V(2).Info("getting installed eks addons to update status", "cluster", eksClusterName)
+	s.scope.Debug("getting installed eks addons to update status", "cluster", eksClusterName)
 	addonState, err := s.getInstalledState(eksClusterName, addonNames)
 	if err != nil {
 		return fmt.Errorf("getting installed state of eks addons: %w", err)
@@ -92,13 +92,13 @@ func (s *Service) reconcileAddons(ctx context.Context) error {
 		return fmt.Errorf("failed to update control plane: %w", err)
 	}
 	record.Eventf(s.scope.ControlPlane, "SuccessfulReconcileEKSClusterAddons", "Reconciled addons for EKS Cluster %s", s.scope.KubernetesClusterName())
-	s.scope.V(2).Info("Reconcile EKS addons completed successfully")
+	s.scope.Debug("Reconcile EKS addons completed successfully")
 
 	return nil
 }
 
 func (s *Service) getClusterAddonsInstalled(eksClusterName string, addonNames []*string) ([]*eksaddons.EKSAddon, error) {
-	s.V(2).Info("getting eks addons installed")
+	s.Debug("getting eks addons installed")
 
 	addonsInstalled := []*eksaddons.EKSAddon{}
 	if len(addonNames) == 0 {
@@ -119,7 +119,7 @@ func (s *Service) getClusterAddonsInstalled(eksClusterName string, addonNames []
 		if describeOutput.Addon == nil {
 			continue
 		}
-		s.scope.V(2).Info("describe output", "output", describeOutput.Addon)
+		s.scope.Debug("describe output", "output", describeOutput.Addon)
 
 		installedAddon := &eksaddons.EKSAddon{
 			Name:                  describeOutput.Addon.AddonName,
@@ -140,7 +140,7 @@ func (s *Service) getClusterAddonsInstalled(eksClusterName string, addonNames []
 }
 
 func (s *Service) getInstalledState(eksClusterName string, addonNames []*string) ([]ekscontrolplanev1.AddonState, error) {
-	s.V(2).Info("getting eks addons installed to create state")
+	s.Debug("getting eks addons installed to create state")
 
 	addonState := []ekscontrolplanev1.AddonState{}
 	if len(addonNames) == 0 {
@@ -161,7 +161,7 @@ func (s *Service) getInstalledState(eksClusterName string, addonNames []*string)
 		if describeOutput.Addon == nil {
 			continue
 		}
-		s.scope.V(2).Info("describe output", "output", describeOutput.Addon)
+		s.scope.Debug("describe output", "output", describeOutput.Addon)
 
 		installedAddonState := converters.AddonSDKToAddonState(describeOutput.Addon)
 		addonState = append(addonState, *installedAddonState)
@@ -171,7 +171,7 @@ func (s *Service) getInstalledState(eksClusterName string, addonNames []*string)
 }
 
 func (s *Service) listAddons(eksClusterName string) ([]*string, error) {
-	s.V(2).Info("getting list of eks addons")
+	s.Debug("getting list of eks addons")
 
 	input := &eks.ListAddonsInput{
 		ClusterName: &eksClusterName,
diff --git a/pkg/cloud/services/eks/cluster.go b/pkg/cloud/services/eks/cluster.go
index f9336fc1b..ffc502af0 100644
--- a/pkg/cloud/services/eks/cluster.go
+++ b/pkg/cloud/services/eks/cluster.go
@@ -26,6 +26,7 @@ import (
 	"github.com/aws/aws-sdk-go/aws/awserr"
 	"github.com/aws/aws-sdk-go/aws/request"
 	"github.com/aws/aws-sdk-go/service/eks"
+	"github.com/go-logr/logr"
 	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/version"
@@ -33,7 +34,6 @@ import (
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/v2/controlplane/eks/api/v1beta2"
-	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/awserrors"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/wait"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/internal/cidr"
@@ -45,7 +45,7 @@ import (
 )
 
 func (s *Service) reconcileCluster(ctx context.Context) error {
-	s.scope.V(2).Info("Reconciling EKS cluster")
+	s.scope.Debug("Reconciling EKS cluster")
 
 	eksClusterName := s.scope.KubernetesClusterName()
 
@@ -72,7 +72,7 @@ func (s *Service) reconcileCluster(ctx context.Context) error {
 			return fmt.Errorf("EKS cluster resource %q must have a tag with key %q or %q", eksClusterName, oldTagKey, tagKey)
 		}
 
-		s.scope.V(2).Info("Found owned EKS cluster in AWS", "cluster", klog.KRef("", eksClusterName))
+		s.scope.Debug("Found owned EKS cluster in AWS", "cluster", klog.KRef("", eksClusterName))
 	}
 
 	if err := s.setStatus(cluster); err != nil {
@@ -94,7 +94,7 @@ func (s *Service) reconcileCluster(ctx context.Context) error {
 		return nil
 	}
 
-	s.scope.V(2).Info("EKS Control Plane active", "endpoint", *cluster.Endpoint)
+	s.scope.Debug("EKS Control Plane active", "endpoint", *cluster.Endpoint)
 
 	s.scope.ControlPlane.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
 		Host: *cluster.Endpoint,
@@ -175,14 +175,14 @@ func (s *Service) deleteCluster() error {
 	eksClusterName := s.scope.KubernetesClusterName()
 
 	if eksClusterName == "" {
-		s.scope.V(2).Info("no EKS cluster name, skipping EKS cluster deletion")
+		s.scope.Debug("no EKS cluster name, skipping EKS cluster deletion")
 		return nil
 	}
 
 	cluster, err := s.describeEKSCluster(eksClusterName)
 	if err != nil {
 		if awserrors.IsNotFound(err) {
-			s.scope.V(4).Info("eks cluster does not exist")
+			s.scope.Trace("eks cluster does not exist")
 			return nil
 		}
 		return errors.Wrap(err, "unable to describe eks cluster")
@@ -538,12 +538,12 @@ func (s *Service) reconcileEKSEncryptionConfig(currentClusterConfig []*eks.Encry
 	updatedEncryptionConfigs := makeEksEncryptionConfigs(encryptionConfigs)
 
 	if compareEncryptionConfig(currentClusterConfig, updatedEncryptionConfigs) {
-		s.V(2).Info("encryption configuration unchanged, no action")
+		s.Debug("encryption configuration unchanged, no action")
 		return nil
 	}
 
 	if len(currentClusterConfig) == 0 && len(updatedEncryptionConfigs) > 0 {
-		s.V(2).Info("enabling encryption for eks cluster", "cluster", s.scope.KubernetesClusterName())
+		s.Debug("enabling encryption for eks cluster", "cluster", s.scope.KubernetesClusterName())
 		if err := s.updateEncryptionConfig(updatedEncryptionConfigs); err != nil {
 			record.Warnf(s.scope.ControlPlane, "FailedUpdateEKSControlPlane", "failed to update the EKS control plane encryption configuration: %v", err)
 			return errors.Wrapf(err, "failed to update EKS cluster")
@@ -592,7 +592,7 @@ func (s *Service) reconcileClusterVersion(cluster *eks.Cluster) error {
 			// status is ACTIVE and the update would be tried again
 			if err := s.EKSClient.WaitUntilClusterUpdating(
 				&eks.DescribeClusterInput{Name: aws.String(s.scope.KubernetesClusterName())},
-				request.WithWaiterLogger(&awslog{s}),
+				request.WithWaiterLogger(&awslog{s.GetLogger()}),
 			); err != nil {
 				return false, err
 			}
@@ -649,7 +649,7 @@ func (s *Service) updateEncryptionConfig(updatedEncryptionConfigs []*eks.Encrypt
 		// status is ACTIVE and the update would be tried again
 		if err := s.EKSClient.WaitUntilClusterUpdating(
 			&eks.DescribeClusterInput{Name: aws.String(s.scope.KubernetesClusterName())},
-			request.WithWaiterLogger(&awslog{s}),
+			request.WithWaiterLogger(&awslog{s.GetLogger()}),
 		); err != nil {
 			return false, err
 		}
@@ -666,7 +666,7 @@ func (s *Service) updateEncryptionConfig(updatedEncryptionConfigs []*eks.Encrypt
 
 // An internal type to satisfy aws' log interface.
 type awslog struct {
-	cloud.Logger
+	logr.Logger
 }
 
 func (a *awslog) Log(args ...interface{}) {
diff --git a/pkg/cloud/services/eks/config.go b/pkg/cloud/services/eks/config.go
index e063f8920..7298e9f61 100644
--- a/pkg/cloud/services/eks/config.go
+++ b/pkg/cloud/services/eks/config.go
@@ -45,7 +45,7 @@ const (
 )
 
 func (s *Service) reconcileKubeconfig(ctx context.Context, cluster *eks.Cluster) error {
-	s.scope.V(2).Info("Reconciling EKS kubeconfigs for cluster", "cluster-name", s.scope.KubernetesClusterName())
+	s.scope.Debug("Reconciling EKS kubeconfigs for cluster", "cluster-name", s.scope.KubernetesClusterName())
 
 	clusterRef := types.NamespacedName{
 		Name:      s.scope.Cluster.Name,
@@ -77,7 +77,7 @@ func (s *Service) reconcileKubeconfig(ctx context.Context, cluster *eks.Cluster)
 }
 
 func (s *Service) reconcileAdditionalKubeconfigs(ctx context.Context, cluster *eks.Cluster) error {
-	s.scope.V(2).Info("Reconciling additional EKS kubeconfigs for cluster", "cluster-name", s.scope.KubernetesClusterName())
+	s.scope.Debug("Reconciling additional EKS kubeconfigs for cluster", "cluster-name", s.scope.KubernetesClusterName())
 
 	clusterRef := types.NamespacedName{
 		Name:      s.scope.Cluster.Name + "-user",
@@ -141,7 +141,7 @@ func (s *Service) createCAPIKubeconfigSecret(ctx context.Context, cluster *eks.C
 }
 
 func (s *Service) updateCAPIKubeconfigSecret(ctx context.Context, configSecret *corev1.Secret, cluster *eks.Cluster) error {
-	s.scope.V(2).Info("Updating EKS kubeconfigs for cluster", "cluster-name", s.scope.KubernetesClusterName())
+	s.scope.Debug("Updating EKS kubeconfigs for cluster", "cluster-name", s.scope.KubernetesClusterName())
 
 	data, ok := configSecret.Data[secret.KubeconfigDataName]
 	if !ok {
@@ -265,7 +265,7 @@ func (s *Service) generateToken() (string, error) {
 
 	req, output := s.STSClient.GetCallerIdentityRequest(&sts.GetCallerIdentityInput{})
 	req.HTTPRequest.Header.Add(clusterNameHeader, eksClusterName)
-	s.Logger.V(4).Info("generating token for AWS identity", "user", output.UserId, "account", output.Account, "arn", output.Arn)
+	s.Trace("generating token for AWS identity", "user", output.UserId, "account", output.Account, "arn", output.Arn)
 
 	presignedURL, err := req.Presign(tokenAgeMins * time.Minute)
 	if err != nil {
diff --git a/pkg/cloud/services/eks/eks.go b/pkg/cloud/services/eks/eks.go
index 78fda11c4..ac1acc7b8 100644
--- a/pkg/cloud/services/eks/eks.go
+++ b/pkg/cloud/services/eks/eks.go
@@ -32,7 +32,7 @@ import (
 
 // ReconcileControlPlane reconciles a EKS control plane.
 func (s *Service) ReconcileControlPlane(ctx context.Context) error {
-	s.scope.V(2).Info("Reconciling EKS control plane", "cluster", klog.KRef(s.scope.Cluster.Namespace, s.scope.Cluster.Name))
+	s.scope.Debug("Reconciling EKS control plane", "cluster", klog.KRef(s.scope.Cluster.Namespace, s.scope.Cluster.Name))
 
 	// Control Plane IAM Role
 	if err := s.reconcileControlPlaneIAMRole(); err != nil {
@@ -62,13 +62,13 @@ func (s *Service) ReconcileControlPlane(ctx context.Context) error {
 	}
 	conditions.MarkTrue(s.scope.ControlPlane, ekscontrolplanev1.EKSIdentityProviderConfiguredCondition)
 
-	s.scope.V(2).Info("Reconcile EKS control plane completed successfully")
+	s.scope.Debug("Reconcile EKS control plane completed successfully")
 	return nil
 }
 
 // DeleteControlPlane deletes the EKS control plane.
 func (s *Service) DeleteControlPlane() (err error) {
-	s.scope.V(2).Info("Deleting EKS control plane")
+	s.scope.Debug("Deleting EKS control plane")
 
 	// EKS Cluster
 	if err := s.deleteCluster(); err != nil {
@@ -85,13 +85,13 @@ func (s *Service) DeleteControlPlane() (err error) {
 		return err
 	}
 
-	s.scope.V(2).Info("Delete EKS control plane completed successfully")
+	s.scope.Debug("Delete EKS control plane completed successfully")
 	return nil
 }
 
 // ReconcilePool is the entrypoint for ManagedMachinePool reconciliation.
 func (s *NodegroupService) ReconcilePool() error {
-	s.scope.V(2).Info("Reconciling EKS nodegroup")
+	s.scope.Debug("Reconciling EKS nodegroup")
 
 	if err := s.reconcileNodegroupIAMRole(); err != nil {
 		conditions.MarkFalse(
@@ -123,14 +123,14 @@ func (s *NodegroupService) ReconcilePool() error {
 // ReconcilePoolDelete is the entrypoint for ManagedMachinePool deletion
 // reconciliation.
 func (s *NodegroupService) ReconcilePoolDelete() error {
-	s.scope.V(2).Info("Reconciling deletion of EKS nodegroup")
+	s.scope.Debug("Reconciling deletion of EKS nodegroup")
 
 	eksNodegroupName := s.scope.NodegroupName()
 
 	ng, err := s.describeNodegroup()
 	if err != nil {
 		if awserrors.IsNotFound(err) {
-			s.scope.V(4).Info("EKS nodegroup does not exist")
+			s.scope.Trace("EKS nodegroup does not exist")
 			return nil
 		}
 		return errors.Wrap(err, "failed to describe EKS nodegroup")
diff --git a/pkg/cloud/services/eks/fargate.go b/pkg/cloud/services/eks/fargate.go
index 98c77609d..30e51adf2 100644
--- a/pkg/cloud/services/eks/fargate.go
+++ b/pkg/cloud/services/eks/fargate.go
@@ -45,7 +45,7 @@ func requeueRoleUpdating() reconcile.Result {
 
 // Reconcile is the entrypoint for FargateProfile reconciliation.
 func (s *FargateService) Reconcile() (reconcile.Result, error) {
-	s.scope.V(2).Info("Reconciling EKS fargate profile")
+	s.scope.Debug("Reconciling EKS fargate profile")
 
 	requeue, err := s.reconcileFargateIAMRole()
 	if err != nil {
@@ -106,7 +106,7 @@ func (s *FargateService) reconcileFargateProfile() (requeue bool, err error) {
 		if ownedTag == nil {
 			return false, errors.New("owned tag not found for this cluster")
 		}
-		s.scope.V(2).Info("Found owned EKS fargate profile", "cluster-name", eksClusterName, "profile-name", profileName)
+		s.scope.Debug("Found owned EKS fargate profile", "cluster-name", eksClusterName, "profile-name", profileName)
 	}
 
 	if err := s.reconcileTags(profile); err != nil {
@@ -117,7 +117,7 @@ func (s *FargateService) reconcileFargateProfile() (requeue bool, err error) {
 }
 
 func (s *FargateService) handleStatus(profile *eks.FargateProfile) (requeue bool) {
-	s.V(2).Info("fargate profile", "status", *profile.Status)
+	s.Debug("fargate profile", "status", *profile.Status)
 	switch *profile.Status {
 	case eks.FargateProfileStatusCreating:
 		s.scope.FargateProfile.Status.Ready = false
@@ -160,7 +160,7 @@ func (s *FargateService) handleStatus(profile *eks.FargateProfile) (requeue bool
 
 // ReconcileDelete is the entrypoint for FargateProfile reconciliation.
 func (s *FargateService) ReconcileDelete() (reconcile.Result, error) {
-	s.scope.V(2).Info("Reconciling EKS fargate profile deletion")
+	s.scope.Debug("Reconciling EKS fargate profile deletion")
 
 	requeue, err := s.deleteFargateProfile()
 	if err != nil {
diff --git a/pkg/cloud/services/eks/iam/iam.go b/pkg/cloud/services/eks/iam/iam.go
index 100c412b0..b4e77b386 100644
--- a/pkg/cloud/services/eks/iam/iam.go
+++ b/pkg/cloud/services/eks/iam/iam.go
@@ -28,13 +28,13 @@ import (
 	"github.com/aws/aws-sdk-go/service/eks"
 	"github.com/aws/aws-sdk-go/service/iam"
 	"github.com/aws/aws-sdk-go/service/iam/iamiface"
-	"github.com/go-logr/logr"
 	"github.com/google/go-cmp/cmp"
 	"github.com/pkg/errors"
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/cmd/clusterawsadm/converters"
 	iamv1 "sigs.k8s.io/cluster-api-provider-aws/v2/iam/api/v1beta1"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 )
 
 const (
@@ -44,7 +44,7 @@ const (
 
 // IAMService defines the specs for an IAM service.
 type IAMService struct {
-	logr.Logger
+	logger.Wrapper
 	IAMClient iamiface.IAMAPI
 	Client    *http.Client
 }
@@ -122,7 +122,7 @@ func (s *IAMService) attachIAMRolePolicy(roleName string, policyARN string) erro
 
 // EnsurePoliciesAttached will ensure the IAMService has policies attached.
 func (s *IAMService) EnsurePoliciesAttached(role *iam.Role, policies []*string) (bool, error) {
-	s.V(2).Info("Ensuring Polices are attached to role")
+	s.Debug("Ensuring Polices are attached to role")
 	existingPolices, err := s.getIAMRolePolicies(*role.RoleName)
 	if err != nil {
 		return false, err
@@ -138,7 +138,7 @@ func (s *IAMService) EnsurePoliciesAttached(role *iam.Role, policies []*string)
 			if err != nil {
 				return false, err
 			}
-			s.V(2).Info("Detached policy from role", "role", role.RoleName, "policy", existingPolicy)
+			s.Debug("Detached policy from role", "role", role.RoleName, "policy", existingPolicy)
 		}
 	}
 
@@ -157,7 +157,7 @@ func (s *IAMService) EnsurePoliciesAttached(role *iam.Role, policies []*string)
 			if err != nil {
 				return false, err
 			}
-			s.V(2).Info("Attached policy to role", "role", role.RoleName, "policy", *policy)
+			s.Debug("Attached policy to role", "role", role.RoleName, "policy", *policy)
 		}
 	}
 
@@ -212,7 +212,7 @@ func (s *IAMService) EnsureTagsAndPolicy(
 	trustRelationship *iamv1.PolicyDocument,
 	additionalTags infrav1.Tags,
 ) (bool, error) {
-	s.V(2).Info("Ensuring tags and AssumeRolePolicyDocument are set on role")
+	s.Debug("Ensuring tags and AssumeRolePolicyDocument are set on role")
 
 	rolePolicyDocumentRaw, err := url.PathUnescape(*role.AssumeRolePolicyDocument)
 	if err != nil {
@@ -286,7 +286,7 @@ func (s *IAMService) EnsureTagsAndPolicy(
 }
 
 func (s *IAMService) detachAllPoliciesForRole(name string) error {
-	s.V(3).Info("Detaching all policies for role", "role", name)
+	s.Debug("Detaching all policies for role", "role", name)
 	input := &iam.ListAttachedRolePoliciesInput{
 		RoleName: &name,
 	}
@@ -295,7 +295,7 @@ func (s *IAMService) detachAllPoliciesForRole(name string) error {
 		return errors.Wrapf(err, "error fetching policies for role %s", name)
 	}
 	for _, p := range policies.AttachedPolicies {
-		s.V(2).Info("Detaching policy", "policy", *p)
+		s.Debug("Detaching policy", "policy", *p)
 		if err := s.detachIAMRolePolicy(name, *p.PolicyArn); err != nil {
 			return err
 		}
diff --git a/pkg/cloud/services/eks/identity_provider.go b/pkg/cloud/services/eks/identity_provider.go
index f247412d8..71c3fedd0 100644
--- a/pkg/cloud/services/eks/identity_provider.go
+++ b/pkg/cloud/services/eks/identity_provider.go
@@ -49,11 +49,10 @@ func (s *Service) reconcileIdentityProvider(ctx context.Context) error {
 		return nil
 	}
 
-	s.scope.V(2).Info("creating oidc provider plan", "desired", desired, "current", current)
+	s.scope.Debug("creating oidc provider plan", "desired", desired, "current", current)
 	procedures, err := identityprovider.
-		NewPlan(clusterName, current, desired, s.EKSClient, s.scope.Logger).
+		NewPlan(clusterName, current, desired, s.EKSClient, s.scope).
 		Create(ctx)
-
 	if err != nil {
 		s.scope.Error(err, "failed creating eks identity provider plan")
 		return fmt.Errorf("creating eks identity provider plan: %w", err)
@@ -64,7 +63,7 @@ func (s *Service) reconcileIdentityProvider(ctx context.Context) error {
 		return nil
 	}
 
-	s.scope.V(2).Info("computed EKS identity provider plan", "numprocs", len(procedures))
+	s.scope.Debug("computed EKS identity provider plan", "numprocs", len(procedures))
 
 	// Perform required operations
 	for _, procedure := range procedures {
diff --git a/pkg/cloud/services/eks/nodegroup.go b/pkg/cloud/services/eks/nodegroup.go
index 8f9aff660..39f62882e 100644
--- a/pkg/cloud/services/eks/nodegroup.go
+++ b/pkg/cloud/services/eks/nodegroup.go
@@ -41,7 +41,7 @@ import (
 func (s *NodegroupService) describeNodegroup() (*eks.Nodegroup, error) {
 	eksClusterName := s.scope.KubernetesClusterName()
 	nodegroupName := s.scope.NodegroupName()
-	s.scope.V(2).Info("describing eks node group", "cluster", eksClusterName, "nodegroup", nodegroupName)
+	s.scope.Debug("describing eks node group", "cluster", eksClusterName, "nodegroup", nodegroupName)
 	input := &eks.DescribeNodegroupInput{
 		ClusterName:   aws.String(eksClusterName),
 		NodegroupName: aws.String(nodegroupName),
@@ -67,7 +67,7 @@ func (s *NodegroupService) describeNodegroup() (*eks.Nodegroup, error) {
 func (s *NodegroupService) describeASGs(ng *eks.Nodegroup) (*autoscaling.Group, error) {
 	eksClusterName := s.scope.KubernetesClusterName()
 	nodegroupName := s.scope.NodegroupName()
-	s.scope.V(2).Info("describing node group ASG", "cluster", eksClusterName, "nodegroup", nodegroupName)
+	s.scope.Debug("describing node group ASG", "cluster", eksClusterName, "nodegroup", nodegroupName)
 
 	if len(ng.Resources.AutoScalingGroups) == 0 {
 		return nil, nil
@@ -401,7 +401,7 @@ func createLabelUpdate(specLabels map[string]string, ng *eks.Nodegroup) *eks.Upd
 }
 
 func (s *NodegroupService) createTaintsUpdate(specTaints expinfrav1.Taints, ng *eks.Nodegroup) (*eks.UpdateTaintsPayload, error) {
-	s.V(2).Info("Creating taints update for node group", "name", *ng.NodegroupName, "num_current", len(ng.Taints), "num_required", len(specTaints))
+	s.Debug("Creating taints update for node group", "name", *ng.NodegroupName, "num_current", len(ng.Taints), "num_required", len(specTaints))
 	current, err := converters.TaintsFromSDK(ng.Taints)
 	if err != nil {
 		return nil, fmt.Errorf("converting taints: %w", err)
@@ -428,17 +428,17 @@ func (s *NodegroupService) createTaintsUpdate(specTaints expinfrav1.Taints, ng *
 		}
 	}
 	if len(payload.AddOrUpdateTaints) > 0 || len(payload.RemoveTaints) > 0 {
-		s.V(2).Info("Node group taints update required", "name", *ng.NodegroupName, "addupdate", len(payload.AddOrUpdateTaints), "remove", len(payload.RemoveTaints))
+		s.Debug("Node group taints update required", "name", *ng.NodegroupName, "addupdate", len(payload.AddOrUpdateTaints), "remove", len(payload.RemoveTaints))
 		return &payload, nil
 	}
 
-	s.V(2).Info("No updates required for node group taints", "name", *ng.NodegroupName)
+	s.Debug("No updates required for node group taints", "name", *ng.NodegroupName)
 	return nil, nil
 }
 
 func (s *NodegroupService) reconcileNodegroupConfig(ng *eks.Nodegroup) error {
 	eksClusterName := s.scope.KubernetesClusterName()
-	s.V(2).Info("reconciling node group config", "cluster", eksClusterName, "name", *ng.NodegroupName)
+	s.Debug("reconciling node group config", "cluster", eksClusterName, "name", *ng.NodegroupName)
 
 	managedPool := s.scope.ManagedMachinePool.Spec
 	input := &eks.UpdateNodegroupConfigInput{
@@ -447,7 +447,7 @@ func (s *NodegroupService) reconcileNodegroupConfig(ng *eks.Nodegroup) error {
 	}
 	var needsUpdate bool
 	if labelPayload := createLabelUpdate(managedPool.Labels, ng); labelPayload != nil {
-		s.V(2).Info("Nodegroup labels need an update", "nodegroup", ng.NodegroupName)
+		s.Debug("Nodegroup labels need an update", "nodegroup", ng.NodegroupName)
 		input.Labels = labelPayload
 		needsUpdate = true
 	}
@@ -456,35 +456,35 @@ func (s *NodegroupService) reconcileNodegroupConfig(ng *eks.Nodegroup) error {
 		return fmt.Errorf("creating taints update payload: %w", err)
 	}
 	if taintsPayload != nil {
-		s.V(2).Info("nodegroup taints need updating")
+		s.Debug("nodegroup taints need updating")
 		input.Taints = taintsPayload
 		needsUpdate = true
 	}
 	if machinePool := s.scope.MachinePool.Spec; machinePool.Replicas == nil {
 		if ng.ScalingConfig.DesiredSize != nil && *ng.ScalingConfig.DesiredSize != 1 {
-			s.V(2).Info("Nodegroup desired size differs from spec, updating scaling configuration", "nodegroup", ng.NodegroupName)
+			s.Debug("Nodegroup desired size differs from spec, updating scaling configuration", "nodegroup", ng.NodegroupName)
 			input.ScalingConfig = s.scalingConfig()
 			needsUpdate = true
 		}
 	} else if ng.ScalingConfig.DesiredSize == nil || int64(*machinePool.Replicas) != *ng.ScalingConfig.DesiredSize {
-		s.V(2).Info("Nodegroup has no desired size or differs from replicas, updating scaling configuration", "nodegroup", ng.NodegroupName)
+		s.Debug("Nodegroup has no desired size or differs from replicas, updating scaling configuration", "nodegroup", ng.NodegroupName)
 		input.ScalingConfig = s.scalingConfig()
 		needsUpdate = true
 	}
 	if managedPool.Scaling != nil && ((aws.Int64Value(ng.ScalingConfig.MaxSize) != int64(aws.Int32Value(managedPool.Scaling.MaxSize))) ||
 		(aws.Int64Value(ng.ScalingConfig.MinSize) != int64(aws.Int32Value(managedPool.Scaling.MinSize)))) {
-		s.V(2).Info("Nodegroup min/max differ from spec, updating scaling configuration", "nodegroup", ng.NodegroupName)
+		s.Debug("Nodegroup min/max differ from spec, updating scaling configuration", "nodegroup", ng.NodegroupName)
 		input.ScalingConfig = s.scalingConfig()
 		needsUpdate = true
 	}
 	currentUpdateConfig := converters.NodegroupUpdateconfigFromSDK(ng.UpdateConfig)
 	if !cmp.Equal(managedPool.UpdateConfig, currentUpdateConfig) {
-		s.V(2).Info("Nodegroup update configuration differs from spec, updating the nodegroup update config", "nodegroup", ng.NodegroupName)
+		s.Debug("Nodegroup update configuration differs from spec, updating the nodegroup update config", "nodegroup", ng.NodegroupName)
 		input.UpdateConfig = s.updateConfig()
 		needsUpdate = true
 	}
 	if !needsUpdate {
-		s.V(2).Info("node group config update not needed", "cluster", eksClusterName, "name", *ng.NodegroupName)
+		s.Debug("node group config update not needed", "cluster", eksClusterName, "name", *ng.NodegroupName)
 		return nil
 	}
 	if err := input.Validate(); err != nil {
@@ -517,7 +517,7 @@ func (s *NodegroupService) reconcileNodegroup() error {
 		if ownedTag == nil {
 			return errors.Errorf("owner of %s mismatch: %s", eksNodegroupName, s.scope.ClusterName())
 		}
-		s.scope.V(2).Info("Found owned EKS nodegroup in AWS", "cluster-name", eksClusterName, "nodegroup-name", eksNodegroupName)
+		s.scope.Debug("Found owned EKS nodegroup in AWS", "cluster-name", eksClusterName, "nodegroup-name", eksNodegroupName)
 	}
 
 	if err := s.setStatus(ng); err != nil {
diff --git a/pkg/cloud/services/eks/oidc.go b/pkg/cloud/services/eks/oidc.go
index 235fb0aaa..e70a86110 100644
--- a/pkg/cloud/services/eks/oidc.go
+++ b/pkg/cloud/services/eks/oidc.go
@@ -124,11 +124,11 @@ func (s *Service) reconcileTrustPolicy() error {
 	if trustPolicyConfigMap.UID == "" {
 		trustPolicyConfigMap.Name = trustPolicyConfigMapName
 		trustPolicyConfigMap.Namespace = trustPolicyConfigMapNamespace
-		s.V(2).Info("Creating new Trust Policy ConfigMap", "cluster", s.scope.Name(), "configmap", trustPolicyConfigMapName)
+		s.Debug("Creating new Trust Policy ConfigMap", "cluster", s.scope.Name(), "configmap", trustPolicyConfigMapName)
 		return remoteClient.Create(ctx, trustPolicyConfigMap)
 	}
 
-	s.V(2).Info("Updating existing Trust Policy ConfigMap", "cluster", s.scope.Name(), "configmap", trustPolicyConfigMapName)
+	s.Debug("Updating existing Trust Policy ConfigMap", "cluster", s.scope.Name(), "configmap", trustPolicyConfigMapName)
 	return remoteClient.Update(ctx, trustPolicyConfigMap)
 }
 
diff --git a/pkg/cloud/services/eks/roles.go b/pkg/cloud/services/eks/roles.go
index 74d51aa4e..3a3eb14d5 100644
--- a/pkg/cloud/services/eks/roles.go
+++ b/pkg/cloud/services/eks/roles.go
@@ -53,7 +53,7 @@ func FargateRolePolicies() []string {
 }
 
 func (s *Service) reconcileControlPlaneIAMRole() error {
-	s.scope.V(2).Info("Reconciling EKS Control Plane IAM Role")
+	s.scope.Debug("Reconciling EKS Control Plane IAM Role")
 
 	if s.scope.ControlPlane.Spec.RoleName == nil {
 		if !s.scope.EnableIAM() {
@@ -87,7 +87,7 @@ func (s *Service) reconcileControlPlaneIAMRole() error {
 	}
 
 	if s.IsUnmanaged(role, s.scope.Name()) {
-		s.scope.V(2).Info("Skipping, EKS control plane role policy assignment as role is unamanged")
+		s.scope.Debug("Skipping, EKS control plane role policy assignment as role is unamanged")
 		return nil
 	}
 
@@ -120,16 +120,16 @@ func (s *Service) deleteControlPlaneIAMRole() error {
 	}
 	roleName := *s.scope.ControlPlane.Spec.RoleName
 	if !s.scope.EnableIAM() {
-		s.scope.V(2).Info("EKS IAM disabled, skipping deleting EKS Control Plane IAM Role")
+		s.scope.Debug("EKS IAM disabled, skipping deleting EKS Control Plane IAM Role")
 		return nil
 	}
 
-	s.scope.V(2).Info("Deleting EKS Control Plane IAM Role")
+	s.scope.Debug("Deleting EKS Control Plane IAM Role")
 
 	role, err := s.GetIAMRole(roleName)
 	if err != nil {
 		if isNotFound(err) {
-			s.V(2).Info("EKS Control Plane IAM Role already deleted")
+			s.Debug("EKS Control Plane IAM Role already deleted")
 			return nil
 		}
 
@@ -137,7 +137,7 @@ func (s *Service) deleteControlPlaneIAMRole() error {
 	}
 
 	if s.IsUnmanaged(role, s.scope.Name()) {
-		s.V(2).Info("Skipping, EKS control plane iam role deletion as role is unamanged")
+		s.Debug("Skipping, EKS control plane iam role deletion as role is unamanged")
 		return nil
 	}
 
@@ -152,7 +152,7 @@ func (s *Service) deleteControlPlaneIAMRole() error {
 }
 
 func (s *NodegroupService) reconcileNodegroupIAMRole() error {
-	s.scope.V(2).Info("Reconciling EKS Nodegroup IAM Role")
+	s.scope.Debug("Reconciling EKS Nodegroup IAM Role")
 
 	if s.scope.RoleName() == "" {
 		var roleName string
@@ -194,7 +194,7 @@ func (s *NodegroupService) reconcileNodegroupIAMRole() error {
 	}
 
 	if s.IsUnmanaged(role, s.scope.ClusterName()) {
-		s.scope.V(2).Info("Skipping, EKS nodegroup role policy assignment as role is unamanged")
+		s.scope.Debug("Skipping, EKS nodegroup role policy assignment as role is unamanged")
 		return nil
 	}
 
@@ -238,16 +238,16 @@ func (s *NodegroupService) deleteNodegroupIAMRole() (reterr error) {
 	}()
 	roleName := s.scope.RoleName()
 	if !s.scope.EnableIAM() {
-		s.scope.V(2).Info("EKS IAM disabled, skipping deleting EKS Nodegroup IAM Role")
+		s.scope.Debug("EKS IAM disabled, skipping deleting EKS Nodegroup IAM Role")
 		return nil
 	}
 
-	s.scope.V(2).Info("Deleting EKS Nodegroup IAM Role")
+	s.scope.Debug("Deleting EKS Nodegroup IAM Role")
 
 	role, err := s.GetIAMRole(roleName)
 	if err != nil {
 		if isNotFound(err) {
-			s.V(2).Info("EKS Nodegroup IAM Role already deleted")
+			s.Debug("EKS Nodegroup IAM Role already deleted")
 			return nil
 		}
 
@@ -255,7 +255,7 @@ func (s *NodegroupService) deleteNodegroupIAMRole() (reterr error) {
 	}
 
 	if s.IsUnmanaged(role, s.scope.ClusterName()) {
-		s.V(2).Info("Skipping, EKS Nodegroup iam role deletion as role is unamanged")
+		s.Debug("Skipping, EKS Nodegroup iam role deletion as role is unamanged")
 		return nil
 	}
 
@@ -270,7 +270,7 @@ func (s *NodegroupService) deleteNodegroupIAMRole() (reterr error) {
 }
 
 func (s *FargateService) reconcileFargateIAMRole() (requeue bool, err error) {
-	s.scope.V(2).Info("Reconciling EKS Fargate IAM Role")
+	s.scope.Debug("Reconciling EKS Fargate IAM Role")
 
 	if s.scope.RoleName() == "" {
 		var roleName string
@@ -346,16 +346,16 @@ func (s *FargateService) deleteFargateIAMRole() (reterr error) {
 	}()
 	roleName := s.scope.RoleName()
 	if !s.scope.EnableIAM() {
-		s.scope.V(2).Info("EKS IAM disabled, skipping deleting EKS fargate IAM Role")
+		s.scope.Debug("EKS IAM disabled, skipping deleting EKS fargate IAM Role")
 		return nil
 	}
 
-	s.scope.V(2).Info("Deleting EKS fargate IAM Role")
+	s.scope.Debug("Deleting EKS fargate IAM Role")
 
 	_, err := s.GetIAMRole(roleName)
 	if err != nil {
 		if isNotFound(err) {
-			s.V(2).Info("EKS fargate IAM Role already deleted")
+			s.Debug("EKS fargate IAM Role already deleted")
 			return nil
 		}
 
diff --git a/pkg/cloud/services/eks/service.go b/pkg/cloud/services/eks/service.go
index e996fdd1e..22684defd 100644
--- a/pkg/cloud/services/eks/service.go
+++ b/pkg/cloud/services/eks/service.go
@@ -70,7 +70,7 @@ func NewService(controlPlaneScope *scope.ManagedControlPlaneScope, opts ...Servi
 			EKSAPI: scope.NewEKSClient(controlPlaneScope, controlPlaneScope, controlPlaneScope, controlPlaneScope.ControlPlane),
 		},
 		IAMService: iam.IAMService{
-			Logger:    controlPlaneScope.Logger,
+			Wrapper:   &controlPlaneScope.Logger,
 			IAMClient: scope.NewIAMClient(controlPlaneScope, controlPlaneScope, controlPlaneScope, controlPlaneScope.ControlPlane),
 			Client:    http.DefaultClient,
 		},
@@ -102,7 +102,7 @@ func NewNodegroupService(machinePoolScope *scope.ManagedMachinePoolScope) *Nodeg
 		AutoscalingClient: scope.NewASGClient(machinePoolScope, machinePoolScope, machinePoolScope, machinePoolScope.ManagedMachinePool),
 		EKSClient:         scope.NewEKSClient(machinePoolScope, machinePoolScope, machinePoolScope, machinePoolScope.ManagedMachinePool),
 		IAMService: iam.IAMService{
-			Logger:    machinePoolScope.Logger,
+			Wrapper:   &machinePoolScope.Logger,
 			IAMClient: scope.NewIAMClient(machinePoolScope, machinePoolScope, machinePoolScope, machinePoolScope.ManagedMachinePool),
 		},
 		STSClient: scope.NewSTSClient(machinePoolScope, machinePoolScope, machinePoolScope, machinePoolScope.ManagedMachinePool),
@@ -124,7 +124,7 @@ func NewFargateService(fargatePoolScope *scope.FargateProfileScope) *FargateServ
 		scope:     fargatePoolScope,
 		EKSClient: scope.NewEKSClient(fargatePoolScope, fargatePoolScope, fargatePoolScope, fargatePoolScope.FargateProfile),
 		IAMService: iam.IAMService{
-			Logger:    fargatePoolScope.Logger,
+			Wrapper:   &fargatePoolScope.Logger,
 			IAMClient: scope.NewIAMClient(fargatePoolScope, fargatePoolScope, fargatePoolScope, fargatePoolScope.FargateProfile),
 		},
 		STSClient: scope.NewSTSClient(fargatePoolScope, fargatePoolScope, fargatePoolScope, fargatePoolScope.FargateProfile),
diff --git a/pkg/cloud/services/eks/tags.go b/pkg/cloud/services/eks/tags.go
index 6c80a0358..37fd8a9fd 100644
--- a/pkg/cloud/services/eks/tags.go
+++ b/pkg/cloud/services/eks/tags.go
@@ -130,7 +130,7 @@ func (s *NodegroupService) reconcileASGTags(ng *eks.Nodegroup) error {
 	}
 
 	tagsToDelete, tagsToAdd := getASGTagUpdates(s.scope.ClusterName(), tagDescriptionsToMap(asg.Tags), s.scope.AdditionalTags())
-	s.scope.V(2).Info("Tags", "tagsToAdd", tagsToAdd, "tagsToDelete", tagsToDelete)
+	s.scope.Debug("Tags", "tagsToAdd", tagsToAdd, "tagsToDelete", tagsToDelete)
 
 	if len(tagsToAdd) > 0 {
 		input := &autoscaling.CreateOrUpdateTagsInput{}
diff --git a/pkg/cloud/services/elb/loadbalancer.go b/pkg/cloud/services/elb/loadbalancer.go
index 90870e859..4467ef5e7 100644
--- a/pkg/cloud/services/elb/loadbalancer.go
+++ b/pkg/cloud/services/elb/loadbalancer.go
@@ -52,7 +52,7 @@ const maxELBsDescribeTagsRequest = 20
 
 // ReconcileLoadbalancers reconciles the load balancers for the given cluster.
 func (s *Service) ReconcileLoadbalancers() error {
-	s.scope.V(2).Info("Reconciling load balancers")
+	s.scope.Debug("Reconciling load balancers")
 
 	// If ELB scheme is set to Internet-facing due to an API bug in versions > v0.6.6 and v0.7.0, change it to internet-facing and patch.
 	if s.scope.ControlPlaneLoadBalancerScheme().String() == infrav1.ClassicELBSchemeIncorrectInternetFacing.String() {
@@ -60,7 +60,7 @@ func (s *Service) ReconcileLoadbalancers() error {
 		if err := s.scope.PatchObject(); err != nil {
 			return err
 		}
-		s.scope.V(4).Info("Patched control plane load balancer scheme")
+		s.scope.Trace("Patched control plane load balancer scheme")
 	}
 
 	// Generate a default control plane load balancer name. The load balancer name cannot be
@@ -87,7 +87,7 @@ func (s *Service) ReconcileLoadbalancers() error {
 		if err != nil {
 			return err
 		}
-		s.scope.V(2).Info("Created new classic load balancer for apiserver", "api-server-elb-name", apiELB.Name)
+		s.scope.Debug("Created new classic load balancer for apiserver", "api-server-elb-name", apiELB.Name)
 	case err != nil:
 		// Failed to describe the classic ELB
 		return err
@@ -131,19 +131,19 @@ func (s *Service) ReconcileLoadbalancers() error {
 			}
 		}
 	} else {
-		s.scope.V(4).Info("Unmanaged control plane load balancer, skipping load balancer configuration", "api-server-elb", apiELB)
+		s.scope.Trace("Unmanaged control plane load balancer, skipping load balancer configuration", "api-server-elb", apiELB)
 	}
 
 	// TODO(vincepri): check if anything has changed and reconcile as necessary.
 	apiELB.DeepCopyInto(&s.scope.Network().APIServerELB)
-	s.scope.V(4).Info("Control plane load balancer", "api-server-elb", apiELB)
+	s.scope.Trace("Control plane load balancer", "api-server-elb", apiELB)
 
-	s.scope.V(2).Info("Reconcile load balancers completed successfully")
+	s.scope.Debug("Reconcile load balancers completed successfully")
 	return nil
 }
 
 func (s *Service) deleteAPIServerELB() error {
-	s.scope.V(2).Info("Deleting control plane load balancer")
+	s.scope.Debug("Deleting control plane load balancer")
 
 	elbName, err := ELBName(s.scope)
 	if err != nil {
@@ -164,11 +164,11 @@ func (s *Service) deleteAPIServerELB() error {
 	}
 
 	if apiELB.IsUnmanaged(s.scope.Name()) {
-		s.scope.V(2).Info("Found unmanaged classic load balancer for apiserver, skipping deletion", "api-server-elb-name", apiELB.Name)
+		s.scope.Debug("Found unmanaged classic load balancer for apiserver, skipping deletion", "api-server-elb-name", apiELB.Name)
 		return nil
 	}
 
-	s.scope.V(3).Info("deleting load balancer", "name", elbName)
+	s.scope.Debug("deleting load balancer", "name", elbName)
 	if err := s.deleteClassicELB(elbName); err != nil {
 		conditions.MarkFalse(s.scope.InfraCluster(), infrav1.LoadBalancerReadyCondition, "DeletingFailed", clusterv1.ConditionSeverityWarning, err.Error())
 		return err
@@ -192,7 +192,7 @@ func (s *Service) deleteAPIServerELB() error {
 // cluster is deleted, its ELB is deleted; the ELBs found in this function will typically be for
 // Services that were not deleted before the cluster was deleted.
 func (s *Service) deleteAWSCloudProviderELBs() error {
-	s.scope.V(2).Info("Deleting AWS cloud provider load balancers (created for LoadBalancer-type Services)")
+	s.scope.Debug("Deleting AWS cloud provider load balancers (created for LoadBalancer-type Services)")
 
 	elbs, err := s.listAWSCloudProviderOwnedELBs()
 	if err != nil {
@@ -200,7 +200,7 @@ func (s *Service) deleteAWSCloudProviderELBs() error {
 	}
 
 	for _, elb := range elbs {
-		s.scope.V(3).Info("Deleting AWS cloud provider load balancer", "arn", elb)
+		s.scope.Debug("Deleting AWS cloud provider load balancer", "arn", elb)
 		if err := s.deleteClassicELB(elb); err != nil {
 			return err
 		}
@@ -222,7 +222,7 @@ func (s *Service) deleteAWSCloudProviderELBs() error {
 
 // DeleteLoadbalancers deletes the load balancers for the given cluster.
 func (s *Service) DeleteLoadbalancers() error {
-	s.scope.V(2).Info("Deleting load balancers")
+	s.scope.Debug("Deleting load balancers")
 
 	if err := s.deleteAPIServerELB(); err != nil {
 		return errors.Wrap(err, "failed to delete control plane load balancer")
@@ -769,14 +769,14 @@ func (s *Service) reconcileELBTags(lb *infrav1.ClassicELB, desiredTags map[strin
 
 	for k, v := range desiredTags {
 		if val, ok := currentTags[k]; !ok || val != v {
-			s.scope.V(4).Info("adding tag to load balancer", "elb-name", lb.Name, "key", k, "value", v)
+			s.scope.Trace("adding tag to load balancer", "elb-name", lb.Name, "key", k, "value", v)
 			addTagsInput.Tags = append(addTagsInput.Tags, &elb.Tag{Key: aws.String(k), Value: aws.String(v)})
 		}
 	}
 
 	for k := range currentTags {
 		if _, ok := desiredTags[k]; !ok {
-			s.scope.V(4).Info("removing tag from load balancer", "elb-name", lb.Name, "key", k)
+			s.scope.Trace("removing tag from load balancer", "elb-name", lb.Name, "key", k)
 			removeTagsInput.Tags = append(removeTagsInput.Tags, &elb.TagKeyOnly{Key: aws.String(k)})
 		}
 	}
diff --git a/pkg/cloud/services/gc/cleanup.go b/pkg/cloud/services/gc/cleanup.go
index 02bacde09..7bb67ff7a 100644
--- a/pkg/cloud/services/gc/cleanup.go
+++ b/pkg/cloud/services/gc/cleanup.go
@@ -109,11 +109,11 @@ func (s *Service) deleteResources(ctx context.Context) error {
 
 func (s *Service) isMatchingResource(resource *AWSResource, serviceName, resourceName string) bool {
 	if resource.ARN.Service != serviceName {
-		s.scope.V(5).Info("Resource not for service", "arn", resource.ARN.String(), "service_name", serviceName, "resource_name", resourceName)
+		s.scope.Debug("Resource not for service", "arn", resource.ARN.String(), "service_name", serviceName, "resource_name", resourceName)
 		return false
 	}
 	if !strings.HasPrefix(resource.ARN.Resource, resourceName+"/") {
-		s.scope.V(5).Info("Resource type does not match", "arn", resource.ARN.String(), "service_name", serviceName, "resource_name", resourceName)
+		s.scope.Debug("Resource type does not match", "arn", resource.ARN.String(), "service_name", serviceName, "resource_name", resourceName)
 		return false
 	}
 
diff --git a/pkg/cloud/services/gc/ec2.go b/pkg/cloud/services/gc/ec2.go
index d101f79c0..83f31d494 100644
--- a/pkg/cloud/services/gc/ec2.go
+++ b/pkg/cloud/services/gc/ec2.go
@@ -28,7 +28,7 @@ import (
 func (s *Service) deleteSecurityGroups(ctx context.Context, resources []*AWSResource) error {
 	for _, resource := range resources {
 		if !s.isSecurityGroupToDelete(resource) {
-			s.scope.V(5).Info("Resource not a security group for deletion", "arn", resource.ARN.String())
+			s.scope.Debug("Resource not a security group for deletion", "arn", resource.ARN.String())
 			continue
 		}
 
@@ -37,7 +37,7 @@ func (s *Service) deleteSecurityGroups(ctx context.Context, resources []*AWSReso
 			return fmt.Errorf("deleting security group %s: %w", groupID, err)
 		}
 	}
-	s.scope.V(2).Info("Finished processing resources for security group deletion")
+	s.scope.Debug("Finished processing resources for security group deletion")
 
 	return nil
 }
@@ -47,10 +47,10 @@ func (s *Service) isSecurityGroupToDelete(resource *AWSResource) bool {
 		return false
 	}
 	if eksClusterName := resource.Tags[eksClusterNameTag]; eksClusterName != "" {
-		s.scope.V(5).Info("Security group was created by EKS directly", "arn", resource.ARN.String(), "check", "securitygroup", "cluster_name", eksClusterName)
+		s.scope.Debug("Security group was created by EKS directly", "arn", resource.ARN.String(), "check", "securitygroup", "cluster_name", eksClusterName)
 		return false
 	}
-	s.scope.V(5).Info("Resource is a security group to delete", "arn", resource.ARN.String(), "check", "securitygroup")
+	s.scope.Debug("Resource is a security group to delete", "arn", resource.ARN.String(), "check", "securitygroup")
 
 	return true
 }
@@ -60,7 +60,7 @@ func (s *Service) deleteSecurityGroup(ctx context.Context, securityGroupID strin
 		GroupId: aws.String(securityGroupID),
 	}
 
-	s.scope.V(2).Info("Deleting security group", "group_id", securityGroupID)
+	s.scope.Debug("Deleting security group", "group_id", securityGroupID)
 	if _, err := s.ec2Client.DeleteSecurityGroupWithContext(ctx, &input); err != nil {
 		return fmt.Errorf("deleting security group: %w", err)
 	}
diff --git a/pkg/cloud/services/gc/loadbalancer.go b/pkg/cloud/services/gc/loadbalancer.go
index d371cabe6..c9d60d568 100644
--- a/pkg/cloud/services/gc/loadbalancer.go
+++ b/pkg/cloud/services/gc/loadbalancer.go
@@ -29,33 +29,33 @@ import (
 func (s *Service) deleteLoadBalancers(ctx context.Context, resources []*AWSResource) error {
 	for _, resource := range resources {
 		if !s.isELBResourceToDelete(resource, "loadbalancer") {
-			s.scope.V(5).Info("Resource not a load balancer for deletion", "arn", resource.ARN.String())
+			s.scope.Debug("Resource not a load balancer for deletion", "arn", resource.ARN.String())
 			continue
 		}
 
 		switch {
 		case strings.HasPrefix(resource.ARN.Resource, "loadbalancer/app/"):
-			s.scope.V(5).Info("Deleting ALB for Service", "arn", resource.ARN.String())
+			s.scope.Debug("Deleting ALB for Service", "arn", resource.ARN.String())
 			if err := s.deleteLoadBalancerV2(ctx, resource.ARN.String()); err != nil {
 				return fmt.Errorf("deleting ALB: %w", err)
 			}
 		case strings.HasPrefix(resource.ARN.Resource, "loadbalancer/net/"):
-			s.scope.V(5).Info("Deleting NLB for Service", "arn", resource.ARN.String())
+			s.scope.Debug("Deleting NLB for Service", "arn", resource.ARN.String())
 			if err := s.deleteLoadBalancerV2(ctx, resource.ARN.String()); err != nil {
 				return fmt.Errorf("deleting NLB: %w", err)
 			}
 		case strings.HasPrefix(resource.ARN.Resource, "loadbalancer/"):
 			name := strings.ReplaceAll(resource.ARN.Resource, "loadbalancer/", "")
-			s.scope.V(5).Info("Deleting classic ELB for Service", "arn", resource.ARN.String(), "name", name)
+			s.scope.Debug("Deleting classic ELB for Service", "arn", resource.ARN.String(), "name", name)
 			if err := s.deleteLoadBalancer(ctx, name); err != nil {
 				return fmt.Errorf("deleting classic ELB: %w", err)
 			}
 		default:
-			s.scope.V(4).Info("Unexpected elasticloadbalancing resource, ignoring", "arn", resource.ARN.String())
+			s.scope.Trace("Unexpected elasticloadbalancing resource, ignoring", "arn", resource.ARN.String())
 		}
 	}
 
-	s.scope.V(2).Info("Finished processing tagged resources for load balancers")
+	s.scope.Debug("Finished processing tagged resources for load balancers")
 
 	return nil
 }
@@ -63,7 +63,7 @@ func (s *Service) deleteLoadBalancers(ctx context.Context, resources []*AWSResou
 func (s *Service) deleteTargetGroups(ctx context.Context, resources []*AWSResource) error {
 	for _, resource := range resources {
 		if !s.isELBResourceToDelete(resource, "targetgroup") {
-			s.scope.V(4).Info("Resource not a target group for deletion", "arn", resource.ARN.String())
+			s.scope.Trace("Resource not a target group for deletion", "arn", resource.ARN.String())
 			continue
 		}
 
@@ -72,7 +72,7 @@ func (s *Service) deleteTargetGroups(ctx context.Context, resources []*AWSResour
 			return fmt.Errorf("deleting target group %s: %w", name, err)
 		}
 	}
-	s.scope.V(2).Info("Finished processing resources for target group deletion")
+	s.scope.Debug("Finished processing resources for target group deletion")
 
 	return nil
 }
@@ -83,7 +83,7 @@ func (s *Service) isELBResourceToDelete(resource *AWSResource, resourceName stri
 	}
 
 	if serviceName := resource.Tags[serviceNameTag]; serviceName == "" {
-		s.scope.V(5).Info("Resource wasn't created for a Service via CCM", "arn", resource.ARN.String(), "resource_name", resourceName)
+		s.scope.Debug("Resource wasn't created for a Service via CCM", "arn", resource.ARN.String(), "resource_name", resourceName)
 		return false
 	}
 
@@ -95,7 +95,7 @@ func (s *Service) deleteLoadBalancerV2(ctx context.Context, lbARN string) error
 		LoadBalancerArn: aws.String(lbARN),
 	}
 
-	s.scope.V(2).Info("Deleting v2 load balancer", "arn", lbARN)
+	s.scope.Debug("Deleting v2 load balancer", "arn", lbARN)
 	if _, err := s.elbv2Client.DeleteLoadBalancerWithContext(ctx, &input); err != nil {
 		return fmt.Errorf("deleting v2 load balancer: %w", err)
 	}
@@ -108,7 +108,7 @@ func (s *Service) deleteLoadBalancer(ctx context.Context, name string) error {
 		LoadBalancerName: aws.String(name),
 	}
 
-	s.scope.V(2).Info("Deleting classic load balancer", "name", name)
+	s.scope.Debug("Deleting classic load balancer", "name", name)
 	if _, err := s.elbClient.DeleteLoadBalancerWithContext(ctx, &input); err != nil {
 		return fmt.Errorf("deleting classic load balancer: %w", err)
 	}
@@ -121,7 +121,7 @@ func (s *Service) deleteTargetGroup(ctx context.Context, targetGroupARN string)
 		TargetGroupArn: aws.String(targetGroupARN),
 	}
 
-	s.scope.V(2).Info("Deleting target group", "arn", targetGroupARN)
+	s.scope.Debug("Deleting target group", "arn", targetGroupARN)
 	if _, err := s.elbv2Client.DeleteTargetGroupWithContext(ctx, &input); err != nil {
 		return fmt.Errorf("deleting target group: %w", err)
 	}
diff --git a/pkg/cloud/services/iamauth/reconcile.go b/pkg/cloud/services/iamauth/reconcile.go
index 634e5c219..64b9f07ef 100644
--- a/pkg/cloud/services/iamauth/reconcile.go
+++ b/pkg/cloud/services/iamauth/reconcile.go
@@ -57,22 +57,22 @@ func (s *Service) ReconcileIAMAuthenticator(ctx context.Context) error {
 			Groups:   NodeGroups,
 		},
 	}
-	s.scope.V(2).Info("Mapping node IAM role", "iam-role", nodesRoleMapping.RoleARN, "user", nodesRoleMapping.UserName)
+	s.scope.Debug("Mapping node IAM role", "iam-role", nodesRoleMapping.RoleARN, "user", nodesRoleMapping.UserName)
 	if err := authBackend.MapRole(nodesRoleMapping); err != nil {
 		return fmt.Errorf("mapping iam node role: %w", err)
 	}
 
-	s.scope.V(2).Info("Mapping additional IAM roles and users")
+	s.scope.Debug("Mapping additional IAM roles and users")
 	iamCfg := s.scope.IAMAuthConfig()
 	for _, roleMapping := range iamCfg.RoleMappings {
-		s.scope.V(2).Info("Mapping IAM role", "iam-role", roleMapping.RoleARN, "user", roleMapping.UserName)
+		s.scope.Debug("Mapping IAM role", "iam-role", roleMapping.RoleARN, "user", roleMapping.UserName)
 		if err := authBackend.MapRole(roleMapping); err != nil {
 			return fmt.Errorf("mapping iam role: %w", err)
 		}
 	}
 
 	for _, userMapping := range iamCfg.UserMappings {
-		s.scope.V(2).Info("Mapping IAM user", "iam-user", userMapping.UserARN, "user", userMapping.UserName)
+		s.scope.Debug("Mapping IAM user", "iam-user", userMapping.UserARN, "user", userMapping.UserName)
 		if err := authBackend.MapUser(userMapping); err != nil {
 			return fmt.Errorf("mapping iam user: %w", err)
 		}
diff --git a/pkg/cloud/services/kubeproxy/reconcile.go b/pkg/cloud/services/kubeproxy/reconcile.go
index cdb026b89..bd9a31668 100644
--- a/pkg/cloud/services/kubeproxy/reconcile.go
+++ b/pkg/cloud/services/kubeproxy/reconcile.go
@@ -59,16 +59,16 @@ func (s *Service) deleteKubeProxy(ctx context.Context, remoteClient client.Clien
 	ds := &appsv1.DaemonSet{}
 	if err := remoteClient.Get(ctx, types.NamespacedName{Namespace: kubeProxyNamespace, Name: kubeProxyName}, ds); err != nil {
 		if apierrors.IsNotFound(err) {
-			s.scope.V(2).Info("The kube-proxy DaemonSet is not found, no action")
+			s.scope.Debug("The kube-proxy DaemonSet is not found, no action")
 			return nil
 		}
 		return fmt.Errorf("getting kube-proxy daemonset: %w", err)
 	}
 
-	s.scope.V(2).Info("The kube-proxy DaemonSet found, deleting")
+	s.scope.Debug("The kube-proxy DaemonSet found, deleting")
 	if err := remoteClient.Delete(ctx, ds, &client.DeleteOptions{}); err != nil {
 		if apierrors.IsNotFound(err) {
-			s.scope.V(2).Info("The kube-proxy DaemonSet is not found, not deleted")
+			s.scope.Debug("The kube-proxy DaemonSet is not found, not deleted")
 			return nil
 		}
 		return fmt.Errorf("deleting kube-proxy DaemonSet: %w", err)
diff --git a/pkg/cloud/services/network/egress_only_gateways.go b/pkg/cloud/services/network/egress_only_gateways.go
index cc825c8b8..4ba81f734 100644
--- a/pkg/cloud/services/network/egress_only_gateways.go
+++ b/pkg/cloud/services/network/egress_only_gateways.go
@@ -36,11 +36,11 @@ import (
 
 func (s *Service) reconcileEgressOnlyInternetGateways() error {
 	if !s.scope.VPC().IsIPv6Enabled() {
-		s.scope.V(4).Info("Skipping egress only internet gateways reconcile in not ipv6 mode")
+		s.scope.Trace("Skipping egress only internet gateways reconcile in not ipv6 mode")
 		return nil
 	}
 
-	s.scope.V(2).Info("Reconciling egress only internet gateways")
+	s.scope.Debug("Reconciling egress only internet gateways")
 
 	eigws, err := s.describeEgressOnlyVpcInternetGateways()
 	if awserrors.IsNotFound(err) {
@@ -78,7 +78,7 @@ func (s *Service) reconcileEgressOnlyInternetGateways() error {
 
 func (s *Service) deleteEgressOnlyInternetGateways() error {
 	if !s.scope.VPC().IsIPv6Enabled() {
-		s.scope.V(4).Info("Skipping egress only internet gateway deletion in none ipv6 mode")
+		s.scope.Trace("Skipping egress only internet gateway deletion in none ipv6 mode")
 		return nil
 	}
 
diff --git a/pkg/cloud/services/network/gateways.go b/pkg/cloud/services/network/gateways.go
index b11a7617c..1377b4142 100644
--- a/pkg/cloud/services/network/gateways.go
+++ b/pkg/cloud/services/network/gateways.go
@@ -36,11 +36,11 @@ import (
 
 func (s *Service) reconcileInternetGateways() error {
 	if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
-		s.scope.V(4).Info("Skipping internet gateways reconcile in unmanaged mode")
+		s.scope.Trace("Skipping internet gateways reconcile in unmanaged mode")
 		return nil
 	}
 
-	s.scope.V(2).Info("Reconciling internet gateways")
+	s.scope.Debug("Reconciling internet gateways")
 
 	igs, err := s.describeVpcInternetGateways()
 	if awserrors.IsNotFound(err) {
@@ -78,7 +78,7 @@ func (s *Service) reconcileInternetGateways() error {
 
 func (s *Service) deleteInternetGateways() error {
 	if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
-		s.scope.V(4).Info("Skipping internet gateway deletion in unmanaged mode")
+		s.scope.Trace("Skipping internet gateway deletion in unmanaged mode")
 		return nil
 	}
 
@@ -101,7 +101,7 @@ func (s *Service) deleteInternetGateways() error {
 		}
 
 		record.Eventf(s.scope.InfraCluster(), "SuccessfulDetachInternetGateway", "Detached Internet Gateway %q from VPC %q", *ig.InternetGatewayId, s.scope.VPC().ID)
-		s.scope.V(2).Info("Detached internet gateway from VPC", "internet-gateway-id", *ig.InternetGatewayId, "vpc-id", s.scope.VPC().ID)
+		s.scope.Debug("Detached internet gateway from VPC", "internet-gateway-id", *ig.InternetGatewayId, "vpc-id", s.scope.VPC().ID)
 
 		deleteReq := &ec2.DeleteInternetGatewayInput{
 			InternetGatewayId: ig.InternetGatewayId,
@@ -145,7 +145,7 @@ func (s *Service) createInternetGateway() (*ec2.InternetGateway, error) {
 		return nil, errors.Wrapf(err, "failed to attach internet gateway %q to vpc %q", *ig.InternetGateway.InternetGatewayId, s.scope.VPC().ID)
 	}
 	record.Eventf(s.scope.InfraCluster(), "SuccessfulAttachInternetGateway", "Internet Gateway %q attached to VPC %q", *ig.InternetGateway.InternetGatewayId, s.scope.VPC().ID)
-	s.scope.V(2).Info("attached internet gateway to VPC", "internet-gateway-id", *ig.InternetGateway.InternetGatewayId, "vpc-id", s.scope.VPC().ID)
+	s.scope.Debug("attached internet gateway to VPC", "internet-gateway-id", *ig.InternetGateway.InternetGatewayId, "vpc-id", s.scope.VPC().ID)
 
 	return ig.InternetGateway, nil
 }
diff --git a/pkg/cloud/services/network/natgateways.go b/pkg/cloud/services/network/natgateways.go
index 570e3efad..8e42c184c 100644
--- a/pkg/cloud/services/network/natgateways.go
+++ b/pkg/cloud/services/network/natgateways.go
@@ -38,14 +38,14 @@ import (
 
 func (s *Service) reconcileNatGateways() error {
 	if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
-		s.scope.V(4).Info("Skipping NAT gateway reconcile in unmanaged mode")
+		s.scope.Trace("Skipping NAT gateway reconcile in unmanaged mode")
 		return nil
 	}
 
-	s.scope.V(2).Info("Reconciling NAT gateways")
+	s.scope.Debug("Reconciling NAT gateways")
 
 	if len(s.scope.Subnets().FilterPrivate()) == 0 {
-		s.scope.V(2).Info("No private subnets available, skipping NAT gateways")
+		s.scope.Debug("No private subnets available, skipping NAT gateways")
 		conditions.MarkFalse(
 			s.scope.InfraCluster(),
 			infrav1.NatGatewaysReadyCondition,
@@ -54,7 +54,7 @@ func (s *Service) reconcileNatGateways() error {
 			"No private subnets available, skipping NAT gateways")
 		return nil
 	} else if len(s.scope.Subnets().FilterPublic()) == 0 {
-		s.scope.V(2).Info("No public subnets available. Cannot create NAT gateways for private subnets, this might be a configuration error.")
+		s.scope.Debug("No public subnets available. Cannot create NAT gateways for private subnets, this might be a configuration error.")
 		conditions.MarkFalse(
 			s.scope.InfraCluster(),
 			infrav1.NatGatewaysReadyCondition,
@@ -123,15 +123,15 @@ func (s *Service) reconcileNatGateways() error {
 
 func (s *Service) deleteNatGateways() error {
 	if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
-		s.scope.V(4).Info("Skipping NAT gateway deletion in unmanaged mode")
+		s.scope.Trace("Skipping NAT gateway deletion in unmanaged mode")
 		return nil
 	}
 
 	if len(s.scope.Subnets().FilterPrivate()) == 0 {
-		s.scope.V(2).Info("No private subnets available, skipping NAT gateways")
+		s.scope.Debug("No private subnets available, skipping NAT gateways")
 		return nil
 	} else if len(s.scope.Subnets().FilterPublic()) == 0 {
-		s.scope.V(2).Info("No public subnets available. Cannot create NAT gateways for private subnets, this might be a configuration error.")
+		s.scope.Debug("No public subnets available. Cannot create NAT gateways for private subnets, this might be a configuration error.")
 		return nil
 	}
 
diff --git a/pkg/cloud/services/network/network.go b/pkg/cloud/services/network/network.go
index 604ff9c57..6e0572f7a 100644
--- a/pkg/cloud/services/network/network.go
+++ b/pkg/cloud/services/network/network.go
@@ -28,7 +28,7 @@ import (
 
 // ReconcileNetwork reconciles the network of the given cluster.
 func (s *Service) ReconcileNetwork() (err error) {
-	s.scope.V(2).Info("Reconciling network for cluster", "cluster", klog.KRef(s.scope.Namespace(), s.scope.Name()))
+	s.scope.Debug("Reconciling network for cluster", "cluster", klog.KRef(s.scope.Namespace(), s.scope.Name()))
 
 	// VPC.
 	if err := s.reconcileVPC(); err != nil {
@@ -73,13 +73,13 @@ func (s *Service) ReconcileNetwork() (err error) {
 		return err
 	}
 
-	s.scope.V(2).Info("Reconcile network completed successfully")
+	s.scope.Debug("Reconcile network completed successfully")
 	return nil
 }
 
 // DeleteNetwork deletes the network of the given cluster.
 func (s *Service) DeleteNetwork() (err error) {
-	s.scope.V(2).Info("Deleting network")
+	s.scope.Debug("Deleting network")
 
 	vpc := &infrav1.VPCSpec{}
 	// Get VPC used for the cluster
@@ -183,6 +183,6 @@ func (s *Service) DeleteNetwork() (err error) {
 	}
 	conditions.MarkFalse(s.scope.InfraCluster(), infrav1.VpcReadyCondition, clusterv1.DeletedReason, clusterv1.ConditionSeverityInfo, "")
 
-	s.scope.V(2).Info("Delete network completed successfully")
+	s.scope.Debug("Delete network completed successfully")
 	return nil
 }
diff --git a/pkg/cloud/services/network/routetables.go b/pkg/cloud/services/network/routetables.go
index e4f9d4644..b583fa797 100644
--- a/pkg/cloud/services/network/routetables.go
+++ b/pkg/cloud/services/network/routetables.go
@@ -40,11 +40,11 @@ const (
 
 func (s *Service) reconcileRouteTables() error {
 	if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
-		s.scope.V(4).Info("Skipping routing tables reconcile in unmanaged mode")
+		s.scope.Trace("Skipping routing tables reconcile in unmanaged mode")
 		return nil
 	}
 
-	s.scope.V(2).Info("Reconciling routing tables")
+	s.scope.Debug("Reconciling routing tables")
 
 	subnetRouteMap, err := s.describeVpcRouteTablesBySubnet()
 	if err != nil {
@@ -82,7 +82,7 @@ func (s *Service) reconcileRouteTables() error {
 		}
 
 		if rt, ok := subnetRouteMap[sn.ID]; ok {
-			s.scope.V(2).Info("Subnet is already associated with route table", "subnet-id", sn.ID, "route-table-id", *rt.RouteTableId)
+			s.scope.Debug("Subnet is already associated with route table", "subnet-id", sn.ID, "route-table-id", *rt.RouteTableId)
 			// TODO(vincepri): check that everything is in order, e.g. routes match the subnet type.
 
 			// For managed environments we need to reconcile the routes of our tables if there is a mistmatch.
@@ -132,7 +132,7 @@ func (s *Service) reconcileRouteTables() error {
 			return err
 		}
 
-		s.scope.V(2).Info("Subnet has been associated with route table", "subnet-id", sn.ID, "route-table-id", rt.ID)
+		s.scope.Debug("Subnet has been associated with route table", "subnet-id", sn.ID, "route-table-id", rt.ID)
 		sn.RouteTableID = aws.String(rt.ID)
 	}
 	conditions.MarkTrue(s.scope.InfraCluster(), infrav1.RouteTablesReadyCondition)
@@ -210,7 +210,7 @@ func (s *Service) describeVpcRouteTablesBySubnet() (map[string]*ec2.RouteTable,
 
 func (s *Service) deleteRouteTables() error {
 	if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
-		s.scope.V(4).Info("Skipping routing tables deletion in unmanaged mode")
+		s.scope.Trace("Skipping routing tables deletion in unmanaged mode")
 		return nil
 	}
 
@@ -231,7 +231,7 @@ func (s *Service) deleteRouteTables() error {
 			}
 
 			record.Eventf(s.scope.InfraCluster(), "SuccessfulDisassociateRouteTable", "Disassociated managed RouteTable %q from subnet %q", *rt.RouteTableId, *as.SubnetId)
-			s.scope.V(2).Info("Deleted association between route table and subnet", "route-table-id", *rt.RouteTableId, "subnet-id", *as.SubnetId)
+			s.scope.Debug("Deleted association between route table and subnet", "route-table-id", *rt.RouteTableId, "subnet-id", *as.SubnetId)
 		}
 
 		if _, err := s.EC2Client.DeleteRouteTable(&ec2.DeleteRouteTableInput{RouteTableId: rt.RouteTableId}); err != nil {
diff --git a/pkg/cloud/services/network/subnets.go b/pkg/cloud/services/network/subnets.go
index 302517863..4631979c2 100644
--- a/pkg/cloud/services/network/subnets.go
+++ b/pkg/cloud/services/network/subnets.go
@@ -178,7 +178,7 @@ func (s *Service) reconcileSubnets() error {
 		}
 	}
 
-	s.scope.V(2).Info("reconciled subnets", "subnets", subnets)
+	s.scope.Debug("reconciled subnets", "subnets", subnets)
 	conditions.MarkTrue(s.scope.InfraCluster(), infrav1.SubnetsReadyCondition)
 	return nil
 }
@@ -199,7 +199,7 @@ func (s *Service) getDefaultSubnets() (infrav1.Subnets, error) {
 	}
 
 	if len(zones) > maxZones {
-		s.scope.V(2).Info("region has more than AvailabilityZoneUsageLimit availability zones, picking zones to use", "region", s.scope.Region(), "AvailabilityZoneUsageLimit", maxZones)
+		s.scope.Debug("region has more than AvailabilityZoneUsageLimit availability zones, picking zones to use", "region", s.scope.Region(), "AvailabilityZoneUsageLimit", maxZones)
 		if selectionScheme == infrav1.AZSelectionSchemeRandom {
 			rand.Shuffle(len(zones), func(i, j int) {
 				zones[i], zones[j] = zones[j], zones[i]
@@ -209,7 +209,7 @@ func (s *Service) getDefaultSubnets() (infrav1.Subnets, error) {
 			sort.Strings(zones)
 		}
 		zones = zones[:maxZones]
-		s.scope.V(2).Info("zones selected", "region", s.scope.Region(), "zones", zones)
+		s.scope.Debug("zones selected", "region", s.scope.Region(), "zones", zones)
 	}
 
 	// 1 private subnet for each AZ plus 1 other subnet that will be further sub-divided for the public subnets
@@ -276,7 +276,7 @@ func (s *Service) getDefaultSubnets() (infrav1.Subnets, error) {
 
 func (s *Service) deleteSubnets() error {
 	if s.scope.VPC().IsUnmanaged(s.scope.Name()) {
-		s.scope.V(4).Info("Skipping subnets deletion in unmanaged mode")
+		s.scope.Trace("Skipping subnets deletion in unmanaged mode")
 		return nil
 	}
 
@@ -462,7 +462,7 @@ func (s *Service) createSubnet(sn *infrav1.SubnetSpec) (*infrav1.SubnetSpec, err
 		}
 	}
 
-	s.scope.V(2).Info("Created new subnet in VPC with cidr and availability zone ",
+	s.scope.Debug("Created new subnet in VPC with cidr and availability zone ",
 		"subnet-id", *out.Subnet.SubnetId,
 		"vpc-id", *out.Subnet.VpcId,
 		"cidr-block", *out.Subnet.CidrBlock,
diff --git a/pkg/cloud/services/network/vpc.go b/pkg/cloud/services/network/vpc.go
index 6b0ade7fe..b67892a56 100644
--- a/pkg/cloud/services/network/vpc.go
+++ b/pkg/cloud/services/network/vpc.go
@@ -41,7 +41,7 @@ const (
 )
 
 func (s *Service) reconcileVPC() error {
-	s.scope.V(2).Info("Reconciling VPC")
+	s.scope.Debug("Reconciling VPC")
 
 	// If the ID is not nil, VPC is either managed or unmanaged but should exist in the AWS.
 	if s.scope.VPC().ID != "" {
@@ -56,7 +56,7 @@ func (s *Service) reconcileVPC() error {
 
 		// If VPC is unmanaged, return early.
 		if vpc.IsUnmanaged(s.scope.Name()) {
-			s.scope.V(2).Info("Working on unmanaged VPC", "vpc-id", vpc.ID)
+			s.scope.Debug("Working on unmanaged VPC", "vpc-id", vpc.ID)
 			if err := s.scope.PatchObject(); err != nil {
 				return errors.Wrap(err, "failed to patch unmanaged VPC fields")
 			}
@@ -201,7 +201,7 @@ func (s *Service) createVPC() (*infrav1.VPCSpec, error) {
 	}
 
 	record.Eventf(s.scope.InfraCluster(), "SuccessfulCreateVPC", "Created new managed VPC %q", *out.Vpc.VpcId)
-	s.scope.V(2).Info("Created new VPC with cidr", "vpc-id", *out.Vpc.VpcId, "cidr-block", *out.Vpc.CidrBlock)
+	s.scope.Debug("Created new VPC with cidr", "vpc-id", *out.Vpc.VpcId, "cidr-block", *out.Vpc.CidrBlock)
 
 	if !s.scope.VPC().IsIPv6Enabled() {
 		return &infrav1.VPCSpec{
@@ -257,7 +257,7 @@ func (s *Service) deleteVPC() error {
 	vpc := s.scope.VPC()
 
 	if vpc.IsUnmanaged(s.scope.Name()) {
-		s.scope.V(4).Info("Skipping VPC deletion in unmanaged mode")
+		s.scope.Trace("Skipping VPC deletion in unmanaged mode")
 		return nil
 	}
 
@@ -268,7 +268,7 @@ func (s *Service) deleteVPC() error {
 	if _, err := s.EC2Client.DeleteVpc(input); err != nil {
 		// Ignore if it's already deleted
 		if code, ok := awserrors.Code(err); ok && code == awserrors.VPCNotFound {
-			s.scope.V(4).Info("Skipping VPC deletion, VPC not found")
+			s.scope.Trace("Skipping VPC deletion, VPC not found")
 			return nil
 		}
 		record.Warnf(s.scope.InfraCluster(), "FailedDeleteVPC", "Failed to delete managed VPC %q: %v", vpc.ID, err)
diff --git a/pkg/cloud/services/s3/s3.go b/pkg/cloud/services/s3/s3.go
index db5259718..9386fb55a 100644
--- a/pkg/cloud/services/s3/s3.go
+++ b/pkg/cloud/services/s3/s3.go
@@ -224,7 +224,7 @@ func (s *Service) ensureBucketPolicy(bucketName string) error {
 		return errors.Wrap(err, "creating S3 bucket policy")
 	}
 
-	s.scope.V(4).Info("Updated bucket policy", "bucket_name", bucketName)
+	s.scope.Trace("Updated bucket policy", "bucket_name", bucketName)
 
 	return nil
 }
diff --git a/pkg/cloud/services/securitygroup/securitygroups.go b/pkg/cloud/services/securitygroup/securitygroups.go
index acfbc78b6..ce3c3a7f4 100644
--- a/pkg/cloud/services/securitygroup/securitygroups.go
+++ b/pkg/cloud/services/securitygroup/securitygroups.go
@@ -53,7 +53,7 @@ const (
 
 // ReconcileSecurityGroups will reconcile security groups against the Service object.
 func (s *Service) ReconcileSecurityGroups() error {
-	s.scope.V(2).Info("Reconciling security groups")
+	s.scope.Debug("Reconciling security groups")
 
 	if s.scope.Network().SecurityGroups == nil {
 		s.scope.Network().SecurityGroups = make(map[infrav1.SecurityGroupRole]infrav1.SecurityGroup)
@@ -93,7 +93,7 @@ func (s *Service) ReconcileSecurityGroups() error {
 		// if an override exists for this role use it
 		sgOverride, ok := securityGroupOverrides[role]
 		if ok {
-			s.scope.V(2).Info("Using security group override", "role", role, "security group", sgOverride.GroupName)
+			s.scope.Debug("Using security group override", "role", role, "security group", sgOverride.GroupName)
 			sg = sgOverride
 		}
 
@@ -115,7 +115,7 @@ func (s *Service) ReconcileSecurityGroups() error {
 		s.scope.SecurityGroups()[role] = existing
 
 		if s.isEKSOwned(existing) {
-			s.scope.V(2).Info("Security group is EKS owned", "role", role, "security-group", s.scope.SecurityGroups()[role])
+			s.scope.Debug("Security group is EKS owned", "role", role, "security-group", s.scope.SecurityGroups()[role])
 			continue
 		}
 
@@ -138,7 +138,7 @@ func (s *Service) ReconcileSecurityGroups() error {
 	// the specified ingress rules.
 	for i := range s.scope.SecurityGroups() {
 		sg := s.scope.SecurityGroups()[i]
-		s.scope.V(2).Info("second pass security group reconciliation", "group-id", sg.ID, "name", sg.Name, "role", i)
+		s.scope.Debug("second pass security group reconciliation", "group-id", sg.ID, "name", sg.Name, "role", i)
 
 		if s.securityGroupIsOverridden(sg.ID) {
 			// skip rule/tag reconciliation on security groups that are overridden, assuming they're managed by another process
@@ -167,7 +167,7 @@ func (s *Service) ReconcileSecurityGroups() error {
 				return errors.Wrapf(err, "failed to revoke security group ingress rules for %q", sg.ID)
 			}
 
-			s.scope.V(2).Info("Revoked ingress rules from security group", "revoked-ingress-rules", toRevoke, "security-group-id", sg.ID)
+			s.scope.Debug("Revoked ingress rules from security group", "revoked-ingress-rules", toRevoke, "security-group-id", sg.ID)
 		}
 
 		toAuthorize := want.Difference(current)
@@ -181,7 +181,7 @@ func (s *Service) ReconcileSecurityGroups() error {
 				return err
 			}
 
-			s.scope.V(2).Info("Authorized ingress rules in security group", "authorized-ingress-rules", toAuthorize, "security-group-id", sg.ID)
+			s.scope.Debug("Authorized ingress rules in security group", "authorized-ingress-rules", toAuthorize, "security-group-id", sg.ID)
 		}
 	}
 	conditions.MarkTrue(s.scope.InfraCluster(), infrav1.ClusterSecurityGroupsReadyCondition)
@@ -230,7 +230,7 @@ func (s *Service) describeSecurityGroupOverridesByID() (map[infrav1.SecurityGrou
 				continue
 			}
 			if *ec2sg.GroupId == *securityGroupIds[role] {
-				s.scope.V(2).Info("found security group override", "role", role, "security group", *ec2sg.GroupName)
+				s.scope.Debug("found security group override", "role", role, "security group", *ec2sg.GroupName)
 
 				res[role] = ec2sg
 				break
@@ -253,7 +253,7 @@ func (s *Service) ec2SecurityGroupToSecurityGroup(ec2SecurityGroup *ec2.Security
 // DeleteSecurityGroups will delete a service's security groups.
 func (s *Service) DeleteSecurityGroups() error {
 	if s.scope.VPC().ID == "" {
-		s.scope.V(2).Info("Skipping security group deletion, vpc-id is nil", "vpc-id", s.scope.VPC().ID)
+		s.scope.Debug("Skipping security group deletion, vpc-id is nil", "vpc-id", s.scope.VPC().ID)
 		conditions.MarkFalse(s.scope.InfraCluster(), infrav1.ClusterSecurityGroupsReadyCondition, clusterv1.DeletedReason, clusterv1.ConditionSeverityInfo, "")
 		return nil
 	}
@@ -281,7 +281,7 @@ func (s *Service) DeleteSecurityGroups() error {
 			return err
 		}
 
-		s.scope.V(2).Info("Revoked ingress rules from security group", "revoked-ingress-rules", current, "security-group-id", sg.ID)
+		s.scope.Debug("Revoked ingress rules from security group", "revoked-ingress-rules", current, "security-group-id", sg.ID)
 
 		if deleteErr := s.deleteSecurityGroup(&sg, "cluster managed"); deleteErr != nil {
 			err = kerrors.NewAggregate([]error{err, deleteErr})
@@ -460,7 +460,7 @@ func (s *Service) defaultSSHIngressRule(sourceSecurityGroupID string) infrav1.In
 
 func (s *Service) getSecurityGroupIngressRules(role infrav1.SecurityGroupRole) (infrav1.IngressRules, error) {
 	// Set source of CNI ingress rules to be control plane and node security groups
-	s.scope.V(2).Info("getting security group ingress rules", "role", role)
+	s.scope.Debug("getting security group ingress rules", "role", role)
 
 	cniRules := make(infrav1.IngressRules, len(s.scope.CNIIngressRules()))
 	for i, r := range s.scope.CNIIngressRules() {
diff --git a/pkg/eks/identityprovider/plan.go b/pkg/eks/identityprovider/plan.go
index 201b1d0d0..1aeaaf125 100644
--- a/pkg/eks/identityprovider/plan.go
+++ b/pkg/eks/identityprovider/plan.go
@@ -21,13 +21,13 @@ import (
 
 	"github.com/aws/aws-sdk-go/service/eks"
 	"github.com/aws/aws-sdk-go/service/eks/eksiface"
-	"github.com/go-logr/logr"
 
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/planner"
 )
 
 // NewPlan creates plan to manage EKS OIDC identity provider association.
-func NewPlan(clusterName string, currentIdentityProvider, desiredIdentityProvider *OidcIdentityProviderConfig, client eksiface.EKSAPI, log logr.Logger) planner.Plan {
+func NewPlan(clusterName string, currentIdentityProvider, desiredIdentityProvider *OidcIdentityProviderConfig, client eksiface.EKSAPI, log logger.Wrapper) planner.Plan {
 	return &plan{
 		currentIdentityProvider: currentIdentityProvider,
 		desiredIdentityProvider: desiredIdentityProvider,
@@ -42,7 +42,7 @@ type plan struct {
 	currentIdentityProvider *OidcIdentityProviderConfig
 	desiredIdentityProvider *OidcIdentityProviderConfig
 	eksClient               eksiface.EKSAPI
-	log                     logr.Logger
+	log                     logger.Wrapper
 	clusterName             string
 }
 
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
new file mode 100644
index 000000000..c2730c47c
--- /dev/null
+++ b/pkg/logger/logger.go
@@ -0,0 +1,83 @@
+// Package logger
+//nolint: logrlint
+package logger
+
+import (
+	"context"
+
+	"github.com/go-logr/logr"
+)
+
+const (
+	logLevelDebug = 2
+	logLevelWarn  = 3
+	logLevelTrace = 4
+)
+
+// Wrapper defines a convenient interface to use to log things.
+type Wrapper interface {
+	Info(msg string, keysAndValues ...any)
+	Debug(msg string, keysAndValues ...any)
+	Warn(msg string, keysAndValues ...any)
+	Trace(msg string, keysAndValues ...any)
+	Error(err error, msg string, keysAndValues ...any)
+	WithValues(keysAndValues ...any) *Logger
+	WithName(name string) *Logger
+	GetLogger() logr.Logger
+}
+
+// Logger is a concrete logger using logr underneath.
+type Logger struct {
+	logger logr.Logger
+}
+
+// NewLogger creates a logger with a passed in logr.Logger implementation directly.
+func NewLogger(log logr.Logger) *Logger {
+	return &Logger{
+		logger: log,
+	}
+}
+
+// FromContext retrieves the logr implementation from Context and uses it as underlying logger.
+func FromContext(ctx context.Context) *Logger {
+	log := logr.FromContextOrDiscard(ctx)
+	return &Logger{
+		logger: log,
+	}
+}
+
+var _ Wrapper = &Logger{}
+
+func (c *Logger) Info(msg string, keysAndValues ...any) {
+	c.logger.Info(msg, keysAndValues...)
+}
+
+func (c *Logger) Debug(msg string, keysAndValues ...any) {
+	c.logger.V(logLevelDebug).Info(msg, keysAndValues...)
+}
+
+func (c *Logger) Warn(msg string, keysAndValues ...any) {
+	c.logger.V(logLevelWarn).Info(msg, keysAndValues...)
+}
+
+func (c *Logger) Trace(msg string, keysAndValues ...any) {
+	c.logger.V(logLevelTrace).Info(msg, keysAndValues...)
+}
+
+func (c *Logger) Error(err error, msg string, keysAndValues ...any) {
+	c.logger.Error(err, msg, keysAndValues...)
+}
+
+func (c *Logger) GetLogger() logr.Logger {
+	return c.logger
+}
+
+func (c *Logger) WithValues(keysAndValues ...any) *Logger {
+	c.logger = c.logger.WithValues(keysAndValues...)
+	return c
+}
+
+func (c *Logger) WithName(name string) *Logger {
+	c.logger = c.logger.WithName(name)
+	return c
+}


Test patch:
diff --git a/controllers/awscluster_controller_unit_test.go b/controllers/awscluster_controller_unit_test.go
index dbd747ba5..46e46749e 100644
--- a/controllers/awscluster_controller_unit_test.go
+++ b/controllers/awscluster_controller_unit_test.go
@@ -38,6 +38,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/mock_services"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/util"
 )
@@ -571,7 +572,7 @@ func TestAWSClusterReconciler_RequeueAWSClusterForUnpausedCluster(t *testing.T)
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
 			g := NewWithT(t)
-			log := ctrl.LoggerFrom(ctx)
+			log := logger.FromContext(ctx)
 			reconciler := &AWSClusterReconciler{
 				Client: testEnv.Client,
 			}
diff --git a/controllers/awsmachine_controller_unit_test.go b/controllers/awsmachine_controller_unit_test.go
index 3d94eaa5b..0548d224b 100644
--- a/controllers/awsmachine_controller_unit_test.go
+++ b/controllers/awsmachine_controller_unit_test.go
@@ -46,6 +46,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/mock_services"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/controllers/noderefutil"
 	capierrors "sigs.k8s.io/cluster-api/errors"
@@ -2028,7 +2029,7 @@ func TestAWSMachineReconciler_AWSClusterToAWSMachines(t *testing.T) {
 				g.Expect(testEnv.Cleanup(ctx, tc.awsCluster, ns)).To(Succeed())
 			})
 
-			requests := reconciler.AWSClusterToAWSMachines(klog.Background())(tc.awsCluster)
+			requests := reconciler.AWSClusterToAWSMachines(logger.NewLogger(klog.Background()))(tc.awsCluster)
 			if tc.requests != nil {
 				if len(tc.requests) > 0 {
 					tc.requests[0].Namespace = ns.Name
@@ -2064,7 +2065,7 @@ func TestAWSMachineReconciler_requeueAWSMachinesForUnpausedCluster(t *testing.T)
 				Client: testEnv.Client,
 				Log:    klog.Background(),
 			}
-			requests := reconciler.requeueAWSMachinesForUnpausedCluster(klog.Background())(tc.ownerCluster)
+			requests := reconciler.requeueAWSMachinesForUnpausedCluster(logger.NewLogger(klog.Background()))(tc.ownerCluster)
 			if tc.requests != nil {
 				g.Expect(requests).To(ConsistOf(tc.requests))
 			} else {
diff --git a/exp/controllers/awsmachinepool_controller_test.go b/exp/controllers/awsmachinepool_controller_test.go
index df610b681..fe6c3977a 100644
--- a/exp/controllers/awsmachinepool_controller_test.go
+++ b/exp/controllers/awsmachinepool_controller_test.go
@@ -41,6 +41,7 @@ import (
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/mock_services"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	"sigs.k8s.io/cluster-api/controllers/noderefutil"
 	capierrors "sigs.k8s.io/cluster-api/errors"
@@ -623,7 +624,7 @@ func TestASGNeedsUpdates(t *testing.T) {
 							},
 						},
 					},
-					Logger: logr.Discard(),
+					Logger: *logger.NewLogger(logr.Discard()),
 				},
 				existingASG: &expinfrav1.AutoScalingGroup{
 					DesiredCapacity:      pointer.Int32(1),
@@ -663,7 +664,7 @@ func TestASGNeedsUpdates(t *testing.T) {
 							},
 						},
 					},
-					Logger: logr.Discard(),
+					Logger: *logger.NewLogger(logr.Discard()),
 				},
 				existingASG: &expinfrav1.AutoScalingGroup{
 					DesiredCapacity:           pointer.Int32(1),
diff --git a/pkg/cloud/scope/session_test.go b/pkg/cloud/scope/session_test.go
index 4a2816138..13bffa1a9 100644
--- a/pkg/cloud/scope/session_test.go
+++ b/pkg/cloud/scope/session_test.go
@@ -32,6 +32,7 @@ import (
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/identity"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/util/system"
 	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
 )
@@ -488,7 +489,7 @@ func TestPrincipalParsing(t *testing.T) {
 			k8sClient := fake.NewClientBuilder().WithScheme(scheme).Build()
 			tc.setup(t, k8sClient)
 			clusterScope.AWSCluster = &tc.awsCluster
-			providers, err := getProvidersForCluster(context.Background(), k8sClient, clusterScope, klog.Background())
+			providers, err := getProvidersForCluster(context.Background(), k8sClient, clusterScope, logger.NewLogger(klog.Background()))
 			if tc.expectError {
 				if err == nil {
 					t.Fatal("Expected an error but didn't get one")
diff --git a/pkg/cloud/scope/shared_test.go b/pkg/cloud/scope/shared_test.go
index 18844a828..04627f512 100644
--- a/pkg/cloud/scope/shared_test.go
+++ b/pkg/cloud/scope/shared_test.go
@@ -19,11 +19,11 @@ package scope
 import (
 	"testing"
 
-	"github.com/go-logr/logr"
 	. "github.com/onsi/gomega"
 	"k8s.io/klog/v2"
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 )
 
 func TestSubnetPlacement(t *testing.T) {
@@ -33,7 +33,7 @@ func TestSubnetPlacement(t *testing.T) {
 		specAZs             []string
 		parentAZs           []string
 		controlPlaneSubnets infrav1.Subnets
-		logger              logr.Logger
+		logger              *logger.Logger
 		expectedSubnetIDs   []string
 		expectError         bool
 	}{
@@ -56,7 +56,7 @@ func TestSubnetPlacement(t *testing.T) {
 					AvailabilityZone: "eu-west-1c",
 				},
 			},
-			logger:            klog.Background(),
+			logger:            logger.NewLogger(klog.Background()),
 			expectedSubnetIDs: []string{"az1"},
 			expectError:       false,
 		},
@@ -79,7 +79,7 @@ func TestSubnetPlacement(t *testing.T) {
 					AvailabilityZone: "eu-west-1c",
 				},
 			},
-			logger:            klog.Background(),
+			logger:            logger.NewLogger(klog.Background()),
 			expectedSubnetIDs: []string{"az2"},
 			expectError:       false,
 		},
@@ -102,7 +102,7 @@ func TestSubnetPlacement(t *testing.T) {
 					AvailabilityZone: "eu-west-1c",
 				},
 			},
-			logger:            klog.Background(),
+			logger:            logger.NewLogger(klog.Background()),
 			expectedSubnetIDs: []string{"az3"},
 			expectError:       false,
 		},
@@ -128,7 +128,7 @@ func TestSubnetPlacement(t *testing.T) {
 					IsPublic:         true,
 				},
 			},
-			logger:            klog.Background(),
+			logger:            logger.NewLogger(klog.Background()),
 			expectedSubnetIDs: []string{"az1", "az2"},
 			expectError:       false,
 		},
@@ -138,7 +138,7 @@ func TestSubnetPlacement(t *testing.T) {
 			specAZs:             []string{},
 			parentAZs:           []string{},
 			controlPlaneSubnets: infrav1.Subnets{},
-			logger:              klog.Background(),
+			logger:              logger.NewLogger(klog.Background()),
 			expectedSubnetIDs:   []string{},
 			expectError:         true,
 		},
@@ -148,7 +148,7 @@ func TestSubnetPlacement(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			g := NewGomegaWithT(t)
 
-			strategy, err := newDefaultSubnetPlacementStrategy(&tc.logger)
+			strategy, err := newDefaultSubnetPlacementStrategy(tc.logger)
 			g.Expect(err).NotTo(HaveOccurred())
 
 			actualSubnetIDs, err := strategy.Place(&placementInput{
diff --git a/pkg/eks/identityprovider/plan_test.go b/pkg/eks/identityprovider/plan_test.go
index 4eb330054..9809988ee 100644
--- a/pkg/eks/identityprovider/plan_test.go
+++ b/pkg/eks/identityprovider/plan_test.go
@@ -28,13 +28,14 @@ import (
 
 	infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/services/eks/mock_eksiface"
+	"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 )
 
 func TestEKSAddonPlan(t *testing.T) {
 	clusterName := "default.cluster"
 	identityProviderARN := "aws:mock:provider:arn"
 	idnetityProviderName := "IdentityProviderConfigName"
-	log := klog.Background()
+	log := logger.NewLogger(klog.Background())
 
 	testCases := []struct {
 		name                    string
diff --git a/test/mocks/capa_clusterscoper_mock.go b/test/mocks/capa_clusterscoper_mock.go
index 50a4082bd..0fb8d7b2e 100644
--- a/test/mocks/capa_clusterscoper_mock.go
+++ b/test/mocks/capa_clusterscoper_mock.go
@@ -29,6 +29,7 @@ import (
 	v1beta2 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
 	cloud "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud"
 	throttle "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/throttle"
+	logger "sigs.k8s.io/cluster-api-provider-aws/v2/pkg/logger"
 	v1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
 	client0 "sigs.k8s.io/controller-runtime/pkg/client"
 )
@@ -126,18 +127,21 @@ func (mr *MockClusterScoperMockRecorder) ControllerName() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ControllerName", reflect.TypeOf((*MockClusterScoper)(nil).ControllerName))
 }
 
-// Enabled mocks base method.
-func (m *MockClusterScoper) Enabled() bool {
+// Debug mocks base method.
+func (m *MockClusterScoper) Debug(arg0 string, arg1 ...interface{}) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Enabled")
-	ret0, _ := ret[0].(bool)
-	return ret0
+	varargs := []interface{}{arg0}
+	for _, a := range arg1 {
+		varargs = append(varargs, a)
+	}
+	m.ctrl.Call(m, "Debug", varargs...)
 }
 
-// Enabled indicates an expected call of Enabled.
-func (mr *MockClusterScoperMockRecorder) Enabled() *gomock.Call {
+// Debug indicates an expected call of Debug.
+func (mr *MockClusterScoperMockRecorder) Debug(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Enabled", reflect.TypeOf((*MockClusterScoper)(nil).Enabled))
+	varargs := append([]interface{}{arg0}, arg1...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockClusterScoper)(nil).Debug), varargs...)
 }
 
 // Error mocks base method.
@@ -157,6 +161,20 @@ func (mr *MockClusterScoperMockRecorder) Error(arg0, arg1 interface{}, arg2 ...i
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockClusterScoper)(nil).Error), varargs...)
 }
 
+// GetLogger mocks base method.
+func (m *MockClusterScoper) GetLogger() logr.Logger {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetLogger")
+	ret0, _ := ret[0].(logr.Logger)
+	return ret0
+}
+
+// GetLogger indicates an expected call of GetLogger.
+func (mr *MockClusterScoperMockRecorder) GetLogger() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogger", reflect.TypeOf((*MockClusterScoper)(nil).GetLogger))
+}
+
 // IdentityRef mocks base method.
 func (m *MockClusterScoper) IdentityRef() *v1beta2.AWSIdentityReference {
 	m.ctrl.T.Helper()
@@ -340,25 +358,45 @@ func (mr *MockClusterScoperMockRecorder) SetFailureDomain(arg0, arg1 interface{}
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFailureDomain", reflect.TypeOf((*MockClusterScoper)(nil).SetFailureDomain), arg0, arg1)
 }
 
-// V mocks base method.
-func (m *MockClusterScoper) V(arg0 int) logr.Logger {
+// Trace mocks base method.
+func (m *MockClusterScoper) Trace(arg0 string, arg1 ...interface{}) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "V", arg0)
-	ret0, _ := ret[0].(logr.Logger)
-	return ret0
+	varargs := []interface{}{arg0}
+	for _, a := range arg1 {
+		varargs = append(varargs, a)
+	}
+	m.ctrl.Call(m, "Trace", varargs...)
 }
 
-// V indicates an expected call of V.
-func (mr *MockClusterScoperMockRecorder) V(arg0 interface{}) *gomock.Call {
+// Trace indicates an expected call of Trace.
+func (mr *MockClusterScoperMockRecorder) Trace(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "V", reflect.TypeOf((*MockClusterScoper)(nil).V), arg0)
+	varargs := append([]interface{}{arg0}, arg1...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Trace", reflect.TypeOf((*MockClusterScoper)(nil).Trace), varargs...)
+}
+
+// Warn mocks base method.
+func (m *MockClusterScoper) Warn(arg0 string, arg1 ...interface{}) {
+	m.ctrl.T.Helper()
+	varargs := []interface{}{arg0}
+	for _, a := range arg1 {
+		varargs = append(varargs, a)
+	}
+	m.ctrl.Call(m, "Warn", varargs...)
+}
+
+// Warn indicates an expected call of Warn.
+func (mr *MockClusterScoperMockRecorder) Warn(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]interface{}{arg0}, arg1...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockClusterScoper)(nil).Warn), varargs...)
 }
 
 // WithName mocks base method.
-func (m *MockClusterScoper) WithName(arg0 string) logr.Logger {
+func (m *MockClusterScoper) WithName(arg0 string) *logger.Logger {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "WithName", arg0)
-	ret0, _ := ret[0].(logr.Logger)
+	ret0, _ := ret[0].(*logger.Logger)
 	return ret0
 }
 
@@ -369,14 +407,14 @@ func (mr *MockClusterScoperMockRecorder) WithName(arg0 interface{}) *gomock.Call
 }
 
 // WithValues mocks base method.
-func (m *MockClusterScoper) WithValues(arg0 ...interface{}) logr.Logger {
+func (m *MockClusterScoper) WithValues(arg0 ...interface{}) *logger.Logger {
 	m.ctrl.T.Helper()
 	varargs := []interface{}{}
 	for _, a := range arg0 {
 		varargs = append(varargs, a)
 	}
 	ret := m.ctrl.Call(m, "WithValues", varargs...)
-	ret0, _ := ret[0].(logr.Logger)
+	ret0, _ := ret[0].(*logger.Logger)
 	return ret0
 }