Another potential option I just remembered is to adopt the plugin system as it exists to define a new elementType value. An example of something that has only half-way integrated this way are the various converter keys for pattern layout.
As for a tree structure, I mean that the plugin system is mostly structured around transforming a tree of nodes (originally XML data, but it's any configuration format) into the runtime graph of objects. As such, it's almost like Spring's XML minus the ability to reference the beans you instantiate other than in specific use cases like AppenderRef. I think this limitation has led to a bit of a mess over time with a weird in-between state where some objects might want to be configurable or extensible in ways that don't fit this original pattern. On the dependency-free side, that's the idea behind the rewrite I started. I think part of what lost momentum was trying to incorporate support for the existing plugin system syntax while including it into a broader and more consistent DI system. The goal of that DI system was to be as minimal as necessary for our use case so that we don't need any dependencies or unnecessary features. I had initially looked into how to potentially generate code from the annotation processor rather than probing objects at runtime via reflection, though I was still merging the models together before I could experiment with that idea. For some help, if you're familiar with how CDI works, perhaps you can help figure out a way to model the LoggerContext as a sort of scoped context similar to session-scope and request-scope. I believe this was one of the key integration points I was trying to reach before getting derailed. My previous work is still in this branch, though I haven't merged back from master in a while: https://github.com/apache/logging-log4j2/tree/mean-bean-machine In particular, the API and SPI structure I was working on is here: https://github.com/apache/logging-log4j2/tree/mean-bean-machine/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api https://github.com/apache/logging-log4j2/tree/mean-bean-machine/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi And the reflection-based default implementation: https://github.com/apache/logging-log4j2/tree/mean-bean-machine/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults Note related to ServiceLoader that I haven't quite gotten to that point, but I'd like this to ideally only load bean metadata for things referenced in the config file so we can lazy-load classes to avoid long startup times. In fact, this entire bean system is detached from the rest of the codebase still as I was still working on defining the scoping rules for things. It's been a while since I was working on this code, but it's mostly based on extracting the core logic from Weld, OpenWebBeans, and Guice, following an API that's a sort of simplified hybrid of the two. On Fri, 29 Jan 2021 at 13:15, Volkan Yazıcı <volkan.yaz...@gmail.com> wrote: > > a) I understand the policy that core shouldn't have any dependencies. > b) Maybe `ServiceLoader`s? > c) I see your point and agree with that. But replacing `new > PluginManager(T.class).getPlugins()` with injection via `@PluginElement > Set<T> plugins` shouldn't be complicating the current system that much, I > guess. > > Classpath scanning is indeed a pain point regarding the start-up > performance and `PluginManager#getPlugins` is doing exactly that. > > By the way, an alternative approach for JsonTemplateLayout I had in my is > this: > > 1. Make `EventResolverFactories` and each individual `EventResolver` & > `EventResolverFactory` public. > 2. Add a > `JsonTemplateLayout.Builder#eventTemplateResolverFactoryMapProvider` field > of type `String` which is pointing to a method extending from > `Supplier<Map<String, TemplateResolverFactory<LogEvent, > EventResolverContext, ? extends TemplateResolver<LogEvent>>>` > 3. Set the default value of the `eventTemplateResolverFactoryMapProvider` > to `EventResolverFactories.RESOLVER_FACTORY_BY_NAME`. > > So that if need arises, users can introduce their own map providers. While > doing that, they can leverage the entire set of existing resolvers, or > inject custom ones in isolation. (Of course the actual necessary set of > plumbing that needs to be on our side is a little bit hairy than the 3 > bullets I have listed above.) > > On Fri, Jan 29, 2021 at 7:26 PM Ralph Goers <ralph.go...@dslextreme.com> > wrote: > > > My concern over this has always been that a) Log4j should not have any > > required dependencies (and in 3.0 should only require the java base > > module), b) The JDK itself doesn’t have dependency injection built in and > > c) creating a large dependency injection framework in Log4j itself seems > > like overkill. What was built to start with was the simplest thing I could > > do that would allow users to add their own components. The plugin system > > has been enhanced well beyond what I originally created but still isn’t > > overly complicated. We also need to avoid Class scanning during startup as > > much as possible as that has proven to be very slow. > > > > Ralph > > > > > On Jan 29, 2021, at 10:28 AM, Matt Sicker <boa...@gmail.com> wrote: > > > > > > That sounds like one of the main goals of the DI rewrite I started. To > > > give more context on the existing system works, the ability to inject > > > complex objects into plugins is primarily used for mapping nested > > > configurations. Basically, anything corresponding to an XML element > > > was injectable into other elements corresponding to the configuration > > > file structure. Not every component in Log4j is integrated into the > > > system yet, so there are some pain points like this in 2.x which can > > > be seen in some things being configurable via system properties while > > > others have custom plugin elements defined for users to define their > > > configuration inside. Some of these testability concerns were likely > > > missed because their underlying code was fast enough to not need > > > mocks. > > > > > > Now if you'd like to inject objects instantiated from some arbitrary > > > point similar to Spring/Guice/etc., that will require a larger > > > overhaul (likely by rebooting my mean-bean-machine branch at some > > > point), but if your needs are already structured in a tree, then > > > @PluginElement is the equivalent of @Inject, while @PluginFactory is > > > equivalent to @Produces. Depending on your use case, it could be > > > supportable with the existing plugin system, or it may require > > > revisiting the DI rewrite, especially if there's wider interest in > > > completing that. > > > > > > On Fri, 29 Jan 2021 at 06:56, Volkan Yazıcı <volkan.yaz...@gmail.com> > > wrote: > > >> > > >> Hello, > > >> > > >> I am doing some investigation on how to replace statically bound > > >> JsonTemplateLayout resolvers with plugins. For inspiration I was > > looking at > > >> PatternLayout and found the following: PatternLayout calls PatternParser > > >> ctor, which performs the following: > > >> > > >> final PluginManager manager = new PluginManager(converterKey); > > >> manager.collectPlugins(config == null ? null : > > config.getPluginPackages()); > > >> final Map<String, PluginType<?>> plugins = manager.getPlugins(); > > >> final Map<String, Class<PatternConverter>> converters = new > > >> LinkedHashMap<>(); > > >> > > >> for (final PluginType<?> type : plugins.values()) { > > >> try { > > >> @SuppressWarnings("unchecked") > > >> final Class<PatternConverter> clazz = (Class<PatternConverter>) > > >> type.getPluginClass(); > > >> if (filterClass != null && !filterClass.isAssignableFrom(clazz)) > > { > > >> continue; > > >> } > > >> > > >> > > >> This got me really puzzled. I was expecting matched plugins to be > > injected > > >> into the @PluginFactory ctor of PatternLayout as a parameter of type > > >> List<PatternConverter> like every other DI system out there. That is, > > >> > > >> @PluginFactory > > >> AwesomeLayout( > > >> @PluginConfiguration Configuration config, > > >> @PluginElement("converters") List<PatternConverter> converters) { > > >> // ... > > >> } > > >> > > >> > > >> I find usage of PluginManager::new an anti-pattern. For one, it is not > > >> possible to unit test Pattern{Layout,Parser} by injecting only an > > isolated > > >> set of converters. Second, it is not possible to mock PluginManager > > either. > > >> > > >> Is injection of plugins extending a certain interface/class as a > > >> @PluginFactory ctor parameter something we can introduce to the plugin > > >> system? > > >> > > >> Kind regards. > > > > > > > > >