Skip to content

Commit 448f56b

Browse files
committed
feat: add a configurable SECURE_LOGGER_LOG_LEVEL defaulted to logging.default()
1 parent 0d528b5 commit 448f56b

File tree

4 files changed

+55
-14
lines changed

4 files changed

+55
-14
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ secure_logger accepts optional parameters which you can configure as either bash
9797
- **SECURE_LOGGER_SENSITIVE_KEYS**: a Python list of dictionary keys. Not case sensitive.
9898
- **SECURE_LOGGER_REDACTION_MESSAGE**: a string value that will replace the sensitive key values
9999
- **SECURE_LOGGER_INDENT**: number of characters to indent JSON string output when logging output
100+
- **SECURE_LOGGER_LOG_LEVEL**: the level at which secure_logger generates log entries. One of: 'CRITICAL', 'FATAL', 'ERROR', 'WARN', 'WARNING', 'INFO', 'DEBUG'
100101

101102
Additionally, you can override individual invocations of the decorator by passing *sensitive_keys*, *message* and *indent*:
102103

@@ -129,6 +130,7 @@ SECURE_LOGGER_SENSITIVE_KEYS = [
129130
"aws-access-key-id",
130131
"aws-secret-access-key",
131132
]
133+
SECURE_LOGGER_LOG_LEVEL = 'DEBUG'
132134
```
133135

134136
### Contributing

secure_logger/conf.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33

44
import logging
55

6-
from pydantic import BaseModel, Field, ValidationError
6+
from pydantic import BaseModel, Field, ValidationError, constr, validator
77

88
from secure_logger.exceptions import SecureLoggerConfigurationError
99

1010

11+
LOGGER_NAME = "decorator_logger"
12+
13+
_logger = logging.getLogger(LOGGER_NAME)
14+
15+
1116
_SECURE_LOGGER_SENSITIVE_KEYS = [
1217
"password",
1318
"token",
@@ -26,6 +31,10 @@
2631
]
2732
_SECURE_LOGGER_REDACTION_MESSAGE = "*** -- secure_logger() -- ***"
2833
_SECURE_LOGGER_INDENT = 4
34+
_SECURE_LOGGER_LOG_LEVEL = "DEBUG"
35+
36+
# pylint: disable=protected-access
37+
_SECURE_LOGGER_LOG_LEVELS = [level for level in logging._nameToLevel if level != "NOTSET"]
2938

3039

3140
class Settings(BaseModel):
@@ -36,6 +45,35 @@ class Settings(BaseModel):
3645
_SECURE_LOGGER_REDACTION_MESSAGE, env="SECURE_LOGGER_REDACTION_MESSAGE"
3746
)
3847
secure_logger_indent: int = Field(_SECURE_LOGGER_INDENT, env="SECURE_LOGGER_INDENT")
48+
secure_logger_logging_level: constr(min_length=4, max_length=8) = Field(
49+
_SECURE_LOGGER_LOG_LEVEL, env="SECURE_LOGGER_LOG_LEVEL"
50+
)
51+
52+
@validator("secure_logger_logging_level")
53+
@classmethod
54+
def must_be_valid_log_level(cls, v):
55+
"""Validate the log level"""
56+
if v not in _SECURE_LOGGER_LOG_LEVELS:
57+
raise ValueError("invalid log level")
58+
return v
59+
60+
@property
61+
def logger(self):
62+
"""Returns the logger function for the specified logging level"""
63+
log_levels = {
64+
"DEBUG": _logger.debug,
65+
"INFO": _logger.info,
66+
"WARNING": _logger.warning,
67+
"ERROR": _logger.error,
68+
"CRITICAL": _logger.critical,
69+
}
70+
return log_levels.get(self.secure_logger_logging_level, logging.debug)
71+
72+
@property
73+
def logger_level(self) -> int:
74+
"""Returns the logger level for the specified logging level"""
75+
# pylint: disable=protected-access
76+
return logging._nameToLevel.get(self.secure_logger_logging_level, logging.DEBUG)
3977

4078

4179
settings = None
@@ -44,6 +82,7 @@ class Settings(BaseModel):
4482
except ValidationError as e:
4583
raise SecureLoggerConfigurationError("Invalid configuration: " + str(e)) from e
4684

47-
logging.debug("SECURE_LOGGER_SENSITIVE_KEYS: %s", settings.secure_logger_sensitive_keys)
48-
logging.debug("SECURE_LOGGER_REDACTION_MESSAGE: %s", settings.secure_logger_redaction_message)
49-
logging.debug("SECURE_LOGGER_INDENT: %s", settings.secure_logger_indent)
85+
_logger.debug("SECURE_LOGGER_SENSITIVE_KEYS: %s", settings.secure_logger_sensitive_keys)
86+
_logger.debug("SECURE_LOGGER_REDACTION_MESSAGE: %s", settings.secure_logger_redaction_message)
87+
_logger.debug("SECURE_LOGGER_INDENT: %s", settings.secure_logger_indent)
88+
_logger.debug("SECURE_LOGGER_LOG_LEVEL: %s", settings.secure_logger_logging_level)

secure_logger/decorators.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"""Python Secure Logger."""
33
# python stuff
44
import inspect
5-
import logging
65
from functools import wraps
76

87
# our stuff
@@ -11,7 +10,7 @@
1110

1211

1312
# module initializations
14-
logger = logging.getLogger(__name__)
13+
logger = settings.logger
1514

1615

1716
def secure_logger(
@@ -72,7 +71,7 @@ def wrapper(*args, **kwargs):
7271
kwargs, sensitive_keys=sensitive_keys, indent=indent, message=message
7372
)
7473

75-
logger.info(
74+
logger(
7675
"secure_logger: %s %s %s",
7776
name_spec,
7877
positional_args if positional_args else "",

secure_logger/tests/tests.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# pylint: disable=too-few-public-methods,C0413
33
"""Simple test bank."""
44
import json
5-
import logging
65
import sys
76
import unittest
87

@@ -108,10 +107,10 @@ def test_decorator_output(self):
108107

109108
# noqa: C0301
110109
expected_output = (
111-
"INFO:secure_logger.decorators:secure_logger: tests.mock_decorated_def() "
110+
settings.secure_logger_logging_level + ":decorator_logger:secure_logger: tests.mock_decorated_def() "
112111
"['<tests.TestModuleDefDecorator testMethod=test_decorator_output>', " + hello_world
113112
)
114-
with self.assertLogs(level=logging.DEBUG) as cm:
113+
with self.assertLogs(level=settings.logger_level) as cm:
115114
self.mock_decorated_def("hello world")
116115

117116
self.assertEqual(cm.output[0][0:100], expected_output[0:100])
@@ -142,11 +141,11 @@ def decorator_with_custom_params(self, test_dict, test_list):
142141
def test_class_method_with_default_params(self):
143142
"""Test class method with default parameters."""
144143
expected_output = (
145-
"INFO:secure_logger.decorators:secure_logger: tests.decorator_with_defaults() "
144+
settings.secure_logger_logging_level + ":decorator_logger:secure_logger: tests.decorator_with_defaults() "
146145
"['<tests.TestClassMethodDecorator.MockClass"
147146
)
148147

149-
with self.assertLogs(level=logging.DEBUG) as cm:
148+
with self.assertLogs(level=settings.logger_level) as cm:
150149
self.mock_class.decorator_with_defaults(self.test_dict, self.test_list)
151150

152151
self.assertEqual(cm.output[0][0:100], expected_output[0:100])
@@ -162,9 +161,11 @@ def test_class_with_default_params(self):
162161
class MockDecoratedClass:
163162
"""Test 3: decorate a class."""
164163

165-
expected_output = "INFO:secure_logger.decorators:secure_logger: tests.MockDecoratedClass. "
164+
expected_output = (
165+
settings.secure_logger_logging_level + ":decorator_logger:secure_logger: tests.MockDecoratedClass. "
166+
)
166167

167-
with self.assertLogs(level=logging.DEBUG) as cm:
168+
with self.assertLogs(level=settings.logger_level) as cm:
168169
MockDecoratedClass()
169170

170171
self.assertEqual(cm.output[0][0:100], expected_output[0:100])

0 commit comments

Comments
 (0)