[ 
https://issues.apache.org/jira/browse/LOG4J2-2803?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Matt Sicker updated LOG4J2-2803:
--------------------------------
    Description: 
h2. Context

The existing plugin system revolves around {{@Plugin}} annotations which group 
together configurable interfaces into categories. The category of a plugin 
typically determines the configuration and instantiation strategy for the 
plugin classes involved which leads to some ad hoc methods for different 
plugins to obtain or configure different aspects of the system. All classes 
that are included in the plugin system can be queried at runtime by 
constructing a {{PluginManager}} with the category name as its constructor 
argument. To date, there are a few plugin categories (case-insensitive):
 * {{Core}}: used for configuration elements in config files for general core 
plugins like appenders, filters, layouts, etc. These are provided values from 
parsed configuration nodes and can also obtain the {{Configuration}} instance 
to access other services.
 * {{Level}}: used for specifying custom log levels so that they can be 
instantiated early enough for configurations to make use of it.
 * {{ConfigurationFactory}}: used for parsing and validating configuration 
sources in some format into a configuration node tree which is transformed into 
a {{Configuration}}. Also useful for programmatic configuration.
 * {{Lookup}}: used for variable interpolation in strings via {{StrLookup}} 
classes.
 * {{TypeConverter}}: used for converting strings into other objects. Mostly 
useful for conversion of plugin attributes into common types along with use in 
certain core plugins such as database column mappers.
 * {{Converter}}: used for pattern layout conversion keys. These are used for 
formatting log event info into a layout.
 * {{Watcher}}: used for watching configuration sources for changes in order to 
allow for reconfiguration.

Then there are some system-wide plugin-like classes that can be specified via 
system properties including:
 * {{ClockFactory}}: controls {{Clock}} instances for obtaining the current 
time. Mostly useful in testing and scenarios where approximate time is more 
useful than exact time.
 * {{LogEventFactory}}: constructs {{LogEvent}} instances for a given log 
message and parameters. Useful for implementing different allocation strategies 
for log events.
 * {{MergeStrategy}}: used for composite configurations.
 * {{ContextSelector}}: used for obtaining a {{LoggerContext}} for an 
invocation context when initializing or obtaining {{Logger}} instances. By 
default, this maps contexts to class loaders, and additional strategies are 
available including async loggers, a singleton context, a JNDI-lookup-based 
context, and an OSGi bundle context.
 * {{LoggerContextFactory}}: used for binding a logging implementation to the 
Logging API.
 * {{ShutdownCallbackRegistry}}: used for registering a logging system cleanup 
shutdown callback in various systems. The default strategy hooks in to the 
{{Runtime}} shutdown threads API.
 * {{ContextDataInjector}}: used for adding initial context data into the 
{{ThreadContext}} of a {{LogEvent}}.

h2. Proposal

This system grew organically over time, and after identifying various 
duplicated plugin functionality around the codebase, the plugin system should 
be overhauled along the lines of a more standard {{@Inject}}-style dependency 
injection framework. By adopting more conventional DI patterns, this will 
remove the need for ad hoc plugin mechanisms in different subsystems, allow for 
more flexible declarations of plugins by specifying direct classes needed by 
plugins rather than doing manual lookups on the {{Configuration}} instance, 
allow for smarter initialization strategies to minimize startup time and 
resource usage based on configurations rather than eagerly loading plugins, 
make it simpler to write unit tests, and make it generally simpler to write and 
maintain plugins without requiring deep expertise of esoteric subsystems.

Taking inspiration from [CDI|https://docs.jboss.org/cdi/api/2.0/], 
[@Inject|https://docs.oracle.com/javaee/6/api/javax/inject/Inject.html], and 
[Guice|https://github.com/google/guice], the plugin system should be updated to 
a new API that is broken up into a few useful concepts:
 * Beans: beans are analogous to Java classes with additional metadata to 
enable lifecycle management and dependency injection. Unlike a normal Java 
class, a bean has injection points (like constructor arguments, methods, and 
fields) where other managed instances can be injected at runtime based on the 
currently configured beans. As these are managed, they can have post-construct 
and pre-destruct callbacks for further lifecycle customization.
 ** Scopes: scopes control where the instances are stored or to what lifecycle 
they're tied to. At minimum, this includes dependent-scoped (creates new 
instances for every injection point) and singleton-scoped (creates and 
maintains a single instance for all injection points). In general DI 
frameworks, other scopes include things like HTTP sessions and HTTP requests. 
For this framework, the {{ContextSelector}} configuration API corresponds to a 
{{Configuration}} scope.
 ** Qualifiers: qualifiers add metadata to a bean besides its type to match on 
for injection. Typical qualifiers are names. Plugin configuration attribute 
names would likely make a good match here as another example.
 * Injection points: in order to obtain injectable values into a bean instance, 
injection points control how these instances are bound to the bean. This is 
essentially the strategy for binding data to fields, constructors, and methods.
 * Providers: providers are analogous to plugin builders in that they specify 
how to create an instance of type {{T}} given some plugin builder class 
{{Builder<T> implements Provider<T>}}.
 * Producer and disposer methods: beans can specify producer and disposer 
methods as alternative ways to specify how to construct a bean instance. These 
are analogous to plugin factory methods.

Adopting such a plugin system based on beans, all the plugins that currently 
require either a {{Configuration}} or {{Node}} instance to do anything should 
be refactored to rely on proper dependency injection. This should be used to 
replace as many disparate dependency injection and configuration systems as 
possible to reduce the complexity required to implement plugins that rely on 
shared components.
h2. Additional Details

 

Beyond the dependency injection API itself, this epic is motivated by the 
following goals:
 * Simplify plugin classes that currently require manual method calls to 
{{Configuration}} or {{Node}}.
 * Unify the various ad hoc dependency injection for configuration-scoped or 
singleton-scoped classes (the latter which are overridden via system 
properties) including:
 ** {{StrSubstitutor}}
 ** {{ConfigurationScheduler}} (or scheduled tasks in general)
 ** {{ScriptManager}}
 ** {{WatchManager}}
 ** {{NanoClock}}
 ** {{ShutdownCallbackRegistry}}
 ** {{Clock}}
 ** {{ContextSelector}}
 ** {{ConfigurationFactory}}
 ** {{LogEventFactory}}
 ** {{ContextDataFactory}}
 * Make dependencies between classes more explicit via inversion of control 
which allows for easier testing and modularity.
 * Avoid the use of manual configuration handling at the API level of any 
plugins including basic string parsing (as currently supported through the 
{{TypeConverter}} API), class loading from strings, and ad hoc reflection. This 
relates to the changes made in LOG4J2-2621 and LOG4J2-1917.
 * Provide smart initialization logic to continue (or maintain) improvements to 
startup time and avoid loading plugins that aren't referenced in the loaded 
configuration.
 * Provide scopes for other ad hoc scopes/contexts used in various plugins such 
as HTTP request, HTTP session, servlet context, etc.

  was:
In Log4j 2.x, the plugin API provides several {{@Plugin}} annotations which are 
used for performing dependency injection of configuration-scoped data (plugin 
attributes, values) and other configuration-scoped plugins (plugin elements). 
While this API works well enough for a large majority of our use cases, the 
remaining injection points include the {{Node}} and {{Configuration}} 
corresponding to the currently parsed node and configuration respectively which 
are used to perform manual lookups and other glue code that would be more 
appropriately modeled through inversion of control with a more complete 
dependency injection API.

In Log4j 3.0, this epic introduces a new plugin system API inspired by 
[CDI|https://docs.jboss.org/cdi/api/2.0/], 
[@Inject|https://docs.oracle.com/javaee/6/api/javax/inject/Inject.html], and 
[Guice|https://github.com/google/guice] that maintains full backward 
compatibility with the annotations from 2.x so that plugins do not need to be 
needlessly rewritten for 3.x. This epic was initially developed as a result of 
work done in LOG4J2-2694.

Beyond the dependency injection API itself, this epic is motivated by the 
following goals:
 * Simplify plugin classes that currently require manual method calls to 
{{Configuration}} or {{Node}}.
 * Unify the various ad hoc dependency injection for configuration-scoped or 
singleton-scoped classes (the latter which are overridden via system 
properties) including:
 ** {{StrSubstitutor}}
 ** {{ConfigurationScheduler}} (or scheduled tasks in general)
 ** {{ScriptManager}}
 ** {{WatchManager}}
 ** {{NanoClock}}
 ** {{ShutdownCallbackRegistry}}
 ** {{Clock}}
 ** {{ContextSelector}}
 ** {{ConfigurationFactory}}{{}}
 ** {{LogEventFactory}}
 ** {{ContextDataFactory}}
 * Make dependencies between classes more explicit via inversion of control 
which allows for easier testing and modularity.
 * Avoid the use of manual configuration handling at the API level of any 
plugins including basic string parsing (as currently supported through the 
{{TypeConverter}} API), class loading from strings, and ad hoc reflection. This 
relates to the changes made in LOG4J2-2621 and LOG4J2-1917.
 * Provide smart initialization logic to continue (or maintain) improvements to 
startup time and avoid loading plugins that aren't referenced in the loaded 
configuration.
 * Provide scopes for other ad hoc scopes/contexts used in various plugins such 
as HTTP request, HTTP session, servlet context, etc.


> Create standardized scopes and dependency injection API
> -------------------------------------------------------
>
>                 Key: LOG4J2-2803
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-2803
>             Project: Log4j 2
>          Issue Type: Epic
>          Components: Configuration, Core, Plugins
>            Reporter: Matt Sicker
>            Assignee: Matt Sicker
>            Priority: Major
>             Fix For: 3.0.0
>
>
> h2. Context
> The existing plugin system revolves around {{@Plugin}} annotations which 
> group together configurable interfaces into categories. The category of a 
> plugin typically determines the configuration and instantiation strategy for 
> the plugin classes involved which leads to some ad hoc methods for different 
> plugins to obtain or configure different aspects of the system. All classes 
> that are included in the plugin system can be queried at runtime by 
> constructing a {{PluginManager}} with the category name as its constructor 
> argument. To date, there are a few plugin categories (case-insensitive):
>  * {{Core}}: used for configuration elements in config files for general core 
> plugins like appenders, filters, layouts, etc. These are provided values from 
> parsed configuration nodes and can also obtain the {{Configuration}} instance 
> to access other services.
>  * {{Level}}: used for specifying custom log levels so that they can be 
> instantiated early enough for configurations to make use of it.
>  * {{ConfigurationFactory}}: used for parsing and validating configuration 
> sources in some format into a configuration node tree which is transformed 
> into a {{Configuration}}. Also useful for programmatic configuration.
>  * {{Lookup}}: used for variable interpolation in strings via {{StrLookup}} 
> classes.
>  * {{TypeConverter}}: used for converting strings into other objects. Mostly 
> useful for conversion of plugin attributes into common types along with use 
> in certain core plugins such as database column mappers.
>  * {{Converter}}: used for pattern layout conversion keys. These are used for 
> formatting log event info into a layout.
>  * {{Watcher}}: used for watching configuration sources for changes in order 
> to allow for reconfiguration.
> Then there are some system-wide plugin-like classes that can be specified via 
> system properties including:
>  * {{ClockFactory}}: controls {{Clock}} instances for obtaining the current 
> time. Mostly useful in testing and scenarios where approximate time is more 
> useful than exact time.
>  * {{LogEventFactory}}: constructs {{LogEvent}} instances for a given log 
> message and parameters. Useful for implementing different allocation 
> strategies for log events.
>  * {{MergeStrategy}}: used for composite configurations.
>  * {{ContextSelector}}: used for obtaining a {{LoggerContext}} for an 
> invocation context when initializing or obtaining {{Logger}} instances. By 
> default, this maps contexts to class loaders, and additional strategies are 
> available including async loggers, a singleton context, a JNDI-lookup-based 
> context, and an OSGi bundle context.
>  * {{LoggerContextFactory}}: used for binding a logging implementation to the 
> Logging API.
>  * {{ShutdownCallbackRegistry}}: used for registering a logging system 
> cleanup shutdown callback in various systems. The default strategy hooks in 
> to the {{Runtime}} shutdown threads API.
>  * {{ContextDataInjector}}: used for adding initial context data into the 
> {{ThreadContext}} of a {{LogEvent}}.
> h2. Proposal
> This system grew organically over time, and after identifying various 
> duplicated plugin functionality around the codebase, the plugin system should 
> be overhauled along the lines of a more standard {{@Inject}}-style dependency 
> injection framework. By adopting more conventional DI patterns, this will 
> remove the need for ad hoc plugin mechanisms in different subsystems, allow 
> for more flexible declarations of plugins by specifying direct classes needed 
> by plugins rather than doing manual lookups on the {{Configuration}} 
> instance, allow for smarter initialization strategies to minimize startup 
> time and resource usage based on configurations rather than eagerly loading 
> plugins, make it simpler to write unit tests, and make it generally simpler 
> to write and maintain plugins without requiring deep expertise of esoteric 
> subsystems.
> Taking inspiration from [CDI|https://docs.jboss.org/cdi/api/2.0/], 
> [@Inject|https://docs.oracle.com/javaee/6/api/javax/inject/Inject.html], and 
> [Guice|https://github.com/google/guice], the plugin system should be updated 
> to a new API that is broken up into a few useful concepts:
>  * Beans: beans are analogous to Java classes with additional metadata to 
> enable lifecycle management and dependency injection. Unlike a normal Java 
> class, a bean has injection points (like constructor arguments, methods, and 
> fields) where other managed instances can be injected at runtime based on the 
> currently configured beans. As these are managed, they can have 
> post-construct and pre-destruct callbacks for further lifecycle customization.
>  ** Scopes: scopes control where the instances are stored or to what 
> lifecycle they're tied to. At minimum, this includes dependent-scoped 
> (creates new instances for every injection point) and singleton-scoped 
> (creates and maintains a single instance for all injection points). In 
> general DI frameworks, other scopes include things like HTTP sessions and 
> HTTP requests. For this framework, the {{ContextSelector}} configuration API 
> corresponds to a {{Configuration}} scope.
>  ** Qualifiers: qualifiers add metadata to a bean besides its type to match 
> on for injection. Typical qualifiers are names. Plugin configuration 
> attribute names would likely make a good match here as another example.
>  * Injection points: in order to obtain injectable values into a bean 
> instance, injection points control how these instances are bound to the bean. 
> This is essentially the strategy for binding data to fields, constructors, 
> and methods.
>  * Providers: providers are analogous to plugin builders in that they specify 
> how to create an instance of type {{T}} given some plugin builder class 
> {{Builder<T> implements Provider<T>}}.
>  * Producer and disposer methods: beans can specify producer and disposer 
> methods as alternative ways to specify how to construct a bean instance. 
> These are analogous to plugin factory methods.
> Adopting such a plugin system based on beans, all the plugins that currently 
> require either a {{Configuration}} or {{Node}} instance to do anything should 
> be refactored to rely on proper dependency injection. This should be used to 
> replace as many disparate dependency injection and configuration systems as 
> possible to reduce the complexity required to implement plugins that rely on 
> shared components.
> h2. Additional Details
>  
> Beyond the dependency injection API itself, this epic is motivated by the 
> following goals:
>  * Simplify plugin classes that currently require manual method calls to 
> {{Configuration}} or {{Node}}.
>  * Unify the various ad hoc dependency injection for configuration-scoped or 
> singleton-scoped classes (the latter which are overridden via system 
> properties) including:
>  ** {{StrSubstitutor}}
>  ** {{ConfigurationScheduler}} (or scheduled tasks in general)
>  ** {{ScriptManager}}
>  ** {{WatchManager}}
>  ** {{NanoClock}}
>  ** {{ShutdownCallbackRegistry}}
>  ** {{Clock}}
>  ** {{ContextSelector}}
>  ** {{ConfigurationFactory}}
>  ** {{LogEventFactory}}
>  ** {{ContextDataFactory}}
>  * Make dependencies between classes more explicit via inversion of control 
> which allows for easier testing and modularity.
>  * Avoid the use of manual configuration handling at the API level of any 
> plugins including basic string parsing (as currently supported through the 
> {{TypeConverter}} API), class loading from strings, and ad hoc reflection. 
> This relates to the changes made in LOG4J2-2621 and LOG4J2-1917.
>  * Provide smart initialization logic to continue (or maintain) improvements 
> to startup time and avoid loading plugins that aren't referenced in the 
> loaded configuration.
>  * Provide scopes for other ad hoc scopes/contexts used in various plugins 
> such as HTTP request, HTTP session, servlet context, etc.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to