Skip to content

Commit

Permalink
Support injectable loggers
Browse files Browse the repository at this point in the history
- resolves #13510
  • Loading branch information
mkouba committed Nov 30, 2020
1 parent d51cbc0 commit a50e292
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 0 deletions.
26 changes: 26 additions & 0 deletions docs/src/main/asciidoc/logging.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,32 @@ public class ExampleResource {

NOTE: If you use JBoss Logging but one of your libraries uses a different logging API, you may need to configure a link:#logging-adapters[Logging Adapter].

You can also inject a configured `org.jboss.logging.Logger` instance in your beans and resource classes.

[source, java]
----
import org.jboss.logging.Logger;
@ApplicationScoped
class SimpleBean {
@Inject
Logger log; <1>
@LoggerName("foo")
Logger fooLog; <2>
public void ping() {
log.info("Simple!");
fooLog.info("Goes to _foo_ logger!");
}
}
----
<1> The FQCN of the declaring class is used as a logger name, i.e. `org.jboss.logging.Logger.getLogger(SimpleBean.class)` will be used.
<2> In this case, the name _foo_ is used as a logger name, i.e. `org.jboss.logging.Logger.getLogger("foo")` will be used.

NOTE: The logger instances are cached internally. Therefore, a logger injected e.g. into a `@RequestScoped` bean is shared for all bean instances to avoid possible performance penalty associated with logger instantiation.

=== What about Apache Log4j ?

link:https://logging.apache.org/log4j/2.x/[Log4j] is a logging implementation: it contains a logging backend and a logging facade.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.arc.runtime.LaunchModeProducer;
import io.quarkus.arc.runtime.LifecycleEventRunner;
import io.quarkus.arc.runtime.LoggerProducer;
import io.quarkus.bootstrap.BootstrapDebug;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
Expand Down Expand Up @@ -509,6 +510,11 @@ AdditionalBeanBuildItem launchMode() {
return new AdditionalBeanBuildItem(LaunchModeProducer.class);
}

@BuildStep
AdditionalBeanBuildItem loggerProducer() {
return new AdditionalBeanBuildItem(LoggerProducer.class);
}

@BuildStep
CustomScopeAnnotationsBuildItem exposeCustomScopeNames(List<ContextRegistrarBuildItem> contextBuildItems) {
Set<DotName> names = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package io.quarkus.arc.test.log;

import static org.junit.jupiter.api.Assertions.assertEquals;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;

import org.jboss.logging.Logger;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.log.LoggerName;
import io.quarkus.test.QuarkusUnitTest;

public class InjectedLoggerTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(SimpleBean.class));

@Inject
SimpleBean simpleBean;

@Inject
AnotherSimpleBean anotherSimpleBean;

@Test
public void testInjectedLogger() {
assertEquals(SimpleBean.class.getName(), simpleBean.getLog().getName());
assertEquals("shared", simpleBean.getSharedLog().getName());
assertEquals(AnotherSimpleBean.class.getName(), anotherSimpleBean.getLog().getName());
assertEquals(simpleBean.getSharedLog(), anotherSimpleBean.getSharedLog());
}

@ApplicationScoped
static class SimpleBean {

@Inject
Logger log;

@LoggerName("shared")
Logger sharedLog;

public Logger getLog() {
log.info("Someone is here!");
return log;
}

public Logger getSharedLog() {
return sharedLog;
}

}

@Dependent
static class AnotherSimpleBean {

private final Logger log;

@LoggerName("shared")
Logger sharedLog;

public AnotherSimpleBean(Logger log) {
this.log = log;
}

public Logger getLog() {
return log;
}

public Logger getSharedLog() {
sharedLog.info("Yet another someone is here!");
return sharedLog;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.quarkus.arc.log;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.enterprise.util.AnnotationLiteral;
import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target({ FIELD, PARAMETER, METHOD })
public @interface LoggerName {

/**
* The logger name must not be empty.
*
* @return the logger name
*/
@Nonbinding
String value();

/**
* Supports inline instantiation of this qualifier.
*/
public static final class Literal extends AnnotationLiteral<LoggerName> implements LoggerName {

private static final long serialVersionUID = 1L;

private final String value;

public Literal(String value) {
this.value = value;
}

@Override
public String value() {
return value;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.quarkus.arc.runtime;

import java.lang.annotation.Annotation;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.annotation.PreDestroy;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Singleton;

import org.jboss.logging.Logger;

import io.quarkus.arc.log.LoggerName;

@Singleton
public class LoggerProducer {

private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<>();

@Dependent
@Produces
Logger getSimpleLogger(InjectionPoint injectionPoint) {
return loggers.computeIfAbsent(injectionPoint.getMember().getDeclaringClass().getName(), Logger::getLogger);
}

@LoggerName("")
@Dependent
@Produces
Logger getLoggerWithCustomName(InjectionPoint injectionPoint) {
String name = null;
for (Annotation qualifier : injectionPoint.getQualifiers()) {
if (qualifier.annotationType().equals(LoggerName.class)) {
name = ((LoggerName) qualifier).value();
}
}
if (name == null || name.isEmpty()) {
throw new IllegalStateException("Unable to derive the logger name at " + injectionPoint);
}
return loggers.computeIfAbsent(name, Logger::getLogger);
}

@PreDestroy
void destroy() {
loggers.clear();
}

}

0 comments on commit a50e292

Please sign in to comment.