diff --git a/oslo_log/formatters.py b/oslo_log/formatters.py index 4ffd444..26cf77c 100644 --- a/oslo_log/formatters.py +++ b/oslo_log/formatters.py @@ -11,6 +11,7 @@ # under the License. import datetime +import debtcollector import itertools import logging import logging.config @@ -28,11 +29,21 @@ from oslo_serialization import jsonutils def _dictify_context(context): - if context is None: - return {} - if not isinstance(context, dict) and getattr(context, 'to_dict', None): - context = context.to_dict() - return context + if getattr(context, 'get_logging_values', None): + return context.get_logging_values() + elif getattr(context, 'to_dict', None): + debtcollector.deprecate( + 'The RequestContext.get_logging_values() ' + 'method should be defined for logging context specific ' + 'information. The to_dict() method is deprecated ' + 'for oslo.log use.', version='3.8.0', removal_version='5.0.0') + return context.to_dict() + # This dict only style logging format will become deprecated + # when projects using a dictionary object for context are updated + elif isinstance(context, dict): + return context + + return {} # A configuration object is given to us when the application registers @@ -48,19 +59,20 @@ def _store_global_conf(conf): def _update_record_with_context(record): """Given a log record, update it with context information. - The request context, if there is one, will either be in the - extra values for the incoming record or in the global - thread-local store. + The request context, if there is one, will either be passed with the + incoming record or in the global thread-local store. """ context = record.__dict__.get( 'context', context_utils.get_current() ) - d = _dictify_context(context) - # Copy the context values directly onto the record so they can be - # used by the formatting strings. - for k, v in d.items(): - setattr(record, k, v) + if context: + d = _dictify_context(context) + # Copy the context values directly onto the record so they can be + # used by the formatting strings. + for k, v in d.items(): + setattr(record, k, v) + return context @@ -238,7 +250,7 @@ class ContextFormatter(logging.Formatter): # Set the "user_identity" value of "logging_context_format_string" # by using "logging_user_identity_format" and - # "to_dict()" of oslo.context. + # get_logging_values of oslo.context. if context: record.user_identity = ( self.conf.logging_user_identity_format % diff --git a/oslo_log/tests/unit/test_formatters.py b/oslo_log/tests/unit/test_formatters.py new file mode 100644 index 0000000..3bb2cd4 --- /dev/null +++ b/oslo_log/tests/unit/test_formatters.py @@ -0,0 +1,85 @@ +# Copyright (c) 2016 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Unit Tests for oslo.log formatter""" + +import mock + +from oslo_context import context +from oslo_log import formatters +from oslotest import base as test_base + + +def _fake_context(): + ctxt = context.RequestContext(user="user", + tenant="tenant", + project_domain="pdomain", + user_domain="udomain", + overwrite=True) + + return ctxt + + +class AlternativeRequestContext(object): + + def __init__(self, user=None, tenant=None): + self.user = user + self.tenant = tenant + + def to_dict(self): + return {'user': self.user, + 'tenant': self.tenant} + + +class FormatterTest(test_base.BaseTestCase): + + def setUp(self): + super(FormatterTest, self).setUp() + + def test_replace_false_value_exists(self): + d = {"user": "user1"} + s = "%(user)s" % formatters._ReplaceFalseValue(d) + self.assertEqual(d['user'], s) + + def test_replace_false_value_not_exists(self): + d = {"user": "user1"} + s = "%(project)s" % formatters._ReplaceFalseValue(d) + self.assertEqual("-", s) + + def test_dictify_context_empty(self): + self.assertEqual({}, formatters._dictify_context(None)) + + @mock.patch("debtcollector.deprecate") + def test_dictify_context_with_dict(self, mock_deprecate): + d = {"user": "user"} + self.assertEqual(d, formatters._dictify_context(d)) + mock_deprecate.assert_not_called() + + @mock.patch("debtcollector.deprecate") + def test_dictify_context_with_context(self, mock_deprecate): + ctxt = _fake_context() + self.assertEqual(ctxt.get_logging_values(), + formatters._dictify_context(ctxt)) + mock_deprecate.assert_not_called() + + @mock.patch("debtcollector.deprecate") + def test_dictify_context_without_get_logging_values(self, mock_deprecate): + ctxt = AlternativeRequestContext(user="user", tenant="tenant") + d = {"user": "user", "tenant": "tenant"} + self.assertEqual(d, formatters._dictify_context(ctxt)) + mock_deprecate.assert_called_with( + 'The RequestContext.get_logging_values() ' + 'method should be defined for logging context specific ' + 'information. The to_dict() method is deprecated ' + 'for oslo.log use.', removal_version='5.0.0', version='3.8.0') diff --git a/oslo_log/tests/unit/test_log.py b/oslo_log/tests/unit/test_log.py index 297c74f..937d2c0 100644 --- a/oslo_log/tests/unit/test_log.py +++ b/oslo_log/tests/unit/test_log.py @@ -699,7 +699,7 @@ class DomainTestCase(LogTestBase): def test_domain_in_log_msg(self): ctxt = _fake_context() - user_identity = ctxt.to_dict()['user_identity'] + user_identity = ctxt.get_logging_values()['user_identity'] self.assertTrue(ctxt.domain in user_identity) self.assertTrue(ctxt.project_domain in user_identity) self.assertTrue(ctxt.user_domain in user_identity)