[
https://issues.apache.org/jira/browse/LOG4J2-952?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14718315#comment-14718315
]
Bart S. commented on LOG4J2-952:
--------------------------------
Pff. Confusing, everything. I think the assembler solution you have arrived at
is confusing in any case... :(.
One, thing, first:
The change from {{get}} methods to {{new}} methods is the best one you have
suggested, Gary! That feels really good. Indeed they are all factory or
construction things.
Look, I am not much a fan of the "Builder" pattern because I'm not much fan of
_patterns_. Whenever someone talks about "design patterns" I try to run away as
far as I can because it kills me, it kills the vibe, it kills the spirit.
Programming is fun and creative, but these patterns are boring and reactive.
They seem to think or agree that there is a set way of doing things, when
creativity is the opposite of that.
So I agree with ralph that you have to think outside of the box and feel what
feels right for you.
The problem I have with your solution Gary to not call {{.assemble()}}
yourself, is that you have an object that is half finished; by using
compositing assemblers; it is exposed ANYWAY. You either choose NOT to expose
them (and not have to deal with assemble() calls at all in the public
interface) or you choose to expose them and complete the assembling/building.
Because what happens if a user does call assemble() even when it is not
intended? Are you going to make it protected? That will make it even more ugly.
Like I've said before [in
LOG4J2-1095|https://issues.apache.org/jira/browse/LOG4J2-1095] I feel the only
right way is to use specifications. I also don't like the "factory" pattern as
a must for doing things. Why a factory? Why these factories everywhere? Java is
littered with them. And with "impl" classes and methods. I do not understand
that. My mind is very intuitive and it does not understand that. It will have
to _learn_ that, it does not come of itself, because it is not truth.
I think you should have a very good reason for using a factory, if you don't
have it, don't use it. The Log4J framework uses factories in one of these ill
places for which there is not really a good reason (other than conforming to
the pattern). So you have to work with that which makes any solution that
actually is really elegant, it has to become less elegant to fit in, or rather,
in order to harmonize it has to fit the existing model.
Not wanting to insult the framework here ;-).
So when I see this API and IMPL packages I immediately get scared and want to
run away ;-). And it becomes incomprehensible to me.
Because it's just a pattern that someone made up for no good reason. (The worst
is the _anti_patterns. People who start to discuss everything people do WRONG;
how insightful, how constructive, how embellishing, how nurturing!). I guess
I'm one of those now, I'm anti-patterning the anti-pattern :P.
Anyway, let's see if Gary made more good comments.
Assembler vs. Builder. You see we are in a pinch because you can feel and Ralph
does feel likely and apparently and perhaps obviously (so) that the builder
'pattern' in itself might not be suitable.
There is really very little reason, for instance, to actually make a Builder
interface. How many times do you really need to pass abstract builder instances
around? That you type with generics?. I mean generic builder instances that
implement some master interface? It is a bit weird and redundant. The only
purpose such an interface really has is to "designate it as a pattern use
case". It's a signature, nothing more, but not for the compiler, but for the
programmer who cares about these things. From a functional perspective it's a
bit superfluous. Much like generics itself, it's not very functional for the
execution of actual programming code. If an apple falls from the tree and lands
on my head, I do not care about what some God has written about how apples
/should/ fall, I only care about the fact that it bounces off and I can eat it.
So we are like researching, you and I, and Ralph mostly with the code he has
written, which is of course very instructive perhaps only how not to do it ;-)
but in any case it is something that makes everything clearer, as he said to me
when he wanted me to write real code in the emails or that he would like to
write real code instead of only debating.
So the code is a great advancement, that at first. So thank you people in any
case :).
And I know I'm probably a turd in saying these things and an arrogant asshole,
but still.
When I devised my idea for Behrooz or what's his name's existing code offering
I was not all that happy about SpecificationBuilder.newAppenderSpecBuilder()
either of course. That's the compositing thing.
I was hinting at SpecificationBuilder.addAppender(real, parameters, for, that,
thing) without any compositing taking place, or even
SpecificationBuilder.addAppender("name).setParam().setParam();, which is still
the best thing I have come up with, even if it is incongruent or even a bit
dissonant with itself.
You see {{SpecificationBuilder.addAppender()}} would result a
AppenderSpecBuilder -- what would happen is this:.
- addAppender registers the new SpecBuilder(of type Appender)
- the registered object takes new values from its setParam or addAttribute or
whatever methods
- there is never any build method called on the thing because there is no build
method or perhaps it is hidden; in any case when the specification is processed
it is checked for incomplete objects (mandatory arguments) and such; this is at
the time probably of creating the Configuration; by the factory that receives
the specification. It could also be earlier but I would think validation is
best when it is actually used.
Here is the key: by using a thing like this, we are using a more scripting
approach than a compile-type-thing approach. Even the regular config files for
Log4J are a scripting approach; they are validated when used, not at "compile
time" (when you write the spec in your text editor). It is important to
understand that an XML file is also a SPEC.
And the Node Tree that results from it is also a SPEC.
And the configuration that results from it (it is created now with {{start()}}
but I really think THAT is TOO late, but that aside for now) --- why should
START be the thing that CREATES? It makes no sense really.....
but if the solution has to fall within THAT system, it would probably be
suboptimal from a good user's point of view.
Everything is intertwined and the 'feel' of one part of the system is going to
be the same as the feel of another part in the system, because the system has
to function as a whole. If there are important architectural constraints that
dictate that within the system the "best" solution is to make use of those
methods (like start()) but if those constraints are not of a high
energy/frequency/faith/trust/belief/truth/nature if those constraints are not
as beautiful or as pretty (goes the asshole again ;-)) it means...
Just saying that you have to work with what you have but what you have might
render anything you create suboptimal or not elegant from a user point of view
or even a developer point of view.
Because it also has to harmonize with the system that is.
----
I would make the following suggestions:
# If you break away from the idea or requirement or truth that the
Configuration is to be actually constructed and composed (including all of its
objects) at START time, and towards the idea that all of it is constructed at
CREATE time (e.g. by its factory) then you will end up seeing a better solution
and the end solution you arrive at will be more elegant and more useful to a
user actually using this thing.
Because simply put, this design choice favours any type of solution that chimes
in with the existing XML-to-Node hierarchy and quite puts any other kind of
solution at a disadvantage.
To make it even unworkable.
I don't mind creating a solution of my own, as indicated, if only for
exploration and study but it might take a bit of time as I don't understand the
system very well but it could at least be a proof of concept as to what I say,
but I need to get my system in working order again in any case first :(.
I believe the power of Ralph is that he just transforms the programmatic thing
into ' XML ' and that means he can address every possible object, class,
configuration and type of objects, appenders, etc., with very little code.
Anything I could write would probably require support for every single class
unless I can find a way around that (perhaps by generating code).
And it's clear I don't know enough about the system to do that, which is also
why I have been holding back.
So that's recommendation number one: ensure that the Configuration can be
created fully at create time if you want this to be good. I'm not even sure if
that is possible.
Next I believe it is still possible to turn it into more of a fluid code.
Fluent code. You can still let assembler methods return all an assembler
themselves. Makes for prettier code.
So to recap, key points:
- we are talking about SPECs
- coding is script-like rather than compile-like
- new is better than get
- a specification is processed by a factory that puts out objects
- a start method should really not construct any objects (???????).
- once you have a configuration you should be able to manipulate its objects
- it should be possible to maintain multiple configurations independently and
by hand
- it should be possible to install new configurations with ease
- the flow from object to object needs to improve, because there are too many
entry-points to the code
- there are too many objects that perform a function of control, without there
being a path from the one to the other
- I personally always prefer to store even a single important object in my
application and use it to access every other important object, but perhaps
that's just me.
- libraries should not be dealing with these important objects anyway, as they
need to abstract away from the logging framework used
- even if they do not abstract away, they still need to abstract away from
using these control objects, as it should be the application that's in control,
not the library
- it would not be bad if the library could on the other hand indicate a kind of
default configuration (in terms of levels, perhaps)
- that configuration would probably be some kind of default config file for the
library (like a cascading system of config files?........ dunno?.........).
- unless the library really has its own logging facility it should be up to the
application to configure and start?.
- I think SLF4J is a shameless plug ;-).
I think it would be very powerful to have Configurations that you can install
at will. Personally my only use case at this point is to change logger levels
en masse according to a certain setup - that doesn't even require new appenders
and such, and as such perhaps not even a new Configuration object. You might
imagine a different kind of mapping from LoggerConfig to Level and you just
install that LevelSetup into the configuration in the sense of
Configuration.applyLevelScheme() or even Context.applyLevelScheme(). Thoughts?
Is that a good idea?.... Perhaps too soon for it.
Anyway I need to break. I'll leave it at this for now. Regards.
> FAQ: How do I configure log4j2 programmatically in code without a
> configuration file?
> -------------------------------------------------------------------------------------
>
> Key: LOG4J2-952
> URL: https://issues.apache.org/jira/browse/LOG4J2-952
> Project: Log4j 2
> Issue Type: Bug
> Components: API, Configurators, Documentation
> Affects Versions: 2.1
> Reporter: Joe Merten
> Attachments: LOG4J2-952-2.patch, LOG4J2-952-3.patch,
> LOG4J2-952-4.patch, LOG4J2-952.patch
>
>
> I found [this
> link|http://logging.apache.org/log4j/2.x/faq.html#config_from_code] which
> said:
> {quote}
> You could use the static method #initialize(String contextName, ClassLoader
> loader, String configLocation) in
> org.apache.logging.log4j.core.config.Configurator. (You can pass null for the
> class loader.) Be aware that this class is not part of the public API so your
> code may break with any minor release.
> {quote}
> This documentation is unclear because it points to a member function which
> needs a filename {{configLocation}} where as the topic is »without a
> configuration file«.
> It shoud rather point to the member function
> {{org.apache.logging.log4j.core.config.Configurator.initialize(ClassLoader
> loader, ConfigurationSource source)}}.
> Example:
> {code:java}
> import org.apache.logging.log4j.core.config.ConfigurationSource;
> import org.apache.logging.log4j.core.config.Configurator;
> final String hardCodedXmlConfig =
> "<?xml version='1.0' encoding='UTF-8'?>\n" +
> "<Configuration status='INFO'>\n" +
> " <Appenders>\n" +
> " <Console name='Console' target='SYSTEM_OUT'>\n" +
> " <PatternLayout pattern='%d{HH:mm:ss.SSS} [%t] %-5level
> %logger{36} - %msg%n'/>\n" +
> " </Console>\n" +
> " </Appenders>\n" +
> " <Loggers>\n" +
> " <Root level='debug'>\n" +
> " <AppenderRef ref='Console'/>\n" +
> " </Root>\n" +
> " </Loggers>\n" +
> "</Configuration>\n";
> try {
> Configurator.initialize(null, new ConfigurationSource(new
> ByteArrayInputStream(hardCodedXmlConfig.getBytes())));
> } catch (IOException e) {
> e.printStackTrace();
> }
> {code}
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]