Skip to content
Open
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
190 changes: 165 additions & 25 deletions podman_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -3215,7 +3215,22 @@ async def compose_up(compose: PodmanCompose, args: argparse.Namespace) -> int |

max_service_length = 0
for cnt in compose.containers:
curr_length = len(cnt["_service"])
if cnt["_service"] in excluded:
continue

service_name = cnt["_service"]
container_name = cnt["name"]

if getattr(args, 'names', False):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate code

expected_name = compose.format_name(service_name, str(cnt["num"]))
if container_name == expected_name:
display_name = compose.join_name_parts(service_name, str(cnt["num"]))
else:
display_name = container_name
else:
display_name = container_name

curr_length = len(display_name)
max_service_length = curr_length if curr_length > max_service_length else max_service_length

tasks: set[asyncio.Task] = set()
Expand All @@ -3237,11 +3252,24 @@ async def handle_sigint() -> None:
loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(handle_sigint()))

for i, cnt in enumerate(compose.containers):
# Add colored service prefix to output by piping output through sed
color_idx = i % len(compose.console_colors)
color = compose.console_colors[color_idx]
space_suffix = " " * (max_service_length - len(cnt["_service"]) + 1)
log_formatter = "{}[{}]{}|\x1b[0m".format(color, cnt["_service"], space_suffix)

service_name = cnt["_service"]
container_name = cnt["name"]

if getattr(args, 'names', False):
expected_name = compose.format_name(service_name, str(cnt["num"]))
if container_name == expected_name:
display_name = compose.join_name_parts(service_name, str(cnt["num"]))
else:
display_name = container_name
else:
display_name = container_name

space_suffix = " " * (max_service_length + 1 - len(display_name))
log_formatter = "{}{}{}|\x1b[0m".format(color, display_name, space_suffix)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate code, including space_suffix.


if cnt["_service"] in excluded:
log.debug("** skipping: %s", cnt["name"])
continue
Expand Down Expand Up @@ -3596,29 +3624,135 @@ async def compose_logs(compose: PodmanCompose, args: argparse.Namespace) -> None
if not args.services and not args.latest:
args.services = container_names_by_service.keys()
compose.assert_services(args.services)

targets = []
service_by_container = {}

for service in args.services:
targets.extend(container_names_by_service[service])
podman_args = []
if args.follow:
podman_args.append("-f")
if args.latest:
podman_args.append("-l")
if args.names:
podman_args.append("-n")
if args.since:
podman_args.extend(["--since", args.since])
# the default value is to print all logs which is in podman = 0 and not
# needed to be passed
if args.tail and args.tail != "all":
podman_args.extend(["--tail", args.tail])
if args.timestamps:
podman_args.append("-t")
if args.until:
podman_args.extend(["--until", args.until])
for target in targets:
podman_args.append(target)
await compose.podman.run([], "logs", podman_args)
containers = container_names_by_service[service]
targets.extend(containers)
for container in containers:
service_by_container[container] = service

should_use_colors = (
(len(args.services) > 1 or args.names)
and not args.latest
and sys.stdout.isatty()
and not getattr(args, "no_color", False)
)

if should_use_colors:
max_service_length = 0
for target in targets:
cnt = compose.container_by_name[target]
service_name = cnt["_service"]
container_name = cnt["name"]

if getattr(args, 'names', False):
expected_name = compose.format_name(service_name, str(cnt["num"]))

if container_name == expected_name:
display_name = compose.join_name_parts(service_name, str(cnt["num"]))
else:
display_name = container_name
else:
display_name = container_name

curr_length = len(display_name)
max_service_length = (
curr_length if curr_length > max_service_length else max_service_length
)

tasks = []
service_colors: dict[str, str] = {}

for target in targets:
cnt = compose.container_by_name[target]
service_name = cnt["_service"]
container_name = cnt["name"]

if getattr(args, 'names', False):
expected_name = compose.format_name(service_name, str(cnt["num"]))
if container_name == expected_name:
display_name = compose.join_name_parts(service_name, str(cnt["num"]))
else:
display_name = container_name
else:
display_name = container_name

if service_name not in service_colors:
color_idx = len(service_colors) % len(compose.console_colors)
service_colors[service_name] = compose.console_colors[color_idx]

color = service_colors[service_name]

space_suffix = " " * (max_service_length + 1 - len(display_name))
log_formatter = "{}{}{}|\x1b[0m".format(color, display_name, space_suffix)

podman_args = []
if args.follow:
podman_args.append("-f")
if args.names:
podman_args.append("-n")
if args.since:
podman_args.extend(["--since", args.since])
if args.tail and args.tail != "all":
podman_args.extend(["--tail", args.tail])
if args.timestamps:
podman_args.append("-t")
if args.until:
podman_args.extend(["--until", args.until])
podman_args.append(target)

task = asyncio.create_task(
compose.podman.run([], "logs", podman_args, log_formatter=log_formatter),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future improvement (not this PR): run only single podman logs command and improve the formatter to be able to apply different formatting depending on container ID that podman logs emits on every line.

name=f"logs-{service_name}-{target}",
)
tasks.append(task)

async def handle_sigint() -> None:
log.info("Caught SIGINT or Ctrl+C, stopping log streaming...")
for task in tasks:
if not task.done():
task.cancel()

if sys.platform != 'win32':
loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(handle_sigint()))

try:
await asyncio.gather(*tasks)
except KeyboardInterrupt:
for task in tasks:
if not task.done():
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
except Exception as e:
log.error("Error in logs command: %s", e)
for task in tasks:
if not task.done():
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
raise
else:
podman_args = []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like duplicate code now.

if args.follow:
podman_args.append("-f")
if args.latest:
podman_args.append("-l")
if args.names:
podman_args.append("-n")
if args.since:
podman_args.extend(["--since", args.since])
if args.tail and args.tail != "all":
podman_args.extend(["--tail", args.tail])
if args.timestamps:
podman_args.append("-t")
if args.until:
podman_args.extend(["--until", args.until])
for target in targets:
podman_args.append(target)
await compose.podman.run([], "logs", podman_args)


@cmd_run(podman_compose, "config", "displays the compose file")
Expand Down Expand Up @@ -3877,6 +4011,12 @@ def compose_up_parse(parser: argparse.ArgumentParser) -> None:
help="Return the exit code of the selected service container. "
"Implies --abort-on-container-exit.",
)
parser.add_argument(
"-n",
"--names",
action="store_true",
help="Show short service names instead of full container names in logs",
)


@cmd_parse(podman_compose, "down")
Expand Down