And with this detail about the possibility for a Logger to be returned while 
LoggerContext::start is being executed is indeed a reason for why 
DefaultConfiguration _normally_ exists. I’m looking at two additional scenarios:

1. What if you provide a configuration location String or URI to the 
LoggerContext constructor? We already have a configuration source in this case 
(or at least we do if the URI corresponds to a local file), but we ignore this 
source until we start the LoggerContext. It is this situation where I thought 
it may be possible to start loading ConfigurationFactory plugins here to load 
and parse the specified configuration as the initial Configuration instance 
rather than an instance of DefaultConfiguration. This wasn’t previously 
possible due to various code dependencies, but given the fact that we have 
access to an initialized Injector instance inside the LoggerContext constructor 
(this may be a key detail I haven’t been too explicit about) means that we can 
start loading whatever subset of plugins we want at this point.

2. If you don’t provide a configuration location, we could still attempt to 
load the ConfigurationFactory plugins to bootstrap here. This is even more 
realistic now that we’ve removed the plugin package scanning feature, so we 
know that plugin service classes will already be available to load at this time.

What I’m thinking here is a fairly innocuous-looking change may allow us to 
load the configuration earlier. As it is now, the LoggerContext is getOrCreated 
by the ContextSelector before Log4jContextSelector checks its state to see if 
it should be started (in which case it will then load ConfigurationFactory 
plugins to start the real configuration). What I think might be possible is 
moving this ConfigurationFactory bit deeper into the call chain by creating it 
when the LoggerContext is being created. This would mean the LoggerContext 
would have the custom configuration instance _before_ LoggerContext::start is 
invoked.

One of the main tradeoffs here would be startup time I suppose. By deferring 
configuration loading until LoggerContext::start is invoked, we do indeed get 
out of the way as soon as possible to start accepting log messages and 
returning control to user code. So I suppose even if it turns out to be 
possible to load the configuration right away, this would by definition cause 
blocking as the configuration is loaded, so this would probably make sense as a 
configurable option.
—
Matt Sicker

> On Dec 28, 2022, at 11:44, Ralph Goers <ralph.go...@dslextreme.com> wrote:
> 
> 
> 
>> On Dec 28, 2022, at 10:37 AM, Ralph Goers <ralph.go...@dslextreme.com> wrote:
>> 
>> 
>> 
>> Ralph
>> 
>>> On Dec 28, 2022, at 7:01 AM, Piotr P. Karwasz <piotr.karw...@gmail.com> 
>>> wrote:
>>> 
>>> Hi Matt,
>>> 
>>> On Sun, 18 Dec 2022 at 20:30, Matt Sicker <m...@musigma.org> wrote:
>>>> During this bootstrapping, if the configuration location is available 
>>>> (such as for a unit test), should LoggerContext set up the configuration 
>>>> provided? Or is there some sort of cyclic dependency here preventing us 
>>>> from loading ConfigurationFactory right away? So far, the only cyclic 
>>>> dependencies I’ve found are related to plugins created in the 
>>>> DefaultConfiguration (or the NullConfiguration in some cases), but those 
>>>> are already commented as such (like in PatternLayout).
>>> 
>>> I think we should rely more on our `LifeCycle` abstraction:
>>> `Configuration` starts in the "initializing" state and does not have
>>> any subcomponents (especially those that require a `LoggerContext` to
>>> be present) until `initialize()` is called.
>> 
>> Actually, a LoggerContext is hard-wired with a DefaultConfiguration. So as 
>> soon as it exists it has a Configuration. Once the Configuration identified 
>> from the configLocation is created the DefaultConfiguration is replaced.  
>> This is the discussion Matt was having in his previous email and didn’t 
>> understand why it is required to work that way. Note that once the 
>> LoggerContext is constructed it will be “wired” by the ContextSelector and 
>> will be usable by any thread.
>> 
>>> 
>>> We can do the same thing with `LoggerContext`: after the constructor
>>> exits it does not have a configuration and is in the "initializing"
>>> state.
>> 
>> After the Constructor exists is has a DefaultConfiguration.
>> 
>>> The configuration will be created on an `initialize()` call
>>> (maybe chained from `start()`). By then we can safely pass a reference
>>> to `LoggerContext` to the `AbstractConfiguration` constructor.
>>> 
>>> Logging does not occur until `start()` is called.
>> 
>> I don’t believe this is true. Once the ContextSelector registers it any 
>> calls to log will use it.
>> 
>> Ralph
> 
> Note that LoggerContext.start() uses tryLock. If initialization is in 
> progress then start() will simply return. So other threads will log while 
> initialization is occurring. This is intentional.
> 
> Ralph


Reply via email to