diff --git a/layers/tests/e2e/layerPublisher.class.test.functionCode.ts b/layers/tests/e2e/layerPublisher.class.test.functionCode.ts
index a3ce0497dd..440875dfd1 100644
--- a/layers/tests/e2e/layerPublisher.class.test.functionCode.ts
+++ b/layers/tests/e2e/layerPublisher.class.test.functionCode.ts
@@ -17,7 +17,7 @@ import { SSMClient } from '@aws-sdk/client-ssm';
 const logger = new Logger({
   logLevel: 'DEBUG',
 });
-const metrics = new Metrics();
+const metrics = new Metrics({ logger });
 const tracer = new Tracer();
 
 // Instantiating these clients and the respective providers/persistence layers
diff --git a/layers/tests/e2e/layerPublisher.test.ts b/layers/tests/e2e/layerPublisher.test.ts
index 3cb7697031..cbffc8abfa 100644
--- a/layers/tests/e2e/layerPublisher.test.ts
+++ b/layers/tests/e2e/layerPublisher.test.ts
@@ -143,15 +143,10 @@ describe('Layers E2E tests', () => {
       const logs = invocationLogs.getFunctionLogs('WARN');
 
       expect(logs.length).toBe(1);
-      expect(
-        invocationLogs.doesAnyFunctionLogsContains(
-          /Namespace should be defined, default used/,
-          'WARN'
-        )
-      ).toBe(true);
-      /* expect(logEntry.message).toEqual(
+      const logEntry = TestInvocationLogs.parseFunctionLog(logs[0]);
+      expect(logEntry.message).toEqual(
         'Namespace should be defined, default used'
-      ); */
+      );
     }
   );
 
diff --git a/packages/commons/src/types/GenericLogger.ts b/packages/commons/src/types/GenericLogger.ts
new file mode 100644
index 0000000000..0e5da7a716
--- /dev/null
+++ b/packages/commons/src/types/GenericLogger.ts
@@ -0,0 +1,17 @@
+// biome-ignore lint/suspicious/noExplicitAny: We intentionally use `any` here to represent any type of data and keep the logger is as flexible as possible.
+type Anything = any[];
+
+/**
+ * Interface for a generic logger object.
+ *
+ * This interface is used to define the shape of a logger object that can be passed to a Powertools for AWS utility.
+ *
+ * It can be an instance of Logger from Powertools for AWS, or any other logger that implements the same methods.
+ */
+export interface GenericLogger {
+  trace?: (...content: Anything) => void;
+  debug: (...content: Anything) => void;
+  info: (...content: Anything) => void;
+  warn: (...content: Anything) => void;
+  error: (...content: Anything) => void;
+}
diff --git a/packages/commons/src/types/index.ts b/packages/commons/src/types/index.ts
index 24fb3b8032..080d13ed12 100644
--- a/packages/commons/src/types/index.ts
+++ b/packages/commons/src/types/index.ts
@@ -5,6 +5,7 @@ export type {
   MiddlewareFn,
   CleanupFunction,
 } from './middy.js';
+export type { GenericLogger } from './GenericLogger.js';
 export type { SdkClient, MiddlewareArgsLike } from './awsSdk.js';
 export type {
   JSONPrimitive,
diff --git a/packages/metrics/src/Metrics.ts b/packages/metrics/src/Metrics.ts
index 559ff71b91..abe8fac81b 100644
--- a/packages/metrics/src/Metrics.ts
+++ b/packages/metrics/src/Metrics.ts
@@ -1,6 +1,9 @@
 import { Console } from 'node:console';
 import { Utility } from '@aws-lambda-powertools/commons';
-import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types';
+import type {
+  GenericLogger,
+  HandlerMethodDecorator,
+} from '@aws-lambda-powertools/commons/types';
 import type { Callback, Context, Handler } from 'aws-lambda';
 import { EnvironmentVariablesService } from './config/EnvironmentVariablesService.js';
 import {
@@ -159,6 +162,13 @@ class Metrics extends Utility implements MetricsInterface {
    */
   private functionName?: string;
 
+  /**
+   * Custom logger object used for emitting debug, warning, and error messages.
+   *
+   * Note that this logger is not used for emitting metrics which are emitted to standard output using the `Console` object.
+   */
+  readonly #logger: GenericLogger;
+
   /**
    * Flag indicating if this is a single metric instance
    * @default false
@@ -193,6 +203,7 @@ class Metrics extends Utility implements MetricsInterface {
 
     this.dimensions = {};
     this.setOptions(options);
+    this.#logger = options.logger || this.console;
   }
 
   /**
@@ -439,6 +450,13 @@ class Metrics extends Utility implements MetricsInterface {
     this.storedMetrics = {};
   }
 
+  /**
+   * Check if there are stored metrics in the buffer.
+   */
+  public hasStoredMetrics(): boolean {
+    return Object.keys(this.storedMetrics).length > 0;
+  }
+
   /**
    * A class method decorator to automatically log metrics after the method returns or throws an error.
    *
@@ -539,9 +557,9 @@ class Metrics extends Utility implements MetricsInterface {
    * ```
    */
   public publishStoredMetrics(): void {
-    const hasMetrics = Object.keys(this.storedMetrics).length > 0;
+    const hasMetrics = this.hasStoredMetrics();
     if (!this.shouldThrowOnEmptyMetrics && !hasMetrics) {
-      console.warn(
+      this.#logger.warn(
         'No application metrics to publish. The cold-start metric may be published if enabled. ' +
           'If application metrics should never be empty, consider using `throwOnEmptyMetrics`'
       );
@@ -584,7 +602,7 @@ class Metrics extends Utility implements MetricsInterface {
     }
 
     if (!this.namespace)
-      console.warn('Namespace should be defined, default used');
+      this.#logger.warn('Namespace should be defined, default used');
 
     // We reduce the stored metrics to a single object with the metric
     // name as the key and the value as the value.
@@ -731,6 +749,7 @@ class Metrics extends Utility implements MetricsInterface {
       serviceName: this.dimensions.service,
       defaultDimensions: this.defaultDimensions,
       singleMetric: true,
+      logger: this.#logger,
     });
   }
 
diff --git a/packages/metrics/src/types/Metrics.ts b/packages/metrics/src/types/Metrics.ts
index 1c65dc5212..f02f9b183a 100644
--- a/packages/metrics/src/types/Metrics.ts
+++ b/packages/metrics/src/types/Metrics.ts
@@ -1,4 +1,7 @@
-import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types';
+import type {
+  GenericLogger,
+  HandlerMethodDecorator,
+} from '@aws-lambda-powertools/commons/types';
 import type {
   MetricResolution as MetricResolutions,
   MetricUnit as MetricUnits,
@@ -57,6 +60,15 @@ type MetricsOptions = {
    * @see {@link MetricsInterface.setDefaultDimensions | `setDefaultDimensions()`}
    */
   defaultDimensions?: Dimensions;
+  /**
+   * Logger object to be used for emitting debug, warning, and error messages.
+   *
+   * If not provided, debug messages will be suppressed, and warning and error messages will be sent to stdout.
+   *
+   * Note that EMF metrics are always sent directly to stdout, regardless of the logger
+   * to avoid compatibility issues with custom loggers.
+   */
+  logger?: GenericLogger;
 };
 
 /**
diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts
index b68d2b7e38..8b75a21faf 100644
--- a/packages/metrics/tests/unit/Metrics.test.ts
+++ b/packages/metrics/tests/unit/Metrics.test.ts
@@ -27,6 +27,8 @@ jest.mock('node:console', () => ({
   ...jest.requireActual('node:console'),
   Console: jest.fn().mockImplementation(() => ({
     log: jest.fn(),
+    warn: jest.fn(),
+    debug: jest.fn(),
   })),
 }));
 jest.spyOn(console, 'warn').mockImplementation(() => ({}));
@@ -1254,9 +1256,17 @@ describe('Class: Metrics', () => {
   describe('Methods: publishStoredMetrics', () => {
     test('it should log warning if no metrics are added & throwOnEmptyMetrics is false', () => {
       // Prepare
-      const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE });
-      const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
-      const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
+      const customLogger = {
+        warn: jest.fn(),
+        debug: jest.fn(),
+        error: jest.fn(),
+        info: jest.fn(),
+      };
+      const metrics: Metrics = new Metrics({
+        namespace: TEST_NAMESPACE,
+        logger: customLogger,
+      });
+      const consoleWarnSpy = jest.spyOn(customLogger, 'warn');
 
       // Act
       metrics.publishStoredMetrics();
@@ -1266,7 +1276,6 @@ describe('Class: Metrics', () => {
       expect(consoleWarnSpy).toHaveBeenCalledWith(
         'No application metrics to publish. The cold-start metric may be published if enabled. If application metrics should never be empty, consider using `throwOnEmptyMetrics`'
       );
-      expect(consoleLogSpy).not.toHaveBeenCalled();
     });
 
     test('it should call serializeMetrics && log the stringified return value of serializeMetrics', () => {
@@ -1355,8 +1364,14 @@ describe('Class: Metrics', () => {
     test('it should print warning, if no namespace provided in constructor or environment variable', () => {
       // Prepare
       process.env.POWERTOOLS_METRICS_NAMESPACE = '';
-      const metrics: Metrics = new Metrics();
-      const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
+      const customLogger = {
+        warn: jest.fn(),
+        debug: jest.fn(),
+        error: jest.fn(),
+        info: jest.fn(),
+      };
+      const metrics: Metrics = new Metrics({ logger: customLogger });
+      const consoleWarnSpy = jest.spyOn(customLogger, 'warn');
 
       // Act
       metrics.serializeMetrics();
diff --git a/packages/metrics/tests/unit/middleware/middy.test.ts b/packages/metrics/tests/unit/middleware/middy.test.ts
index fc9ddec7e8..8d2a17ec51 100644
--- a/packages/metrics/tests/unit/middleware/middy.test.ts
+++ b/packages/metrics/tests/unit/middleware/middy.test.ts
@@ -14,6 +14,8 @@ jest.mock('node:console', () => ({
   ...jest.requireActual('node:console'),
   Console: jest.fn().mockImplementation(() => ({
     log: jest.fn(),
+    warn: jest.fn(),
+    debug: jest.fn(),
   })),
 }));
 jest.spyOn(console, 'warn').mockImplementation(() => ({}));
@@ -68,6 +70,7 @@ describe('Middy middleware', () => {
       const metrics = new Metrics({
         namespace: 'serverlessAirline',
         serviceName: 'orders',
+        logger: console,
       });
       const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
       const handler = middy(async (): Promise<void> => undefined).use(
diff --git a/packages/metrics/tests/unit/repro.test.ts b/packages/metrics/tests/unit/repro.test.ts
deleted file mode 100644
index c1ac3dafed..0000000000
--- a/packages/metrics/tests/unit/repro.test.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import middy from '@middy/core';
-import type { Context } from 'aws-lambda';
-import { Metrics } from '../../src/Metrics.js';
-import { logMetrics } from '../../src/middleware/middy.js';
-
-describe('Metrics', () => {
-  beforeAll(() => {
-    jest.spyOn(console, 'warn').mockImplementation();
-  });
-
-  afterEach(() => {
-    jest.resetAllMocks();
-  });
-
-  it('does not log', () => {
-    process.env.POWERTOOLS_DEV = 'true';
-    const metrics = new Metrics({
-      serviceName: 'foo',
-      namespace: 'bar',
-      defaultDimensions: {
-        aws_account_id: '123456789012',
-        aws_region: 'us-west-2',
-      },
-    });
-    const logSpy = jest.spyOn(console, 'log').mockImplementation();
-
-    metrics.publishStoredMetrics();
-
-    expect(logSpy).toHaveBeenCalledTimes(0);
-  });
-
-  it('does log because of captureColdStartMetric enabled', () => {
-    process.env.POWERTOOLS_DEV = 'true';
-    const metrics = new Metrics({
-      serviceName: 'foo',
-      namespace: 'bar',
-      defaultDimensions: {
-        aws_account_id: '123456789012',
-        aws_region: 'us-west-2',
-      },
-    });
-    const logSpy = jest.spyOn(console, 'log').mockImplementation();
-    const handler = middy(() => {}).use(
-      logMetrics(metrics, { captureColdStartMetric: true })
-    );
-
-    handler({}, {} as Context);
-
-    expect(logSpy).toHaveBeenCalledTimes(1);
-  });
-
-  it('does not log because of captureColdStartMetric disabled', () => {
-    process.env.POWERTOOLS_DEV = 'true';
-    const metrics = new Metrics({
-      serviceName: 'foo',
-      namespace: 'bar',
-      defaultDimensions: {
-        aws_account_id: '123456789012',
-        aws_region: 'us-west-2',
-      },
-    });
-    const logSpy = jest.spyOn(console, 'log').mockImplementation();
-    const handler = middy(() => {}).use(
-      logMetrics(metrics, { captureColdStartMetric: false })
-    );
-
-    handler({}, {} as Context);
-
-    expect(logSpy).toHaveBeenCalledTimes(0);
-  });
-});