-
Notifications
You must be signed in to change notification settings - Fork 537
feat(podman-logs): improve logs formatting and behavior like docker-c… #1269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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): | ||
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() | ||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
@@ -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), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Future improvement (not this PR): run only single |
||
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 = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") | ||
|
@@ -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") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate code