[epilogue] Add an annotation-based logging framework for Java programs (#6584)

This commit is contained in:
Sam Carlberg
2024-07-16 20:25:43 -04:00
committed by GitHub
parent 30c7632ab8
commit 59256f0e00
51 changed files with 5147 additions and 7 deletions

View File

@@ -0,0 +1,44 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.epilogue.logging;
import static org.junit.jupiter.api.Assertions.assertEquals;
import edu.wpi.first.epilogue.Logged;
import java.util.List;
import org.junit.jupiter.api.Test;
class ClassSpecificLoggerTest {
@Logged
record Point2d(double x, double y, int dim) {
static class Logger extends ClassSpecificLogger<Point2d> {
Logger() {
super(Point2d.class);
}
@Override
protected void update(DataLogger dataLogger, Point2d object) {
dataLogger.log("x", object.x);
dataLogger.log("y", object.y);
dataLogger.log("dim", object.dim);
}
}
}
@Test
void testReadPrivate() {
var point = new Point2d(1, 4, 2);
var logger = new Point2d.Logger();
var dataLog = new TestLogger();
logger.update(dataLog.getSubLogger("Point"), point);
assertEquals(
List.of(
new TestLogger.LogEntry<>("Point/x", 1.0),
new TestLogger.LogEntry<>("Point/y", 4.0),
new TestLogger.LogEntry<>("Point/dim", 2)),
dataLog.getEntries());
}
}

View File

@@ -0,0 +1,56 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.epilogue.logging;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import java.util.List;
import org.junit.jupiter.api.Test;
class LazyLoggerTest {
@Test
void lazyOfLazyReturnsSelf() {
var lazy = new LazyLogger(new NullLogger());
assertSame(lazy, lazy.lazy());
}
@Test
void lazyInt() {
var logger = new TestLogger();
var lazy = new LazyLogger(logger);
{
// First time logging to "int" should go through
lazy.log("int", 0);
assertEquals(List.of(new TestLogger.LogEntry<>("int", 0)), logger.getEntries());
}
{
// Logging the current value shouldn't go through
lazy.log("int", 0);
assertEquals(List.of(new TestLogger.LogEntry<>("int", 0)), logger.getEntries());
}
{
// Logging a new value should go through
lazy.log("int", 1);
assertEquals(
List.of(new TestLogger.LogEntry<>("int", 0), new TestLogger.LogEntry<>("int", 1)),
logger.getEntries());
}
{
// Logging a previous value should go through
lazy.log("int", 0);
assertEquals(
List.of(
new TestLogger.LogEntry<>("int", 0),
new TestLogger.LogEntry<>("int", 1),
new TestLogger.LogEntry<>("int", 0)),
logger.getEntries());
}
}
}

View File

@@ -0,0 +1,109 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.epilogue.logging;
import edu.wpi.first.util.struct.Struct;
import edu.wpi.first.util.struct.StructBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SuppressWarnings("PMD.TestClassWithoutTestCases") // This is not a test class!
public class TestLogger implements DataLogger {
public record LogEntry<T>(String identifier, T value) {}
private final Map<String, SubLogger> m_subLoggers = new HashMap<>();
private final List<LogEntry<?>> m_entries = new ArrayList<>();
public List<LogEntry<?>> getEntries() {
return m_entries;
}
@Override
public DataLogger getSubLogger(String path) {
return m_subLoggers.computeIfAbsent(path, k -> new SubLogger(k, this));
}
@Override
public void log(String identifier, int value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, long value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, float value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, double value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, boolean value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, byte[] value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, int[] value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, long[] value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, float[] value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, double[] value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, boolean[] value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, String value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public void log(String identifier, String[] value) {
m_entries.add(new LogEntry<>(identifier, value));
}
@Override
public <S> void log(String identifier, S value, Struct<S> struct) {
var serialized = StructBuffer.create(struct).write(value).array();
m_entries.add(new LogEntry<>(identifier, serialized));
}
@Override
public <S> void log(String identifier, S[] value, Struct<S> struct) {
var serialized = StructBuffer.create(struct).writeArray(value).array();
m_entries.add(new LogEntry<>(identifier, serialized));
}
}