Use new logging specific method for context info

Use new dedicated method for context aware attributes in oslo.log which
will enable future improvements for common and project specific logging
format parameters. Fall back to legacy method if not found in passed
context object and provide a deprecated warning of missing method.

Change-Id: Ica445ad5dfe9bd27dfcf1b1bcb6401a0bc9496a5
Implements: blueprint app-agnostic-logging-parameters
Depends-On: I963a6db4aef74f7348e75a642e2e195cedacecaa
Depends-On: I5450e105dc914f822a2b4c03b759a682d8b4a3e7
Depends-On: I9025770693d4b15efe4bd3556e6704326b1c915e
This commit is contained in:
Ronald Bradford 2016-01-29 17:48:02 +00:00
parent dbf33a8bb0
commit 77355b1e72
3 changed files with 112 additions and 15 deletions

View File

@ -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 %

View File

@ -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')

View File

@ -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)