Elasticsearch: Can't use Java client due to NoClassDefFoundError: org/apache/log4j/Priority

Created on 13 Jul 2016  ·  53Comments  ·  Source: elastic/elasticsearch

I had to add these dependencies to resolve:

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>1.5.11</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>

Elasticsearch version:
5.0.0-alpha4

JVM version:
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

OS version:
OSX 10.11.5

Description of the problem including expected versus actual behavior:
Can't use Java client, due to NoDefFound from ESLogger.

Steps to reproduce:

  1. Create Maven project
  2. Depend on
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>5.0.0-alpha4</version>
        </dependency>
  1. Try to make a client:
    private Client client = TransportClient.builder().build().addTransportAddress(
            new InetSocketTransportAddress(new InetSocketAddress("localhost", 9300)));

Provide logs (if relevant):

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Priority
    at org.elasticsearch.common.logging.ESLoggerFactory.getLogger(ESLoggerFactory.java:42)
    at org.elasticsearch.common.logging.ESLoggerFactory.getLogger(ESLoggerFactory.java:46)
    at org.elasticsearch.common.logging.Loggers.getLogger(Loggers.java:123)
    at org.elasticsearch.common.settings.Setting.<clinit>(Setting.java:110)
    at org.elasticsearch.common.logging.ESLoggerFactory.<clinit>(ESLoggerFactory.java:33)
    at org.elasticsearch.common.logging.Loggers.getLogger(Loggers.java:119)
    at org.elasticsearch.transport.netty.NettyInternalESLoggerFactory.newInstance(NettyInternalESLoggerFactory.java:33)
    at org.elasticsearch.common.netty.NettyUtils$1.newInstance(NettyUtils.java:91)
    at org.jboss.netty.logging.InternalLoggerFactory.getInstance(InternalLoggerFactory.java:67)
    at org.jboss.netty.logging.InternalLoggerFactory.getInstance(InternalLoggerFactory.java:60)
    at org.jboss.netty.util.ThreadRenamingRunnable.<clinit>(ThreadRenamingRunnable.java:32)
    at org.elasticsearch.common.netty.NettyUtils.<clinit>(NettyUtils.java:95)
    at org.elasticsearch.transport.netty.NettyTransport.<clinit>(NettyTransport.java:145)
    at org.elasticsearch.client.transport.TransportClient$Builder.newPluginService(TransportClient.java:108)
    at org.elasticsearch.client.transport.TransportClient$Builder.build(TransportClient.java:120)
    at edge.model.TestElastic.<clinit>(TestElastic.java:10)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Priority
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 16 more

Most helpful comment

As I just ran into the same issue, I would like to share my solution to this. I know this is rather crude, but may work at least for some people.

https://github.com/ctron/de.dentrassi.elasticsearch.log4j2-mock/

I wrote a small mock implementation of log4j2-core which has enough functionality to trick Elasticsearch into thinking it configured log4j2 properly. So if you are using this in combination with org.apache.logging.log4j:log4j-to-slf4j then you should have all your output over SLF4J and simply ignore the internal calls to log4j-core which ES does to manipulate the log4j configuration during runtime.

All 53 comments

Hi @fakeh

Thanks for trying out the alpha. What you report is correct, and it is listed in the breaking changes: https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking_50_java_api_changes.html#_elasticsearch_will_no_longer_detect_logging_implementations

Thanks for the swift response. If the libraries are required, why not pull in the dependencies transitively?

We don't use transitive dependencies at all.

Hi @clintongormley , is there a doc explaining the reason why Elasticsearch 5.0 Java SDK uses Log4j2 instead of Slf4j that is IMHO much less intrusive?

Hi @clintongormley , is there a doc explaining the reason why Elasticsearch 5.0 Java SDK uses Log4j2 instead of Slf4j that is IMHO much less intrusive?

Because Elasticsearch is a server we don't feel the need to talk to a logging abstraction layer. We'd have used it if we liked the API better but we don't.

The transport client is a problem in this case, because it uses Elasticsearch's code base as though it were a library. This isn't a thing we can live with in the long term because it boxes us in in the choices we can make. So the elasticsearch java REST client is becoming a thing, though it honestly is too low level for Java developers to really enjoy using it at this point.

Unfortunately, the Elasticsearch Java Client is not a server. If we want to use the API Jar in one of our applications, this choice forces us to use log4j.

Unfortunately, there does not seem to be a log4j2-over-slf4j library, the log4j-over-slf4j library only seems to work with log4j 1.x.

Edit:
I think I found a bridge that allows to continue using logback, see http://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-to-slf4j/2.7

Unfortunately, the Elasticsearch Java Client is not a server. If we want to use the API Jar in one of our applications, this choice forces us to use log4j.

That is the problem with transport client. It is the server. The same bits. You don't want it to be the server and neither do we. Thus the REST client which is, sadly, far from ready yet.

I think I found a bridge that allows to continue using logback, see http://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-to-slf4j/2.7

Yeah, I didn't read to your edit and was about to post https://logging.apache.org/log4j/2.0/log4j-to-slf4j/index.html which looks like the same link.

Er, looks like it is talking about the same artifact, rather.

@davidbilge May be in the meantime you can shade elasticsearch server when it is used with the transport client?
In that case, this article may help: https://www.elastic.co/blog/to-shade-or-not-to-shade

@dadoonet Thanks for the idea, I will look into that. For now, I'm perfectly fine using the log4j-to-slf4j bridge.

@davidbilge I wonder if it is worth adding that in the doc. I mean in the Transport Client java documentation. Would you like to describe what you did to help other users?
Like what was made with this specific page? https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/_deploying_in_jboss_eap6_module.html

Sure thing. The dependencies in my pom.xml basically look like this:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>5.0.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-to-slf4j</artifactId>
    <version>2.7</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.1.7</version>
</dependency>

That solves the missing java.lang.ClassNotFoundException if you use a logger that implements slf4j (like logback does).

That is the problem with transport client. It is the server

Client is not server. You can't say "client is server", this is absurd, it makes no sense. Client is client, server is server, period.

I get what you mean though - client reuses some classes from server.

it uses Elasticsearch's code base as though it were a library

This is clear.

But, when you actually spell it this way, e.g. correct way, then the fix becomes obvious - decouple shared classes from server and put it into separate library, not dependent on Log4J. Trivial.

Client is not server. You can't say "client is server", this is absurd, it makes no sense. Client is client, server is server, period.

But, when you actually spell it this way, e.g. correct way

These establish an adversarial relationship rather than a collaborative one. We strongly prefer the latter, and this matters to us a lot.

I get what you mean though - client reuses some classes from server.

It can't be both "absurd" and "make no sense", and you get what he means. You left off a key sentence that elucidates exactly what he meant:

It is the server. The same bits.

The two sentences go together.

then the fix becomes obvious - decouple shared classes from server and put it into separate library [...] Trivial.

The fix is obvious, but obvious is not the same as trivial. There is a lot of entanglement. We know the fix, we want to do the fix, but on this subject, for the client effort, we value pragmatism over idealism (see previous comments on this very subject).

It can't be both "absurd" and "make no sense"

Ok. Pick the one you like more.

The two sentences go together.

You do realize the implications of stating that any two codebases that share common classes are the same ("client and server share classes, therefore client is server")? Let's consider an example: Tomcat uses java util logging and Weblogic uses java util logging - therefore Tomcat is Weblogic (but not vice versa)? Absurd or makes no sense - pick one you like.

The fix is obvious, but obvious is not the same as trivial

You're picking on words instead of figuring out the meaning behind words. Speaking of adversarial - let's not pick on words and start a demagogue contest, shall we?

It would be interesting to know what kind of entanglement prevents one from simply moving shared classes into shared library. The information on the problem - not opinions of the comments on the problem - that would be collaborative instead of adversarial.

Well done @mvmn - way to kill a thread...

The information on the problem - not opinions of the comments on the problem - that would be collaborative instead of adversarial.

The above dependencies do not help if I run elasticsearch (v5.1.2) embedded (we do this for testing)...

as soon as I create an index, e.g.

        CreateIndexRequest createIndexRequest = new CreateIndexRequest("indexname");
        IndicesAdminClient indices = es.getNode().client().admin().indices();
        indices.create(createIndexRequest).actionGet();

I get this:

11:27:32.397 [main] INFO  org.elasticsearch.http.HttpServer - publish_address {127.0.0.1:9200}, bound_addresses {[fe80::1]:9200}, {[::1]:9200}, {127.0.0.1:9200}
11:27:32.397 [main] INFO  org.elasticsearch.node.Node - started
Exception in thread "elasticsearch[V3ONMVd][clusterService#updateTask][T#1]" java.lang.NoClassDefFoundError: org/apache/logging/log4j/core/config/Configurator
    at org.elasticsearch.common.logging.Loggers.setLevel(Loggers.java:149)
    at org.elasticsearch.common.logging.Loggers.setLevel(Loggers.java:144)
    at org.elasticsearch.index.SearchSlowLog.setLevel(SearchSlowLog.java:111)
    at org.elasticsearch.index.SearchSlowLog.<init>(SearchSlowLog.java:106)
    at org.elasticsearch.index.IndexModule.<init>(IndexModule.java:127)
    at org.elasticsearch.indices.IndicesService.createIndexService(IndicesService.java:421)
    at org.elasticsearch.indices.IndicesService.createIndex(IndicesService.java:394)
    at org.elasticsearch.cluster.metadata.MetaDataCreateIndexService$1.execute(MetaDataCreateIndexService.java:352)
    at org.elasticsearch.cluster.ClusterStateUpdateTask.execute(ClusterStateUpdateTask.java:45)
    at org.elasticsearch.cluster.service.ClusterService.runTasksForExecutor(ClusterService.java:581)
    at org.elasticsearch.cluster.service.ClusterService$UpdateTask.run(ClusterService.java:920)
    at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingRunnable.run(ThreadContext.java:458)
    at org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.runAndClean(PrioritizedEsThreadPoolExecutor.java:238)
    at org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor$TieBreakingPrioritizedRunnable.run(PrioritizedEsThreadPoolExecutor.java:201)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.core.config.Configurator
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)

it seems we are doomed to switch to log4j just because we use elasticsearch embedded within our tests :(

it seems we are doomed to switch to log4j just because we use elasticsearch embedded within our tests :(

Actually this is stated in the thread - you have to have Log4J in classpath if you use ElasticSearch (be it server or client - doesn't matter).

You can still use other logging system - but you must have Log4J stuff in your classpath (which might cause conflicts for some logging systems or classpath-detection using things like Spring Boot).

Anyone managed to run the 5.* version of the TransportClient in Spring Boot/Spring IO Platform application?
If so, please share how you managed to resolve the dependency conflicts.
For me, it is either ClassNotFoundException for org.apache.logging.log4j.core.config.Configurator when log4j-core is not added as a dependency or SLF4JLoggerContext cannot be cast to org.apache.logging.log4j.core.LoggerContext with added log4j-core dependency.
I am using SLF4J with Logback:

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.1.8</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.1.8</version>
    </dependency>        
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>transport</artifactId>
        <version>5.1.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-to-slf4j</artifactId>
        <version>2.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.7</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.21</version>
    </dependency>

I am trying to avoid the dependency shading overkill which I saw proposed in some places.

@kjussakov Would you please open a topic on the forum and link to it from here? However, when you open that topic, please provide a stack trace, I can not help you without a stack trace. Before you do that though, would you please look at my example on #22671 and see if it helps you solve your problems? In particular, I think that you might be able to remove the log4j-api dependency.

Try this in your pom.xml

    <properties>
        <elasticsearch.version>5.1.2</elasticsearch.version>
    </properties>

@kjussakov can you please add a comment with the link to the new topic?

Hi all,
Thanks for your prompt reply! I will start with making a minimal possible example which has the issue I described and then open a new topic in the forum. I already started with a simple maven project with the dependencies I mentioned above. This also follows the official 5.* doc

Without any Spring related dependencies it seems it works so will try now to add more until I broke it.

Will keep you posted.

Best regards,
Rumen

And here is the link to the new topic

I describe the workaround which I found to resolve the log4j dependency when running our tests with embedded Elasticsearch node.

@kjussakov Add

compile group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: '2.7'

It works for me with Spring Boot 1.4.2.

Hi,
I am using below configuration with elasticsearch 5.2.1, still getting error java.lang.ClassNotFoundException: org.apache.logging.log4j.core.Filter.

Can someone please help me resolve this?

my confuguration:
"org.slf4j:slf4j-api:1.7.21",
"org.apache.logging.log4j:log4j-api:2.7",
"org.apache.logging.log4j:log4j-to-slf4j:2.7",

Stacktrace:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/logging/log4j/core/Filter
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.getConstructor(Class.java:1825)
at org.springframework.boot.logging.LoggingSystem.get(LoggingSystem.java:134)
at org.springframework.boot.logging.LoggingSystem.get(LoggingSystem.java:125)
at org.springframework.boot.logging.LoggingApplicationListener.onApplicationStartedEvent(LoggingApplicationListener.java:189)
at org.springframework.boot.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:173)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:163)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:136)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:119)
at org.springframework.boot.context.event.EventPublishingRunListener.publishEvent(EventPublishingRunListener.java:111)
at org.springframework.boot.context.event.EventPublishingRunListener.started(EventPublishingRunListener.java:60)
at org.springframework.boot.SpringApplicationRunListeners.started(SpringApplicationRunListeners.java:48)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:293)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1112)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1101)
at com.audiencescience.media.search.Application.main(Application.java:14)
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.core.Filter
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 18 more

The stacktrace is not related to elasticsearch as far as I can see.

@pritamm If your issue is related to Elasticsearch (it does not look like it), please ask questions on the forum.

@nik9000

We'd have used it if we liked the API better but we don't.

Dude...

Hello, the fact that ES 5.x org.elasticsearch.client transport artifact explicitly expects log4j2 2.7 made things quite hard for us. I don't understand the exact reason it's not using the slf4j API. Using log4j-over-slf4j is not an option since there will very likely be log4j-slf4j-impl in the class path to process the logs of "standard" libraries that don't make an explicit choice about the logging framework.

Our particular - not so fancy - use case:
We use the ES 5.x client in the code for a Storm topology. The Storm distribution packs log4j2 2.1 - which is on the class path of all Storm processes on the cluster. ES client is not content with that version due to some new log4j2 API changes I guess so what are the options:

  • replace all log4j2 2.1 jars on the Storm cluster with 2.7
  • attempt to roll a shaded *.jar which packs log4j2 2.7 with relocation of org.apache.logging.log4jmoved to different location, META-INF/log4j-provider.properties updated to point at that new location, etc., etc. - spent half a day on it, still not working!

... not knowing the exact log4j2-specific features you rely on (being able to log objects?) it seems just using slf4j would have been more user-friendly :/

As I just ran into the same issue, I would like to share my solution to this. I know this is rather crude, but may work at least for some people.

https://github.com/ctron/de.dentrassi.elasticsearch.log4j2-mock/

I wrote a small mock implementation of log4j2-core which has enough functionality to trick Elasticsearch into thinking it configured log4j2 properly. So if you are using this in combination with org.apache.logging.log4j:log4j-to-slf4j then you should have all your output over SLF4J and simply ignore the internal calls to log4j-core which ES does to manipulate the log4j configuration during runtime.

@ctron

https://github.com/ctron/de.dentrassi.elasticsearch.log4j2-mock/

Nice :) A terrible solution to a asinine problem. I'm using it!

@ctron Thanks a million, this works like a charm. Good to see that someone tries for solutions when the developers aim for trouble 8-(

We do not aim for troubles @fjalvingh. In fact, we strive to make the use of Elasticsearch to developers joyful. We take this accusation very seriously so now I ask you to elaborate what you think it is that we are doing to aim for troubles so that we can rectify the situation. Otherwise, do not throw such statements around so loosely.

@ctron Why exactly did you go through all the trouble? log4j-to-slf4j seems to do exactly what you describe. Am I missing something?

(see my previous answer)

@jasontedor You state that separating shared classes into shared JAR is non-trivial, but you never explain how come. We both know there's nothing non-trivial in creation of shared JAR and moving some classes from one JAR to another. Why don't you elaborate on the actual problem you have, so we could perhaps come up with some solutions to help you fix it, instead of just telling us how bad we are for even asking for a fix.

@mvmn It has been explained previously, the client is completely intertwined with the server, there is a lot more to it than merely moving shared classes.

instead of just telling us how bad we are for even asking for a fix

This never happened. We feel your pain. As said already in this thread: no one likes the entanglement, we wish it was not a thing, but we can't snap our fingers and make it go away.

Well, @jasontedor, my remark came from a bit of frustration. I spent more than a day in upgrading our application to ElasticSearch 5.5. It proved to be an awful lot of work to keep a local node running while this worked very easily before. And the way to get that server to run was decidedly non-obvious.
The additional trouble caused by ES hard depending on a logging _implementation_ caused yet more grief.

I was also put off by the way discussions went here. It appeared that form was more important than fact. Instead of helping with solutions there are discussions on how to say things proper.

The fact of having to delve deeply into a lot of things, having to circumvent a lot things done to the code that made it hard to do what was previously simple, caused my remark.

I'm sorry you experienced so much frustration here due to the logging infrastructure. I hate that it's causing users pain. Would you please elaborate on the technical details of what caused you pain so we can factor it into our thinking? I had hoped that making it clear that we are bound to the Log4j API but not the implementation for the client would help clarify the situation. Can you explain why this is not the case?

It appeared that form was more important than fact. Instead of helping with solutions there are discussions on how to say things proper.

Form is important. We have a community here, we have standards, we will maintain them, and we will not tolerate being treated as adversaries.

Also, I disagree we have not been helping in this thread. We've provided example implementations, clarified the dependencies, listened to feedback, etc.

The pain was caused by more than just the logging infrastructure ;) My main challenge was to get an embedded server node running as that was the solution chosen a few years ago. This proved to be hard. First because NodeBuilder was removed and even though the notes said that similar functionality could be had by using the Node constructor with Settings this proved to be hard to do (I needed a plugin to get HTTP functionality and for that you need to override a class).
After that the logging issue appeared. The core issue here is that ES depends on a logging _implementation_ (log4j2-core) because it apparently tries to change the logging _configuration_ at runtime.

Using a bridge jar works fine for switching the loggers themselves to send their output to another logger implementation. But the bridge does not contain the parts that ES uses to change the configuration (as they are implementation dependent). Hence the error, hence the frustration, and hence the happiness with @ctron's great contribution.
Switching logging implementations is not a small issue for us. I think ES is a great product, don't get me wrong. But with great power comes great responsibility ;) So making changes like these can hurt.

As far as helping is concerned I was less than happy, and that showed.

I fully understand you want to have normal standards in communication. I do not think I crossed any line here.

I am wondering if the sort-of accepted solution to the issue: using the log4j-to-slf4j bridge does not bear some risks. Quoting from the log4j2 documentation:

Use of the Log4j 2 SLF4J Binding (log4j-slf4j-impl-2.0.jar) together with the SLF4J adapter (log4j-to-slf4j-2.0.jar) should never be attempted, as it will cause events to endlessly be routed between SLF4J and Log4j 2.

I think that using log4j-slf4j-impl is very common in cases where applications follow the "standard" approach of using the slf4j API and binding the implementation later on.

Perhaps people were wondering why Elasticsearch doesn't do the same way. A comment further up mentioned that you don't like the slf4j API.

My main challenge was to get an embedded server node running as that was the solution chosen a few years ago.

@fjalvingh I'm sorry to hear about the troubles that you're having with embedding the server. Unfortunately, we do not support this use case anymore. I would encourage you to migrate to standing up a real server instance (I'm assuming this is for testing, this is what we encourage users to do now).

The core issue here is that ES depends on a logging implementation (log4j2-core) because it apparently tries to change the logging configuration at runtime.

This is correct, the server binds the configuration during startup and that requires core. This is not the case for the transport client which is why we can get away with depending on the API but not the implementation.

I fully understand you want to have normal standards in communication. I do not think I crossed any line here.

I agree, you did not cross any line. However, since you asserted that the developers aim for troubles, and then later that we were focusing on form over fact instead of helping users, I must explain why we focus on form when we see non-collaborative behavior that we will not tolerate, and disagree with your assertions that we are aiming for troubles and not trying to help.

Perhaps people were wondering why Elasticsearch doesn't do the same way. A comment further up mentioned that you don't like the slf4j API.

I can explain this. We chose Log4j as our logging backend. We chose this for specific reasons:

  • the flexibility that it provides our end users to configure logging (e.g., rolling logs, compressing logs, various appenders, features that our users were asking for, etc.)
  • we only want to support one logging backend, maintaining one is already hard enough
  • specific features of the Log4j API that we want to utilize (as but one example amongst a few, lazy construction of parameterized messages when a certain logging level is not enabled, say trace)
  • using SLF4J would entail yet another dependency

When an abstraction/facade like SLF4J is used:

  • we lose the ability to use specific features of the Log4j API that we want to use in the server implementation and are instead bound to the lowest common denominator of the facade API (personally, I have the same problem with ORMs)
  • it sends a mixed signal that it's okay to swap out the logging backend for the server

All in all it simply looks to me as if like ES has different priorities than some of its users. And those priorities have changed over time. And in general that is fine.

But that also causes troubles for users which depend on those features. So those people will look for alternatives and may ask themselves why those changes were made.

As ES is an open source project, tinkering around with it is a valid approach. Now the people behind ES can approach this on two ways. Take those people seriously and try to embrace those workarounds, making their life simpler while maintaining their own priorities. Or stick to their position and don't care, making it more complicated for others in the future.

I don't think I need to tell anyone which the road to success is.

The issue I see here is that a decision was made that an abstraction layer like slf4j was not needed based on a false premise: that the code using it was only used as a server, not as a library, when that is not actually true. I understand that the dev team would like it not to be true at some point in the future, but the fact remains that it _is_ used as a library, and that perhaps the most friendly thing to do for developers on the JVM who don't (by your own admission) have any great client alternatives except for the one that has a hard dependency on log4j2, might have been to use slf4j even if you didn't like the API better than log4j2.

As for some of the specific reasons listed above for using log4j directly:

the flexibility that it provides our end users to configure logging (e.g., rolling logs, compressing logs, various appenders, features that our users were asking for, etc.)

As far as I know, using SLF4J backed by log4j does not prevent or eliminate this flexibility.

we only want to support one logging backend, maintaining one is already hard enough

So write your code to support SLF4J, and your configuration and maintenance tools to support a log4j backing, and make it clear that using other backends in ES servers is unsupported.

I understand that there are, as always, tradeoffs involved, but the discussion thus far doesn't really fully cover the tradeoffs involved in the decision that was actually made (i.e. should an abstraction layer be used in code that is distributed as a library)

The issue I see here is that a decision was made that an abstraction layer like slf4j was not needed based on a false premise: that the code using it was only used as a server, not as a library, when that is not actually true.

I'm sorry, but this is simply false. We did consider the impact that using Log4j directly would have on users of the transport client. We clearly did not foresee all the problems this might cause, but we absolutely did deliberate it and truly considered the impact it would have on our users before going down this path.

As for some of the specific reasons listed above for using log4j directly:

the flexibility that it provides our end users to configure logging (e.g., rolling logs, compressing logs, various appenders, features that our users were asking for, etc.)

As far as I know, using SLF4J backed by log4j does not prevent or eliminate this flexibility.

I think there is a miscommunication here. I did not claim nor intend to imply that using SLF4J would prevent this. I was merely enumerating reasons why we chose Log4j and why we chose to not use the SLF4J facade, it is not necessarily the case that all reasons provided cover both aspects of our decision making here. I'm sorry for the confusion.

I'm sorry, but this is simply false. We did consider the impact that using Log4j directly would have on users of the transport client. We clearly did not foresee all the problems this might cause, but we absolutely did deliberate it and truly considered the impact it would have on our users before going down this path.

Well, perhaps you'll forgive me for making an assumption based on what I've read in this conversation thus far. The statement made very early on in this conversation:

Because Elasticsearch is a server we don't feel the need to talk to a logging abstraction layer. We'd have used it if we liked the API better but we don't.

gave no indication that consideration was given to Java clients at the time, and was even in the present tense, implying that the need for an abstraction layer was still not felt. That said, I trust you that it was, but the impression I have from reading this conversation was that the transport client was an afterthought, so I guess there was a miscommunication issue there.

I would really like to solve the technical problems that users are having here. I think that the actual problems are getting lost in the discussion (this is not to say that we should stop the discussion, we should not stop as we truly welcome feedback from the community here).

I would like to ask this: if you're having a technical problem with a supported configuration (i.e., not embedded which was raised as a problem here a few times yet we do not support it) please open a new issue, ping me on the issue, and link to it from here. If you're having a technical problem covered by a newly opened issue, please comment or 👍 the issue.

Well, perhaps you'll forgive me for making an assumption based on what I've read in this conversation thus far.

Yes, of course!

The statement made very early on in this conversation:

Because Elasticsearch is a server we don't feel the need to talk to a logging abstraction layer. We'd have used it if we liked the API better but we don't.

That statement is unfortunate as it does not fully reflect the reality of internal conversations and decisions correctly (I'm sorry @nik9000); given that statement I can see why you reached the conclusion that you did. For the record:

  • we had internal conversations about the impact the move to Log4j would have on users of the transport client
  • we did not reach the conclusion to blow it off thinking of the server only or because the transport client is going away
  • instead, we reached the conclusion that using the bridge would solve the needs that we foresaw
  • we have provided documentation since the beginning on how to use another logger
  • we considered writing a blog post covering this transition including the impact on the transport client (although we ultimately decided the above documentation would be enough)
  • I personally experimented with different logging backends with the transport client to ensure what we intended here would work as expected

Obviously the real world is messier than the samples that I experimented with so it appears there are unforeseen problems that we have to solve. Let's clearly enumerate those problems and see what we can do.

Take those people seriously and try to embrace those workarounds, making their life simpler while maintaining their own priorities. Or stick to their position and don't care, making it more complicated for others in the future.

Sorry, this is a false dichotomy.

So at the moment, the best solution is @ctron 's cluge (thanks @ctron !!) ?
Or am I missing something in this too long thread?

@ctron , thanks a lot!

Whilst the guys here spent LOT of time meta-discussing usage of the right words in the discussion, @ctron made it! You are the absolute winner, bro!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

malpani picture malpani  ·  3Comments

makeyang picture makeyang  ·  3Comments

clintongormley picture clintongormley  ·  3Comments

brwe picture brwe  ·  3Comments

matthughes picture matthughes  ·  3Comments