Alright, let me clarify then:

Based on how things initialize in Log4j (at least in master, though probably 
similarly in 2.x), the creation of a LoggerContext is effectively an atomic 
operation, though it may be constructed more than once before returning a 
common singleton instance (per ClassLoader or whatever your strategy is). This 
is more obvious when checking a stack trace of a test that relies on LogManager 
instead of using a JUnit fixture, though it looks something like this:

LogManager::getLogger -> LogManager::getContext -> 
Log4jContextFactory::getContext -> ContextSelector::getContext -> [assuming 
ClassLoaderContextSelector: logic to look for existing LoggerContext; if none 
found] -> LoggerContext::new -> DefaultConfiguration::new

So while it’s not a strict blocking singleton (works more like Lazy::relaxed), 
only one LoggerContext instance is returned from the ContextSelector before 
it’s potentially started. In either case, by the time the LoggerContext has 
returned and we unwind from the call stack, we should actually have a started 
configuration by the time any log messages could possibly be processed. 
Usually, this is a DefaultConfiguration instance, but it seems like it _could_ 
be a proper Configuration instance, too, assuming we can load 
ConfigurationFactory plugins at this point.

I can’t really think of a way to violate this and cause log messages to be sent 
to the default configuration when a custom configuration is started right away. 
There may be a way to abuse reflection to set things up in an inconsistent 
state, but there seems to be appropriate happens-before relationships during 
initialization such that startup is not necessarily “blocking” in the I/O 
sense, but it’s blocking in the data dependencies in the program. As it is now, 
the DefaultConfiguration instance is created in the LoggerContext constructor; 
this same place could potentially load a ConfigurationFactory if a config 
location is specified (or just in general). I’m refactoring this area to use 
builder classes, too, so it doesn’t even have to physically be in the 
constructor.

—
Matt Sicker

> On Dec 28, 2022, at 01:52, Ralph Goers <ralph.go...@dslextreme.com> wrote:
> 
> Applications can be multi-threaded. I believe we load the 
> DefaultConfiguration so other threads aren’t blocked by Log4j initialization. 
> The statement that “no logging can possibly occur until after at least said 
> construction” isn’t true.
> 
> Ralph
> 
>> On Dec 27, 2022, at 12:12 PM, Matt Sicker <m...@musigma.org> wrote:
>> 
>> In this particular scenario, a file URI has been provided to the 
>> LoggerContext constructor. Given the fact that a Logger instance itself is 
>> not constructed until _after_ the LoggerContext has been constructed, no 
>> logging can possibly occur until at least after said construction. In our 
>> current implementation, we always load a DefaultConfiguration in 
>> LoggerContext before reconfiguring to use the real config.
>> 
>> This applies to any possible ContextSelector, by the way, since those 
>> control the creation of LoggerContext instances in the first place (unless 
>> you’re using something lower level to start things up like one of the 
>> LoggerContext constructors I’m referring to, but that’s always handled by 
>> the underlying ContextSelector in Core). The Java memory model guarantees 
>> that constructing a class is an atomic operation; the result of the 
>> constructor is safely published and available for any and all threads to 
>> consume. This is the main reason why using the `final` keyword is useful in 
>> Java: final fields in classes are the safely published state (assuming the 
>> underlying reference is an immutable or thread-safe structure of course), 
>> and volatile fields require memory fencing before doing any reads or writes 
>> to them in order for threads to all see the same state. In theory, we may be 
>> able to load a configuration during the constructor which would help avoid 
>> all those weird hacks in place by Spring Boot et al. for disabling logging 
>> until they can bootstrap themselves first.
>> —
>> Matt Sicker
>> 
>>> On Dec 19, 2022, at 08:11, Ralph Goers <ralph.go...@dslextreme.com> wrote:
>>> 
>>> I am not really sure what the question here is. When you pass in a 
>>> configuration location you are going to end up with a LoggerContext that 
>>> references a Configuration derived from the information at that location. 
>>> Yes, it may create a DefaultConfiguration along the way but that is 
>>> primarily because logging may need to occur while Log4j is configuring. 
>>> 
>>> As for the external context, that was initially created to support binding 
>>> with things like Spring. However, the way Spring Boot ended up using it 
>>> caused some issues for adding extended Spring support so the LoggerContext 
>>> now contains a Map so that multiple “external” values can be captured. The 
>>> ExternalContext is just stored in a specific key. Given how the injector 
>>> worked the last time I looked at it I don’t really see how Spring could use 
>>> it.
>>> 
>>> Ralph
>>> 
>>> 
>>> 
>>>> On Dec 18, 2022, at 12:29 PM, Matt Sicker <m...@musigma.org> wrote:
>>>> 
>>>> So I’m working on the properties enhancement issue, and one thing I’ve 
>>>> come across to clean up are the situations where the LoggerContext may be 
>>>> null in the constructor to AbstractConfiguration. Previously, this only 
>>>> seemed to be the case for NullConfiguration, but it may also apply to 
>>>> DefaultConfiguration during startup. However, suppose I construct a 
>>>> LoggerContext with a configuration location corresponding to a file (so 
>>>> the only supported file path type via that constructor). This location 
>>>> does not get used until the LoggerContext starts; before this, it 
>>>> constructs a DefaultConfiguration.
>>>> 
>>>> 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’ll note for reference that LoggerContext has two families of 
>>>> constructors that fan out into the following parameters:
>>>> 
>>>> String name, Object externalContext, String/URI configLocation, Injector 
>>>> injector, PropertyResolver propertyResolver -> LoggerContext
>>>> 
>>>> The external context concept here seems to mainly be used for passing 
>>>> along a ClassLoader instance or something derived from it (I think a 
>>>> Bundle does or something from OSGi at least). These constructors do call 
>>>> Injector::init, so the point of providing an Injector instance here would 
>>>> be if you preconfigured some beans (like in a unit test) before wanting to 
>>>> load the default bundles of beans/bindings.
>>>> —
>>>> Matt Sicker
>>>> 
>>> 
>> 
> 

Reply via email to