diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/ApplicationConnectionState.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/ApplicationConnectionState.java index d4acea6404..f8d5d80950 100644 --- a/java_tools/proxy_server/src/main/java/com/rusefi/server/ApplicationConnectionState.java +++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/ApplicationConnectionState.java @@ -10,6 +10,7 @@ public class ApplicationConnectionState { @NotNull private final IoStream clientStream; private final ControllerConnectionState state; + private final Birthday birthday = new Birthday(); public ApplicationConnectionState(UserDetails userDetails, IoStream clientStream, ControllerConnectionState state) { this.userDetails = Objects.requireNonNull(userDetails, "userDetails"); @@ -31,6 +32,10 @@ public class ApplicationConnectionState { return userDetails; } + public Birthday getBirthday() { + return birthday; + } + public void close() { try { clientStream.close(); diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java index f2ae0e1121..159207b8dc 100644 --- a/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java +++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/Backend.java @@ -34,8 +34,10 @@ import static com.devexperts.logging.Logging.getLogging; import static com.rusefi.Timeouts.SECOND; /** - * See NetworkConnectorStartup + * See NetworkConnectorStartup - NetworkConnector connects an ECU to this backend * + * @see ControllerConnectionState ECU session + * @see ApplicationConnectionState tuning application session * @see BackendLauncher */ public class Backend implements Closeable { @@ -51,6 +53,7 @@ public class Backend implements Closeable { * @see BinaryProtocolProxy#USER_IO_TIMEOUT */ private static final int APPLICATION_INACTIVITY_TIMEOUT = 3 * Timeouts.MINUTE; + static final String AGE = "age"; private final FkRegex showOnlineControllers = new FkRegex(ProxyClient.LIST_CONTROLLERS_PATH, (Take) req -> getControllersOnline() @@ -261,6 +264,7 @@ public class Backend implements Closeable { JsonObject applicationObject = Json.createObjectBuilder() .add(UserDetails.USER_ID, application.getUserDetails().getUserId()) .add(UserDetails.USERNAME, application.getUserDetails().getUserName()) + .add(AGE, application.getBirthday().getDuration()) .add(MAX_PACKET_GAP, application.getClientStream().getStreamStats().getMaxPacketGap()) .build(); builder.add(applicationObject); @@ -281,6 +285,7 @@ public class Backend implements Closeable { JsonObjectBuilder objectBuilder = Json.createObjectBuilder() .add(UserDetails.USER_ID, client.getUserDetails().getUserId()) .add(UserDetails.USERNAME, client.getUserDetails().getUserName()) + .add(AGE, client.getBirthday().getDuration()) .add(ProxyClient.IS_USED, client.getTwoKindSemaphore().isUsed()) .add(ControllerStateDetails.RPM, rpm) .add(ControllerStateDetails.CLT, clt) @@ -351,6 +356,10 @@ public class Backend implements Closeable { isClosed = true; } + public boolean isClosed() { + return isClosed; + } + public List getControllers() { synchronized (lock) { return new ArrayList<>(controllers); diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/Birthday.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/Birthday.java new file mode 100644 index 0000000000..bd64ff2c87 --- /dev/null +++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/Birthday.java @@ -0,0 +1,26 @@ +package com.rusefi.server; + +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; + +public class Birthday { + private final long createAt = System.currentTimeMillis(); + + public String getDuration() { + return humanReadableFormat(System.currentTimeMillis() - createAt); + } + + @NotNull + static String humanReadableFormat(long millis) { + return humanReadableFormat(Duration.ofMillis(millis)); + } + + @NotNull + static String humanReadableFormat(Duration duration) { + return duration.toString() + .substring(2) + .replaceAll("(\\d[HMS])(?!$)", "$1 ") + .toLowerCase(); + } +} diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/ControllerConnectionState.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/ControllerConnectionState.java index 63408c91e4..4e3ba721eb 100644 --- a/java_tools/proxy_server/src/main/java/com/rusefi/server/ControllerConnectionState.java +++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/ControllerConnectionState.java @@ -36,6 +36,7 @@ public class ControllerConnectionState { private final TwoKindSemaphore twoKindSemaphore = new TwoKindSemaphore(); private final SensorsHolder sensorsHolder = new SensorsHolder(); + private final Birthday birthday = new Birthday(); public ControllerConnectionState(Socket clientSocket, UserDetailsResolver userDetailsResolver) { this.clientSocket = clientSocket; @@ -48,6 +49,10 @@ public class ControllerConnectionState { } } + public Birthday getBirthday() { + return birthday; + } + public IoStream getStream() { return stream; } diff --git a/java_tools/proxy_server/src/main/java/com/rusefi/server/Monitoring.java b/java_tools/proxy_server/src/main/java/com/rusefi/server/Monitoring.java index fd6b26c97f..c19e30d3b9 100644 --- a/java_tools/proxy_server/src/main/java/com/rusefi/server/Monitoring.java +++ b/java_tools/proxy_server/src/main/java/com/rusefi/server/Monitoring.java @@ -1,5 +1,6 @@ package com.rusefi.server; +import com.devexperts.logging.Logging; import com.rusefi.rusEFIVersion; import com.rusefi.tools.online.ProxyClient; import org.takes.Take; @@ -13,15 +14,22 @@ import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.util.Date; +import static com.rusefi.Timeouts.SECOND; +import static com.rusefi.binaryprotocol.BinaryProtocol.sleep; + public class Monitoring { public static final String STATUS = "/status"; + private static final Logging log = Logging.getLogging(Monitoring.class); + private static final int PERIOD = 10 * SECOND; final FkRegex showStatistics; private final Backend backend; + private final Birthday birthday = new Birthday(); + public Monitoring(Backend backend) { this.backend = backend; - showStatistics = new FkRegex(STATUS, - (Take) req -> getStatus()); + showStatistics = new FkRegex(STATUS, (Take) req -> getStatus()); + startHeartbeatThread(); } private static String formatSize(long v) { @@ -30,6 +38,26 @@ public class Monitoring { return String.format("%.1f %sB", (double) v / (1L << (z * 10)), " KMGTPE".charAt(z)); } + public void startHeartbeatThread() { + new Thread(new Runnable() { + @Override + public void run() { + while (!backend.isClosed()) { + writeLogEntry(); + sleep(PERIOD); + } + } + }, "heartbeat").start(); + } + + private void writeLogEntry() { + OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); + log.info("cpu=" + operatingSystemMXBean.getSystemLoadAverage() + + ",free=" + Runtime.getRuntime().freeMemory() + + ",sessions=" + Backend.totalSessions.get() + + ",threads=" + Thread.getAllStackTraces().size()); + } + private RsJson getStatus() throws IOException { JsonObjectBuilder builder = Json.createObjectBuilder(); OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); @@ -46,6 +74,7 @@ public class Monitoring { builder.add("framework version", rusEFIVersion.CONSOLE_VERSION); builder.add("compiled", new Date(rusEFIVersion.classBuildTimeMillis()).toString()); builder.add("now", System.currentTimeMillis()); + builder.add(Backend.AGE, birthday.getDuration()); return new RsJson(builder.build()); } diff --git a/java_tools/proxy_server/src/test/java/com/rusefi/server/BirthdayTest.java b/java_tools/proxy_server/src/test/java/com/rusefi/server/BirthdayTest.java new file mode 100644 index 0000000000..3ab8274acb --- /dev/null +++ b/java_tools/proxy_server/src/test/java/com/rusefi/server/BirthdayTest.java @@ -0,0 +1,16 @@ +package com.rusefi.server; + +import org.junit.Test; + +import static com.rusefi.Timeouts.MINUTE; +import static com.rusefi.Timeouts.SECOND; +import static com.rusefi.server.Birthday.humanReadableFormat; +import static org.junit.Assert.assertEquals; + +public class BirthdayTest { + @Test + public void testDurationString() { + assertEquals("1s", humanReadableFormat(SECOND)); + assertEquals("1m", humanReadableFormat(MINUTE)); + } +}