Skip to content

[FEATURE] #21

@mzpqnxow

Description

@mzpqnxow

Big caveat: I'm on mobile

Runtime templating of YaML file to complement expand/environment; possible addition of include directive

I would like feedback as to whether you think these ideas have a place as enhancements or if it should be a separate derivative work. They start to stray far outside being "only" an enhanced abstraction around DictConfig

The ideas, roughly- you could define variables in one location in a special block in the YaML file and then reference them as variables elsewhere in the file. It would require two passes to fully load the YaML, or at least that's how I've done it

I've also been considering implementing an include directive, so multiple logging configurations could reference a common one (e.g. a separate formatters.yml file)

Implementation

I have several projects that have a YaML loader that behaves roughly like this:

  1. Create a dict with some commonly used dynamic values (e.g. an application name, machine hostname or FQDN, OS family, YYYYMMDD value, etc. Example: {"yyyymmdd": datetime.datetime.now().strftime("%Y%m%d"), "fqdn": socket.getfqdn(), "hostname": socket.gethostname, ...}
  2. Load YaML file
  3. Pull out one specific section of the file (a dict, usually named globals or variables, usually defined at the top of the YaML file)
  4. Apply Jinja2 (or manual) templating over the YaML logging config data (now existing as a dict in Python)

The data provided to the templating engine in # 4 is the preprepared mapping I mentioned in # 1 as well as the k/v pairs from the file itself I mentioned in # 3

This way things like the hostname or OS family are exposed to the logging config sections, even when only using the "safe" YaML loader. You can get the hostname, for example, without running code from the YaML file, using the string {{ hostname }} as part of any YaML string value

Use-Case: Templating

Here's a pseudo-code example. I'm on mobile so bear with me :)

variables:
    app: myapp
    revision: 1.2a
    full_app: '{{ app }}-{{ revision }}'
    ...
formatters:
    ....

handlers:
    ....
    somehandler:
        filename: '{{ full_app }}.log'
    ....
...

This is obviously a very contrived example, and in most deployments could be done via environment variables, so you'll have to be imaginative about what other things this could facilitate

Use-Case: Include Files

Another example of what I'm thinking about doing is implementing a standard include feature- where the loggers, formatters, or any other section would support including configuration info from a logging.d/ directory (or wherever)

This would be a much heavier and invasive change than the simple templating. It could also be done in several ways, with or without a templating engine like Jinja2

Here's my specific use-case; I always have the same formatters section in all of my apps, but the other sections may be very app-specific. In practice this means when I change one, I usually want to change all of them. That causes two pain points:

  1. Redundant boilerplate in each app's logger YaML
  2. I have to remember to sync the YaML in each project when I make enhancements/changes in one, as I want the formatting to be consistent; this is the case especially for structured loggers and loggers sending to a SOC via something like syslog, where a consistent format is important

It would be nice to reference one common file from the logging YaML of several apps. In my pattern, I install the apps to a venv. Data files go to a venv etc directory, the path being dependable upon the project and app name

I use this pattern for package naming- I'm big into name-spacing for several reasons I won't get into ;)

Visually, package (well, egg/repo) name:

<myorg>.<myproj>.<appname>

Within each app, I generally have a data directory that is installed by setuptools into $VIRTUAL_ENV/etc/<myproj>/<appname>/

For data files that span multiple apps within the same project, I put them in $VIRTUAL_ENV/etc/<myproj>/. It's nice and orderly :)

I would like to use something like this with logging-extras:

$VIRTUAL_ENV/
    etc/
        <myorg>/
            <myproj>/
                common-formatters.yml
                common-handlers.yml
                <app1>/
                    loggers.yml
                <app2>/
                    loggers.yml

Notes

  • I would only implement this using "safe" YaML loaders and "safe" Jinja2 loaders. I don't want to make data into code
  • The first use-case (simple templating of the value portions within the YaML file) is probably easiest to implement with something like Jinja2, but there are other approaches I'm sure
  • The second use-case (an include mechanism) could be probably also be done with the assistance of Jinja2 but there may be a simpler way to do it. Maybe there's even a way native to YaML/PyYAML that I'm not aware of. I've not read any of the YaML specs and have used only the simplest interfaces of PyYaML

This isn't really a concrete plan or proposal. I think what I'm interested in knowing is whether you think features like this would be too creeping for your vision of logging-extras to send as a PR

If they are beyond what you'd like, I'm OK with forking and creating a derivative work based on logging-extras, with the necessary credit/license but with a different name to avoid confusion. I don't thing logging-extras-extras would be a good name :)

One way would be to implement it as an extras in setuptools (pip install logging-extras[templating]?) but that would still pollute your code base

If you think this direction is somewhere you'd like to go, I can work on it here with your input as a 'v2' (or just plain old 'dev') branch, whichever you preferred

Either way, thanks again, I've really simplified my logging with the YaML support your project provides, and the environment var and shell expansion have been key as well. It's made a huge difference for me and I appreciate the work you put into it. Also, despite what my ideas may suggest, I really appreciate how simple you kept the implementation- there's certainly tremendous value in keeping it simple and with a well-defined goal/vision

Any feedback you have the time to provide is appreciated. I already have implemented some of this separately but I won't send any PRs unless you're interested

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions