Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions samcli/commands/_utils/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from samcli.commands._utils.custom_options.option_nargs import OptionNargs
from samcli.commands._utils.template import get_template_artifacts_format
from samcli.lib.observability.util import OutputOption
from samcli.lib.utils.packagetype import ZIP, IMAGE

_TEMPLATE_OPTION_DEFAULT_VALUE = "template.[yaml|yml|json]"
Expand Down Expand Up @@ -369,12 +370,14 @@ def common_observability_click_options():
"become available. [Beta Feature] If in beta --tail without a --name will pull from all possible resources",
),
click.option(
"--unformatted",
"-u",
is_flag=True,
help="[Beta Feature] "
"Print events without any text formatting in JSON. This option might be useful if you are reading "
"output into another tool.",
"--output",
help="""
[Beta Feature]
The formatting style of the command output. Following options are available:\n
TEXT: Prints information as regular text with some formatting (default option)\n
JSON: Prints each line as JSON without formatting
""",
type=click.Choice(OutputOption.__members__, case_sensitive=False),
),
]

Expand Down
11 changes: 6 additions & 5 deletions samcli/commands/logs/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
@print_cmdline_args
@force_experimental_option("include_traces", config_entry=ExperimentalFlag.Accelerate) # pylint: disable=E1120
@force_experimental_option("cw_log_group", config_entry=ExperimentalFlag.Accelerate) # pylint: disable=E1120
@force_experimental_option("unformatted", config_entry=ExperimentalFlag.Accelerate) # pylint: disable=E1120
@force_experimental_option("output", config_entry=ExperimentalFlag.Accelerate) # pylint: disable=E1120
def cli(
ctx,
name,
Expand All @@ -96,7 +96,7 @@ def cli(
include_traces,
start_time,
end_time,
unformatted,
output,
cw_log_group,
config_file,
config_env,
Expand All @@ -115,7 +115,7 @@ def cli(
start_time,
end_time,
cw_log_group,
unformatted,
output,
ctx.region,
ctx.profile,
) # pragma: no cover
Expand All @@ -130,7 +130,7 @@ def do_cli(
start_time,
end_time,
cw_log_groups,
unformatted,
output,
region,
profile,
):
Expand All @@ -142,6 +142,7 @@ def do_cli(

from samcli.commands.logs.logs_context import parse_time, ResourcePhysicalIdResolver
from samcli.commands.logs.puller_factory import generate_puller
from samcli.lib.observability.util import OutputOption
from samcli.lib.utils.boto_utils import get_boto_client_provider_with_config, get_boto_resource_provider_with_config

if not names or len(names) > 1:
Expand All @@ -167,7 +168,7 @@ def do_cli(
resource_logical_id_resolver.get_resource_information(fetch_all_when_no_resource_name_given),
filter_pattern,
cw_log_groups,
unformatted,
OutputOption(output) if output else OutputOption.text,
include_tracing,
)

Expand Down
33 changes: 17 additions & 16 deletions samcli/commands/logs/puller_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
ObservabilityEventConsumer,
ObservabilityCombinedPuller,
)
from samcli.lib.observability.util import OutputOption
from samcli.lib.utils.boto_utils import BotoProviderType
from samcli.lib.utils.cloudformation import CloudFormationResourceSummary
from samcli.lib.utils.colors import Colored
Expand All @@ -43,7 +44,7 @@ def generate_puller(
resource_information_list: List[CloudFormationResourceSummary],
filter_pattern: Optional[str] = None,
additional_cw_log_groups: Optional[List[str]] = None,
unformatted: bool = False,
output: OutputOption = OutputOption.text,
include_tracing: bool = False,
) -> ObservabilityPuller:
"""
Expand All @@ -62,9 +63,9 @@ def generate_puller(
additional_cw_log_groups : Optional[str]
Optional list of additional CloudWatch log groups which will be used to fetch
log events from.
unformatted : bool
By default, logs and traces are printed with a format for terminal. If this option is provided, the events
will be printed unformatted in JSON.
output : OutputOption
Decides how the output will be presented in the console. It is been used to select correct consumer type
between (default) text consumer or json consumer
include_tracing: bool
A flag to include the xray traces log or not

Expand All @@ -87,7 +88,7 @@ def generate_puller(
LOG.debug("Can't find CloudWatch LogGroup name for resource (%s)", resource_information.logical_resource_id)
continue

consumer = generate_consumer(filter_pattern, unformatted, resource_information.logical_resource_id)
consumer = generate_consumer(filter_pattern, output, resource_information.logical_resource_id)
pullers.append(
CWLogPuller(
boto_client_provider("logs"),
Expand All @@ -99,7 +100,7 @@ def generate_puller(

# populate puller instances for the additional CloudWatch log groups
for cw_log_group in additional_cw_log_groups:
consumer = generate_consumer(filter_pattern, unformatted)
consumer = generate_consumer(filter_pattern, output)
pullers.append(
CWLogPuller(
boto_client_provider("logs"),
Expand All @@ -110,7 +111,7 @@ def generate_puller(

# if tracing flag is set, add the xray traces puller to fetch debug traces
if include_tracing:
trace_puller = generate_trace_puller(boto_client_provider("xray"), unformatted)
trace_puller = generate_trace_puller(boto_client_provider("xray"), output)
pullers.append(trace_puller)

# if no puller have been collected, raise an exception since there is nothing to pull
Expand All @@ -122,22 +123,22 @@ def generate_puller(


def generate_consumer(
filter_pattern: Optional[str] = None, unformatted: bool = False, resource_name: Optional[str] = None
filter_pattern: Optional[str] = None, output: OutputOption = OutputOption.text, resource_name: Optional[str] = None
):
"""
Generates consumer instance with the given variables.
If unformatted is True, then it will return consumer with formatters for just JSON.
If not, it will return console consumer
If output is JSON, then it will return consumer with formatters for just JSON.
Otherwise, it will return regular text console consumer
"""
if unformatted:
return generate_unformatted_consumer()
if output == OutputOption.json:
return generate_json_consumer()

return generate_console_consumer(filter_pattern)
return generate_text_consumer(filter_pattern)


def generate_unformatted_consumer() -> ObservabilityEventConsumer:
def generate_json_consumer() -> ObservabilityEventConsumer:
"""
Creates event consumer, which prints CW Log Events unformatted as JSON into terminal
Creates event consumer, which prints CW Log Events as JSON into terminal

Returns
-------
Expand All @@ -151,7 +152,7 @@ def generate_unformatted_consumer() -> ObservabilityEventConsumer:
)


def generate_console_consumer(filter_pattern: Optional[str]) -> ObservabilityEventConsumer:
def generate_text_consumer(filter_pattern: Optional[str]) -> ObservabilityEventConsumer:
"""
Creates a console event consumer, which is used to display events in the user's console

Expand Down
9 changes: 5 additions & 4 deletions samcli/commands/traces/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from samcli.cli.cli_config_file import configuration_option, TomlProvider
from samcli.cli.main import pass_context, common_options as cli_framework_options, aws_creds_options, print_cmdline_args
from samcli.commands._utils.options import common_observability_options
from samcli.lib.observability.util import OutputOption
from samcli.lib.telemetry.metric import track_command
from samcli.lib.utils.version_checker import check_newer_version
from samcli.commands._utils.experimental import ExperimentalFlag, force_experimental
Expand Down Expand Up @@ -41,17 +42,17 @@ def cli(
start_time,
end_time,
tail,
unformatted,
output,
config_file,
config_env,
):
"""
`sam traces` command entry point
"""
do_cli(trace_id, start_time, end_time, tail, unformatted, ctx.region)
do_cli(trace_id, start_time, end_time, tail, output, ctx.region)


def do_cli(trace_ids, start_time, end_time, tailing, unformatted, region):
def do_cli(trace_ids, start_time, end_time, tailing, output, region):
"""
Implementation of the ``cli`` method
"""
Expand All @@ -68,7 +69,7 @@ def do_cli(trace_ids, start_time, end_time, tailing, unformatted, region):
xray_client = boto3.client("xray", config=boto_config)

# generate puller depending on the parameters
puller = generate_trace_puller(xray_client, unformatted)
puller = generate_trace_puller(xray_client, OutputOption(output) if output else OutputOption.text)

if trace_ids:
puller.load_events(trace_ids)
Expand Down
37 changes: 19 additions & 18 deletions samcli/commands/traces/traces_puller_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ObservabilityEventConsumerDecorator,
ObservabilityCombinedPuller,
)
from samcli.lib.observability.util import OutputOption
from samcli.lib.observability.xray_traces.xray_event_mappers import (
XRayTraceConsoleMapper,
XRayServiceGraphConsoleMapper,
Expand All @@ -22,7 +23,7 @@

def generate_trace_puller(
xray_client: Any,
unformatted: bool = False,
output: OutputOption = OutputOption.text,
) -> ObservabilityPuller:
"""
Generates puller instance with correct consumer and/or mapper configuration
Expand All @@ -31,22 +32,22 @@ def generate_trace_puller(
----------
xray_client : Any
boto3 xray client to be used in XRayTracePuller instance
unformatted : bool
By default, logs and traces are printed with a format for terminal. If this option is provided, the events
will be printed unformatted in JSON.
output : OutputOption
Decides how the output will be presented in the console. It is been used to select correct consumer type
between (default) text consumer or json consumer

Returns
-------
Puller instance with desired configuration
"""
pullers: List[ObservabilityPuller] = []
pullers.append(XRayTracePuller(xray_client, generate_xray_event_consumer(unformatted)))
pullers.append(XRayServiceGraphPuller(xray_client, generate_xray_service_graph_consumer(unformatted)))
pullers.append(XRayTracePuller(xray_client, generate_xray_event_consumer(output)))
pullers.append(XRayServiceGraphPuller(xray_client, generate_xray_service_graph_consumer(output)))

return ObservabilityCombinedPuller(pullers)


def generate_unformatted_xray_event_consumer() -> ObservabilityEventConsumer:
def generate_json_xray_event_consumer() -> ObservabilityEventConsumer:
"""
Generates unformatted consumer, which will print XRay events unformatted JSON into terminal

Expand All @@ -68,18 +69,18 @@ def generate_xray_event_console_consumer() -> ObservabilityEventConsumer:
return ObservabilityEventConsumerDecorator([XRayTraceConsoleMapper()], XRayTraceConsoleConsumer())


def generate_xray_event_consumer(unformatted: bool = False) -> ObservabilityEventConsumer:
def generate_xray_event_consumer(output: OutputOption = OutputOption.text) -> ObservabilityEventConsumer:
"""
Generates consumer instance with the given variables.
If unformatted is True, then it will return consumer with formatters for just JSON.
If not, it will return console consumer
If output is JSON, then it will return consumer with formatters for just JSON.
Otherwise, it will return regular text console consumer
"""
if unformatted:
return generate_unformatted_xray_event_consumer()
if output == OutputOption.json:
return generate_json_xray_event_consumer()
return generate_xray_event_console_consumer()


def generate_unformatted_xray_service_graph_consumer() -> ObservabilityEventConsumer:
def generate_json_xray_service_graph_consumer() -> ObservabilityEventConsumer:
"""
Generates unformatted consumer, which will print XRay events unformatted JSON into terminal

Expand All @@ -101,12 +102,12 @@ def generate_xray_service_graph_console_consumer() -> ObservabilityEventConsumer
return ObservabilityEventConsumerDecorator([XRayServiceGraphConsoleMapper()], XRayTraceConsoleConsumer())


def generate_xray_service_graph_consumer(unformatted: bool = False) -> ObservabilityEventConsumer:
def generate_xray_service_graph_consumer(output: OutputOption = OutputOption.text) -> ObservabilityEventConsumer:
"""
Generates consumer instance with the given variables.
If unformatted is True, then it will return consumer with formatters for just JSON.
If not, it will return console consumer
If output is JSON, then it will return consumer with formatters for just JSON.
Otherwise, it will return regular text console consumer
"""
if unformatted:
return generate_unformatted_xray_service_graph_consumer()
if output == OutputOption.json:
return generate_json_xray_service_graph_consumer()
return generate_xray_service_graph_console_consumer()
13 changes: 13 additions & 0 deletions samcli/lib/observability/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""
Utility classes and methods for observability commands and functionality
"""
from enum import Enum


class OutputOption(Enum): # pragma: no cover
"""
Used to configure how output will be presented with observability commands
"""

text = "text" # default
json = "json"
35 changes: 9 additions & 26 deletions tests/unit/commands/logs/test_command.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import itertools
from unittest import TestCase
from unittest.mock import Mock, patch, call, ANY

from parameterized import parameterized

from samcli.commands.logs.command import do_cli
from samcli.lib.observability.util import OutputOption


@patch("samcli.commands._utils.experimental.is_experimental_enabled")
Expand All @@ -16,33 +18,13 @@ def setUp(self):
self.filter_pattern = "filter"
self.start_time = "start"
self.end_time = "end"
self.output_dir = "output_dir"
self.region = "region"
self.profile = "profile"

@parameterized.expand(
[
(
True,
False,
[],
),
(
False,
False,
[],
),
(
True,
False,
["cw_log_group"],
),
(
False,
False,
["cw_log_group", "cw_log_group2"],
),
]
itertools.product(
[True, False], [True, False], [[], ["cw_log_group"], ["cw_log_group", "cw_log_group2"]], ["text", "json"]
)
)
@patch("samcli.commands.logs.puller_factory.generate_puller")
@patch("samcli.commands.logs.logs_context.ResourcePhysicalIdResolver")
Expand All @@ -54,6 +36,7 @@ def test_logs_command(
tailing,
include_tracing,
cw_log_group,
output,
patched_boto_resource_provider,
patched_boto_client_provider,
patched_parse_time,
Expand Down Expand Up @@ -89,7 +72,7 @@ def test_logs_command(
self.start_time,
self.end_time,
cw_log_group,
self.output_dir,
output,
self.region,
self.profile,
)
Expand All @@ -116,8 +99,8 @@ def test_logs_command(
mocked_resource_information,
self.filter_pattern,
cw_log_group,
self.output_dir,
False,
OutputOption(output),
include_tracing,
)

if tailing:
Expand Down
Loading