Skip to content
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

DM-30996: Implement log4cxx appender which logs to Python logging #55

Merged
merged 13 commits into from Jul 12, 2021

Conversation

andy-slac
Copy link
Contributor

Adds new log4cxx Appender class that forwards everything to Python logging.

src/PyLogAppender.cc Outdated Show resolved Hide resolved
src/PyLogAppender.cc Outdated Show resolved Hide resolved
src/PyLogAppender.h Outdated Show resolved Hide resolved
};

/**
* Re-raise Python exception as C++ exception.
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure that we can't let this propagate through Python directly without catching it and possibly losing information? Seems like a shame otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not possible unfortunately. Usually C++ wrappers catch C++ exceptions and replace existing Python exception with contents of C++ exception message, so the only way is to save whatever info we could in C++ exception.

Copy link
Contributor

Choose a reason for hiding this comment

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

Even if we were to define an additional C++ exception type like PythonException and change pex_exceptions to detect it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is controlled by the code which wraps the C++ calls, catches C++ exceptions and converts them to Python (pybind11 or anything else). I'm not sure that different exception class can provide more control over that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried to look at that but it will take time for me to learn how it all works. If we want to add pex_exceptions as another dependency here maybe there is someone already knowledgeable at that who can look at new code. OTOH, most of the exceptions that I generate should not have Python traceback, so maybe it's fine to drop it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, it's fine to keep this as is.

src/PyLogAppender.cc Outdated Show resolved Hide resolved
doc/mainpage.dox Outdated Show resolved Hide resolved
doc/mainpage.dox Outdated Show resolved Hide resolved
src/PyObjectPtr.h Outdated Show resolved Hide resolved
src/PyObjectPtr.h Outdated Show resolved Hide resolved
src/PyObjectPtr.h Outdated Show resolved Hide resolved
andy-slac and others added 2 commits July 7, 2021 11:35
Co-authored-by: Kian-Tat Lim <ktl@slac.stanford.edu>
@andy-slac
Copy link
Contributor Author

@ktlim, I am messing around with all ideas and I have found issues that further complicate my already complicated plan.

I was hoping to make layout configuration optional and it almost worked that way, but unfortunately log4cxx generates an error message when layout is skipped in configuration:

    log4cxx: Could not find value for key log4j.appender.PyLog.layout

It is possible to suppress errors when parsing configuration but this also means missing all other possible errors in configuration. It looks like the only option for now is to drop support for layout and modifying message part of the LogRecord, and instead rely on LogRecord.MDC attribute and its rendering by logging formatters.

Another issue is the interaction between log4cxx and Python in MDC control/initialization, I'll need a bit more time to think about that.

@ktlim
Copy link
Contributor

ktlim commented Jul 8, 2021

The error doesn't have something to do with https://github.com/lsst/log/blob/accbc96b7321847d4fc4cb6631d63964bfd86104/src/PyLogAppender.cc#L220-222, does it?

@andy-slac
Copy link
Contributor Author

It does. I wanted to make layout optional but if I make requiresLayout return false then layout is never created even if it exists in configuration. Returning true does make layout optional, it prints this error message but continues to run without layout. I guess I need some other way to make a layout.

@andy-slac
Copy link
Contributor Author

@ktlim, I have re-implemented much of the appender code to make/update one MDC attribute of the LogRecord. I do not know how to implement that convenience method that you mentioned, the code that needs that method (in daf_butler today) is supposed to run without lsst.log being setup, and there is some additional per-logger logic there that does not fit that simple model. I guess we'll figure it out when we update daf_butler.

@andy-slac andy-slac requested a review from ktlim July 8, 2021 18:19
If LogRecord does not have `MDC` attribute already then create it as an
instance of a special `lsst.log.utils._MDC` dictionary class. If `MDC`
attribute is already there (added by record factory) then assume it is a
dictionary and update it with MDC contents. This allows adding MDC items
at Python level as well.
Copy link
Contributor

@ktlim ktlim left a comment

Choose a reason for hiding this comment

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

Looks great!

doc/mainpage.dox Outdated Show resolved Hide resolved
doc/mainpage.dox Outdated Show resolved Hide resolved
doc/mainpage.dox Outdated Show resolved Hide resolved
doc/mainpage.dox Outdated Show resolved Hide resolved
This is internal class used for better formatting of MDC in Python logging
output. It behaves like `defaultdict(str)` but overrides ``__str__`` and
``__repr__`` method to produce output better suited for logging records.
It also provides namespace-like access to
Copy link
Contributor

Choose a reason for hiding this comment

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

"too."? Or "to keys"? Or something else?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was obviously distracted by something important (squirrels or 🐱), will expand 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am actually inclined to drop attribute-like access and only keep indexing access. It may be convenient to say MDC.key instead of MDC[key] in format string but it does not work for keys that do not look like identifiers, and it sort of adds additional requirement on other implementations of MDC dictionary.

Copy link
Member

Choose a reason for hiding this comment

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

I'm fine with just doing **MDC and having the formatting find the right element. We can always add the .key later. In python logging I would probably add **MDC directly into the kwargs in the LoggingAdapter and pull them out of the LogRecord directly later on.

Copy link
Contributor

Choose a reason for hiding this comment

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

But that exposes you to potential attribute collision.

Copy link
Member

Choose a reason for hiding this comment

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

Yes. If someone sets MDC to lineno or something. People can indeed shoot themselves in the foot like that but why would they? I don't think python lets me use a format string of %(extra['LABEL'])s though so how would I ever include it in a logging format string?

Copy link
Contributor

Choose a reason for hiding this comment

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

Use style='{'.

Copy link
Member

Choose a reason for hiding this comment

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

Okay. Hmm. So we are okay with a LogFormatter using f-style but all the Loggers using %-style?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Loggers don't have an option for style (would be nice to have but probably hard to implement). Formatter style is set in the same place as format string so we should be OK if we do it consistently.

python/lsst/log/utils.py Outdated Show resolved Hide resolved
src/PyLogAppender.h Outdated Show resolved Hide resolved
doc/mainpage.dox Outdated Show resolved Hide resolved
andy-slac and others added 2 commits July 9, 2021 08:57
Co-authored-by: Kian-Tat Lim <ktl@slac.stanford.edu>
`_MDC` is renamed to `MDCDict` and moved to `lsst.log` to avoid circular
dependencies when implementing new `configure_pylog_MDC` method.
@andy-slac
Copy link
Contributor Author

OK, I think I resolved all comments now, will merge in ~1 hour unless there are objections.

New convenience method to configure forwarding is configure_pylog_MDC(level: str, MDC_class: type[dict] = MDCDcit) and MDCDict had to be moved from lsst.log.utils to lsst.log to avoid circular imports.

Previous location caused usual mess with dynamic libraries in Python, it
should work better in Python module. Both locations are probably not right
for that piece of code but I do not want to introduce another dynamic
library just for that class (and it also depends on Python module).
Just an additional protection, not really needed in this code, but may
be useful if someone else uses it.
@andy-slac andy-slac merged commit d2ef7b4 into master Jul 12, 2021
@andy-slac andy-slac deleted the tickets/DM-30996 branch July 12, 2021 16:05
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.

None yet

3 participants