Skip to content

feat: Provide an option to change the juju-log formatting#1881

Closed
addyess wants to merge 1 commit intocanonical:mainfrom
addyess:issue/1880/slightly-improved-logger
Closed

feat: Provide an option to change the juju-log formatting#1881
addyess wants to merge 1 commit intocanonical:mainfrom
addyess:issue/1880/slightly-improved-logger

Conversation

@addyess
Copy link
Copy Markdown

@addyess addyess commented Jul 2, 2025

Addresses #1880

Provides the charmer to adjust the formatter to something more pleasant without forcing it on others.

Usage

class MyCharm(ops.CharmBase):
    def __init__(self, *args):
        ops.log.JujuLogHandler.set_formatting(ops.log.JujuLogHandler.VERBOSE_FORMATTING)

@addyess
Copy link
Copy Markdown
Author

addyess commented Jul 2, 2025

Thanks for a look @benhoyt or @dimaqq

@addyess addyess changed the title Provide an option to change the juju-log formatting feat: Provide an option to change the juju-log formatting Jul 2, 2025
@dimaqq
Copy link
Copy Markdown
Contributor

dimaqq commented Jul 3, 2025

Ben is on vacation, so let's ping @tonyandrewmeyer instead.

Copy link
Copy Markdown
Contributor

@dimaqq dimaqq left a comment

Choose a reason for hiding this comment

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

Please describe why a different format is needed?
What kind of format is needed?
Are only DEFAULT and VERBOSE going to be used, or do you envision a very custom format?

How would a given charm decide when to toggle the format?
Do you expect every app in a deployment to use the same format, or are disparate formats good enough?

Considering all the other ops users, I feel that there needs to be a good reason to tweak the format:

  • today we don't make any promised on the exact format
  • with this change, we'd on the hook to support the verbose format forever?

Comment thread ops/log.py
"""A handler for sending logs and warnings to Juju via juju-log."""

DEFAULT_FORMATTING = None
VERBOSE_FORMATTING = '%(name)s:%(lineno)-5d %(message)s'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hot take with a dash of sarcasm: expect less pretty output at 100K LOC?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also docstring is needed under the public constant.

Comment thread ops/log.py
root_logger = logging.getLogger()
for handler in root_logger.handlers:
if isinstance(handler, cls):
handler.setFormatter(logging.Formatter(format or cls.DEFAULT_FORMATTING))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm a little confused here. DEAULT is None, so why format or DEFAULT?
Is this to escape user supplying an empty string?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, an empty string would render the logs silent. Every message would be:

- unit-my-charm-0: 13:05:35 INFO unit.my-charm/0.juju-log something the charm wishes to log
+ unit-my-charm-0: 13:05:35 INFO unit.my-charm/0.juju-log

Comment thread test/test_log.py
method(message)
calls = backend.calls(clear=True)
level, message = result
expected = re.compile(rf'{logger.name}:\d+\s+{message}')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

While I like the test, I would prefer a test vector.
Like the exact log line produced with default and verbose formats.

If that's somehow hard (e.g. time), then I'd like to see examples in a comment.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

i think i didn't do precisely that because the line-number of the call to line 108

You end up with a log message that looks something like this.

test_log:108   critical

Comment thread ops/log.py
Comment on lines +34 to +36
@classmethod
def set_formatting(cls, format: str | None = DEFAULT_FORMATTING):
"""Adjust the log format output."""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Better docstring is needed for a public function.

Comment thread ops/log.py
Comment on lines +38 to +40
for handler in root_logger.handlers:
if isinstance(handler, cls):
handler.setFormatter(logging.Formatter(format or cls.DEFAULT_FORMATTING))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not a fan of this loop. Can this be better?

Also, it seems that early logging would use the default format and only later the format would be switched. Am I reading this right?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Also, it seems that early logging would use the default format and only later the format would be switched. Am I reading this right?

I'm not a big fan of this either, although charms could obviously switch up the formatting at any point anyway.

I think perhaps we ought to be consistently using getLogger(__name__) in the ops modules (e.g. we do in charm and model, we don't in storage), and we should have a consistent format for those, which means we'd have a handler that is specific for those. We could then leave the root logger for charms to use and they can configure the root logger's (juju-log) handler's formatter to be whatever they want.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It's fine... at least the PR gets ideas rolling. I''m mostly just currently bummed by not having the following in my logs: %(name)s and %(lineno)s. If you want to change anything around underneath -- fine with me. It's just tough at the moment to not have these two items in my logs.

Here's a sample from the k8s charm with and without it

- unit-k8s-0: 13:05:35 INFO unit.k8s/0.juju-log k8s/0(juju-04ff10-noble-0) current cluster-name=k8s-bd08d4f998c4e658c2c4139659f824ac
- unit-k8s-0: 13:06:24 INFO unit.k8s/0.juju-log Feature 'load-balancer' enabled=False,deployed=False,ver=v0.14.9-ck0,updated_at=2025-07-01 22:01:42+00:00: disabled
+ unit-k8s-0: 13:05:35 INFO unit.k8s/0.juju-log __main__:384   k8s/0(juju-04ff10-noble-0) current cluster-name=k8s-bd08d4f998c4e658c2c4139659f824ac
+ unit-k8s-0: 13:06:24 INFO unit.k8s/0.juju-log events.update_status:128   Feature 'load-balancer' enabled=False,deployed=False,ver=v0.14.9-ck0,updated_at=2025-07-01 22:01:42+00:00: disabled

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

all the stuff juju-log puts out front is nice i guess.

  • I like the timestamp,
  • I like the level.

I don't know why the unit name is in there two times? unit-k8s-0 and unit-k8s/0.juju-log. It feels cluttered
But ops can't change any of that...

you have control over everything past juju-log here -- lets make the most of that real-estate

@addyess addyess closed this Jul 3, 2025
@addyess
Copy link
Copy Markdown
Author

addyess commented Jul 3, 2025

Closed based on the comments on the issue leaning towards a more unified solution rather than quick-fix of my very mundane problem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants