Log fewer errors (#2246)

## Description

We currently log some things with ERROR status and include stack traces
for events that are typical behavior. This pollutes the logs and makes
it harder to track down real errors.

This PR changes the way that some events are logged:
* missing configs in the database are logged as [INFO] without the
exception stack trace.
* skip parsing NPU usage when the command is blank so that it doesn't
throw a NumberFormatException.
* log warn instead of error for unsupported NN backends (added by
@samfreund)
* skip warn when we don't add a model, only debug when we add it (added
by @samfreund)

Before:
```
Oct 22 20:56:26 photonvision java[831]: [2024-10-22 20:56:26] [Config - SqlConfigProvider] [ERROR] Could not deserialize apriltag layout! Loading defaults: Provided empty string for class edu.wpi.first.apriltag.AprilTagFieldLayout
Oct 22 20:56:26 photonvision java[831]: [2024-10-22 20:56:26] [Config - SqlConfigProvider] [ERROR] org.eclipse.jetty.io.EofException: Provided empty string for class edu.wpi.first.apriltag.AprilTagFieldLayout
Oct 22 20:56:26 photonvision java[831]:         at org.photonvision.common.util.file.JacksonUtils.deserialize(JacksonUtils.java:136)
Oct 22 20:56:26 photonvision java[831]:         at org.photonvision.common.configuration.SqlConfigProvider.load(SqlConfigProvider.java:298)
Oct 22 20:56:26 photonvision java[831]:         at org.photonvision.common.configuration.ConfigManager.load(ConfigManager.java:198)
Oct 22 20:56:26 photonvision java[831]:         at org.photonvision.Main.main(Main.java:290)
```

After:
```
Dec 15 22:29:09 photonvision java[662]: [2025-12-15 22:29:09] [Config - SqlConfigProvider] [INFO] Missing AprilTag Field Layout in database. Loading defaults
```

## Meta

Merge checklist:
- [x] Pull Request title is [short, imperative
summary](https://cbea.ms/git-commit/) of proposed changes
- [x] The description documents the _what_ and _why_
- [ ] If this PR changes behavior or adds a feature, user documentation
is updated
- [ ] If this PR touches photon-serde, all messages have been
regenerated and hashes have not changed unexpectedly
- [ ] If this PR touches configuration, this is backwards compatible
with settings back to v2025.3.2
- [ ] If this PR touches pipeline settings or anything related to data
exchange, the frontend typing is updated
- [ ] If this PR addresses a bug, a regression test for it is added

---------

Co-authored-by: samfreund <samf.236@proton.me>
Co-authored-by: Gold856 <117957790+Gold856@users.noreply.github.com>
This commit is contained in:
Craig Schardt
2025-12-19 20:35:18 -06:00
committed by GitHub
parent 7a88487131
commit 9d587d5746
4 changed files with 64 additions and 67 deletions

View File

@@ -416,9 +416,7 @@ public class NeuralNetworkModelManager {
for (ModelProperties model : getShippedProperties(modelsDirectory).getModels()) {
if (supportedBackends.contains(model.family())) {
supportedProperties.addModelProperties(model);
} else {
logger.warn(
"Skipping model " + model.nickname() + " as it is not supported on this platform.");
logger.debug("Added shipped model: " + model.modelPath().getFileName().toString());
}
}

View File

@@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.photonvision.common.configuration.CameraConfiguration.LegacyCameraConfigStruct;
import org.photonvision.common.configuration.DatabaseSchema.Columns;
@@ -253,6 +254,45 @@ public class SqlConfigProvider extends ConfigProvider {
return true;
}
private <T> T loadConfigOrDefault(
Connection conn, String key, Class<T> ref, Supplier<T> factory) {
String configString = getOneConfigFile(conn, key);
T configObj;
if (!configString.isBlank()) {
try {
configObj = JacksonUtils.deserialize(configString, ref);
logger.info("Loaded " + ref.getSimpleName() + " from database");
return configObj;
} catch (IOException e) {
logger.error("Could not deserialize " + ref.getSimpleName() + " from database!", e);
}
} else {
logger.debug("No " + ref.getSimpleName() + " in database");
}
// either the config entry is empty or Jackson threw and exception
try {
configObj = factory.get();
logger.info("Loaded default " + ref.getSimpleName());
return configObj;
} catch (Exception e) {
logger.error("Failed to construct a default instance of " + ref.getSimpleName(), e);
}
return null;
}
private AprilTagFieldLayout atflDefault() {
AprilTagFieldLayout atfl;
try {
atfl = AprilTagFieldLayout.loadField(AprilTagFields.kDefaultField);
logger.info("Loaded " + AprilTagFields.kDefaultField.toString() + " field");
} catch (UncheckedIOException e) {
logger.error("Error loading WPILib field", e);
logger.info("Creating an empty field");
atfl = new AprilTagFieldLayout(List.of(), 1, 1);
}
return atfl;
}
@Override
public void load() {
logger.debug("Loading config...");
@@ -260,68 +300,24 @@ public class SqlConfigProvider extends ConfigProvider {
if (conn == null) return;
synchronized (m_mutex) {
HardwareConfig hardwareConfig;
HardwareSettings hardwareSettings;
NetworkConfig networkConfig;
AprilTagFieldLayout atfl;
NeuralNetworkPropertyManager nnProps;
try {
hardwareConfig =
JacksonUtils.deserialize(
getOneConfigFile(conn, GlobalKeys.HARDWARE_CONFIG), HardwareConfig.class);
} catch (IOException e) {
logger.error("Could not deserialize hardware config! Loading defaults", e);
hardwareConfig = new HardwareConfig();
}
try {
hardwareSettings =
JacksonUtils.deserialize(
getOneConfigFile(conn, GlobalKeys.HARDWARE_SETTINGS), HardwareSettings.class);
} catch (IOException e) {
logger.error("Could not deserialize hardware settings! Loading defaults", e);
hardwareSettings = new HardwareSettings();
}
try {
networkConfig =
JacksonUtils.deserialize(
getOneConfigFile(conn, GlobalKeys.NETWORK_CONFIG), NetworkConfig.class);
} catch (IOException e) {
logger.error("Could not deserialize network config! Loading defaults", e);
networkConfig = new NetworkConfig();
}
try {
atfl =
JacksonUtils.deserialize(
getOneConfigFile(conn, GlobalKeys.ATFL_CONFIG_FILE), AprilTagFieldLayout.class);
} catch (IOException e) {
logger.error("Could not deserialize apriltag layout! Loading defaults", e);
try {
atfl = AprilTagFieldLayout.loadField(AprilTagFields.kDefaultField);
} catch (UncheckedIOException e2) {
logger.error("Error loading WPILib field", e);
atfl = null;
}
if (atfl == null) {
// what do we even do here lmao -- wpilib should always work
logger.error("Field layout is *still* null??????");
atfl = new AprilTagFieldLayout(List.of(), 1, 1);
}
}
try {
nnProps =
JacksonUtils.deserialize(
getOneConfigFile(conn, GlobalKeys.NEURAL_NETWORK_PROPERTIES),
NeuralNetworkPropertyManager.class);
} catch (IOException e) {
logger.error("Could not deserialize neural network properties! Loading defaults", e);
nnProps = new NeuralNetworkPropertyManager();
}
var hardwareConfig =
loadConfigOrDefault(
conn, GlobalKeys.HARDWARE_CONFIG, HardwareConfig.class, HardwareConfig::new);
var hardwareSettings =
loadConfigOrDefault(
conn, GlobalKeys.HARDWARE_SETTINGS, HardwareSettings.class, HardwareSettings::new);
var networkConfig =
loadConfigOrDefault(
conn, GlobalKeys.NETWORK_CONFIG, NetworkConfig.class, NetworkConfig::new);
var nnProps =
loadConfigOrDefault(
conn,
GlobalKeys.NEURAL_NETWORK_PROPERTIES,
NeuralNetworkPropertyManager.class,
NeuralNetworkPropertyManager::new);
var atfl =
loadConfigOrDefault(
conn, GlobalKeys.ATFL_CONFIG_FILE, AprilTagFieldLayout.class, this::atflDefault);
var cams = loadCameraConfigs(conn);
try {

View File

@@ -199,6 +199,9 @@ public class MetricsManager {
* @return An array of doubles representing NPU usage, or null if parsing fails.
*/
public double[] getNpuUsage() {
if (cmds.npuUsageCommand.isBlank()) {
return new double[0];
}
String[] usages = safeExecute(cmds.npuUsageCommand).split(",");
double[] usageDoubles = new double[usages.length];
for (int i = 0; i < usages.length; i++) {

View File

@@ -241,13 +241,13 @@ public class Main {
if (Platform.isRK3588()) {
tryLoadJNI(JNITypes.RKNN_DETECTOR);
} else {
logger.error("Platform does not support RKNN based machine learning!");
logger.warn("Platform does not support RKNN based machine learning!");
}
if (Platform.isQCS6490()) {
tryLoadJNI(JNITypes.RUBIK_DETECTOR);
} else {
logger.error("Platform does not support Rubik based machine learning!");
logger.warn("Platform does not support Rubik based machine learning!");
}
if (Platform.isWindows() || Platform.isLinux()) {