We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Consider and add common sensitive names
**Description** 

Since sensitive informations in the connection object (specifically the extras field) are now being masked based on sensitive key names, we should consider adding some common sensitive key names.

`private_key` from [ssh connection](https://airflow.apache.org/docs/apache-airflow-providers-ssh/stable/connections/ssh.html) is an examples.

**Use case / motivation**

Extras field used to be blocked out entirely before the sensitive value masking feature (#15599).

[Before in 2.0.2](https://github.com/apache/airflow/blob/2.0.2/airflow/hooks/base.py#L78
) and [after in 2.1.0](https://github.com/apache/airflow/blob/2.1.0/airflow/hooks/base.py#L78
).

Extras field containing sensitive information now shown unless the key contains sensitive names.

**Are you willing to submit a PR?** 

@ashb has expressed interest in adding this.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `airflow/utils/log/secrets_masker.py`
Content:
```
1 # Licensed to the Apache Software Foundation (ASF) under one
2 # or more contributor license agreements.  See the NOTICE file
3 # distributed with this work for additional information
4 # regarding copyright ownership.  The ASF licenses this file
5 # to you under the Apache License, Version 2.0 (the
6 # "License"); you may not use this file except in compliance
7 # with the License.  You may obtain a copy of the License at
8 #
9 #   http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing,
12 # software distributed under the License is distributed on an
13 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 # KIND, either express or implied.  See the License for the
15 # specific language governing permissions and limitations
16 # under the License.
17 """Mask sensitive information from logs"""
18 import collections
19 import io
20 import logging
21 import re
22 from typing import TYPE_CHECKING, Iterable, Optional, Set, TypeVar, Union
23 
24 from airflow.compat.functools import cache, cached_property
25 
26 if TYPE_CHECKING:
27     from airflow.typing_compat import RePatternType
28 
29     RedactableItem = TypeVar('RedactableItem')
30 
31 
32 log = logging.getLogger(__name__)
33 
34 
35 DEFAULT_SENSITIVE_FIELDS = frozenset(
36     {
37         'password',
38         'secret',
39         'passwd',
40         'authorization',
41         'api_key',
42         'apikey',
43         'access_token',
44     }
45 )
46 """Names of fields (Connection extra, Variable key name etc.) that are deemed sensitive"""
47 
48 
49 @cache
50 def get_sensitive_variables_fields():
51     """Get comma-separated sensitive Variable Fields from airflow.cfg."""
52     from airflow.configuration import conf
53 
54     sensitive_fields = DEFAULT_SENSITIVE_FIELDS.copy()
55     sensitive_variable_fields = conf.get('core', 'sensitive_var_conn_names')
56     if sensitive_variable_fields:
57         sensitive_fields |= frozenset({field.strip() for field in sensitive_variable_fields.split(',')})
58     return sensitive_fields
59 
60 
61 def should_hide_value_for_key(name):
62     """Should the value for this given name (Variable name, or key in conn.extra_dejson) be hidden"""
63     from airflow import settings
64 
65     if name and settings.HIDE_SENSITIVE_VAR_CONN_FIELDS:
66         name = name.strip().lower()
67         return any(s in name for s in get_sensitive_variables_fields())
68     return False
69 
70 
71 def mask_secret(secret: Union[str, dict, Iterable], name: str = None) -> None:
72     """
73     Mask a secret from appearing in the task logs.
74 
75     If ``name`` is provided, then it will only be masked if the name matches
76     one of the configured "sensitive" names.
77 
78     If ``secret`` is a dict or a iterable (excluding str) then it will be
79     recursively walked and keys with sensitive names will be hidden.
80     """
81     # Delay import
82     from airflow import settings
83 
84     # Filtering all log messages is not a free process, so we only do it when
85     # running tasks
86     if not settings.MASK_SECRETS_IN_LOGS or not secret:
87         return
88 
89     _secrets_masker().add_mask(secret, name)
90 
91 
92 def redact(value: "RedactableItem", name: str = None) -> "RedactableItem":
93     """Redact any secrets found in ``value``."""
94     return _secrets_masker().redact(value, name)
95 
96 
97 @cache
98 def _secrets_masker() -> "SecretsMasker":
99 
100     for flt in logging.getLogger('airflow.task').filters:
101         if isinstance(flt, SecretsMasker):
102             return flt
103     raise RuntimeError("No SecretsMasker found!")
104 
105 
106 class SecretsMasker(logging.Filter):
107     """Redact secrets from logs"""
108 
109     replacer: Optional["RePatternType"] = None
110     patterns: Set[str]
111 
112     ALREADY_FILTERED_FLAG = "__SecretsMasker_filtered"
113 
114     def __init__(self):
115         super().__init__()
116         self.patterns = set()
117 
118     @cached_property
119     def _record_attrs_to_ignore(self) -> Iterable[str]:
120         # Doing log.info(..., extra={'foo': 2}) sets extra properties on
121         # record, i.e. record.foo. And we need to filter those too. Fun
122         #
123         # Create a record, and look at what attributes are on it, and ignore
124         # all the default ones!
125 
126         record = logging.getLogRecordFactory()(
127             # name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None,
128             "x",
129             logging.INFO,
130             __file__,
131             1,
132             "",
133             tuple(),
134             exc_info=None,
135             func="funcname",
136         )
137         return frozenset(record.__dict__).difference({'msg', 'args'})
138 
139     def filter(self, record) -> bool:
140         if self.ALREADY_FILTERED_FLAG in record.__dict__:
141             # Filters are attached to multiple handlers and logs, keep a
142             # "private" flag that stops us needing to process it more than once
143             return True
144 
145         if self.replacer:
146             for k, v in record.__dict__.items():
147                 if k in self._record_attrs_to_ignore:
148                     continue
149                 record.__dict__[k] = self.redact(v)
150             if record.exc_info and record.exc_info[1] is not None:
151                 exc = record.exc_info[1]
152                 # I'm not sure if this is a good idea!
153                 exc.args = (self.redact(v) for v in exc.args)
154         record.__dict__[self.ALREADY_FILTERED_FLAG] = True
155 
156         return True
157 
158     def _redact_all(self, item: "RedactableItem") -> "RedactableItem":
159         if isinstance(item, dict):
160             return {dict_key: self._redact_all(subval) for dict_key, subval in item.items()}
161         elif isinstance(item, str):
162             return '***'
163         elif isinstance(item, (tuple, set)):
164             # Turn set in to tuple!
165             return tuple(self._redact_all(subval) for subval in item)
166         elif isinstance(item, Iterable):
167             return list(self._redact_all(subval) for subval in item)
168         else:
169             return item
170 
171     # pylint: disable=too-many-return-statements
172     def redact(self, item: "RedactableItem", name: str = None) -> "RedactableItem":
173         """
174         Redact an any secrets found in ``item``, if it is a string.
175 
176         If ``name`` is given, and it's a "sensitive" name (see
177         :func:`should_hide_value_for_key`) then all string values in the item
178         is redacted.
179 
180         """
181         try:
182             if name and should_hide_value_for_key(name):
183                 return self._redact_all(item)
184 
185             if isinstance(item, dict):
186                 return {dict_key: self.redact(subval, dict_key) for dict_key, subval in item.items()}
187             elif isinstance(item, str):
188                 if self.replacer:
189                     # We can't replace specific values, but the key-based redacting
190                     # can still happen, so we can't short-circuit, we need to walk
191                     # the structure.
192                     return self.replacer.sub('***', item)
193                 return item
194             elif isinstance(item, (tuple, set)):
195                 # Turn set in to tuple!
196                 return tuple(self.redact(subval) for subval in item)
197             elif isinstance(item, io.IOBase):
198                 return item
199             elif isinstance(item, Iterable):
200                 return list(self.redact(subval) for subval in item)
201             else:
202                 return item
203         except Exception as e:  # pylint: disable=broad-except
204             log.warning(
205                 "Unable to redact %r, please report this via <https://github.com/apache/airflow/issues>. "
206                 "Error was: %s: %s",
207                 item,
208                 type(e).__name__,
209                 str(e),
210             )
211             return item
212 
213     # pylint: enable=too-many-return-statements
214 
215     def add_mask(self, secret: Union[str, dict, Iterable], name: str = None):
216         """Add a new secret to be masked to this filter instance."""
217         if isinstance(secret, dict):
218             for k, v in secret.items():
219                 self.add_mask(v, k)
220         elif isinstance(secret, str):
221             if not secret:
222                 return
223             pattern = re.escape(secret)
224             if pattern not in self.patterns and (not name or should_hide_value_for_key(name)):
225                 self.patterns.add(pattern)
226                 self.replacer = re.compile('|'.join(self.patterns))
227         elif isinstance(secret, collections.abc.Iterable):
228             for v in secret:
229                 self.add_mask(v, name)
230
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/airflow/utils/log/secrets_masker.py b/airflow/utils/log/secrets_masker.py
--- a/airflow/utils/log/secrets_masker.py
+++ b/airflow/utils/log/secrets_masker.py
@@ -34,13 +34,15 @@
 
 DEFAULT_SENSITIVE_FIELDS = frozenset(
     {
-        'password',
-        'secret',
-        'passwd',
-        'authorization',
+        'access_token',
         'api_key',
         'apikey',
-        'access_token',
+        'authorization',
+        'passphrase',
+        'passwd',
+        'password',
+        'private_key',
+        'secret',
     }
 )
 """Names of fields (Connection extra, Variable key name etc.) that are deemed sensitive"""

{"golden_diff": "diff --git a/airflow/utils/log/secrets_masker.py b/airflow/utils/log/secrets_masker.py\n--- a/airflow/utils/log/secrets_masker.py\n+++ b/airflow/utils/log/secrets_masker.py\n@@ -34,13 +34,15 @@\n \n DEFAULT_SENSITIVE_FIELDS = frozenset(\n     {\n-        'password',\n-        'secret',\n-        'passwd',\n-        'authorization',\n+        'access_token',\n         'api_key',\n         'apikey',\n-        'access_token',\n+        'authorization',\n+        'passphrase',\n+        'passwd',\n+        'password',\n+        'private_key',\n+        'secret',\n     }\n )\n \"\"\"Names of fields (Connection extra, Variable key name etc.) that are deemed sensitive\"\"\"\n", "issue": "Consider and add common sensitive names\n**Description** \r\n\r\nSince sensitive informations in the connection object (specifically the extras field) are now being masked based on sensitive key names, we should consider adding some common sensitive key names.\r\n\r\n`private_key` from [ssh connection](https://airflow.apache.org/docs/apache-airflow-providers-ssh/stable/connections/ssh.html) is an examples.\r\n\r\n**Use case / motivation**\r\n\r\nExtras field used to be blocked out entirely before the sensitive value masking feature (#15599).\r\n\r\n[Before in 2.0.2](https://github.com/apache/airflow/blob/2.0.2/airflow/hooks/base.py#L78\r\n) and [after in 2.1.0](https://github.com/apache/airflow/blob/2.1.0/airflow/hooks/base.py#L78\r\n).\r\n\r\nExtras field containing sensitive information now shown unless the key contains sensitive names.\r\n\r\n**Are you willing to submit a PR?** \r\n\r\n@ashb has expressed interest in adding this.\r\n\n", "before_files": [{"content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\"\"\"Mask sensitive information from logs\"\"\"\nimport collections\nimport io\nimport logging\nimport re\nfrom typing import TYPE_CHECKING, Iterable, Optional, Set, TypeVar, Union\n\nfrom airflow.compat.functools import cache, cached_property\n\nif TYPE_CHECKING:\n    from airflow.typing_compat import RePatternType\n\n    RedactableItem = TypeVar('RedactableItem')\n\n\nlog = logging.getLogger(__name__)\n\n\nDEFAULT_SENSITIVE_FIELDS = frozenset(\n    {\n        'password',\n        'secret',\n        'passwd',\n        'authorization',\n        'api_key',\n        'apikey',\n        'access_token',\n    }\n)\n\"\"\"Names of fields (Connection extra, Variable key name etc.) that are deemed sensitive\"\"\"\n\n\n@cache\ndef get_sensitive_variables_fields():\n    \"\"\"Get comma-separated sensitive Variable Fields from airflow.cfg.\"\"\"\n    from airflow.configuration import conf\n\n    sensitive_fields = DEFAULT_SENSITIVE_FIELDS.copy()\n    sensitive_variable_fields = conf.get('core', 'sensitive_var_conn_names')\n    if sensitive_variable_fields:\n        sensitive_fields |= frozenset({field.strip() for field in sensitive_variable_fields.split(',')})\n    return sensitive_fields\n\n\ndef should_hide_value_for_key(name):\n    \"\"\"Should the value for this given name (Variable name, or key in conn.extra_dejson) be hidden\"\"\"\n    from airflow import settings\n\n    if name and settings.HIDE_SENSITIVE_VAR_CONN_FIELDS:\n        name = name.strip().lower()\n        return any(s in name for s in get_sensitive_variables_fields())\n    return False\n\n\ndef mask_secret(secret: Union[str, dict, Iterable], name: str = None) -> None:\n    \"\"\"\n    Mask a secret from appearing in the task logs.\n\n    If ``name`` is provided, then it will only be masked if the name matches\n    one of the configured \"sensitive\" names.\n\n    If ``secret`` is a dict or a iterable (excluding str) then it will be\n    recursively walked and keys with sensitive names will be hidden.\n    \"\"\"\n    # Delay import\n    from airflow import settings\n\n    # Filtering all log messages is not a free process, so we only do it when\n    # running tasks\n    if not settings.MASK_SECRETS_IN_LOGS or not secret:\n        return\n\n    _secrets_masker().add_mask(secret, name)\n\n\ndef redact(value: \"RedactableItem\", name: str = None) -> \"RedactableItem\":\n    \"\"\"Redact any secrets found in ``value``.\"\"\"\n    return _secrets_masker().redact(value, name)\n\n\n@cache\ndef _secrets_masker() -> \"SecretsMasker\":\n\n    for flt in logging.getLogger('airflow.task').filters:\n        if isinstance(flt, SecretsMasker):\n            return flt\n    raise RuntimeError(\"No SecretsMasker found!\")\n\n\nclass SecretsMasker(logging.Filter):\n    \"\"\"Redact secrets from logs\"\"\"\n\n    replacer: Optional[\"RePatternType\"] = None\n    patterns: Set[str]\n\n    ALREADY_FILTERED_FLAG = \"__SecretsMasker_filtered\"\n\n    def __init__(self):\n        super().__init__()\n        self.patterns = set()\n\n    @cached_property\n    def _record_attrs_to_ignore(self) -> Iterable[str]:\n        # Doing log.info(..., extra={'foo': 2}) sets extra properties on\n        # record, i.e. record.foo. And we need to filter those too. Fun\n        #\n        # Create a record, and look at what attributes are on it, and ignore\n        # all the default ones!\n\n        record = logging.getLogRecordFactory()(\n            # name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None,\n            \"x\",\n            logging.INFO,\n            __file__,\n            1,\n            \"\",\n            tuple(),\n            exc_info=None,\n            func=\"funcname\",\n        )\n        return frozenset(record.__dict__).difference({'msg', 'args'})\n\n    def filter(self, record) -> bool:\n        if self.ALREADY_FILTERED_FLAG in record.__dict__:\n            # Filters are attached to multiple handlers and logs, keep a\n            # \"private\" flag that stops us needing to process it more than once\n            return True\n\n        if self.replacer:\n            for k, v in record.__dict__.items():\n                if k in self._record_attrs_to_ignore:\n                    continue\n                record.__dict__[k] = self.redact(v)\n            if record.exc_info and record.exc_info[1] is not None:\n                exc = record.exc_info[1]\n                # I'm not sure if this is a good idea!\n                exc.args = (self.redact(v) for v in exc.args)\n        record.__dict__[self.ALREADY_FILTERED_FLAG] = True\n\n        return True\n\n    def _redact_all(self, item: \"RedactableItem\") -> \"RedactableItem\":\n        if isinstance(item, dict):\n            return {dict_key: self._redact_all(subval) for dict_key, subval in item.items()}\n        elif isinstance(item, str):\n            return '***'\n        elif isinstance(item, (tuple, set)):\n            # Turn set in to tuple!\n            return tuple(self._redact_all(subval) for subval in item)\n        elif isinstance(item, Iterable):\n            return list(self._redact_all(subval) for subval in item)\n        else:\n            return item\n\n    # pylint: disable=too-many-return-statements\n    def redact(self, item: \"RedactableItem\", name: str = None) -> \"RedactableItem\":\n        \"\"\"\n        Redact an any secrets found in ``item``, if it is a string.\n\n        If ``name`` is given, and it's a \"sensitive\" name (see\n        :func:`should_hide_value_for_key`) then all string values in the item\n        is redacted.\n\n        \"\"\"\n        try:\n            if name and should_hide_value_for_key(name):\n                return self._redact_all(item)\n\n            if isinstance(item, dict):\n                return {dict_key: self.redact(subval, dict_key) for dict_key, subval in item.items()}\n            elif isinstance(item, str):\n                if self.replacer:\n                    # We can't replace specific values, but the key-based redacting\n                    # can still happen, so we can't short-circuit, we need to walk\n                    # the structure.\n                    return self.replacer.sub('***', item)\n                return item\n            elif isinstance(item, (tuple, set)):\n                # Turn set in to tuple!\n                return tuple(self.redact(subval) for subval in item)\n            elif isinstance(item, io.IOBase):\n                return item\n            elif isinstance(item, Iterable):\n                return list(self.redact(subval) for subval in item)\n            else:\n                return item\n        except Exception as e:  # pylint: disable=broad-except\n            log.warning(\n                \"Unable to redact %r, please report this via <https://github.com/apache/airflow/issues>. \"\n                \"Error was: %s: %s\",\n                item,\n                type(e).__name__,\n                str(e),\n            )\n            return item\n\n    # pylint: enable=too-many-return-statements\n\n    def add_mask(self, secret: Union[str, dict, Iterable], name: str = None):\n        \"\"\"Add a new secret to be masked to this filter instance.\"\"\"\n        if isinstance(secret, dict):\n            for k, v in secret.items():\n                self.add_mask(v, k)\n        elif isinstance(secret, str):\n            if not secret:\n                return\n            pattern = re.escape(secret)\n            if pattern not in self.patterns and (not name or should_hide_value_for_key(name)):\n                self.patterns.add(pattern)\n                self.replacer = re.compile('|'.join(self.patterns))\n        elif isinstance(secret, collections.abc.Iterable):\n            for v in secret:\n                self.add_mask(v, name)\n", "path": "airflow/utils/log/secrets_masker.py"}], "after_files": [{"content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\"\"\"Mask sensitive information from logs\"\"\"\nimport collections\nimport io\nimport logging\nimport re\nfrom typing import TYPE_CHECKING, Iterable, Optional, Set, TypeVar, Union\n\nfrom airflow.compat.functools import cache, cached_property\n\nif TYPE_CHECKING:\n    from airflow.typing_compat import RePatternType\n\n    RedactableItem = TypeVar('RedactableItem')\n\n\nlog = logging.getLogger(__name__)\n\n\nDEFAULT_SENSITIVE_FIELDS = frozenset(\n    {\n        'access_token',\n        'api_key',\n        'apikey',\n        'authorization',\n        'passphrase',\n        'passwd',\n        'password',\n        'private_key',\n        'secret',\n    }\n)\n\"\"\"Names of fields (Connection extra, Variable key name etc.) that are deemed sensitive\"\"\"\n\n\n@cache\ndef get_sensitive_variables_fields():\n    \"\"\"Get comma-separated sensitive Variable Fields from airflow.cfg.\"\"\"\n    from airflow.configuration import conf\n\n    sensitive_fields = DEFAULT_SENSITIVE_FIELDS.copy()\n    sensitive_variable_fields = conf.get('core', 'sensitive_var_conn_names')\n    if sensitive_variable_fields:\n        sensitive_fields |= frozenset({field.strip() for field in sensitive_variable_fields.split(',')})\n    return sensitive_fields\n\n\ndef should_hide_value_for_key(name):\n    \"\"\"Should the value for this given name (Variable name, or key in conn.extra_dejson) be hidden\"\"\"\n    from airflow import settings\n\n    if name and settings.HIDE_SENSITIVE_VAR_CONN_FIELDS:\n        name = name.strip().lower()\n        return any(s in name for s in get_sensitive_variables_fields())\n    return False\n\n\ndef mask_secret(secret: Union[str, dict, Iterable], name: str = None) -> None:\n    \"\"\"\n    Mask a secret from appearing in the task logs.\n\n    If ``name`` is provided, then it will only be masked if the name matches\n    one of the configured \"sensitive\" names.\n\n    If ``secret`` is a dict or a iterable (excluding str) then it will be\n    recursively walked and keys with sensitive names will be hidden.\n    \"\"\"\n    # Delay import\n    from airflow import settings\n\n    # Filtering all log messages is not a free process, so we only do it when\n    # running tasks\n    if not settings.MASK_SECRETS_IN_LOGS or not secret:\n        return\n\n    _secrets_masker().add_mask(secret, name)\n\n\ndef redact(value: \"RedactableItem\", name: str = None) -> \"RedactableItem\":\n    \"\"\"Redact any secrets found in ``value``.\"\"\"\n    return _secrets_masker().redact(value, name)\n\n\n@cache\ndef _secrets_masker() -> \"SecretsMasker\":\n\n    for flt in logging.getLogger('airflow.task').filters:\n        if isinstance(flt, SecretsMasker):\n            return flt\n    raise RuntimeError(\"No SecretsMasker found!\")\n\n\nclass SecretsMasker(logging.Filter):\n    \"\"\"Redact secrets from logs\"\"\"\n\n    replacer: Optional[\"RePatternType\"] = None\n    patterns: Set[str]\n\n    ALREADY_FILTERED_FLAG = \"__SecretsMasker_filtered\"\n\n    def __init__(self):\n        super().__init__()\n        self.patterns = set()\n\n    @cached_property\n    def _record_attrs_to_ignore(self) -> Iterable[str]:\n        # Doing log.info(..., extra={'foo': 2}) sets extra properties on\n        # record, i.e. record.foo. And we need to filter those too. Fun\n        #\n        # Create a record, and look at what attributes are on it, and ignore\n        # all the default ones!\n\n        record = logging.getLogRecordFactory()(\n            # name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None,\n            \"x\",\n            logging.INFO,\n            __file__,\n            1,\n            \"\",\n            tuple(),\n            exc_info=None,\n            func=\"funcname\",\n        )\n        return frozenset(record.__dict__).difference({'msg', 'args'})\n\n    def filter(self, record) -> bool:\n        if self.ALREADY_FILTERED_FLAG in record.__dict__:\n            # Filters are attached to multiple handlers and logs, keep a\n            # \"private\" flag that stops us needing to process it more than once\n            return True\n\n        if self.replacer:\n            for k, v in record.__dict__.items():\n                if k in self._record_attrs_to_ignore:\n                    continue\n                record.__dict__[k] = self.redact(v)\n            if record.exc_info and record.exc_info[1] is not None:\n                exc = record.exc_info[1]\n                # I'm not sure if this is a good idea!\n                exc.args = (self.redact(v) for v in exc.args)\n        record.__dict__[self.ALREADY_FILTERED_FLAG] = True\n\n        return True\n\n    def _redact_all(self, item: \"RedactableItem\") -> \"RedactableItem\":\n        if isinstance(item, dict):\n            return {dict_key: self._redact_all(subval) for dict_key, subval in item.items()}\n        elif isinstance(item, str):\n            return '***'\n        elif isinstance(item, (tuple, set)):\n            # Turn set in to tuple!\n            return tuple(self._redact_all(subval) for subval in item)\n        elif isinstance(item, Iterable):\n            return list(self._redact_all(subval) for subval in item)\n        else:\n            return item\n\n    # pylint: disable=too-many-return-statements\n    def redact(self, item: \"RedactableItem\", name: str = None) -> \"RedactableItem\":\n        \"\"\"\n        Redact an any secrets found in ``item``, if it is a string.\n\n        If ``name`` is given, and it's a \"sensitive\" name (see\n        :func:`should_hide_value_for_key`) then all string values in the item\n        is redacted.\n\n        \"\"\"\n        try:\n            if name and should_hide_value_for_key(name):\n                return self._redact_all(item)\n\n            if isinstance(item, dict):\n                return {dict_key: self.redact(subval, dict_key) for dict_key, subval in item.items()}\n            elif isinstance(item, str):\n                if self.replacer:\n                    # We can't replace specific values, but the key-based redacting\n                    # can still happen, so we can't short-circuit, we need to walk\n                    # the structure.\n                    return self.replacer.sub('***', item)\n                return item\n            elif isinstance(item, (tuple, set)):\n                # Turn set in to tuple!\n                return tuple(self.redact(subval) for subval in item)\n            elif isinstance(item, io.IOBase):\n                return item\n            elif isinstance(item, Iterable):\n                return list(self.redact(subval) for subval in item)\n            else:\n                return item\n        except Exception as e:  # pylint: disable=broad-except\n            log.warning(\n                \"Unable to redact %r, please report this via <https://github.com/apache/airflow/issues>. \"\n                \"Error was: %s: %s\",\n                item,\n                type(e).__name__,\n                str(e),\n            )\n            return item\n\n    # pylint: enable=too-many-return-statements\n\n    def add_mask(self, secret: Union[str, dict, Iterable], name: str = None):\n        \"\"\"Add a new secret to be masked to this filter instance.\"\"\"\n        if isinstance(secret, dict):\n            for k, v in secret.items():\n                self.add_mask(v, k)\n        elif isinstance(secret, str):\n            if not secret:\n                return\n            pattern = re.escape(secret)\n            if pattern not in self.patterns and (not name or should_hide_value_for_key(name)):\n                self.patterns.add(pattern)\n                self.replacer = re.compile('|'.join(self.patterns))\n        elif isinstance(secret, collections.abc.Iterable):\n            for v in secret:\n                self.add_mask(v, name)\n", "path": "airflow/utils/log/secrets_masker.py"}]}