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