import os
import logging

from twisted.python.failure import Failure

from scrapy.utils.request import referer_str

SCRAPEDMSG = u"Scraped from %(src)s" + os.linesep + "%(item)s"
DROPPEDMSG = u"Dropped: %(exception)s" + os.linesep + "%(item)s"
CRAWLEDMSG = u"Crawled (%(status)s) %(request)s%(request_flags)s (referer: %(referer)s)%(response_flags)s"


class LogFormatter(object):
    """Class for generating log messages for different actions.

    All methods must return a dictionary listing the parameters `level`, `msg`
    and `args` which are going to be used for constructing the log message when
    calling logging.log.

    Dictionary keys for the method outputs:
        * `level` should be the log level for that action, you can use those
        from the python logging library: logging.DEBUG, logging.INFO,
        logging.WARNING, logging.ERROR and logging.CRITICAL.

        * `msg` should be a string that can contain different formatting
        placeholders. This string, formatted with the provided `args`, is going
        to be the log message for that action.

        * `args` should be a tuple or dict with the formatting placeholders for
        `msg`.  The final log message is computed as output['msg'] %
        output['args'].
    """

    def crawled(self, request, response, spider):
        request_flags = ' %s' % str(request.flags) if request.flags else ''
        response_flags = ' %s' % str(response.flags) if response.flags else ''
        return {
            'level': logging.DEBUG,
            'msg': CRAWLEDMSG,
            'args': {
                'status': response.status,
                'request': request,
                'request_flags' : request_flags,
                'referer': referer_str(request),
                'response_flags': response_flags,
            }
        }

    def scraped(self, item, response, spider):
        if isinstance(response, Failure):
            src = response.getErrorMessage()
        else:
            src = response
        return {
            'level': logging.DEBUG,
            'msg': SCRAPEDMSG,
            'args': {
                'src': src,
                'item': item,
            }
        }

    def dropped(self, item, exception, response, spider):
        return {
            'level': logging.WARNING,
            'msg': DROPPEDMSG,
            'args': {
                'exception': exception,
                'item': item,
            }
        }

    @classmethod
    def from_crawler(cls, crawler):
        return cls()

scrapy/logformatter.py
import os
import logging

from twisted.python.failure import Failure

from scrapy.utils.request import referer_str

SCRAPEDMSG = u"Scraped from %(src)s" + os.linesep + "%(item)s"
DROPPEDMSG = u"Dropped: %(exception)s" + os.linesep + "%(item)s"
CRAWLEDMSG = u"Crawled (%(status)s) %(request)s%(request_flags)s (referer: %(referer)s)%(response_flags)s"


class LogFormatter(object):
    """Class for generating log messages for different actions.

    All methods must return a dictionary listing the parameters `level`, `msg`
    and `args` which are going to be used for constructing the log message when
    calling logging.log.

    Dictionary keys for the method outputs:
        * `level` should be the log level for that action, you can use those
        from the python logging library: logging.DEBUG, logging.INFO,
        logging.WARNING, logging.ERROR and logging.CRITICAL.

        * `msg` should be a string that can contain different formatting
        placeholders. This string, formatted with the provided `args`, is going
        to be the log message for that action.

        * `args` should be a tuple or dict with the formatting placeholders for
        `msg`.  The final log message is computed as output['msg'] %
        output['args'].
    """

    def crawled(self, request, response, spider):
        request_flags = ' %s' % str(request.flags) if request.flags else ''
        response_flags = ' %s' % str(response.flags) if response.flags else ''
        return {
            'level': logging.DEBUG,
            'msg': CRAWLEDMSG,
            'args': {
                'status': response.status,
                'request': request,
                'request_flags' : request_flags,
                'referer': referer_str(request),
                'response_flags': response_flags,
                # backward compatibility with Scrapy logformatter below 1.4 version
                'flags': response_flags
            }
        }

    def scraped(self, item, response, spider):
        if isinstance(response, Failure):
            src = response.getErrorMessage()
        else:
            src = response
        return {
            'level': logging.DEBUG,
            'msg': SCRAPEDMSG,
            'args': {
                'src': src,
                'item': item,
            }
        }

    def dropped(self, item, exception, response, spider):
        return {
            'level': logging.WARNING,
            'msg': DROPPEDMSG,
            'args': {
                'exception': exception,
                'item': item,
            }
        }

    @classmethod
    def from_crawler(cls, crawler):
        return cls()

scrapy/logformatter.py
diff --git a/scrapy/logformatter.py b/scrapy/logformatter.py
index e7bf7942e21..075a6d862d6 100644
--- a/scrapy/logformatter.py
+++ b/scrapy/logformatter.py
@@ -43,6 +43,8 @@ def crawled(self, request, response, spider):
                 'request_flags' : request_flags,
                 'referer': referer_str(request),
                 'response_flags': response_flags,
+                # backward compatibility with Scrapy logformatter below 1.4 version
+                'flags': response_flags
             }
         }
 
diff --git a/tests/test_logformatter.py b/tests/test_logformatter.py
index 11fe7b653e4..94e6c9fde03 100644
--- a/tests/test_logformatter.py
+++ b/tests/test_logformatter.py
@@ -64,5 +64,30 @@ def test_scraped(self):
         assert all(isinstance(x, six.text_type) for x in lines)
         self.assertEqual(lines, [u"Scraped from <200 http://www.example.com>", u'name: \xa3'])
 
+
+class LogFormatterSubclass(LogFormatter):
+    def crawled(self, request, response, spider):
+        kwargs = super(LogFormatterSubclass, self).crawled(
+        request, response, spider)
+        CRAWLEDMSG = (
+            u"Crawled (%(status)s) %(request)s (referer: "
+            u"%(referer)s)%(flags)s"
+        )
+        return {
+            'level': kwargs['level'],
+            'msg': CRAWLEDMSG,
+            'args': kwargs['args']
+        }
+
+
+class LogformatterSubclassTest(LoggingContribTest):
+    def setUp(self):
+        self.formatter = LogFormatterSubclass()
+        self.spider = Spider('default')
+
+    def test_flags_in_request(self):
+        pass
+
+
 if __name__ == "__main__":
     unittest.main()

After adding request flags subclasses of logformatter that rely on 'flags' format string are broken
#2082 added flags to request but it also renamed formatting string key from flags to response_flags/request_flags
```
CRAWLEDMSG = u"Crawled (%(status)s) %(request)s (referer: %(referer)s)%(flags)s"
 +CRAWLEDMSG = u"Crawled (%(status)s) %(request)s%(request_flags)s (referer: %(referer)s)%(response_flags)s" 
```

Scrapy allows you to override logformatter and this is what I have in my project. I have logformatter looking rouhgly like this


```python
# dirbot/logf.py
from scrapy.logformatter import LogFormatter


class CustomLogFormatter(LogFormatter):
    def crawled(self, request, response, spider):
        kwargs = super(CustomLogFormatter, self).crawled(
            request, response, spider)
        kwargs['msg'] = (
            u"Crawled (%(status)s) %(request)s "
            u"(referer: %(referer)s, latency: %(latency).2f s)%(flags)s"
        )
        kwargs['args']['latency'] = response.meta.get('download_latency', 0)
        return kwargs
```

now if you enable it in settings `LOG_FORMATTER = 'dirbot.logf.CustomLogFormatter'
` and  try to run it with recent master you'll get KeyError

```
2017-03-13 14:15:26 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
Traceback (most recent call last):
  File "/usr/lib/python2.7/logging/__init__.py", line 851, in emit
    msg = self.format(record)
  File "/usr/lib/python2.7/logging/__init__.py", line 724, in format
    return fmt.format(record)
  File "/usr/lib/python2.7/logging/__init__.py", line 464, in format
    record.message = record.getMessage()
  File "/usr/lib/python2.7/logging/__init__.py", line 328, in getMessage
    msg = msg % self.args
KeyError: u'flags'
Logged from file engine.py, line 238
Traceback (most recent call last):
  File "/usr/lib/python2.7/logging/__init__.py", line 851, in emit
    msg = self.format(record)
  File "/usr/lib/python2.7/logging/__init__.py", line 724, in format
    return fmt.format(record)
  File "/usr/lib/python2.7/logging/__init__.py", line 464, in format
    record.message = record.getMessage()
  File "/usr/lib/python2.7/logging/__init__.py", line 328, in getMessage
    msg = msg % self.args
KeyError: u'flags'
Logged from file engine.py, line 238
2017-03-13 14:15:27 [scrapy.core.scraper] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/>
```

So this change that renamed `flags` to `response_flags/request_flags` seems backward incompatible. 
