diff --git a/README.md b/README.md index 4c797ef..2f72cd5 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Define a logger interface: package com.foo; ... -@Log +@Logger public interface MyLog { @Message("Hello %s") // Uses java.util.Formatter and defaults to `INFO` level void hello(String name); @@ -61,12 +61,12 @@ public interface MyLog { } ``` -This will generate a class `com.foo.MyLog$impl` which actually does the logging. For Maven this can be seen in the `target/generated-sources/annotations/` folder for reference. +This will generate a class `com.foo.MyLogImpl` which actually does the logging. For Maven this can be seen in the `target/generated-sources/annotations/` folder for reference. -To get an instance use `uk.dansiviter.jule.LogProducer`: +To get an instance use `uk.dansiviter.jule.LogFactory`: ```java public class MyClass { - private final static MyLog LOG = LogProducer.log(MyLog.class); + private final static MyLog LOG = LogFactory.log(MyLog.class); public void myMethod() { LOG.hello("foo"); @@ -79,18 +79,17 @@ public class MyClass { ## CDI ## -Simply create your own factory: +If you wish for CDI to manage the logger and and make it available for injection, just use: ```java -@ApplicationScoped -public class MyLogFactory { - @Produces @Dependent // Always use dependent scope to prevent proxying - public static MyLog myLog(InjectionPoint ip) { - return LogProducer.log(MyLog.class, ip.getMember().getDeclaringClass()); - } +@Logger(lifecycle = Lifecycle.CDI) +interface MyLog { + ... } ``` +This will generage a `@Dependent` logger implementation. + Then just inject: ```java @ApplicationScoped diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml index a100c7a..2c8403d 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -4,7 +4,7 @@ uk.dansiviter.jule jule-project - 0.7.0-SNAPSHOT + 0.7.2-SNAPSHOT benchmarks diff --git a/benchmarks/src/main/java/uk/dansiviter/jule/LogBenchmark.java b/benchmarks/src/main/java/uk/dansiviter/jule/LogBenchmark.java index e5fd266..03f5c00 100644 --- a/benchmarks/src/main/java/uk/dansiviter/jule/LogBenchmark.java +++ b/benchmarks/src/main/java/uk/dansiviter/jule/LogBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,12 @@ */ package uk.dansiviter.jule; -import java.util.logging.Logger; - import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.annotations.Logger; import uk.dansiviter.jule.annotations.Message; import uk.dansiviter.jule.annotations.Message.Level; @@ -33,17 +31,17 @@ public static class TestState { @Setup public void setup() { - log = LogProducer.log(BenchmarkLog.class, LogBenchmark.class); + log = LoggerFactory.log(BenchmarkLog.class, LogBenchmark.class); } } @State(Scope.Benchmark) public static class LegacyState { - Logger log; + java.util.logging.Logger log; @Setup public void setup() { - log = Logger.getLogger(LogBenchmark.class.getSimpleName()); + log = java.util.logging.Logger.getLogger(LogBenchmark.class.getSimpleName()); } } @@ -63,7 +61,7 @@ public void legLog(LegacyState state) { } } - @Log + @Logger public interface BenchmarkLog { @Message("Hello %d") void hello(int i); diff --git a/core/src/main/java/uk/dansiviter/jule/AsyncHandler.java b/core/src/main/java/uk/dansiviter/jule/AsyncHandler.java index ad0b616..a3c5c0b 100644 --- a/core/src/main/java/uk/dansiviter/jule/AsyncHandler.java +++ b/core/src/main/java/uk/dansiviter/jule/AsyncHandler.java @@ -15,7 +15,6 @@ */ package uk.dansiviter.jule; -import static java.nio.charset.Charset.defaultCharset; import static java.util.concurrent.ForkJoinPool.commonPool; import static java.util.logging.ErrorManager.GENERIC_FAILURE; import static java.util.logging.ErrorManager.OPEN_FAILURE; diff --git a/core/src/main/java/uk/dansiviter/jule/BaseJulLog.java b/core/src/main/java/uk/dansiviter/jule/BaseJulLogger.java similarity index 74% rename from core/src/main/java/uk/dansiviter/jule/BaseJulLog.java rename to core/src/main/java/uk/dansiviter/jule/BaseJulLogger.java index e50d60a..89eb4ea 100644 --- a/core/src/main/java/uk/dansiviter/jule/BaseJulLog.java +++ b/core/src/main/java/uk/dansiviter/jule/BaseJulLogger.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package uk.dansiviter.jule; +import static java.lang.String.format; import static java.util.logging.Logger.getLogger; import java.util.function.Supplier; @@ -26,10 +27,10 @@ /** * Base {@code java.util.Logger} implementation. */ -public interface BaseJulLog extends BaseLog { +public interface BaseJulLogger extends BaseLogger { @Override default Logger delegate(String name) { - var bundleName = log().resourceBundleName(); + var bundleName = logger().resourceBundleName(); return getLogger(name, bundleName.isBlank() ? null : bundleName); } @@ -49,19 +50,26 @@ default void log(Message.Level level, Supplier msg, Throwable thrown) { } } + @Override + default String render(String msg, Object... params) { + var resourceBundle = delegate().getResourceBundle(); + return format(resourceBundle != null ? resourceBundle.getString(msg) : msg, params); + } + private static Level level(Message.Level level) { switch (level) { case ERROR: - return Level.SEVERE; + return Level.SEVERE; case WARN: - return Level.WARNING; + return Level.WARNING; case INFO: - return Level.INFO; + return Level.INFO; case DEBUG: - return Level.FINE; + return Level.FINE; case TRACE: - return Level.FINER; + return Level.FINER; + default: + return Level.OFF; } - throw new IllegalArgumentException("Unknown type! [" + level + "]"); } } diff --git a/core/src/main/java/uk/dansiviter/jule/BaseLog.java b/core/src/main/java/uk/dansiviter/jule/BaseLogger.java similarity index 91% rename from core/src/main/java/uk/dansiviter/jule/BaseLog.java rename to core/src/main/java/uk/dansiviter/jule/BaseLogger.java index a9b0561..33f28e3 100644 --- a/core/src/main/java/uk/dansiviter/jule/BaseLog.java +++ b/core/src/main/java/uk/dansiviter/jule/BaseLogger.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,22 +29,22 @@ import java.util.function.LongSupplier; import java.util.function.Supplier; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.annotations.Logger; import uk.dansiviter.jule.annotations.Message; /** * Defines the base implementation of the logger interface. */ -public interface BaseLog { +public interface BaseLogger { /** * @return the delegate logger. */ L delegate(); /** - * @return the {@link Log} instance. + * @return the {@link Logger} instance. */ - Log log(); + Logger logger(); /** * Gets delegate logger instance. @@ -72,7 +72,7 @@ public interface BaseLog { */ default void logp(Message.Level level, String msg, Object... params) { // isLoggable check will already be done - BaseLog.expand(params); + BaseLogger.expand(params); Throwable thrown = null; if (params.length > 0 && params[params.length - 1] instanceof Throwable) { @@ -81,7 +81,11 @@ default void logp(Message.Level level, String msg, Object... params) { } var finalParams = params; - log(level, () -> format(msg, finalParams), thrown); + log(level, () -> this.render(msg, finalParams), thrown); + } + + default String render(String msg, Object... params) { + return format(msg, params); } void log(Message.Level level, Supplier msg, Throwable thrown); diff --git a/core/src/main/java/uk/dansiviter/jule/BaseSystemLog.java b/core/src/main/java/uk/dansiviter/jule/BaseSystemLog.java index 9cca7e8..9bb18f4 100644 --- a/core/src/main/java/uk/dansiviter/jule/BaseSystemLog.java +++ b/core/src/main/java/uk/dansiviter/jule/BaseSystemLog.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,12 +30,11 @@ * Note: This API has no mechanism for finding source class and method so will likely just state this * class as the source. */ -public interface BaseSystemLog extends BaseLog { - +public interface BaseSystemLog extends BaseLogger { @Override default Logger delegate(String name) { - var bundleName = log().resourceBundleName(); + var bundleName = logger().resourceBundleName(); if (bundleName.isEmpty()) { return getLogger(name); } @@ -62,16 +61,17 @@ default void log(Message.Level level, Supplier msg, Throwable thrown) { private static Level level(Message.Level level) { switch (level) { case ERROR: - return Level.ERROR; + return Level.ERROR; case WARN: - return Level.WARNING; + return Level.WARNING; case INFO: - return Level.INFO; + return Level.INFO; case DEBUG: - return Level.DEBUG; + return Level.DEBUG; case TRACE: - return Level.TRACE; + return Level.TRACE; + default: + return Level.OFF; } - return Level.OFF; } } diff --git a/core/src/main/java/uk/dansiviter/jule/LogProducer.java b/core/src/main/java/uk/dansiviter/jule/LoggerFactory.java similarity index 80% rename from core/src/main/java/uk/dansiviter/jule/LogProducer.java rename to core/src/main/java/uk/dansiviter/jule/LoggerFactory.java index a6ceabe..5dcfa59 100644 --- a/core/src/main/java/uk/dansiviter/jule/LogProducer.java +++ b/core/src/main/java/uk/dansiviter/jule/LoggerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,14 +22,14 @@ import java.util.Map; import java.util.WeakHashMap; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.annotations.Logger; /** * This class provides instances of the log wrappers. */ -public enum LogProducer { ; +public enum LoggerFactory { ; private static final Map LOGS = new WeakHashMap<>(); - public static final String SUFFIX = "$impl"; + public static final String SUFFIX = "Impl"; /** * Return an instance of the given type. This will attempt to walk the stack @@ -60,23 +60,23 @@ public static L log(Class log, Class name) { * * @param the log type. * @param logClass the log class type. - * @param name the log ename. + * @param name the log name. * @return log instance. This may come from a cache of instances. */ public static L log(Class logClass, String name) { - if (!logClass.isAnnotationPresent(Log.class)) { - throw new IllegalArgumentException(format("@Log annotation not present! [%s]", logClass.getName())); + if (!logClass.isAnnotationPresent(Logger.class)) { + throw new IllegalArgumentException(format("@Logger annotation not present! [%s]", logClass.getName())); } var key = key(logClass, name); - return logClass.cast(LOGS.computeIfAbsent(key, k -> create(key, logClass, name))); + return logClass.cast(LOGS.computeIfAbsent(key, k -> create(logClass, name))); } - private static Object create(String key, Class logClass, String name) { + private static Object create(Class logClass, String name) { var className = logClass.getName().concat(SUFFIX); try { return Class.forName(className, true, logClass.getClassLoader()) - .getDeclaredConstructor(String.class, String.class) - .newInstance(name, key); + .getDeclaredConstructor(String.class) + .newInstance(name); } catch (ReflectiveOperationException e) { throw new IllegalStateException(format("Unable to instantiate class! [%s]", className), e); } @@ -90,7 +90,7 @@ private static Object create(String key, Class logClass, String name) { * @param name the log name. * @return the key. */ - public static String key(Class log, String name) { + private static String key(Class log, String name) { return format("%s-%s", log.getName(), requireNonNull(name)).intern(); } } diff --git a/core/src/main/java/uk/dansiviter/jule/annotations/Log.java b/core/src/main/java/uk/dansiviter/jule/annotations/Logger.java similarity index 75% rename from core/src/main/java/uk/dansiviter/jule/annotations/Log.java rename to core/src/main/java/uk/dansiviter/jule/annotations/Logger.java index 0ec9c9d..0fa8a27 100644 --- a/core/src/main/java/uk/dansiviter/jule/annotations/Log.java +++ b/core/src/main/java/uk/dansiviter/jule/annotations/Logger.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ */ @Target({ TYPE, FIELD }) @Retention(RUNTIME) -public @interface Log { +public @interface Logger { /** * @return the name of the resource bundle. * @see java.util.logging.Logger#setResourceBundle(java.util.ResourceBundle) @@ -50,6 +50,15 @@ */ Type type() default Type.JUL; + /** + * @return the lifecycle management of the logger. + */ + Lifecycle lifecycle() default Lifecycle.DEFAULT; + + + /** + * Type of underlying delegate logger. + */ public enum Type { /** * Uses {@link java.util.logging.Logger} as delegate. @@ -60,4 +69,18 @@ public enum Type { */ SYSTEM } + + /** + * Type of lifecycle for the logger. + */ + public enum Lifecycle { + /** + * Logger can be accessed via {@code LogFactory#log(...)} methods. + */ + DEFAULT, + /** + * In addition to {@link #DEFAULT} logger lifecycle will be managed by CDI as a {@link Dependent} + */ + CDI + } } diff --git a/core/src/test/java/uk/dansiviter/jule/BaseJulLogTest.java b/core/src/test/java/uk/dansiviter/jule/BaseJulLoggerTest.java similarity index 80% rename from core/src/test/java/uk/dansiviter/jule/BaseJulLogTest.java rename to core/src/test/java/uk/dansiviter/jule/BaseJulLoggerTest.java index 001123b..f232306 100644 --- a/core/src/test/java/uk/dansiviter/jule/BaseJulLogTest.java +++ b/core/src/test/java/uk/dansiviter/jule/BaseJulLoggerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,6 @@ import java.util.function.IntSupplier; import java.util.function.LongSupplier; import java.util.function.Supplier; -import java.util.logging.Logger; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -42,58 +41,58 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.annotations.Logger; import uk.dansiviter.jule.annotations.Message.Level; /** - * Tests for {@link BaseJulLog}. + * Tests for {@link BaseJulLogger}. */ @ExtendWith(MockitoExtension.class) -class BaseJulLogTest { +class BaseJulLoggerTest { @Mock - private Logger delegate; + private java.util.logging.Logger delegate; @Mock - private Log log; + private Logger annotation; - private BaseJulLog baseLog; + private BaseJulLogger baseLog; @BeforeEach void before() { - this.baseLog = new BaseJulLog() { + this.baseLog = new BaseJulLogger() { @Override - public Logger delegate() { + public java.util.logging.Logger delegate() { return delegate; } @Override - public Log log() { - return log; + public Logger logger() { + return annotation; } }; } @Test void delegate() { - when(this.log.resourceBundleName()).thenReturn(""); + when(this.annotation.resourceBundleName()).thenReturn(""); - Logger logger = this.baseLog.delegate("BaseLogTest#delegate"); + var logger = this.baseLog.delegate("BaseLogTest#delegate"); assertThat(logger.getName(), equalTo("BaseLogTest#delegate")); assertThat(logger.getResourceBundleName(), nullValue()); - verify(this.log).resourceBundleName(); + verify(this.annotation).resourceBundleName(); } @Test void delegate_resourceBundle() { - when(this.log.resourceBundleName()).thenReturn(BaseJulLogTest.class.getName()); + when(this.annotation.resourceBundleName()).thenReturn(BaseJulLoggerTest.class.getName()); - Logger logger = this.baseLog.delegate("BaseLogTest#resourceBundle"); + var logger = this.baseLog.delegate("BaseLogTest#resourceBundle"); assertThat(logger.getName(), equalTo("BaseLogTest#resourceBundle")); - assertThat(logger.getResourceBundleName(), equalTo(BaseJulLogTest.class.getName())); + assertThat(logger.getResourceBundleName(), equalTo(BaseJulLoggerTest.class.getName())); assertThat(logger.getResourceBundle(), notNullValue()); - verify(this.log).resourceBundleName(); + verify(this.annotation).resourceBundleName(); } @Test diff --git a/core/src/test/java/uk/dansiviter/jule/BaseSystemLogTest.java b/core/src/test/java/uk/dansiviter/jule/BaseSystemLoggerTest.java similarity index 79% rename from core/src/test/java/uk/dansiviter/jule/BaseSystemLogTest.java rename to core/src/test/java/uk/dansiviter/jule/BaseSystemLoggerTest.java index f526076..1efca52 100644 --- a/core/src/test/java/uk/dansiviter/jule/BaseSystemLogTest.java +++ b/core/src/test/java/uk/dansiviter/jule/BaseSystemLoggerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.lang.System.Logger; import java.util.Optional; import java.util.OptionalDouble; import java.util.OptionalInt; @@ -40,18 +39,18 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.annotations.Logger; import uk.dansiviter.jule.annotations.Message.Level; /** * Tests for {@link BaseSystemLog}. */ @ExtendWith(MockitoExtension.class) -class BaseSystemLogTest { +class BaseSystemLoggerTest { @Mock - private Logger delegate; + private java.lang.System.Logger delegate; @Mock - private Log log; + private Logger annotation; private BaseSystemLog baseLog; @@ -59,36 +58,36 @@ class BaseSystemLogTest { void before() { this.baseLog = new BaseSystemLog() { @Override - public Logger delegate() { + public java.lang.System.Logger delegate() { return delegate; } @Override - public Log log() { - return log; + public Logger logger() { + return annotation; } }; } @Test void delegate() { - when(this.log.resourceBundleName()).thenReturn(""); + when(this.annotation.resourceBundleName()).thenReturn(""); - Logger logger = this.baseLog.delegate("BaseLogTest#delegate"); + var logger = this.baseLog.delegate("BaseLoggerTest#delegate"); - assertThat(logger.getName(), equalTo("BaseLogTest#delegate")); - verify(this.log).resourceBundleName(); + assertThat(logger.getName(), equalTo("BaseLoggerTest#delegate")); + verify(this.annotation).resourceBundleName(); } @Test void delegate_resourceBundle() { - when(this.log.resourceBundleName()).thenReturn(BaseJulLogTest.class.getName()); + when(this.annotation.resourceBundleName()).thenReturn(BaseJulLoggerTest.class.getName()); - Logger logger = this.baseLog.delegate("BaseLogTest#resourceBundle"); + var logger = this.baseLog.delegate("BaseLoggerTest#resourceBundle"); - assertThat(logger.getName(), equalTo("BaseLogTest#resourceBundle")); + assertThat(logger.getName(), equalTo("BaseLoggerTest#resourceBundle")); - verify(this.log).resourceBundleName(); + verify(this.annotation).resourceBundleName(); } @Test diff --git a/core/src/test/java/uk/dansiviter/jule/LogProducerTest.java b/core/src/test/java/uk/dansiviter/jule/LoggerFactoryTest.java similarity index 64% rename from core/src/test/java/uk/dansiviter/jule/LogProducerTest.java rename to core/src/test/java/uk/dansiviter/jule/LoggerFactoryTest.java index 5285be1..63c7745 100644 --- a/core/src/test/java/uk/dansiviter/jule/LogProducerTest.java +++ b/core/src/test/java/uk/dansiviter/jule/LoggerFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,63 +21,61 @@ import org.junit.jupiter.api.Test; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.annotations.Logger; /** - * Tests for {@link LogProducer}. + * Tests for {@link LoggerFactory}. *

* Note, it's not possible to test much of the functionality in this project. See tests in {@code ../processor} module. */ -class LogProducerTest { +class LoggerFactoryTest { @Test void log() { - var log = (MyLog$impl) LogProducer.log(MyLog.class); - assertThat("uk.dansiviter.jule.LogProducerTest", equalTo(log.name)); + var log = (MyLogImpl) LoggerFactory.log(MyLog.class); + assertThat("uk.dansiviter.jule.LoggerFactoryTest", equalTo(log.name)); } @Test void log_class() { - var log = (MyLog$impl) LogProducer.log(MyLog.class, String.class); + var log = (MyLogImpl) LoggerFactory.log(MyLog.class, String.class); assertThat("java.lang.String", equalTo(log.name)); } @Test void log_name() { - var log = (MyLog$impl) LogProducer.log(MyLog.class, "foo"); + var log = (MyLogImpl) LoggerFactory.log(MyLog.class, "foo"); assertThat("foo", equalTo(log.name)); } @Test void log_noAnnotation() { - var e = assertThrows(IllegalArgumentException.class, () -> LogProducer.log(NoAnnotation.class)); - assertThat("@Log annotation not present! [uk.dansiviter.jule.LogProducerTest$NoAnnotation]", equalTo(e.getMessage())); + var e = assertThrows(IllegalArgumentException.class, () -> LoggerFactory.log(NoAnnotation.class)); + assertThat("@Logger annotation not present! [uk.dansiviter.jule.LoggerFactoryTest$NoAnnotation]", equalTo(e.getMessage())); } @Test void log_classNotFound() { - var e = assertThrows(IllegalStateException.class, () -> LogProducer.log(NoImplemenatation.class)); - assertThat("Unable to instantiate class! [uk.dansiviter.jule.LogProducerTest$NoImplemenatation$impl]", equalTo(e.getMessage())); + var e = assertThrows(IllegalStateException.class, () -> LoggerFactory.log(NoImplemenatation.class)); + assertThat("Unable to instantiate class! [uk.dansiviter.jule.LoggerFactoryTest$NoImplemenatationImpl]", equalTo(e.getMessage())); } // --- Internal Classes --- - @Log + @Logger interface MyLog { } /** * This would normally be auto-generated. */ - public static class MyLog$impl implements MyLog { + public static class MyLogImpl implements MyLog { final String name; - final String key; - MyLog$impl(String name, String key) { + MyLogImpl(String name) { this.name = name; - this.key = key; } } - @Log + @Logger interface NoImplemenatation { } interface NoAnnotation { } diff --git a/core/src/test/resources/uk/dansiviter/jule/BaseJulLogTest.properties b/core/src/test/resources/uk/dansiviter/jule/BaseJulLoggerTest.properties similarity index 100% rename from core/src/test/resources/uk/dansiviter/jule/BaseJulLogTest.properties rename to core/src/test/resources/uk/dansiviter/jule/BaseJulLoggerTest.properties diff --git a/processor/pom.xml b/processor/pom.xml index 7470487..39ca266 100644 --- a/processor/pom.xml +++ b/processor/pom.xml @@ -30,6 +30,12 @@ 1.1.2 + + jakarta.enterprise + jakarta.enterprise.cdi-api + 3.0.1 + + com.google.testing.compile compile-testing diff --git a/processor/src/main/java/uk/dansiviter/jule/processor/LogProcessor.java b/processor/src/main/java/uk/dansiviter/jule/processor/LoggerProcessor.java similarity index 81% rename from processor/src/main/java/uk/dansiviter/jule/processor/LogProcessor.java rename to processor/src/main/java/uk/dansiviter/jule/processor/LoggerProcessor.java index 43d4839..7695069 100644 --- a/processor/src/main/java/uk/dansiviter/jule/processor/LogProcessor.java +++ b/processor/src/main/java/uk/dansiviter/jule/processor/LoggerProcessor.java @@ -30,7 +30,6 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; -import java.util.logging.Logger; import java.util.stream.Stream; import javax.annotation.processing.AbstractProcessor; @@ -42,12 +41,13 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; -import javax.lang.model.type.TypeMirror; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.spi.InjectionPoint; +import jakarta.inject.Inject; import jakarta.json.Json; import com.squareup.javapoet.AnnotationSpec; @@ -57,31 +57,32 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; -import uk.dansiviter.jule.BaseJulLog; +import uk.dansiviter.jule.BaseJulLogger; import uk.dansiviter.jule.BaseSystemLog; -import uk.dansiviter.jule.LogProducer; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.LoggerFactory; +import uk.dansiviter.jule.annotations.Logger; +import uk.dansiviter.jule.annotations.Logger.Lifecycle; +import uk.dansiviter.jule.annotations.Logger.Type; import uk.dansiviter.jule.annotations.Message; -import uk.dansiviter.jule.annotations.Log.Type; import uk.dansiviter.jule.annotations.Message.Level; /** - * Processes {@link Log} annotations. + * Processes {@link Logger} annotations. */ -@SupportedAnnotationTypes("uk.dansiviter.jule.annotations.Log") +@SupportedAnnotationTypes("uk.dansiviter.jule.annotations.Logger") @SupportedSourceVersion(SourceVersion.RELEASE_11) -public class LogProcessor extends AbstractProcessor { +public class LoggerProcessor extends AbstractProcessor { private final Supplier nowSupplier; /** * Creates a new processor. */ - public LogProcessor() { + public LoggerProcessor() { this(Instant::now); } - LogProcessor(Supplier nowSupplier) { + LoggerProcessor(Supplier nowSupplier) { this.nowSupplier = nowSupplier; } @@ -93,16 +94,14 @@ public boolean process(Set annotations, RoundEnvironment private void process(TypeElement element) { var pkg = this.processingEnv.getElementUtils().getPackageOf(element); - var type = element.asType(); var className = className(element); - var concreteName = className.concat(LogProducer.SUFFIX); - createConcrete(className, element, type, concreteName, pkg); + var concreteName = className.concat(LoggerFactory.SUFFIX); + createConcrete(className, element, concreteName, pkg); } private void createConcrete( String className, TypeElement type, - TypeMirror typeMirror, String concreteName, PackageElement pkg) { @@ -111,11 +110,11 @@ private void createConcrete( format("Generating class for: %s.%s", pkg.getQualifiedName(), className), type); - var log = type.getAnnotation(Log.class); + var log = type.getAnnotation(Logger.class); Class logType; if (log.type() == Type.JUL) { - logType = Logger.class; + logType = java.util.logging.Logger.class; } else if (log.type() == Type.SYSTEM) { logType = java.lang.System.Logger.class; } else { @@ -124,11 +123,9 @@ private void createConcrete( } var constructor = MethodSpec.constructorBuilder() - .addModifiers(Modifier.PUBLIC) + .addModifiers(PUBLIC) .addParameter(String.class, "name") - .addParameter(String.class, "key") - .addStatement("this.log = $T.class.getAnnotation($T.class)", typeMirror, Log.class) - .addStatement("this.key = key") + .addStatement("this.logger = $T.class.getAnnotation($T.class)", type, Logger.class) .addStatement("this.delegate = delegate(name)") .build(); var delegateMethod = MethodSpec.methodBuilder("delegate") @@ -138,17 +135,17 @@ private void createConcrete( .addStatement("return this.delegate") .addJavadoc("@returns the delegate logger.") .build(); - var logMethod = MethodSpec.methodBuilder("log") + var logMethod = MethodSpec.methodBuilder("logger") .addAnnotation(Override.class) .addModifiers(PUBLIC, FINAL) - .returns(Log.class) - .addStatement("return this.log") + .returns(Logger.class) + .addStatement("return this.logger") .addJavadoc("@returns the annotation instance.") .build(); Class baseLogType; if (log.type() == Type.JUL) { - baseLogType = BaseJulLog.class; + baseLogType = BaseJulLogger.class; } else if (log.type() == Type.SYSTEM) { baseLogType = BaseSystemLog.class; } else { @@ -165,13 +162,15 @@ private void createConcrete( .addMember("date", "$S", this.nowSupplier.get().toString()) .build()) .addSuperinterface(baseLogType) - .addSuperinterface(typeMirror) - .addMethod(constructor) - .addField(Log.class, "log", PRIVATE, FINAL) - .addMethod(delegateMethod) - .addField(String.class, "key", PUBLIC, FINAL) // purposefully public + .addSuperinterface(type.asType()); + + cdiConstructor(typeBuilder, log); + + typeBuilder.addMethod(constructor) + .addField(Logger.class, "logger", PRIVATE, FINAL) .addMethod(logMethod) - .addField(logType, "delegate", PRIVATE, FINAL); + .addField(logType, "delegate", PRIVATE, FINAL) + .addMethod(delegateMethod); methods(type).forEach(m -> processMethod(typeBuilder, m)); @@ -201,6 +200,22 @@ private void createConcrete( } } + private void cdiConstructor(TypeSpec.Builder builder, Logger log) { + if (log.lifecycle() != Lifecycle.CDI) { + return; + } + + builder.addAnnotation(Dependent.class); + + var cdiConstructor = MethodSpec.constructorBuilder() + .addModifiers(PUBLIC) + .addAnnotation(Inject.class) + .addParameter(InjectionPoint.class, "ip") + .addStatement("this(ip.getMember().getDeclaringClass().getName())") + .build(); + builder.addMethod(cdiConstructor); + } + private Stream methods(TypeElement type) { var methods = type.getEnclosedElements().stream() .filter(e -> e.getKind() == ElementKind.METHOD && e.getAnnotation(Message.class) != null) diff --git a/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor index dfb0569..51ce5ca 100644 --- a/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -1 +1 @@ -uk.dansiviter.jule.processor.LogProcessor +uk.dansiviter.jule.processor.LoggerProcessor diff --git a/processor/src/test/java/uk/dansiviter/jule/processor/LogProducerTest.java b/processor/src/test/java/uk/dansiviter/jule/processor/LoggerFactoryTest.java similarity index 85% rename from processor/src/test/java/uk/dansiviter/jule/processor/LogProducerTest.java rename to processor/src/test/java/uk/dansiviter/jule/processor/LoggerFactoryTest.java index 914a98d..ee67705 100644 --- a/processor/src/test/java/uk/dansiviter/jule/processor/LogProducerTest.java +++ b/processor/src/test/java/uk/dansiviter/jule/processor/LoggerFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Daniel Siviter + * Copyright 2023 Daniel Siviter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,6 @@ import java.util.function.Supplier; import java.util.logging.Handler; import java.util.logging.LogRecord; -import java.util.logging.Logger; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -43,41 +42,41 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import uk.dansiviter.jule.LogProducer; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.LoggerFactory; +import uk.dansiviter.jule.annotations.Logger; import uk.dansiviter.jule.annotations.Message; -import uk.dansiviter.jule.annotations.Log.Type; +import uk.dansiviter.jule.annotations.Logger.Type; import uk.dansiviter.jule.annotations.Message.Level; /** - * Unit test for {@link LogProducer}. This has to be in a different project as + * Unit test for {@link LoggerFactory}. This has to be in a different project as * the generated classes are here. */ @ExtendWith(MockitoExtension.class) -class LogProducerTest { +class LoggerFactoryTest { private static Collection HANDLERS; - private final MyJulLog log = LogProducer.log(MyJulLog.class); - private final MySysLog sysLog = LogProducer.log(MySysLog.class); + private final MyJulLog log = LoggerFactory.log(MyJulLog.class); + private final MySysLog sysLog = LoggerFactory.log(MySysLog.class); @BeforeAll public static void beforeAll() { - var root = Logger.getLogger(""); + var root = java.util.logging.Logger.getLogger(""); HANDLERS = Arrays.asList(root.getHandlers()); HANDLERS.forEach(root::removeHandler); } @Test void equivalence() { - assertSame(this.log, LogProducer.log(MyJulLog.class, LogProducerTest.class)); - assertSame(LogProducer.log(MyJulLog.class, "foo"), LogProducer.log(MyJulLog.class, "foo")); - assertNotSame(this.log, LogProducer.log(MyJulLog.class, "foo")); + assertSame(this.log, LoggerFactory.log(MyJulLog.class, LoggerFactoryTest.class)); + assertSame(LoggerFactory.log(MyJulLog.class, "foo"), LoggerFactory.log(MyJulLog.class, "foo")); + assertNotSame(this.log, LoggerFactory.log(MyJulLog.class, "foo")); } @Test void julLog(@Mock Handler handler) { System.getLogger("foo").isLoggable(System.Logger.Level.ERROR); - Logger.getLogger("").addHandler(handler); + java.util.logging.Logger.getLogger("").addHandler(handler); assertNotNull(log); log.doLog(); @@ -102,7 +101,7 @@ var record = records.next(); assertThat(record.getMessage(), is("Hello world!")); assertThat(record.getParameters(), nullValue()); assertThat(record.getThrown(), nullValue()); - assertThat(record.getSourceClassName(), is("uk.dansiviter.jule.processor.LogProducerTest")); + assertThat(record.getSourceClassName(), is("uk.dansiviter.jule.processor.LoggerFactoryTest")); assertThat(record.getSourceMethodName(), is("julLog")); record = records.next(); @@ -141,7 +140,7 @@ record = records.next(); @Test void sysLog(@Mock Handler handler) { System.getLogger("foo").isLoggable(System.Logger.Level.ERROR); - Logger.getLogger("").addHandler(handler); + java.util.logging.Logger.getLogger("").addHandler(handler); assertNotNull(sysLog); sysLog.doLog(); @@ -161,7 +160,7 @@ interface MySuperLog { void doSuperLog(); } - @Log + @Logger interface MyJulLog extends MySuperLog { @Message("Hello world!") void doLog(); @@ -198,7 +197,7 @@ default void anotherMethods(String foo) { } } - @Log(type = Type.SYSTEM) + @Logger(type = Type.SYSTEM) interface MySysLog extends MySuperLog { @Message("Hello world!") void doLog(); @@ -206,7 +205,7 @@ interface MySysLog extends MySuperLog { @AfterAll public static void afterAll() { - var root = Logger.getLogger(""); + var root = java.util.logging.Logger.getLogger(""); HANDLERS.forEach(root::addHandler); } } diff --git a/processor/src/test/java/uk/dansiviter/jule/processor/LogProcessorTest.java b/processor/src/test/java/uk/dansiviter/jule/processor/LoggerProcessorTest.java similarity index 64% rename from processor/src/test/java/uk/dansiviter/jule/processor/LogProcessorTest.java rename to processor/src/test/java/uk/dansiviter/jule/processor/LoggerProcessorTest.java index 0413ba2..4186fc5 100644 --- a/processor/src/test/java/uk/dansiviter/jule/processor/LogProcessorTest.java +++ b/processor/src/test/java/uk/dansiviter/jule/processor/LoggerProcessorTest.java @@ -26,28 +26,40 @@ import org.junit.jupiter.api.Test; /** - * Tests for {@link LogProcessor} + * Tests for {@link LoggerProcessor} */ -class LogProcessorTest { +class LoggerProcessorTest { @Test void process() { var instant = Instant.parse("2023-02-01T01:02:03.000004Z"); Compilation compilation = javac() - .withProcessors(new LogProcessor(() -> instant)) + .withProcessors(new LoggerProcessor(() -> instant)) .compile(JavaFileObjects.forResource("uk/dansiviter/jule/processor/Good.java")); assertThat(compilation).succeeded(); assertThat(compilation).hadNoteContaining("Generating class for: uk.dansiviter.jule.processor.Good"); assertThat(compilation) - .generatedSourceFile("uk/dansiviter/jule/processor/Good$impl") - .hasSourceEquivalentTo(JavaFileObjects.forResource("uk/dansiviter/jule/processor/Good$impl.java")); + .generatedSourceFile("uk/dansiviter/jule/processor/GoodImpl") + .hasSourceEquivalentTo(JavaFileObjects.forResource("uk/dansiviter/jule/processor/GoodImpl.java")); } @Test - void process_bad() - { + void process_cdi() { + var instant = Instant.parse("2023-02-01T01:02:03.000004Z"); + Compilation compilation = javac() + .withProcessors(new LoggerProcessor(() -> instant)) + .compile(JavaFileObjects.forResource("uk/dansiviter/jule/processor/GoodCdi.java")); + assertThat(compilation).succeeded(); + assertThat(compilation).hadNoteContaining("Generating class for: uk.dansiviter.jule.processor.GoodCdi"); + assertThat(compilation) + .generatedSourceFile("uk/dansiviter/jule/processor/GoodCdiImpl") + .hasSourceEquivalentTo(JavaFileObjects.forResource("uk/dansiviter/jule/processor/GoodCdiImpl.java")); + } + + @Test + void process_bad() { Compilation compilation = javac() - .withProcessors(new LogProcessor()) + .withProcessors(new LoggerProcessor()) .compile(JavaFileObjects.forResource("uk/dansiviter/jule/processor/Bad.java")); assertThat(compilation).failed(); assertThat(compilation).hadErrorContaining("Message cannot be empty!"); diff --git a/processor/src/test/resources/uk/dansiviter/jule/processor/Bad.java b/processor/src/test/resources/uk/dansiviter/jule/processor/Bad.java index bfa3aae..822e98c 100644 --- a/processor/src/test/resources/uk/dansiviter/jule/processor/Bad.java +++ b/processor/src/test/resources/uk/dansiviter/jule/processor/Bad.java @@ -1,9 +1,9 @@ package uk.dansiviter.jule.processor; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.annotations.Logger; import uk.dansiviter.jule.annotations.Message; -@Log +@Logger interface Bad { @Message("") void empty(); diff --git a/processor/src/test/resources/uk/dansiviter/jule/processor/Good.java b/processor/src/test/resources/uk/dansiviter/jule/processor/Good.java index 4e60ff0..b781cd9 100644 --- a/processor/src/test/resources/uk/dansiviter/jule/processor/Good.java +++ b/processor/src/test/resources/uk/dansiviter/jule/processor/Good.java @@ -1,9 +1,9 @@ package uk.dansiviter.jule.processor; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.annotations.Logger; import uk.dansiviter.jule.annotations.Message; -@Log +@Logger interface Good { @Message(value = "hello %s", once = true) void foo(String world); diff --git a/processor/src/test/resources/uk/dansiviter/jule/processor/GoodCdi.java b/processor/src/test/resources/uk/dansiviter/jule/processor/GoodCdi.java new file mode 100644 index 0000000..d712a42 --- /dev/null +++ b/processor/src/test/resources/uk/dansiviter/jule/processor/GoodCdi.java @@ -0,0 +1,8 @@ +package uk.dansiviter.jule.processor; + +import uk.dansiviter.jule.annotations.Logger; +import uk.dansiviter.jule.annotations.Logger.Lifecycle; + +@Logger(lifecycle = Lifecycle.CDI) +interface GoodCdi { +} diff --git a/processor/src/test/resources/uk/dansiviter/jule/processor/GoodCdiImpl.java b/processor/src/test/resources/uk/dansiviter/jule/processor/GoodCdiImpl.java new file mode 100644 index 0000000..8adda07 --- /dev/null +++ b/processor/src/test/resources/uk/dansiviter/jule/processor/GoodCdiImpl.java @@ -0,0 +1,48 @@ +package uk.dansiviter.jule.processor; + +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.inject.spi.InjectionPoint; +import jakarta.inject.Inject; +import java.lang.Override; +import java.lang.String; +import javax.annotation.processing.Generated; +import uk.dansiviter.jule.BaseJulLogger; +import uk.dansiviter.jule.annotations.Logger; + +@Generated( + value = "uk.dansiviter.jule.processor.LoggerProcessor", + comments = "https://jule.dansiviter.uk", + date = "2023-02-01T01:02:03.000004Z" +) +@Dependent +public final class GoodCdiImpl implements BaseJulLogger, GoodCdi { + private final Logger logger; + + private final java.util.logging.Logger delegate; + + @Inject + public GoodCdiImpl(InjectionPoint ip) { + this(ip.getMember().getDeclaringClass().getName()); + } + + public GoodCdiImpl(String name) { + this.logger = GoodCdi.class.getAnnotation(Logger.class); + this.delegate = delegate(name); + } + + /** + * @returns the annotation instance. + */ + @Override + public final Logger logger() { + return this.logger; + } + + /** + * @returns the delegate logger. + */ + @Override + public final java.util.logging.Logger delegate() { + return this.delegate; + } +} diff --git a/processor/src/test/resources/uk/dansiviter/jule/processor/Good$impl.java b/processor/src/test/resources/uk/dansiviter/jule/processor/GoodImpl.java similarity index 55% rename from processor/src/test/resources/uk/dansiviter/jule/processor/Good$impl.java rename to processor/src/test/resources/uk/dansiviter/jule/processor/GoodImpl.java index 78a8f94..32e76a0 100644 --- a/processor/src/test/resources/uk/dansiviter/jule/processor/Good$impl.java +++ b/processor/src/test/resources/uk/dansiviter/jule/processor/GoodImpl.java @@ -3,46 +3,42 @@ import java.lang.Override; import java.lang.String; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.logging.Logger; import javax.annotation.processing.Generated; -import uk.dansiviter.jule.BaseJulLog; -import uk.dansiviter.jule.annotations.Log; +import uk.dansiviter.jule.BaseJulLogger; +import uk.dansiviter.jule.annotations.Logger; import uk.dansiviter.jule.annotations.Message; @Generated( - value = "uk.dansiviter.jule.processor.LogProcessor", + value = "uk.dansiviter.jule.processor.LoggerProcessor", comments = "https://jule.dansiviter.uk", date = "2023-02-01T01:02:03.000004Z" ) -public final class Good$impl implements BaseJulLog, Good { +public final class GoodImpl implements BaseJulLogger, Good { private static final AtomicBoolean ONCE__foo = new AtomicBoolean(); - private final Log log; + private final Logger logger; - public final String key; + private final java.util.logging.Logger delegate; - private final Logger delegate; - - public Good$impl(String name, String key) { - this.log = Good.class.getAnnotation(Log.class); - this.key = key; + public GoodImpl(String name) { + this.logger = Good.class.getAnnotation(Logger.class); this.delegate = delegate(name); } /** - * @returns the delegate logger. - */ + * @returns the annotation instance. + */ @Override - public final Logger delegate() { - return this.delegate; + public final Logger logger() { + return this.logger; } /** - * @returns the annotation instance. - */ + * @returns the delegate logger. + */ @Override - public final Log log() { - return this.log; + public final java.util.logging.Logger delegate() { + return this.delegate; } @Override