From adf4211aa01efc2072f5e5c113c76f4b672d8a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Mu=C3=B1oz?= Date: Sun, 21 Apr 2024 17:58:14 +0200 Subject: [PATCH 1/2] implemente a pure logger --- .../tonivade/purefun/effect/util/PureLog.java | 85 +++++++++++++++++++ .../purefun/effect/util/PureLogTest.java | 28 ++++++ 2 files changed, 113 insertions(+) create mode 100644 effect/src/main/java/com/github/tonivade/purefun/effect/util/PureLog.java create mode 100644 effect/src/test/java/com/github/tonivade/purefun/effect/util/PureLogTest.java diff --git a/effect/src/main/java/com/github/tonivade/purefun/effect/util/PureLog.java b/effect/src/main/java/com/github/tonivade/purefun/effect/util/PureLog.java new file mode 100644 index 000000000..a8e3d0a03 --- /dev/null +++ b/effect/src/main/java/com/github/tonivade/purefun/effect/util/PureLog.java @@ -0,0 +1,85 @@ +package com.github.tonivade.purefun.effect.util; + +import java.lang.StackWalker.Option; +import java.lang.StackWalker.StackFrame; +import java.util.Queue; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import com.github.tonivade.purefun.core.Producer; +import com.github.tonivade.purefun.core.Unit; +import com.github.tonivade.purefun.effect.RIO; + +public interface PureLog { + + PureLog.Service logger(); + + interface Service { + + default RIO debug(Producer message) { + return log(Level.FINE, message); + } + + default RIO info(Producer message) { + return log(Level.INFO, message); + } + + default RIO warn(Producer message) { + return log(Level.WARNING, message); + } + + default RIO error(Producer message) { + return log(Level.SEVERE, message); + } + + RIO log(Level level, Producer message); + } + + @SafeVarargs + static PureLog jul(T...reified) { + var clazz = getClassOf(reified); + return new PureLog() { + @Override + public Service logger() { + return (level, message) -> { + var frame = getFrame(clazz); + var logger = Logger.getLogger(frame.getClassName()); + return RIO.exec(() -> logger.logp(level, frame.getClassName(), frame.getMethodName(), message.get())); + }; + } + }; + } + + @SafeVarargs + static PureLog test(final Queue traces, T...reified) { + var clazz = getClassOf(reified); + return new PureLog() { + @Override + public Service logger() { + return (level, message) -> { + var frame = getFrame(clazz); + return RIO.exec(() -> { + LogRecord log = new LogRecord(level, message.get()); + log.setSourceClassName(frame.getClassName()); + log.setSourceMethodName(frame.getMethodName()); + traces.add(log); + }); + }; + } + }; + } + + private static StackFrame getFrame(Class clazz) { + var walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE); + return walker.walk(frames -> frames.filter(f -> f.getClassName().equals(clazz.getName())).findFirst().orElseThrow()); + } + + @SuppressWarnings("unchecked") + private static Class getClassOf(T... reified) { + if (reified.length > 0) { + throw new IllegalArgumentException("do not pass arguments to this function, it's just a trick to get refied types"); + } + return (Class) reified.getClass().getComponentType(); + } +} diff --git a/effect/src/test/java/com/github/tonivade/purefun/effect/util/PureLogTest.java b/effect/src/test/java/com/github/tonivade/purefun/effect/util/PureLogTest.java new file mode 100644 index 000000000..32b396ca1 --- /dev/null +++ b/effect/src/test/java/com/github/tonivade/purefun/effect/util/PureLogTest.java @@ -0,0 +1,28 @@ +package com.github.tonivade.purefun.effect.util; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import org.junit.jupiter.api.Test; + +class PureLogTest { + + @Test + void test() { + Queue traces = new LinkedList<>(); + + var log = PureLog.test(traces); + + log.logger().info(() -> "this is a test").safeRunSync(log); + + LogRecord logRecord = traces.poll(); + assertEquals("this is a test", logRecord.getMessage()); + assertEquals(Level.INFO, logRecord.getLevel()); + assertEquals(PureLogTest.class.getName(), logRecord.getSourceClassName()); + assertEquals("test", logRecord.getSourceMethodName()); + } +} From b058e4bcd9c8d1c0c1e83d6833adb9cb1a50ac98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Mu=C3=B1oz?= Date: Sun, 21 Apr 2024 18:45:29 +0200 Subject: [PATCH 2/2] get class and method before logging --- .../tonivade/purefun/effect/util/PureLog.java | 62 ++++++++++--------- .../tonivade/purefun/effect/RIOTest.java | 18 +++--- .../purefun/effect/util/PureLogTest.java | 4 +- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/effect/src/main/java/com/github/tonivade/purefun/effect/util/PureLog.java b/effect/src/main/java/com/github/tonivade/purefun/effect/util/PureLog.java index a8e3d0a03..b349fdd6e 100644 --- a/effect/src/main/java/com/github/tonivade/purefun/effect/util/PureLog.java +++ b/effect/src/main/java/com/github/tonivade/purefun/effect/util/PureLog.java @@ -13,7 +13,27 @@ public interface PureLog { - PureLog.Service logger(); + PureLog.Service logger(StackFrame frame); + + static RIO debug(Producer message) { + var frame = getFrame(); + return RIO.accessM(env -> env.logger(frame).debug(message)); + } + + static RIO info(Producer message) { + var frame = getFrame(); + return RIO.accessM(env -> env.logger(frame).info(message)); + } + + static RIO warn(Producer message) { + var frame = getFrame(); + return RIO.accessM(env -> env.logger(frame).warn(message)); + } + + static RIO error(Producer message) { + var frame = getFrame(); + return RIO.accessM(env -> env.logger(frame).error(message)); + } interface Service { @@ -36,14 +56,11 @@ default RIO error(Producer message) { RIO log(Level level, Producer message); } - @SafeVarargs - static PureLog jul(T...reified) { - var clazz = getClassOf(reified); + static PureLog javaUtilLogging() { return new PureLog() { @Override - public Service logger() { + public Service logger(StackFrame frame) { return (level, message) -> { - var frame = getFrame(clazz); var logger = Logger.getLogger(frame.getClassName()); return RIO.exec(() -> logger.logp(level, frame.getClassName(), frame.getMethodName(), message.get())); }; @@ -51,35 +68,22 @@ public Service logger() { }; } - @SafeVarargs - static PureLog test(final Queue traces, T...reified) { - var clazz = getClassOf(reified); + static PureLog test(final Queue traces) { return new PureLog() { @Override - public Service logger() { - return (level, message) -> { - var frame = getFrame(clazz); - return RIO.exec(() -> { - LogRecord log = new LogRecord(level, message.get()); - log.setSourceClassName(frame.getClassName()); - log.setSourceMethodName(frame.getMethodName()); - traces.add(log); - }); - }; + public Service logger(StackFrame frame) { + return (level, message) -> RIO.exec(() -> { + var log = new LogRecord(level, message.get()); + log.setSourceClassName(frame.getClassName()); + log.setSourceMethodName(frame.getMethodName()); + traces.add(log); + }); } }; } - private static StackFrame getFrame(Class clazz) { + private static StackFrame getFrame() { var walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE); - return walker.walk(frames -> frames.filter(f -> f.getClassName().equals(clazz.getName())).findFirst().orElseThrow()); - } - - @SuppressWarnings("unchecked") - private static Class getClassOf(T... reified) { - if (reified.length > 0) { - throw new IllegalArgumentException("do not pass arguments to this function, it's just a trick to get refied types"); - } - return (Class) reified.getClass().getComponentType(); + return walker.walk(frames -> frames.skip(2).findFirst().orElseThrow()); } } diff --git a/effect/src/test/java/com/github/tonivade/purefun/effect/RIOTest.java b/effect/src/test/java/com/github/tonivade/purefun/effect/RIOTest.java index 91984c3d2..4f05c0681 100644 --- a/effect/src/test/java/com/github/tonivade/purefun/effect/RIOTest.java +++ b/effect/src/test/java/com/github/tonivade/purefun/effect/RIOTest.java @@ -11,6 +11,7 @@ import static com.github.tonivade.purefun.effect.RIO.sleep; import static com.github.tonivade.purefun.effect.RIO.task; import static com.github.tonivade.purefun.effect.RIOOf.toRIO; +import static com.github.tonivade.purefun.effect.util.PureLog.info; import static java.util.concurrent.ThreadLocalRandom.current; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -36,6 +37,7 @@ import com.github.tonivade.purefun.core.Producer; import com.github.tonivade.purefun.core.Unit; import com.github.tonivade.purefun.data.Sequence; +import com.github.tonivade.purefun.effect.util.PureLog; import com.github.tonivade.purefun.instances.RIOInstances; import com.github.tonivade.purefun.type.Either; import com.github.tonivade.purefun.type.Try; @@ -171,22 +173,22 @@ public void traverse() { @Test void raceA() { - RIO> race = race( - RIO.sleep(Duration.ofMillis(10)).map(x -> 10), - RIO.sleep(Duration.ofMillis(100)).map(x -> "b")); + RIO> race = race( + RIO.sleep(Duration.ofMillis(10)).andThen(info(() -> "done A")).map(x -> 10), + RIO.sleep(Duration.ofMillis(100)).andThen(info(() -> "done B")).map(x -> "b")); - Try> orElseThrow = race.safeRunSync(null); + Try> orElseThrow = race.safeRunSync(PureLog.javaUtilLogging()); assertEquals(Try.success(Either.left(10)), orElseThrow); } @Test void raceB() { - RIO> race = race( - RIO.sleep(Duration.ofMillis(100)).map(x -> 10), - RIO.sleep(Duration.ofMillis(10)).map(x -> "b")); + RIO> race = race( + RIO.sleep(Duration.ofMillis(100)).andThen(info(() -> "done A")).map(x -> 10), + RIO.sleep(Duration.ofMillis(10)).andThen(info(() -> "done B")).map(x -> "b")); - Try> orElseThrow = race.safeRunSync(null); + Try> orElseThrow = race.safeRunSync(PureLog.javaUtilLogging()); assertEquals(Try.success(Either.right("b")), orElseThrow); } diff --git a/effect/src/test/java/com/github/tonivade/purefun/effect/util/PureLogTest.java b/effect/src/test/java/com/github/tonivade/purefun/effect/util/PureLogTest.java index 32b396ca1..4db8a4157 100644 --- a/effect/src/test/java/com/github/tonivade/purefun/effect/util/PureLogTest.java +++ b/effect/src/test/java/com/github/tonivade/purefun/effect/util/PureLogTest.java @@ -15,9 +15,7 @@ class PureLogTest { void test() { Queue traces = new LinkedList<>(); - var log = PureLog.test(traces); - - log.logger().info(() -> "this is a test").safeRunSync(log); + PureLog.info(() -> "this is a test").safeRunSync(PureLog.test(traces)); LogRecord logRecord = traces.poll(); assertEquals("this is a test", logRecord.getMessage());