playground-ls-cli 4.14.1.dev8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- localstack_cli/__init__.py +0 -0
- localstack_cli/cli/__init__.py +10 -0
- localstack_cli/cli/console.py +11 -0
- localstack_cli/cli/core_plugin.py +12 -0
- localstack_cli/cli/exceptions.py +19 -0
- localstack_cli/cli/localstack.py +951 -0
- localstack_cli/cli/lpm.py +138 -0
- localstack_cli/cli/main.py +22 -0
- localstack_cli/cli/plugin.py +39 -0
- localstack_cli/cli/plugins.py +134 -0
- localstack_cli/cli/profiles.py +65 -0
- localstack_cli/config.py +1689 -0
- localstack_cli/constants.py +165 -0
- localstack_cli/logging/__init__.py +0 -0
- localstack_cli/logging/format.py +194 -0
- localstack_cli/logging/setup.py +142 -0
- localstack_cli/packages/__init__.py +25 -0
- localstack_cli/packages/api.py +418 -0
- localstack_cli/packages/core.py +416 -0
- localstack_cli/pro/__init__.py +0 -0
- localstack_cli/pro/core/__init__.py +0 -0
- localstack_cli/pro/core/bootstrap/__init__.py +1 -0
- localstack_cli/pro/core/bootstrap/auth.py +213 -0
- localstack_cli/pro/core/bootstrap/dns_utils.py +55 -0
- localstack_cli/pro/core/bootstrap/entitlements.py +117 -0
- localstack_cli/pro/core/bootstrap/extensions/__init__.py +3 -0
- localstack_cli/pro/core/bootstrap/extensions/__main__.py +106 -0
- localstack_cli/pro/core/bootstrap/extensions/autoinstall.py +63 -0
- localstack_cli/pro/core/bootstrap/extensions/bootstrap.py +97 -0
- localstack_cli/pro/core/bootstrap/extensions/repository.py +374 -0
- localstack_cli/pro/core/bootstrap/licensingv2.py +1259 -0
- localstack_cli/pro/core/bootstrap/pods/__init__.py +0 -0
- localstack_cli/pro/core/bootstrap/pods/api_types.py +17 -0
- localstack_cli/pro/core/bootstrap/pods/constants.py +26 -0
- localstack_cli/pro/core/bootstrap/pods/remotes/__init__.py +0 -0
- localstack_cli/pro/core/bootstrap/pods/remotes/api.py +75 -0
- localstack_cli/pro/core/bootstrap/pods/remotes/configs.py +69 -0
- localstack_cli/pro/core/bootstrap/pods/remotes/params.py +86 -0
- localstack_cli/pro/core/bootstrap/pods_client.py +834 -0
- localstack_cli/pro/core/cli/__init__.py +0 -0
- localstack_cli/pro/core/cli/auth.py +226 -0
- localstack_cli/pro/core/cli/aws.py +16 -0
- localstack_cli/pro/core/cli/cli.py +99 -0
- localstack_cli/pro/core/cli/click_utils.py +21 -0
- localstack_cli/pro/core/cli/cloud_pods.py +465 -0
- localstack_cli/pro/core/cli/diff_view.py +41 -0
- localstack_cli/pro/core/cli/ephemeral.py +199 -0
- localstack_cli/pro/core/cli/extensions.py +492 -0
- localstack_cli/pro/core/cli/iam.py +180 -0
- localstack_cli/pro/core/cli/license.py +90 -0
- localstack_cli/pro/core/cli/localstack.py +118 -0
- localstack_cli/pro/core/cli/replicator.py +378 -0
- localstack_cli/pro/core/cli/state.py +183 -0
- localstack_cli/pro/core/cli/tree_view.py +235 -0
- localstack_cli/pro/core/config.py +556 -0
- localstack_cli/pro/core/constants.py +54 -0
- localstack_cli/pro/core/plugins.py +169 -0
- localstack_cli/runtime/__init__.py +6 -0
- localstack_cli/runtime/exceptions.py +7 -0
- localstack_cli/runtime/hooks.py +73 -0
- localstack_cli/testing/__init__.py +1 -0
- localstack_cli/testing/config.py +4 -0
- localstack_cli/utils/__init__.py +0 -0
- localstack_cli/utils/analytics/__init__.py +12 -0
- localstack_cli/utils/analytics/cli.py +67 -0
- localstack_cli/utils/analytics/client.py +111 -0
- localstack_cli/utils/analytics/events.py +30 -0
- localstack_cli/utils/analytics/logger.py +48 -0
- localstack_cli/utils/analytics/metadata.py +250 -0
- localstack_cli/utils/analytics/publisher.py +160 -0
- localstack_cli/utils/analytics/service_request_aggregator.py +133 -0
- localstack_cli/utils/archives.py +271 -0
- localstack_cli/utils/batching.py +258 -0
- localstack_cli/utils/bootstrap.py +1418 -0
- localstack_cli/utils/checksum.py +313 -0
- localstack_cli/utils/collections.py +554 -0
- localstack_cli/utils/common.py +229 -0
- localstack_cli/utils/container_networking.py +142 -0
- localstack_cli/utils/container_utils/__init__.py +0 -0
- localstack_cli/utils/container_utils/container_client.py +1585 -0
- localstack_cli/utils/container_utils/docker_cmd_client.py +987 -0
- localstack_cli/utils/container_utils/docker_sdk_client.py +1018 -0
- localstack_cli/utils/crypto.py +294 -0
- localstack_cli/utils/docker_utils.py +272 -0
- localstack_cli/utils/files.py +327 -0
- localstack_cli/utils/functions.py +92 -0
- localstack_cli/utils/http.py +326 -0
- localstack_cli/utils/json.py +219 -0
- localstack_cli/utils/net.py +516 -0
- localstack_cli/utils/no_exit_argument_parser.py +19 -0
- localstack_cli/utils/numbers.py +49 -0
- localstack_cli/utils/objects.py +235 -0
- localstack_cli/utils/patch.py +260 -0
- localstack_cli/utils/platform.py +77 -0
- localstack_cli/utils/run.py +514 -0
- localstack_cli/utils/server/__init__.py +0 -0
- localstack_cli/utils/server/tcp_proxy.py +108 -0
- localstack_cli/utils/serving.py +187 -0
- localstack_cli/utils/ssl.py +71 -0
- localstack_cli/utils/strings.py +245 -0
- localstack_cli/utils/sync.py +267 -0
- localstack_cli/utils/threads.py +163 -0
- localstack_cli/utils/time.py +81 -0
- localstack_cli/utils/urls.py +21 -0
- localstack_cli/utils/venv.py +100 -0
- localstack_cli/utils/xml.py +41 -0
- localstack_cli/version.py +34 -0
- playground_ls_cli-4.14.1.dev8.dist-info/METADATA +95 -0
- playground_ls_cli-4.14.1.dev8.dist-info/RECORD +112 -0
- playground_ls_cli-4.14.1.dev8.dist-info/WHEEL +5 -0
- playground_ls_cli-4.14.1.dev8.dist-info/entry_points.txt +17 -0
- playground_ls_cli-4.14.1.dev8.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from localstack_cli.version import __version__
|
|
4
|
+
|
|
5
|
+
VERSION = __version__
|
|
6
|
+
|
|
7
|
+
# HTTP headers used to forward proxy request URLs
|
|
8
|
+
HEADER_LOCALSTACK_EDGE_URL = "x-localstack-edge"
|
|
9
|
+
HEADER_LOCALSTACK_REQUEST_URL = "x-localstack-request-url"
|
|
10
|
+
# HTTP header optionally added to LocalStack responses
|
|
11
|
+
HEADER_LOCALSTACK_IDENTIFIER = "x-localstack"
|
|
12
|
+
# xXx custom localstack authorization header only used in ext
|
|
13
|
+
HEADER_LOCALSTACK_AUTHORIZATION = "x-localstack-authorization"
|
|
14
|
+
HEADER_LOCALSTACK_TARGET = "x-localstack-target"
|
|
15
|
+
HEADER_AMZN_ERROR_TYPE = "X-Amzn-Errortype"
|
|
16
|
+
|
|
17
|
+
# backend service ports, for services that are behind a proxy (counting down from 4566)
|
|
18
|
+
DEFAULT_PORT_EDGE = 4566
|
|
19
|
+
|
|
20
|
+
# host name for localhost
|
|
21
|
+
LOCALHOST = "localhost"
|
|
22
|
+
LOCALHOST_IP = "127.0.0.1"
|
|
23
|
+
LOCALHOST_HOSTNAME = "localhost.localstack.cloud"
|
|
24
|
+
|
|
25
|
+
# User-agent string used in outgoing HTTP requests made by LocalStack
|
|
26
|
+
USER_AGENT_STRING = f"localstack/{VERSION}"
|
|
27
|
+
|
|
28
|
+
# version of the Maven dependency with Java utility code
|
|
29
|
+
LOCALSTACK_MAVEN_VERSION = "0.2.21"
|
|
30
|
+
MAVEN_REPO_URL = "https://repo1.maven.org/maven2"
|
|
31
|
+
|
|
32
|
+
# URL of localstack's artifacts repository on GitHub
|
|
33
|
+
ARTIFACTS_REPO = "https://github.com/localstack/localstack-artifacts"
|
|
34
|
+
|
|
35
|
+
# Artifacts endpoint
|
|
36
|
+
ASSETS_ENDPOINT = "https://assets.localstack.cloud"
|
|
37
|
+
|
|
38
|
+
# Hugging Face endpoint for localstack
|
|
39
|
+
HUGGING_FACE_ENDPOINT = "https://huggingface.co/localstack"
|
|
40
|
+
|
|
41
|
+
# host to bind to when starting the services
|
|
42
|
+
BIND_HOST = "0.0.0.0"
|
|
43
|
+
|
|
44
|
+
# root code folder
|
|
45
|
+
MODULE_MAIN_PATH = os.path.dirname(os.path.realpath(__file__))
|
|
46
|
+
# TODO rename to "ROOT_FOLDER"!
|
|
47
|
+
LOCALSTACK_ROOT_FOLDER = os.path.realpath(os.path.join(MODULE_MAIN_PATH, ".."))
|
|
48
|
+
|
|
49
|
+
# virtualenv folder
|
|
50
|
+
LOCALSTACK_VENV_FOLDER: str = os.environ.get("VIRTUAL_ENV")
|
|
51
|
+
if not LOCALSTACK_VENV_FOLDER:
|
|
52
|
+
# fallback to the previous logic
|
|
53
|
+
LOCALSTACK_VENV_FOLDER = os.path.join(LOCALSTACK_ROOT_FOLDER, ".venv")
|
|
54
|
+
if not os.path.isdir(LOCALSTACK_VENV_FOLDER):
|
|
55
|
+
# assuming this package lives here: <python>/lib/pythonX.X/site-packages/localstack/
|
|
56
|
+
LOCALSTACK_VENV_FOLDER = os.path.realpath(
|
|
57
|
+
os.path.join(LOCALSTACK_ROOT_FOLDER, "..", "..", "..")
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# default volume directory containing shared data
|
|
61
|
+
DEFAULT_VOLUME_DIR = "/var/lib/localstack"
|
|
62
|
+
|
|
63
|
+
# API Gateway path to indicate a user request sent to the gateway
|
|
64
|
+
PATH_USER_REQUEST = "_user_request_"
|
|
65
|
+
|
|
66
|
+
# name of LocalStack Docker image
|
|
67
|
+
DOCKER_IMAGE_NAME_PRO = "localstack/localstack-pro"
|
|
68
|
+
|
|
69
|
+
# backdoor API path used to retrieve or update config variables
|
|
70
|
+
CONFIG_UPDATE_PATH = "/?_config_"
|
|
71
|
+
|
|
72
|
+
# API path for localstack internal resources
|
|
73
|
+
INTERNAL_RESOURCE_PATH = "/_localstack"
|
|
74
|
+
|
|
75
|
+
# environment variable name to tag local test runs
|
|
76
|
+
ENV_INTERNAL_TEST_RUN = "LOCALSTACK_INTERNAL_TEST_RUN"
|
|
77
|
+
|
|
78
|
+
# environment variable name to tag collect metrics during a test run
|
|
79
|
+
ENV_INTERNAL_TEST_COLLECT_METRIC = "LOCALSTACK_INTERNAL_TEST_COLLECT_METRIC"
|
|
80
|
+
|
|
81
|
+
# environment variable name to indicate that metrics should be stored within the container
|
|
82
|
+
ENV_INTERNAL_TEST_STORE_METRICS_IN_LOCALSTACK = "LOCALSTACK_INTERNAL_TEST_METRICS_IN_LOCALSTACK"
|
|
83
|
+
ENV_INTERNAL_TEST_STORE_METRICS_PATH = "LOCALSTACK_INTERNAL_TEST_STORE_METRICS_PATH"
|
|
84
|
+
|
|
85
|
+
# environment variable that flags whether pro was activated. do not use it for security purposes!
|
|
86
|
+
ENV_PRO_ACTIVATED = "PRO_ACTIVATED"
|
|
87
|
+
|
|
88
|
+
# content types / encodings
|
|
89
|
+
HEADER_CONTENT_TYPE = "Content-Type"
|
|
90
|
+
TEXT_XML = "text/xml"
|
|
91
|
+
APPLICATION_AMZ_JSON_1_0 = "application/x-amz-json-1.0"
|
|
92
|
+
APPLICATION_AMZ_JSON_1_1 = "application/x-amz-json-1.1"
|
|
93
|
+
APPLICATION_AMZ_CBOR_1_1 = "application/x-amz-cbor-1.1"
|
|
94
|
+
APPLICATION_CBOR = "application/cbor"
|
|
95
|
+
APPLICATION_JSON = "application/json"
|
|
96
|
+
APPLICATION_XML = "application/xml"
|
|
97
|
+
APPLICATION_OCTET_STREAM = "application/octet-stream"
|
|
98
|
+
APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded"
|
|
99
|
+
HEADER_ACCEPT_ENCODING = "Accept-Encoding"
|
|
100
|
+
|
|
101
|
+
# strings to indicate truthy/falsy values
|
|
102
|
+
TRUE_STRINGS = ("1", "true", "True")
|
|
103
|
+
FALSE_STRINGS = ("0", "false", "False")
|
|
104
|
+
# strings with valid log levels for LS_LOG
|
|
105
|
+
LOG_LEVELS = ("trace-internal", "trace", "debug", "info", "warn", "error", "warning")
|
|
106
|
+
|
|
107
|
+
# API endpoint for analytics events
|
|
108
|
+
API_ENDPOINT = os.environ.get("API_ENDPOINT") or "https://api.localstack.cloud/v1"
|
|
109
|
+
# new analytics API endpoint
|
|
110
|
+
ANALYTICS_API = os.environ.get("ANALYTICS_API") or "https://analytics.localstack.cloud/v1"
|
|
111
|
+
|
|
112
|
+
# environment variable to indicate this process should run the localstack infrastructure
|
|
113
|
+
LOCALSTACK_INFRA_PROCESS = "LOCALSTACK_INFRA_PROCESS"
|
|
114
|
+
|
|
115
|
+
# AWS region us-east-1
|
|
116
|
+
AWS_REGION_US_EAST_1 = "us-east-1"
|
|
117
|
+
|
|
118
|
+
# AWS region eu-west-1
|
|
119
|
+
AWS_REGION_EU_WEST_1 = "eu-west-1"
|
|
120
|
+
|
|
121
|
+
# environment variable to override max pool connections
|
|
122
|
+
try:
|
|
123
|
+
MAX_POOL_CONNECTIONS = int(os.environ["MAX_POOL_CONNECTIONS"])
|
|
124
|
+
except Exception:
|
|
125
|
+
MAX_POOL_CONNECTIONS = 150
|
|
126
|
+
|
|
127
|
+
# Fallback Account ID if not available in the client request
|
|
128
|
+
DEFAULT_AWS_ACCOUNT_ID = "000000000000"
|
|
129
|
+
|
|
130
|
+
# Credentials used for internal calls
|
|
131
|
+
INTERNAL_AWS_ACCESS_KEY_ID = "__internal_call__"
|
|
132
|
+
INTERNAL_AWS_SECRET_ACCESS_KEY = "__internal_call__"
|
|
133
|
+
|
|
134
|
+
# trace log levels (excluding/including internal API calls), configurable via $LS_LOG
|
|
135
|
+
LS_LOG_TRACE = "trace"
|
|
136
|
+
LS_LOG_TRACE_INTERNAL = "trace-internal"
|
|
137
|
+
TRACE_LOG_LEVELS = [LS_LOG_TRACE, LS_LOG_TRACE_INTERNAL]
|
|
138
|
+
|
|
139
|
+
# list of official docker images
|
|
140
|
+
OFFICIAL_IMAGES = [
|
|
141
|
+
"localstack/localstack",
|
|
142
|
+
"localstack/localstack-pro",
|
|
143
|
+
"localstack/snowflake"
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
# port for debug py
|
|
147
|
+
DEFAULT_DEVELOP_PORT = 5678
|
|
148
|
+
|
|
149
|
+
# Default bucket name of the s3 bucket used for local lambda development
|
|
150
|
+
# This name should be accepted by all IaC tools, so should respect s3 bucket naming conventions
|
|
151
|
+
DEFAULT_BUCKET_MARKER_LOCAL = "hot-reload"
|
|
152
|
+
LEGACY_DEFAULT_BUCKET_MARKER_LOCAL = "__local__"
|
|
153
|
+
|
|
154
|
+
# output string that indicates that the stack is ready
|
|
155
|
+
READY_MARKER_OUTPUT = "Ready."
|
|
156
|
+
|
|
157
|
+
# Regex for `Credential` field in the Authorization header in AWS signature version v4
|
|
158
|
+
# The format is as follows:
|
|
159
|
+
# Credential=<access-key-id>/<date>/<region-name>/<service-name>/aws4_request
|
|
160
|
+
# eg.
|
|
161
|
+
# Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request
|
|
162
|
+
AUTH_CREDENTIAL_REGEX = r"Credential=(?P<access_key_id>[a-zA-Z0-9-_.]{1,})/(?P<date>\d{8})/(?P<region_name>[a-z0-9-]{1,})/(?P<service_name>[a-z0-9]{1,})/"
|
|
163
|
+
|
|
164
|
+
# Custom resource tag to override the generated resource ID.
|
|
165
|
+
TAG_KEY_CUSTOM_ID = "_custom_id_"
|
|
File without changes
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""Tools for formatting localstack logs."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import re
|
|
5
|
+
from functools import lru_cache
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from localstack_cli.utils.numbers import format_bytes
|
|
9
|
+
from localstack_cli.utils.strings import to_bytes
|
|
10
|
+
|
|
11
|
+
MAX_THREAD_NAME_LEN = 12
|
|
12
|
+
MAX_NAME_LEN = 26
|
|
13
|
+
|
|
14
|
+
LOG_FORMAT = f"%(asctime)s.%(msecs)03d %(ls_level)5s --- [%(ls_thread){MAX_THREAD_NAME_LEN}s] %(ls_name)-{MAX_NAME_LEN}s : %(message)s"
|
|
15
|
+
LOG_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S"
|
|
16
|
+
LOG_INPUT_FORMAT = "%(input_type)s(%(input)s, headers=%(request_headers)s)"
|
|
17
|
+
LOG_OUTPUT_FORMAT = "%(output_type)s(%(output)s, headers=%(response_headers)s)"
|
|
18
|
+
LOG_CONTEXT_FORMAT = "%(account_id)s/%(region)s"
|
|
19
|
+
|
|
20
|
+
CUSTOM_LEVEL_NAMES = {
|
|
21
|
+
50: "FATAL",
|
|
22
|
+
40: "ERROR",
|
|
23
|
+
30: "WARN",
|
|
24
|
+
20: "INFO",
|
|
25
|
+
10: "DEBUG",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DefaultFormatter(logging.Formatter):
|
|
30
|
+
"""
|
|
31
|
+
A formatter that uses ``LOG_FORMAT`` and ``LOG_DATE_FORMAT``.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, fmt=LOG_FORMAT, datefmt=LOG_DATE_FORMAT):
|
|
35
|
+
super().__init__(fmt=fmt, datefmt=datefmt)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AddFormattedAttributes(logging.Filter):
|
|
39
|
+
"""
|
|
40
|
+
Filter that adds three attributes to a log record:
|
|
41
|
+
|
|
42
|
+
- ls_level: the abbreviated loglevel that's max 5 characters long
|
|
43
|
+
- ls_name: the abbreviated name of the logger (e.g., `l.bootstrap.install`), trimmed to ``MAX_NAME_LEN``
|
|
44
|
+
- ls_thread: the abbreviated thread name (prefix trimmed, .e.g, ``omeThread-108``)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
max_name_len: int
|
|
48
|
+
max_thread_len: int
|
|
49
|
+
|
|
50
|
+
def __init__(self, max_name_len: int = None, max_thread_len: int = None):
|
|
51
|
+
super().__init__()
|
|
52
|
+
self.max_name_len = max_name_len if max_name_len else MAX_NAME_LEN
|
|
53
|
+
self.max_thread_len = max_thread_len if max_thread_len else MAX_THREAD_NAME_LEN
|
|
54
|
+
|
|
55
|
+
def filter(self, record):
|
|
56
|
+
record.ls_level = CUSTOM_LEVEL_NAMES.get(record.levelno, record.levelname)
|
|
57
|
+
record.ls_name = self._get_compressed_logger_name(record.name)
|
|
58
|
+
record.ls_thread = record.threadName[-self.max_thread_len :]
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
@lru_cache(maxsize=256)
|
|
62
|
+
def _get_compressed_logger_name(self, name):
|
|
63
|
+
return compress_logger_name(name, self.max_name_len)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class MaskSensitiveInputFilter(logging.Filter):
|
|
67
|
+
"""
|
|
68
|
+
Filter that hides sensitive from a binary json string in a record input.
|
|
69
|
+
It will find the mathing keys and replace their values with "******"
|
|
70
|
+
|
|
71
|
+
For example, if initialized with `sensitive_keys=["my_key"]`, the input
|
|
72
|
+
b'{"my_key": "sensitive_value"}' would become b'{"my_key": "******"}'.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
patterns: list[tuple[re.Pattern[bytes], bytes]]
|
|
76
|
+
|
|
77
|
+
def __init__(self, sensitive_keys: list[str]):
|
|
78
|
+
super().__init__()
|
|
79
|
+
|
|
80
|
+
self.patterns = [
|
|
81
|
+
(re.compile(to_bytes(rf'"{key}":\s*"[^"]+"')), to_bytes(f'"{key}": "******"'))
|
|
82
|
+
for key in sensitive_keys
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
def filter(self, record):
|
|
86
|
+
if record.input and isinstance(record.input, bytes):
|
|
87
|
+
record.input = self.mask_sensitive_msg(record.input)
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
def mask_sensitive_msg(self, message: bytes) -> bytes:
|
|
91
|
+
for pattern, replacement in self.patterns:
|
|
92
|
+
message = re.sub(pattern, replacement, message)
|
|
93
|
+
return message
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def compress_logger_name(name: str, length: int) -> str:
|
|
97
|
+
"""
|
|
98
|
+
Creates a short version of a logger name. For example ``my.very.long.logger.name`` with length=17 turns into
|
|
99
|
+
``m.v.l.logger.name``.
|
|
100
|
+
|
|
101
|
+
:param name: the logger name
|
|
102
|
+
:param length: the max length of the logger name
|
|
103
|
+
:return: the compressed name
|
|
104
|
+
"""
|
|
105
|
+
if len(name) <= length:
|
|
106
|
+
return name
|
|
107
|
+
|
|
108
|
+
parts = name.split(".")
|
|
109
|
+
parts.reverse()
|
|
110
|
+
|
|
111
|
+
new_parts = []
|
|
112
|
+
|
|
113
|
+
# we start by assuming that all parts are collapsed
|
|
114
|
+
# x.x.x requires 5 = 2n - 1 characters
|
|
115
|
+
cur_length = (len(parts) * 2) - 1
|
|
116
|
+
|
|
117
|
+
for i in range(len(parts)):
|
|
118
|
+
# try to expand the current part and calculate the resulting length
|
|
119
|
+
part = parts[i]
|
|
120
|
+
next_len = cur_length + (len(part) - 1)
|
|
121
|
+
|
|
122
|
+
if next_len > length:
|
|
123
|
+
# if the resulting length would exceed the limit, add only the first letter of the parts of all remaining
|
|
124
|
+
# parts
|
|
125
|
+
new_parts += [p[0] for p in parts[i:]]
|
|
126
|
+
|
|
127
|
+
# but if this is the first item, that means we would display nothing, so at least display as much of the
|
|
128
|
+
# max length as possible
|
|
129
|
+
if i == 0:
|
|
130
|
+
remaining = length - cur_length
|
|
131
|
+
if remaining > 0:
|
|
132
|
+
new_parts[0] = part[: (remaining + 1)]
|
|
133
|
+
|
|
134
|
+
break
|
|
135
|
+
|
|
136
|
+
# expanding the current part, i.e., instead of using just the one character, we add the entire part
|
|
137
|
+
new_parts.append(part)
|
|
138
|
+
cur_length = next_len
|
|
139
|
+
|
|
140
|
+
new_parts.reverse()
|
|
141
|
+
return ".".join(new_parts)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class TraceLoggingFormatter(logging.Formatter):
|
|
145
|
+
aws_trace_log_format = "; ".join([LOG_FORMAT, LOG_INPUT_FORMAT, LOG_OUTPUT_FORMAT])
|
|
146
|
+
bytes_length_display_threshold = 512
|
|
147
|
+
|
|
148
|
+
def __init__(self):
|
|
149
|
+
super().__init__(fmt=self.aws_trace_log_format, datefmt=LOG_DATE_FORMAT)
|
|
150
|
+
|
|
151
|
+
def _replace_large_payloads(self, input: Any) -> Any:
|
|
152
|
+
"""
|
|
153
|
+
Replaces large payloads in the logs with placeholders to avoid cluttering the logs with huge bytes payloads.
|
|
154
|
+
:param input: Input/output extra passed when logging. If it is bytes, it will be replaced if larger than
|
|
155
|
+
bytes_length_display_threshold
|
|
156
|
+
:return: Input, unless it is bytes and longer than bytes_length_display_threshold, then `Bytes(length_of_input)`
|
|
157
|
+
"""
|
|
158
|
+
if isinstance(input, bytes) and len(input) > self.bytes_length_display_threshold:
|
|
159
|
+
return f"Bytes({format_bytes(len(input))})"
|
|
160
|
+
return input
|
|
161
|
+
|
|
162
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
163
|
+
record.input = self._replace_large_payloads(record.input)
|
|
164
|
+
record.output = self._replace_large_payloads(record.output)
|
|
165
|
+
return super().format(record=record)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class AwsTraceLoggingFormatter(TraceLoggingFormatter):
|
|
169
|
+
aws_trace_log_format = "; ".join(
|
|
170
|
+
[LOG_FORMAT, LOG_CONTEXT_FORMAT, LOG_INPUT_FORMAT, LOG_OUTPUT_FORMAT]
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def __init__(self):
|
|
174
|
+
super().__init__()
|
|
175
|
+
|
|
176
|
+
def _copy_service_dict(self, service_dict: dict) -> dict:
|
|
177
|
+
if not isinstance(service_dict, dict):
|
|
178
|
+
return service_dict
|
|
179
|
+
result = {}
|
|
180
|
+
for key, value in service_dict.items():
|
|
181
|
+
if isinstance(value, dict):
|
|
182
|
+
result[key] = self._copy_service_dict(value)
|
|
183
|
+
elif isinstance(value, bytes) and len(value) > self.bytes_length_display_threshold:
|
|
184
|
+
result[key] = f"Bytes({format_bytes(len(value))})"
|
|
185
|
+
elif isinstance(value, list):
|
|
186
|
+
result[key] = [self._copy_service_dict(item) for item in value]
|
|
187
|
+
else:
|
|
188
|
+
result[key] = value
|
|
189
|
+
return result
|
|
190
|
+
|
|
191
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
192
|
+
record.input = self._copy_service_dict(record.input)
|
|
193
|
+
record.output = self._copy_service_dict(record.output)
|
|
194
|
+
return super().format(record=record)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
from localstack_cli import config, constants
|
|
6
|
+
|
|
7
|
+
from ..utils.strings import key_value_pairs_to_dict
|
|
8
|
+
from .format import AddFormattedAttributes, DefaultFormatter
|
|
9
|
+
|
|
10
|
+
# The log levels for modules are evaluated incrementally for logging granularity,
|
|
11
|
+
# from highest (DEBUG) to lowest (TRACE_INTERNAL). Hence, each module below should have
|
|
12
|
+
# higher level which serves as the default.
|
|
13
|
+
|
|
14
|
+
default_log_levels = {
|
|
15
|
+
"asyncio": logging.INFO,
|
|
16
|
+
"boto3": logging.INFO,
|
|
17
|
+
"botocore": logging.ERROR,
|
|
18
|
+
"docker": logging.WARNING,
|
|
19
|
+
"elasticsearch": logging.ERROR,
|
|
20
|
+
"hpack": logging.ERROR,
|
|
21
|
+
"moto": logging.WARNING,
|
|
22
|
+
"requests": logging.WARNING,
|
|
23
|
+
"s3transfer": logging.INFO,
|
|
24
|
+
"urllib3": logging.WARNING,
|
|
25
|
+
"werkzeug": logging.WARNING,
|
|
26
|
+
"rolo": logging.WARNING,
|
|
27
|
+
"parse": logging.WARNING,
|
|
28
|
+
"localstack.aws.accounts": logging.INFO,
|
|
29
|
+
"localstack.aws.protocol.serializer": logging.INFO,
|
|
30
|
+
"localstack.aws.serving.wsgi": logging.WARNING,
|
|
31
|
+
"localstack.request": logging.INFO,
|
|
32
|
+
"localstack.request.internal": logging.WARNING,
|
|
33
|
+
"localstack.state.inspect": logging.INFO,
|
|
34
|
+
"localstack_persistence": logging.INFO,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
trace_log_levels = {
|
|
38
|
+
"rolo": logging.DEBUG,
|
|
39
|
+
"localstack.aws.protocol.serializer": logging.DEBUG,
|
|
40
|
+
"localstack.aws.serving.wsgi": logging.DEBUG,
|
|
41
|
+
"localstack.request": logging.DEBUG,
|
|
42
|
+
"localstack.request.internal": logging.INFO,
|
|
43
|
+
"localstack.state.inspect": logging.DEBUG,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
trace_internal_log_levels = {
|
|
47
|
+
"localstack.aws.accounts": logging.DEBUG,
|
|
48
|
+
"localstack.request.internal": logging.DEBUG,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def setup_logging_for_cli(log_level=logging.INFO):
|
|
53
|
+
logging.basicConfig(level=log_level)
|
|
54
|
+
|
|
55
|
+
# set log levels of loggers
|
|
56
|
+
logging.root.setLevel(log_level)
|
|
57
|
+
logging.getLogger("localstack").setLevel(log_level)
|
|
58
|
+
for logger, level in default_log_levels.items():
|
|
59
|
+
logging.getLogger(logger).setLevel(level)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_log_level_from_config():
|
|
63
|
+
# overriding the log level if LS_LOG has been set
|
|
64
|
+
if config.LS_LOG:
|
|
65
|
+
log_level = str(config.LS_LOG).upper()
|
|
66
|
+
if log_level.lower() in constants.TRACE_LOG_LEVELS:
|
|
67
|
+
log_level = "DEBUG"
|
|
68
|
+
log_level = logging._nameToLevel[log_level]
|
|
69
|
+
return log_level
|
|
70
|
+
|
|
71
|
+
return logging.DEBUG if config.DEBUG else logging.INFO
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def setup_logging_from_config():
|
|
75
|
+
log_level = get_log_level_from_config()
|
|
76
|
+
setup_logging(log_level)
|
|
77
|
+
|
|
78
|
+
if config.is_trace_logging_enabled():
|
|
79
|
+
for name, level in trace_log_levels.items():
|
|
80
|
+
logging.getLogger(name).setLevel(level)
|
|
81
|
+
if config.LS_LOG == constants.LS_LOG_TRACE_INTERNAL:
|
|
82
|
+
for name, level in trace_internal_log_levels.items():
|
|
83
|
+
logging.getLogger(name).setLevel(level)
|
|
84
|
+
|
|
85
|
+
raw_logging_override = config.LOG_LEVEL_OVERRIDES
|
|
86
|
+
if raw_logging_override:
|
|
87
|
+
logging_overrides = key_value_pairs_to_dict(raw_logging_override)
|
|
88
|
+
for logger, level_name in logging_overrides.items():
|
|
89
|
+
level = getattr(logging, level_name, None)
|
|
90
|
+
if not level:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"Failed to configure logging overrides ({raw_logging_override}): '{level_name}' is not a valid log level"
|
|
93
|
+
)
|
|
94
|
+
logging.getLogger(logger).setLevel(level)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def create_default_handler(log_level: int):
|
|
98
|
+
log_handler = logging.StreamHandler(stream=sys.stderr)
|
|
99
|
+
log_handler.setLevel(log_level)
|
|
100
|
+
log_handler.setFormatter(DefaultFormatter())
|
|
101
|
+
log_handler.addFilter(AddFormattedAttributes())
|
|
102
|
+
return log_handler
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def setup_logging(log_level=logging.INFO) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Configures the python logging environment for LocalStack.
|
|
108
|
+
|
|
109
|
+
:param log_level: the optional log level.
|
|
110
|
+
"""
|
|
111
|
+
# set create a default handler for the root logger (basically logging.basicConfig but explicit)
|
|
112
|
+
log_handler = create_default_handler(log_level)
|
|
113
|
+
|
|
114
|
+
# replace any existing handlers
|
|
115
|
+
logging.basicConfig(level=log_level, handlers=[log_handler])
|
|
116
|
+
|
|
117
|
+
# disable some logs and warnings
|
|
118
|
+
warnings.filterwarnings("ignore")
|
|
119
|
+
logging.captureWarnings(True)
|
|
120
|
+
|
|
121
|
+
# set log levels of loggers
|
|
122
|
+
logging.root.setLevel(log_level)
|
|
123
|
+
logging.getLogger("localstack").setLevel(log_level)
|
|
124
|
+
for logger, level in default_log_levels.items():
|
|
125
|
+
logging.getLogger(logger).setLevel(level)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def setup_hypercorn_logger(hypercorn_config) -> None:
|
|
129
|
+
"""
|
|
130
|
+
Sets the hypercorn loggers, which are created in a peculiar way, to the localstack settings.
|
|
131
|
+
|
|
132
|
+
:param hypercorn_config: a hypercorn.Config object
|
|
133
|
+
"""
|
|
134
|
+
logger = hypercorn_config.log.access_logger
|
|
135
|
+
if logger:
|
|
136
|
+
logger.handlers[0].addFilter(AddFormattedAttributes())
|
|
137
|
+
logger.handlers[0].setFormatter(DefaultFormatter())
|
|
138
|
+
|
|
139
|
+
logger = hypercorn_config.log.error_logger
|
|
140
|
+
if logger:
|
|
141
|
+
logger.handlers[0].addFilter(AddFormattedAttributes())
|
|
142
|
+
logger.handlers[0].setFormatter(DefaultFormatter())
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .api import (
|
|
2
|
+
InstallTarget,
|
|
3
|
+
NoSuchVersionException,
|
|
4
|
+
Package,
|
|
5
|
+
PackageException,
|
|
6
|
+
PackageInstaller,
|
|
7
|
+
PackagesPlugin,
|
|
8
|
+
package,
|
|
9
|
+
packages,
|
|
10
|
+
)
|
|
11
|
+
from .core import DownloadInstaller, GitHubReleaseInstaller, SystemNotSupportedException
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Package",
|
|
15
|
+
"PackageInstaller",
|
|
16
|
+
"GitHubReleaseInstaller",
|
|
17
|
+
"DownloadInstaller",
|
|
18
|
+
"InstallTarget",
|
|
19
|
+
"PackageException",
|
|
20
|
+
"NoSuchVersionException",
|
|
21
|
+
"SystemNotSupportedException",
|
|
22
|
+
"PackagesPlugin",
|
|
23
|
+
"package",
|
|
24
|
+
"packages",
|
|
25
|
+
]
|