diff --git a/setup.cfg b/setup.cfg index 931ec68b88fff96d637e1a2a7ed95069ec13e8b4..15f3b2ac87bffffb89bd45327b6f32d31e90bc51 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,7 @@ packages = twisted.plugins install_requires = - attrs + attrs >= 21.3.0 zope.interface eliot >= 1.11,<2 aniso8601 diff --git a/src/_zkapauthorizer/eliot.py b/src/_zkapauthorizer/eliot.py index e9f4d49304300ee1be5f169f5e53ba97623b996e..65a6f3e2d99581da7f01ad45d023decc67809553 100644 --- a/src/_zkapauthorizer/eliot.py +++ b/src/_zkapauthorizer/eliot.py @@ -16,7 +16,8 @@ Eliot field, message, and action definitions for ZKAPAuthorizer. """ -from eliot import ActionType, Field, MessageType +import attrs +from eliot import ActionType, Field, MessageType, register_exception_extractor PRIVACYPASS_MESSAGE = Field( "message", @@ -102,3 +103,13 @@ MUTABLE_PASSES_REQUIRED = MessageType( [CURRENT_SIZES, TW_VECTORS_SUMMARY, NEW_SIZES, NEW_PASSES], "Some number of passes has been computed as the cost of updating a mutable.", ) + + +def register_attr_exception(cls): + """ + Decorator that registers the decorated attrs exception class with eliot. + + The fields of the exception will be included when the exception is logged. + """ + register_exception_extractor(cls, attrs.asdict) + return cls diff --git a/src/_zkapauthorizer/tests/test_eliot.py b/src/_zkapauthorizer/tests/test_eliot.py new file mode 100644 index 0000000000000000000000000000000000000000..84b935192d6f5cf655a276647635a07697778d66 --- /dev/null +++ b/src/_zkapauthorizer/tests/test_eliot.py @@ -0,0 +1,48 @@ +# Copyright PrivateStorage.io, LLC +# +# 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. + +""" +Tests for eliot helpers. +""" +from __future__ import absolute_import, division, print_function, unicode_literals + +import attr +from eliot import start_action +from eliot.testing import assertHasAction, capture_logging +from testtools import TestCase + +from ..eliot import register_attr_exception + + +class RegisterExceptionTests(TestCase): + """ + Tests for :py:`register_attr_exception`. + """ + + @capture_logging(None) + def test_register(self, logger): + @register_attr_exception + @attr.s(auto_exc=True) + class E(Exception): + field = attr.ib() + + try: + with start_action(action_type="test:action"): + raise E(field="value") + except E: + pass + + assertHasAction( + self, logger, "test:action", False, endFields={"field": "value"} + )