Skip to content

Commit

Permalink
Merge pull request #1500 from NASA-AMMOS/refactor/streamline-logging
Browse files Browse the repository at this point in the history
Add streamline Logger
  • Loading branch information
dandelany authored Aug 26, 2024
2 parents f5ae30f + f4ddb29 commit f57c9a7
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package gov.nasa.jpl.aerie.contrib.streamline.debugging;

import gov.nasa.jpl.aerie.merlin.framework.Registrar;

import java.util.Arrays;
import java.util.Map;

import static java.util.stream.Collectors.toMap;

public class Logger {
private static final int LEVEL_INDICATOR_SIZE = Arrays.stream(LogLevel.values())
.map(v -> v.toString().length())
.max(Integer::compareTo)
.orElseThrow();
private static final String LOG_MESSAGE_FORMAT = "[%-" + LEVEL_INDICATOR_SIZE + "s] %s";

private final Map<LogLevel, SimpleLogger> subLoggers;

public Logger(Registrar registrar) {
subLoggers = Arrays.stream(LogLevel.values()).collect(toMap(
level -> level,
level -> new SimpleLogger(level.name(), registrar)));
}

public void log(LogLevel level, String messageFormat, Object... args) {
String message = messageFormat.formatted(args);
subLoggers.get(level).log(LOG_MESSAGE_FORMAT.formatted(level, message));
}

public void debug(String messageFormat, Object... args) {
log(LogLevel.DEBUG, messageFormat, args);
}

public void info(String messageFormat, Object... args) {
log(LogLevel.INFO, messageFormat, args);
}

public void warning(String messageFormat, Object... args) {
log(LogLevel.WARNING, messageFormat, args);
}

public void error(String messageFormat, Object... args) {
log(LogLevel.ERROR, messageFormat, args);
}

public enum LogLevel {
DEBUG,
INFO,
WARNING,
ERROR
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package gov.nasa.jpl.aerie.contrib.streamline.debugging;

import gov.nasa.jpl.aerie.merlin.framework.Registrar;

public final class Logging {
private Logging() {}

/**
* The "main" logger. Unless you have a compelling reason to direct logging somewhere else,
* this logger should be used by virtually all model components.
* This logger will be initialized automatically when a registrar is constructed.
*/
public static Logger LOGGER;

/**
* Initialize the primary logger.
* This is called when constructing a {@link gov.nasa.jpl.aerie.contrib.streamline.modeling.Registrar},
* and does not need to be called directly by the model.
*/
public static void init(final Registrar registrar) {
if (LOGGER == null) {
LOGGER = new Logger(registrar);
} else {
LOGGER.warning("Attempting to re-initialize primary logger. This attempt is being ignored.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package gov.nasa.jpl.aerie.contrib.streamline.debugging;

import gov.nasa.jpl.aerie.merlin.framework.CellRef;
import gov.nasa.jpl.aerie.merlin.framework.Registrar;
import gov.nasa.jpl.aerie.merlin.protocol.model.CellType;
import gov.nasa.jpl.aerie.merlin.protocol.model.EffectTrait;
import gov.nasa.jpl.aerie.merlin.protocol.types.Unit;

import static gov.nasa.jpl.aerie.contrib.serialization.rulesets.BasicValueMappers.string;
import static gov.nasa.jpl.aerie.merlin.protocol.types.Unit.UNIT;

public class SimpleLogger {
private final CellRef<String, Unit> cellRef = CellRef.allocate(UNIT, new CellType<>() {
@Override
public EffectTrait<Unit> getEffectType() {
return new EffectTrait<>() {
@Override
public Unit empty() {
return UNIT;
}

@Override
public Unit sequentially(Unit prefix, Unit suffix) {
return UNIT;
}

@Override
public Unit concurrently(Unit left, Unit right) {
return UNIT;
}
};
}

@Override
public Unit duplicate(Unit unit) {
return unit;
}

@Override
public void apply(Unit unit, Unit s) {
}
}, $ -> UNIT);

public SimpleLogger(String name, Registrar registrar) {
registrar.topic(name, cellRef, string());
}

public void log(String message) {
cellRef.emit(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,31 @@

import gov.nasa.jpl.aerie.contrib.serialization.mappers.IntegerValueMapper;
import gov.nasa.jpl.aerie.contrib.serialization.mappers.NullableValueMapper;
import gov.nasa.jpl.aerie.contrib.serialization.mappers.StringValueMapper;
import gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource;
import gov.nasa.jpl.aerie.contrib.streamline.core.Dynamics;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resources;
import gov.nasa.jpl.aerie.contrib.streamline.core.monads.ThinResourceMonad;
import gov.nasa.jpl.aerie.contrib.streamline.debugging.Logging;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.monads.DiscreteResourceMonad;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.linear.Linear;
import gov.nasa.jpl.aerie.merlin.framework.ValueMapper;
import gov.nasa.jpl.aerie.merlin.protocol.types.RealDynamics;
import gov.nasa.jpl.aerie.merlin.protocol.types.Unit;
import org.apache.commons.lang3.exception.ExceptionUtils;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.resource;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Reactions.whenever;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.currentData;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.currentValue;
import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Logging.LOGGER;
import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Naming.*;
import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Profiling.profile;
import static gov.nasa.jpl.aerie.contrib.streamline.debugging.Tracing.trace;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.Registrar.ErrorBehavior.*;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources.not;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources.when;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.monads.DiscreteDynamicsMonad.effect;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.monads.DiscreteResourceMonad.map;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteEffects.increment;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.DiscreteResources.*;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.linear.Linear.linear;
import static gov.nasa.jpl.aerie.merlin.framework.ModelActions.waitUntil;
import static java.util.stream.Collectors.joining;

/**
* Wrapper for {@link gov.nasa.jpl.aerie.merlin.framework.Registrar} specialized for {@link Resource}.
Expand All @@ -49,12 +39,12 @@ public class Registrar {
private final gov.nasa.jpl.aerie.merlin.framework.Registrar baseRegistrar;
private boolean trace = false;
private boolean profile = false;
private final MutableResource<Discrete<Map<Throwable, Set<String>>>> errors;
private final ErrorBehavior errorBehavior;
private final MutableResource<Discrete<Integer>> numberOfErrors = discreteResource(0);

public enum ErrorBehavior {
/**
* Log errors to the error state,
* Log errors to {@link Logging#LOGGER}
* and replace resource value with null.
*/
Log,
Expand All @@ -66,27 +56,11 @@ public enum ErrorBehavior {

public Registrar(final gov.nasa.jpl.aerie.merlin.framework.Registrar baseRegistrar, final ErrorBehavior errorBehavior) {
Resources.init();
Logging.init(baseRegistrar);
this.baseRegistrar = baseRegistrar;
this.errorBehavior = errorBehavior;
errors = resource(Discrete.discrete(Map.of()));
var errorString = map(errors, errors$ -> errors$.entrySet().stream().map(entry -> formatError(entry.getKey(), entry.getValue())).collect(joining("\n\n")));

// Register the errors and number of errors resources for output
// TODO consider using serializable events, rather than resources, to log errors
discrete("errors", errorString, new StringValueMapper());
discrete("numberOfErrors", map(errors, Map::size), new IntegerValueMapper());
}

private static String formatError(Throwable e, Collection<String> affectedResources) {
return "Error affecting %s:%n%s".formatted(
String.join(", ", affectedResources),
formatException(e));
}

private static String formatException(Throwable e) {
return ExceptionUtils.stream(e)
.map(ExceptionUtils::getMessage)
.collect(joining("\nCaused by: "));
discrete("numberOfErrors", numberOfErrors, new IntegerValueMapper());
}

public void setTrace() {
Expand Down Expand Up @@ -148,21 +122,9 @@ private <D extends Dynamics<?, D>> void logErrors(String name, Resource<D> resou
});
}

// TODO: Consider using a MultiMap instead of doing this by hand below
private Unit logError(String resourceName, Throwable e) {
errors.emit(effect(s -> {
var s$ = new HashMap<>(s);
s$.compute(e, (e$, affectedResources) -> {
if (affectedResources == null) {
return Set.of(resourceName);
} else {
var affectedResources$ = new HashSet<>(affectedResources);
affectedResources$.add(resourceName);
return affectedResources$;
}
});
return s$;
}));
LOGGER.error("Error affecting %s: %s", resourceName, e);
increment(numberOfErrors);
return Unit.UNIT;
}

Expand Down

0 comments on commit f57c9a7

Please sign in to comment.