diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index 2f620a3..ca85879 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -1,4 +1,5 @@
 import re
+import logging
 
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured, PermissionDenied
@@ -59,9 +60,15 @@ def authenticate(**credentials):
         user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
         return user
 
-    # The credentials supplied are invalid to all backends, fire signal
-    user_login_failed.send(sender=__name__,
-            credentials=_clean_credentials(credentials))
+    # The credentials supplied are invalid to all backends, fire signal and log error 
+    credentials=_clean_credentials(credentials)
+    user_login_failed.send(sender=__name__, credentials=credentials)
+
+    logger = logging.getLogger('django.security.FailedLogin')    
+    # Make sure username is in extra context, for formatting purposes
+    if 'username' not in credentials:
+        credentials['username'] = None
+    logger.warning("user login failed", extra=credentials)
 
 
 def login(request, user):
diff --git a/django/test/utils.py b/django/test/utils.py
index 591c588..ee6278e 100644
--- a/django/test/utils.py
+++ b/django/test/utils.py
@@ -421,14 +421,15 @@ class IgnorePendingDeprecationWarningsMixin(IgnoreDeprecationWarningsMixin):
 
 
 @contextmanager
-def patch_logger(logger_name, log_level):
+def patch_logger(logger_name, log_level, formatstr=''):
     """
     Context manager that takes a named logger and the logging level
     and provides a simple mock-like list of messages received
     """
     calls = []
-    def replacement(msg):
-        calls.append(msg)
+    def replacement(msg, *args, **kwargs):
+        extra = kwargs.get('extra',{})
+        calls.append((formatstr % extra) + (msg % args))
     logger = logging.getLogger(logger_name)
     orig = getattr(logger, log_level)
     setattr(logger, log_level, replacement)
diff --git a/docs/ref/contrib/auth.txt b/docs/ref/contrib/auth.txt
index afbc6ec..7fc0343 100644
--- a/docs/ref/contrib/auth.txt
+++ b/docs/ref/contrib/auth.txt
@@ -409,6 +409,11 @@ can be used for notification when a user logs in or out.
         authentication backend. Credentials matching a set of 'sensitive' patterns,
         (including password) will not be sent in the clear as part of the signal.
 
+     It should be noted that the failure is also logged as a warning to ``django.
+     security.FailedLogin``
+
+     .. versionadded:: 1.6
+
 .. _authentication-backends-reference:
 
 Authentication backends
diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
index a88201a..78cf32b 100644
--- a/docs/topics/logging.txt
+++ b/docs/topics/logging.txt
@@ -437,6 +437,8 @@ level or handlers that are installed.
 ``django.security.*``
 ~~~~~~~~~~~~~~~~~~~~~~
 
+.. versionadded:: 1.6
+
 The security loggers will receive messages on any occurrence of
 :exc:`~django.core.exceptions.SuspiciousOperation`. There is a sub-logger for
 each sub-type of SuspiciousOperation. The level of the log event depends on
@@ -463,6 +465,11 @@ specific logger following this example::
                 'propagate': False,
             },
 
+Invalid username/passwords will be logged to ``django.security.FailedLogin`` as
+a warning. This logger will have the following extra context:
+
+* ``username``: the provided username which failed.
+
 Handlers
 --------
 
diff --git a/tests/logging_tests/tests.py b/tests/logging_tests/tests.py
index 0c2d269..ba87d78 100644
--- a/tests/logging_tests/tests.py
+++ b/tests/logging_tests/tests.py
@@ -358,7 +358,9 @@ class SettingsConfigureLogging(TestCase):
 
 
 class SecurityLoggerTest(TestCase):
-
+    """
+    Test that security messages are being logged
+    """
     urls = 'logging_tests.urls'
 
     def test_suspicious_operation_creates_log_message(self):
@@ -374,3 +376,17 @@ class SecurityLoggerTest(TestCase):
                 response = self.client.get('/suspicious_spec/')
                 self.assertEqual(len(calls), 1)
                 self.assertEqual(calls[0], 'dubious')
+
+    def test_user_login_failed_creates_log_message(self):
+        with self.settings(DEBUG=True):
+            with patch_logger('django.security.FailedLogin', 'warning', "%(username)s : ") as calls:
+                self.client.login(username='testclient', password='bad') 
+                self.assertEqual(len(calls), 1)
+                self.assertEqual(calls[0], "testclient : user login failed")
+
+    def test_no_user_login_failed_creates_log_message(self):
+        with self.settings(DEBUG=True):
+            with patch_logger('django.security.FailedLogin', 'warning', "%(username)s : ") as calls:
+                self.client.login() 
+                self.assertEqual(len(calls), 1)
+                self.assertEqual(calls[0], "None : user login failed")
