We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
AWS::Logs::MetricFilter MetricValues permits invalid strings
`cfn-lint 0.30.1`

AWS::Logs::MetricFilter.Properties.MetricTransformations[*].MetricValue allows a bare string not starting with '$' which it appears is never actually valid, ie 
"MetricValue: length" vs "MetricValue: $length"

Assuming I'm reading the documentation correctly MetricValue must always either be a number OR start with a '$' character.

The following fragment lints, but is rejected by CloudFormation at runtime without a $ at the start of the named MetricValue field
```
    QueueLengthMetricFilter:
        Type: AWS::Logs::MetricFilter
        Properties:
            LogGroupName: !Ref LogGroup
            FilterPattern: '[date, time, tag="rh-sched*", x01=throttling, x02="jobs.", ..., x10=Len, x11=of, x12=job, x13="queue*",  length]'
            MetricTransformations:
              - MetricValue: length
                MetricNamespace: !Sub '${EnvironmentName}'
                MetricName: 'JobsQueued'
```


Note: I believe that this is also missed by the AWS ValidateTemplate API

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/cfnlint/rules/resources/properties/AllowedPattern.py`
Content:
```
1 """
2 Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 SPDX-License-Identifier: MIT-0
4 """
5 import re
6 from cfnlint.rules import CloudFormationLintRule
7 from cfnlint.rules import RuleMatch
8 
9 from cfnlint.helpers import RESOURCE_SPECS
10 
11 
12 class AllowedPattern(CloudFormationLintRule):
13     """Check if properties have a valid value"""
14     id = 'E3031'
15     shortdesc = 'Check if property values adhere to a specific pattern'
16     description = 'Check if properties have a valid value in case of a pattern (Regular Expression)'
17     source_url = 'https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#allowedpattern'
18     tags = ['resources', 'property', 'allowed pattern', 'regex']
19 
20     def initialize(self, cfn):
21         """Initialize the rule"""
22         for resource_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes'):
23             self.resource_property_types.append(resource_type_spec)
24         for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):
25             self.resource_sub_property_types.append(property_type_spec)
26 
27     def check_value(self, value, path, property_name, **kwargs):
28         """Check Value"""
29         matches = []
30 
31         # Get the Allowed Pattern Regex
32         value_pattern_regex = kwargs.get('value_specs', {}).get('AllowedPatternRegex', {})
33         # Get the "Human Readable" version for the error message. Optional, if not specified,
34         # the RegEx itself is used.
35         value_pattern = kwargs.get('value_specs', {}).get('AllowedPattern', value_pattern_regex)
36 
37         if value_pattern_regex:
38             regex = re.compile(value_pattern_regex)
39 
40             # Ignore values with dynamic references. Simple check to prevent false-positives
41             # See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html
42             if '{{resolve:' not in value:
43                 if not regex.match(value):
44                     full_path = ('/'.join(str(x) for x in path))
45 
46                     message = '{} contains invalid characters (Pattern: {}) at {}'
47                     matches.append(RuleMatch(path, message.format(
48                         property_name, value_pattern, full_path)))
49 
50         return matches
51 
52     def check(self, cfn, properties, value_specs, property_specs, path):
53         """Check itself"""
54         matches = list()
55         for p_value, p_path in properties.items_safe(path[:]):
56             for prop in p_value:
57                 if prop in value_specs:
58                     value = value_specs.get(prop).get('Value', {})
59                     if value:
60                         value_type = value.get('ValueType', '')
61                         property_type = property_specs.get('Properties').get(prop).get('Type')
62                         matches.extend(
63                             cfn.check_value(
64                                 p_value, prop, p_path,
65                                 check_value=self.check_value,
66                                 value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get(
67                                     'ValueTypes').get(value_type, {}),
68                                 cfn=cfn, property_type=property_type, property_name=prop
69                             )
70                         )
71         return matches
72 
73     def match_resource_sub_properties(self, properties, property_type, path, cfn):
74         """Match for sub properties"""
75         matches = list()
76 
77         specs = RESOURCE_SPECS.get(cfn.regions[0]).get(
78             'PropertyTypes').get(property_type, {}).get('Properties', {})
79         property_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes').get(property_type)
80         matches.extend(self.check(cfn, properties, specs, property_specs, path))
81 
82         return matches
83 
84     def match_resource_properties(self, properties, resource_type, path, cfn):
85         """Check CloudFormation Properties"""
86         matches = list()
87 
88         specs = RESOURCE_SPECS.get(cfn.regions[0]).get(
89             'ResourceTypes').get(resource_type, {}).get('Properties', {})
90         resource_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes').get(resource_type)
91         matches.extend(self.check(cfn, properties, specs, resource_specs, path))
92 
93         return matches
94
```
--- 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/src/cfnlint/rules/resources/properties/AllowedPattern.py b/src/cfnlint/rules/resources/properties/AllowedPattern.py
--- a/src/cfnlint/rules/resources/properties/AllowedPattern.py
+++ b/src/cfnlint/rules/resources/properties/AllowedPattern.py
@@ -3,6 +3,7 @@
 SPDX-License-Identifier: MIT-0
 """
 import re
+import six
 from cfnlint.rules import CloudFormationLintRule
 from cfnlint.rules import RuleMatch
 
@@ -34,18 +35,22 @@
         # the RegEx itself is used.
         value_pattern = kwargs.get('value_specs', {}).get('AllowedPattern', value_pattern_regex)
 
-        if value_pattern_regex:
-            regex = re.compile(value_pattern_regex)
+        if isinstance(value, (int, float)):
+            value = str(value)
 
-            # Ignore values with dynamic references. Simple check to prevent false-positives
-            # See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html
-            if '{{resolve:' not in value:
-                if not regex.match(value):
-                    full_path = ('/'.join(str(x) for x in path))
+        if isinstance(value, six.string_types):
+            if value_pattern_regex:
+                regex = re.compile(value_pattern_regex)
 
-                    message = '{} contains invalid characters (Pattern: {}) at {}'
-                    matches.append(RuleMatch(path, message.format(
-                        property_name, value_pattern, full_path)))
+                # Ignore values with dynamic references. Simple check to prevent false-positives
+                # See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html
+                if '{{resolve:' not in value:
+                    if not regex.match(value):
+                        full_path = ('/'.join(str(x) for x in path))
+
+                        message = '{} contains invalid characters (Pattern: {}) at {}'
+                        matches.append(RuleMatch(path, message.format(
+                            property_name, value_pattern, full_path)))
 
         return matches

{"golden_diff": "diff --git a/src/cfnlint/rules/resources/properties/AllowedPattern.py b/src/cfnlint/rules/resources/properties/AllowedPattern.py\n--- a/src/cfnlint/rules/resources/properties/AllowedPattern.py\n+++ b/src/cfnlint/rules/resources/properties/AllowedPattern.py\n@@ -3,6 +3,7 @@\n SPDX-License-Identifier: MIT-0\n \"\"\"\n import re\n+import six\n from cfnlint.rules import CloudFormationLintRule\n from cfnlint.rules import RuleMatch\n \n@@ -34,18 +35,22 @@\n         # the RegEx itself is used.\n         value_pattern = kwargs.get('value_specs', {}).get('AllowedPattern', value_pattern_regex)\n \n-        if value_pattern_regex:\n-            regex = re.compile(value_pattern_regex)\n+        if isinstance(value, (int, float)):\n+            value = str(value)\n \n-            # Ignore values with dynamic references. Simple check to prevent false-positives\n-            # See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html\n-            if '{{resolve:' not in value:\n-                if not regex.match(value):\n-                    full_path = ('/'.join(str(x) for x in path))\n+        if isinstance(value, six.string_types):\n+            if value_pattern_regex:\n+                regex = re.compile(value_pattern_regex)\n \n-                    message = '{} contains invalid characters (Pattern: {}) at {}'\n-                    matches.append(RuleMatch(path, message.format(\n-                        property_name, value_pattern, full_path)))\n+                # Ignore values with dynamic references. Simple check to prevent false-positives\n+                # See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html\n+                if '{{resolve:' not in value:\n+                    if not regex.match(value):\n+                        full_path = ('/'.join(str(x) for x in path))\n+\n+                        message = '{} contains invalid characters (Pattern: {}) at {}'\n+                        matches.append(RuleMatch(path, message.format(\n+                            property_name, value_pattern, full_path)))\n \n         return matches\n", "issue": "AWS::Logs::MetricFilter MetricValues permits invalid strings\n`cfn-lint 0.30.1`\r\n\r\nAWS::Logs::MetricFilter.Properties.MetricTransformations[*].MetricValue allows a bare string not starting with '$' which it appears is never actually valid, ie \r\n\"MetricValue: length\" vs \"MetricValue: $length\"\r\n\r\nAssuming I'm reading the documentation correctly MetricValue must always either be a number OR start with a '$' character.\r\n\r\nThe following fragment lints, but is rejected by CloudFormation at runtime without a $ at the start of the named MetricValue field\r\n```\r\n    QueueLengthMetricFilter:\r\n        Type: AWS::Logs::MetricFilter\r\n        Properties:\r\n            LogGroupName: !Ref LogGroup\r\n            FilterPattern: '[date, time, tag=\"rh-sched*\", x01=throttling, x02=\"jobs.\", ..., x10=Len, x11=of, x12=job, x13=\"queue*\",  length]'\r\n            MetricTransformations:\r\n              - MetricValue: length\r\n                MetricNamespace: !Sub '${EnvironmentName}'\r\n                MetricName: 'JobsQueued'\r\n```\r\n\r\n\r\nNote: I believe that this is also missed by the AWS ValidateTemplate API\n", "before_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport re\nfrom cfnlint.rules import CloudFormationLintRule\nfrom cfnlint.rules import RuleMatch\n\nfrom cfnlint.helpers import RESOURCE_SPECS\n\n\nclass AllowedPattern(CloudFormationLintRule):\n    \"\"\"Check if properties have a valid value\"\"\"\n    id = 'E3031'\n    shortdesc = 'Check if property values adhere to a specific pattern'\n    description = 'Check if properties have a valid value in case of a pattern (Regular Expression)'\n    source_url = 'https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#allowedpattern'\n    tags = ['resources', 'property', 'allowed pattern', 'regex']\n\n    def initialize(self, cfn):\n        \"\"\"Initialize the rule\"\"\"\n        for resource_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes'):\n            self.resource_property_types.append(resource_type_spec)\n        for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):\n            self.resource_sub_property_types.append(property_type_spec)\n\n    def check_value(self, value, path, property_name, **kwargs):\n        \"\"\"Check Value\"\"\"\n        matches = []\n\n        # Get the Allowed Pattern Regex\n        value_pattern_regex = kwargs.get('value_specs', {}).get('AllowedPatternRegex', {})\n        # Get the \"Human Readable\" version for the error message. Optional, if not specified,\n        # the RegEx itself is used.\n        value_pattern = kwargs.get('value_specs', {}).get('AllowedPattern', value_pattern_regex)\n\n        if value_pattern_regex:\n            regex = re.compile(value_pattern_regex)\n\n            # Ignore values with dynamic references. Simple check to prevent false-positives\n            # See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html\n            if '{{resolve:' not in value:\n                if not regex.match(value):\n                    full_path = ('/'.join(str(x) for x in path))\n\n                    message = '{} contains invalid characters (Pattern: {}) at {}'\n                    matches.append(RuleMatch(path, message.format(\n                        property_name, value_pattern, full_path)))\n\n        return matches\n\n    def check(self, cfn, properties, value_specs, property_specs, path):\n        \"\"\"Check itself\"\"\"\n        matches = list()\n        for p_value, p_path in properties.items_safe(path[:]):\n            for prop in p_value:\n                if prop in value_specs:\n                    value = value_specs.get(prop).get('Value', {})\n                    if value:\n                        value_type = value.get('ValueType', '')\n                        property_type = property_specs.get('Properties').get(prop).get('Type')\n                        matches.extend(\n                            cfn.check_value(\n                                p_value, prop, p_path,\n                                check_value=self.check_value,\n                                value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get(\n                                    'ValueTypes').get(value_type, {}),\n                                cfn=cfn, property_type=property_type, property_name=prop\n                            )\n                        )\n        return matches\n\n    def match_resource_sub_properties(self, properties, property_type, path, cfn):\n        \"\"\"Match for sub properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'PropertyTypes').get(property_type, {}).get('Properties', {})\n        property_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes').get(property_type)\n        matches.extend(self.check(cfn, properties, specs, property_specs, path))\n\n        return matches\n\n    def match_resource_properties(self, properties, resource_type, path, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'ResourceTypes').get(resource_type, {}).get('Properties', {})\n        resource_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes').get(resource_type)\n        matches.extend(self.check(cfn, properties, specs, resource_specs, path))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/properties/AllowedPattern.py"}], "after_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport re\nimport six\nfrom cfnlint.rules import CloudFormationLintRule\nfrom cfnlint.rules import RuleMatch\n\nfrom cfnlint.helpers import RESOURCE_SPECS\n\n\nclass AllowedPattern(CloudFormationLintRule):\n    \"\"\"Check if properties have a valid value\"\"\"\n    id = 'E3031'\n    shortdesc = 'Check if property values adhere to a specific pattern'\n    description = 'Check if properties have a valid value in case of a pattern (Regular Expression)'\n    source_url = 'https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#allowedpattern'\n    tags = ['resources', 'property', 'allowed pattern', 'regex']\n\n    def initialize(self, cfn):\n        \"\"\"Initialize the rule\"\"\"\n        for resource_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes'):\n            self.resource_property_types.append(resource_type_spec)\n        for property_type_spec in RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes'):\n            self.resource_sub_property_types.append(property_type_spec)\n\n    def check_value(self, value, path, property_name, **kwargs):\n        \"\"\"Check Value\"\"\"\n        matches = []\n\n        # Get the Allowed Pattern Regex\n        value_pattern_regex = kwargs.get('value_specs', {}).get('AllowedPatternRegex', {})\n        # Get the \"Human Readable\" version for the error message. Optional, if not specified,\n        # the RegEx itself is used.\n        value_pattern = kwargs.get('value_specs', {}).get('AllowedPattern', value_pattern_regex)\n\n        if isinstance(value, (int, float)):\n            value = str(value)\n\n        if isinstance(value, six.string_types):\n            if value_pattern_regex:\n                regex = re.compile(value_pattern_regex)\n\n                # Ignore values with dynamic references. Simple check to prevent false-positives\n                # See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html\n                if '{{resolve:' not in value:\n                    if not regex.match(value):\n                        full_path = ('/'.join(str(x) for x in path))\n\n                        message = '{} contains invalid characters (Pattern: {}) at {}'\n                        matches.append(RuleMatch(path, message.format(\n                            property_name, value_pattern, full_path)))\n\n        return matches\n\n    def check(self, cfn, properties, value_specs, property_specs, path):\n        \"\"\"Check itself\"\"\"\n        matches = list()\n        for p_value, p_path in properties.items_safe(path[:]):\n            for prop in p_value:\n                if prop in value_specs:\n                    value = value_specs.get(prop).get('Value', {})\n                    if value:\n                        value_type = value.get('ValueType', '')\n                        property_type = property_specs.get('Properties').get(prop).get('Type')\n                        matches.extend(\n                            cfn.check_value(\n                                p_value, prop, p_path,\n                                check_value=self.check_value,\n                                value_specs=RESOURCE_SPECS.get(cfn.regions[0]).get(\n                                    'ValueTypes').get(value_type, {}),\n                                cfn=cfn, property_type=property_type, property_name=prop\n                            )\n                        )\n        return matches\n\n    def match_resource_sub_properties(self, properties, property_type, path, cfn):\n        \"\"\"Match for sub properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'PropertyTypes').get(property_type, {}).get('Properties', {})\n        property_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('PropertyTypes').get(property_type)\n        matches.extend(self.check(cfn, properties, specs, property_specs, path))\n\n        return matches\n\n    def match_resource_properties(self, properties, resource_type, path, cfn):\n        \"\"\"Check CloudFormation Properties\"\"\"\n        matches = list()\n\n        specs = RESOURCE_SPECS.get(cfn.regions[0]).get(\n            'ResourceTypes').get(resource_type, {}).get('Properties', {})\n        resource_specs = RESOURCE_SPECS.get(cfn.regions[0]).get('ResourceTypes').get(resource_type)\n        matches.extend(self.check(cfn, properties, specs, resource_specs, path))\n\n        return matches\n", "path": "src/cfnlint/rules/resources/properties/AllowedPattern.py"}]}