Skip to content
shueja edited this page Jan 18, 2024 · 12 revisions

Monologue

1. n. A one-sided conversation.

Monologue is a Java annotation-based logging library for FRC. With Monologue, extensive telemetry and on-robot logging can be added to your robot code with minimal code footprint and design restrictions.

Getting Started

Installation

For teams already familiar with Oblog, the setup process for Monologue is very similar.

The first step to using Monologue is to add it to your robot project as a dependency. Monologue is distributed using jitpack.

To add the dependency, we only need to add a couple lines to your project’s build.gradle. First, add the following:

repositories {
  maven { url 'https://jitpack.io' }
}

Secondly, add the following to the dependencies list:

implementation 'com.github.shueja:Monologue:RELEASE_TAG'

where RELEASE_TAG is the latest release version tag (e.g. v1.0.0).

Setting Up the Logger

The first step of setting up Monologue is choosing a base class. Robot.java is a good choice for this, though for command-based teams, RobotContainer.java is a good alternative. The important part is to setup the logger after all contained classes have been constructed, typically at the end of robotInit() or the RobotContainer constructor. Your base class needs to implement monologue.Logged, so that Monologue can create a level in the logging structure for it.

A basic configuration for a simple TimedRobot project would be like this:

package frc.robot;

import monologue.Monologue;
import monologue.Logged;

public class Robot extends TimedRobot implements Logged {
  @Override
  public void robotInit() {
      ... 
      boolean fileOnly = false;
      boolean lazyLogging = false;
      Monologue.setupMonologue(this, "Robot", fileOnly, lazyLogging);
  }
  
  @Override
  public void robotPeriodic() {
     // setFileOnly is used to shut off NetworkTables broadcasting for most logging calls.
     // Basing this condition on the connected state of the FMS is a suggestion only.
     Monologue.setFileOnly(DriverStation.isFMSConnected());
     Monologue.updateAll();
  }
}

The first argument to Monologue.setupLogging() is the root Logged class, which is Robot, or this. The second argument is the path to use for the root container. In this case, values being logged would show up in NetworkTables and DataLog under /Robot/variableName.

IMPORTANT: Forward slashes as path separators is permitted in the rootPath. However, do not start your rootPath with more than one leading forward slash. Monologue will accept /Robot or Robot as the same path (and will add a slash if necessary to log under /Robot). However, additional leading slashes lead to client-dependent display behavior and should generally be avoided.

That is all that is required to configure Monologue to capture all your logged values periodically, publish them to NetworkTables, and add them to the on-robot data logging. Next, let's set up some values to log.

The Logging Annotations

Monologue offers two annotations (@Log.NT, @Log.File), which can be applied to variables of supported types, or methods that return the supported types. Note that any method you apply the annotation to will be run every periodic loop. To use these, simply annotate the field or method with the desired annotation. The class containing the annotated field must implement Logged, even your root container. Once a class has been declared Logged, Monologue will automatically search it for annotated fields and getters to capture - as long as it is reachable from the specified root through a direct sequence of Logged parent classes (the logger will recursively search down the tree of all Logged fields from the root container and place the values in the NT/DataLog tree according to the class structure.).

Annotatable Data Types

  • Primitives: int, long, float, double, boolean
  • Primitive arrays: int[], long[], float[], double[], boolean[]
  • String and String[] (note that logging Strings is significantly more performance-heavy than logging primitives)
  • WPILib struct-serializable types: for example, geometry types (Pose2d, etc), other simple structured data (SwerveModuleState, etc). For the full list of struct-serializable types, see the implementing classes of StructSerializable in WPILib's Javadocs.
  • Arrays of the above struct-serializable types.
  • Sendables such as Field2d and Mechanism2d. (Note that these will always publish to NetworkTables, and ignore the file-only and lazy-logging features of Monologue. This is due to limitations in the Sendable API.)

Logging Levels

Each annotated field can be set up with an optional logging level. These determine how Monologue handles the field based on the global fileOnly flag.

fileOnly=false @Log.NT @Log.File fileOnly=true @Log.NT @Log.File
DEFAULT NT, file file file file
NOT_FILE_ONLY NT, file file none none
OVERRIDE_FILE_ONLY NT, file file NT, file file

LogLevel.NOT_FILE_ONLY

When used with a @Log.File annotation, the field will only appear in the datalog, and only while fileOnly is false. When used with a @Log.NT annotation, the field will appear on NetworkTables and in the datalog, but only while fileOnly is false. When fileOnly is true, the field will not be logged at all.

LogLevel.DEFAULT

If a level is not specified, this level will be used. When used with a @Log.File annotation, the field will appear in the datalog at all times, and will not appear on NetworkTables. When used with a @Log.NT annotation, the field will appear in the datalog at all times. Additionally, the field will appear on NetworkTables, only while fileOnly is false.

LogLevel.OVERRIDE_FILE_ONLY

When used with a @Log.File annotation, the field will appear in the datalog at all times, and will not appear on NetworkTables. When used with a @Log.NT annotation, the field will appear in the datalog at all times and on NetworkTables at all times, regardless of the value of fileOnly.

Use OVERRIDE_FILE_ONLY for any values you want to communicate over NetworkTables while fileOnly is false. This includes driver dashboards and custom coprocessor communication (though Monologue is not recommended for building NetworkTables communication protocols to coprocessors).

Imperative logging within Logged classes

By making a class implement Logged, you not only mark it for Monologue to include in the logging tree, you also gain access to methods that allow you to one-shot log additional fields to the right place in the logging tree.

To imperatively log an additional value, call this.log(String key, T value, LogLevel level). The level parameter is optional, and Monologue will use LogLevel.DEFAULT if not specified. The key will be relative to the Logged class's place in the tree. Therefore, if you are in a class that is logged as /Robot/drivetrain, and call log("error", 2.4), an entry will be in the logging tree under /Robot/drivetrain/error, with a value of 2.4.

There are three major caveats with this feature.

  1. log calls made before Monologue.setupMonologue is called will be silently dropped. This includes calls made in the constructor of the Logged class.
  2. Sendables cannot be logged using these calls, but all other data types above are supported.
  3. Using a key that conflicts with the name of an annotated field or method will likely cause conflicting data recorded in the datalog, and will possibly cause NetworkTables errors. Making log calls to the same key from different places in the class will not cause this issue.
Clone this wiki locally