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

Add support for JPMS layer/module filter for log events #3357

Open
ranger2025 opened this issue Jan 5, 2025 · 6 comments
Open

Add support for JPMS layer/module filter for log events #3357

ranger2025 opened this issue Jan 5, 2025 · 6 comments

Comments

@ranger2025
Copy link

ranger2025 commented Jan 5, 2025

I suggest to add possibility to filter log events by ModuleLayer and Module.

Every module (automatic and explicit) has a name, so, there shouldn't be any problems. But with layers situation is more difficult, as layers don't have names and there is DAG of layers. The only way to solve this problem is to create map layer -> name. I mean that log4j2 user must create this map and fill it with values.

It is important to note, that module with same name can exist in different layers, that's why specifying only module is not enough, if it is not boot layer.

@ppkarwasz
Copy link
Contributor

@ranger2025,

Filtering by module name

To filter by module name you can use the existing ScriptFilter. For example, using Groovy you can write:

<ScriptFilter>
  <Script language="groovy"><![CDATA[
    return "foo.bar.baz".equals(logEvent.getSource()?.moduleName)
  ]]></Script>
</ScriptFilter>

Most of the out-of-the-box filters contained in Log4j Core 2 are inherited from Log4j 1. New filters are unlikely to be added to the collection unless:

  • They prove to be useful to many users.
  • And they can not be implemented efficiently using scripts.

Could you explain more, why do you need to filter by module name?

Filtering by module layer

Could you explain the purpose of filtering by module layer?

My guess is that you have a multi-application runtime and you want to separate the logs from different applications. In this case you probably don't need filters, but something similar to the Jakarta EE log separation features:

  • Either you want to implement a ContextSelector that assigns separate logger contexts to separate module layers.
  • Or you want to implement a lookup to be able to tag each log event with the "name" of the module layer that generated it.

@ppkarwasz ppkarwasz added waiting-for-user More information is needed from the user and removed waiting-for-maintainer labels Jan 6, 2025
@ranger2025
Copy link
Author

ranger2025 commented Jan 6, 2025

@ppkarwasz,

Thank you very much for your help. I've understood how to get module name.

About layers. I have a multi layer program where every layer is a subsystem. And often I need to get events only for concrete subsystem. As these subsystems can be added/removed dynamically I need a solution to separate them using java code. I've checked the link you provided (thanks again), but still can' find a way to get a layer from LogEvent. Layers don't have names. So, I need to give name manually, however, to get the name of the layer, I need at least Module or Class instances, but I can get only class string name, that can't provide what I need.

Or I should implement my own selector, using classloader:

public class CustomClassLoaderContextSelector extends ClassLoaderContextSelector {
    @Override
    protected LoggerContext locateContext(String fqcn, ClassLoader loader, boolean currentContext, String configLocation) {
        System.out.println("LogEvent linked to ClassLoader: " + loader);
        return super.locateContext(fqcn, loader, currentContext, configLocation);
    }
}

@github-actions github-actions bot added waiting-for-maintainer and removed waiting-for-user More information is needed from the user labels Jan 6, 2025
@vy
Copy link
Member

vy commented Jan 8, 2025

still can' find a way to get a layer from LogEvent. Layers don't have names. So, I need to give name manually, however, to get the name of the layer, I need at least Module or Class instances, but I can get only class string name, that can't provide what I need.

@ranger2025, I don't know about module layers, but if what you're describing (i.e., having your hands on a Class/Module instance) is the only way to obtain the ModuleLayer so that you can filter, then I can assure you this will have a significant performance impact I doubt if you would want to pay for every single incoming log event.

I recently rewrote the extended exception pattern converter (i.e., %xEx) of Pattern Layout and there I needed to go from a StackTraceElement (containing class name, etc.) to a Class, and it felt like a very nasty hack unpleasant experience. I wouldn't recommend that route to anyone.

About layers. I have a multi layer program where every layer is a subsystem. And often I need to get events only for concrete subsystem. As these subsystems can be added/removed dynamically I need a solution to separate them using java code.

@ranger2025, the sophistication needed in your use case makes me question if there is a better alternative to your approach. Would you mind telling us a little bit more about your use case, please? Can you point us some other multi-layered (F/OSS?) applications we can investigate? Can you show us applications where the problem you describe (i.e., only allow logging from the concrete subsystem) exists?

@vy vy added waiting-for-user More information is needed from the user and removed waiting-for-maintainer labels Jan 8, 2025
@ranger2025
Copy link
Author

@vy, thank you very much for sharing this information.

I understand what you are talking about performance, but we plan to use it only for testing and debugging purposes. At least, now. However, it is necessary to find a solution for this problem, because module name without specifying the layer doesn't give a lot. I want to underline it, because it is very important.

We have an application with plugins where plugin are added/removed dynamically. For for this plugins we use ModuleLayers. So, we have the following layer configuration:

boot layer / main app
├── child layer 1 / plugin foo
│   ├── moduleA
│   ├── moduleC
│   └── moduleX
├── child layer 2 / plugin bar
│   ├── moduleS
│   ├── moduleV
│   └── moduleX
└
etc

As you see, in this configuration the same module is used by both plugins. So, when we want to get messages of the moduleX we definitely want to get them for getting messages of concrete plugin. That's why I say it is important to get not only module name, but also some ID of the layer.

If it is not possible to get layer instance, I can try to get it from ClassLoader, because when a new layer is created we can get its loaders. But I am not sure, how it will work with custom classloaders. Above I posted an example with ClassLoaderContextSelector but I am not sure that this is the way I should go. That's why I asked for help.

@github-actions github-actions bot added waiting-for-maintainer and removed waiting-for-user More information is needed from the user labels Jan 8, 2025
@ranger2025
Copy link
Author

I figured out the problem. It's not about log4j2. It is about JPMS. I've just opened an issue about adding name property to ModuleLayer. Let's see what jigsaw team will say.

@ranger2025
Copy link
Author

This is the link to the issue - https://bugs.openjdk.org/browse/JDK-8347336

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

No branches or pull requests

3 participants