gh-145650: Add `logging.{Formatter,Filter}.__repr__` (GH-145652)
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 39689a5..6eef90a 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -622,6 +622,9 @@ def __init__(self, fmt=None, datefmt=None, style='%', validate=True, *,
         self._fmt = self._style._fmt
         self.datefmt = datefmt
 
+    def __repr__(self):
+        return '<%s (%s)>' % (self.__class__.__name__, self._fmt)
+
     default_time_format = '%Y-%m-%d %H:%M:%S'
     default_msec_format = '%s,%03d'
 
@@ -794,6 +797,9 @@ def __init__(self, name=''):
         self.name = name
         self.nlen = len(name)
 
+    def __repr__(self):
+        return '<%s (%s)>' % (self.__class__.__name__, self.name)
+
     def filter(self, record):
         """
         Determine if the specified record is to be logged.
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 05dcea6..1a76c21 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -404,6 +404,20 @@ def test_empty_filter(self):
         r = logging.makeLogRecord({'name': 'spam.eggs'})
         self.assertTrue(f.filter(r))
 
+    def test_filter_repr(self):
+        f = logging.Filter('myapp')
+        self.assertEqual(repr(f), '<Filter (myapp)>')
+
+    def test_filter_repr_empty(self):
+        f = logging.Filter()
+        self.assertEqual(repr(f), '<Filter ()>')
+
+    def test_filter_repr_subclass(self):
+        class MyFilter(logging.Filter):
+            pass
+        f = MyFilter('myapp')
+        self.assertEqual(repr(f), '<MyFilter (myapp)>')
+
 #
 #   First, we define our levels. There can be as many as you want - the only
 #     limitations are that they should be integers, the lowest should be > 0 and
@@ -4914,6 +4928,20 @@ def test_relativeCreated_has_higher_precision(self):
                 # After PR gh-102412, precision (places) increases from 3 to 7
                 self.assertAlmostEqual(relativeCreated, offset_ns / 1e6, places=7)
 
+    def test_formatter_repr(self):
+        f = logging.Formatter('%(message)s')
+        self.assertEqual(repr(f), '<Formatter (%(message)s)>')
+
+    def test_formatter_repr_default(self):
+        f = logging.Formatter()
+        self.assertEqual(repr(f), '<Formatter (%(message)s)>')
+
+    def test_formatter_repr_subclass(self):
+        class MyFormatter(logging.Formatter):
+            pass
+        f = MyFormatter('%(message)s')
+        self.assertEqual(repr(f), '<MyFormatter (%(message)s)>')
+
 
 class TestBufferingFormatter(logging.BufferingFormatter):
     def formatHeader(self, records):
diff --git a/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145650.LgRepr.rst b/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145650.LgRepr.rst
new file mode 100644
index 0000000..243834d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145650.LgRepr.rst
@@ -0,0 +1,3 @@
+Add :meth:`~object.__repr__` support to :class:`logging.Formatter` and
+:class:`logging.Filter`, showing the format string and filter name
+respectively.