Logging Environment Variables and JVM Arguments in Java

Introduction

Knowing how your Java application is configured and where it’s running can make or break your debugging and deployment processes. In this guide, we’ll cover how to log environment variables, JVM arguments, and all available Java process details like system properties, classpath, and more. We’ll include examples for each section to give you a clear picture of what comprehensive logging looks like in practice.

Why Log Environment Variables and Java Process Details?

Logging this information provides valuable insight into your application’s runtime environment. It allows you to:

  • Debug Efficiently: Understand configurations and diagnose issues quickly.
  • Validate Environments: Ensure the right settings are applied across different stages of deployment.
  • Monitor Application Behavior: Gain full visibility into JVM options, environment variables, and system properties.

Comprehensive Logging in Java

Here’s how to capture all environment variables, JVM arguments, and Java process details, with code examples and sample outputs for clarity.

1. Logging Environment Variables with Password Masking

Environment variables control much of your app’s configuration. We’ll log these while fully masking sensitive ones like passwords, PWDs, and keys:

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

public class EnvironmentLogger {
    private static final List<String> PROTECTED_VARIABLES = Arrays.asList("PASS", "PWD", "KEY");

    public static void logEnvironmentVariables() {
        Map<String, String> env = System.getenv();
        env.forEach((key, value) -> {
            if (isProtectedVariable(key)) {
                value = maskSensitiveData(value);
            }
            System.out.println(key + ": " + value);
        });
    }

    private static boolean isProtectedVariable(String key) {
        return PROTECTED_VARIABLES.stream().anyMatch(key.toUpperCase()::contains);
    }

    private static String maskSensitiveData(String value) {
        return "********";
    }

    public static void main(String[] args) {
        logEnvironmentVariables();
    }
}

Example Output:

JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64
APP_PORT: 8080
DATABASE_PASS: ********
API_KEY: ********

In this example:

  • Sensitive values like DATABASE_PASS and API_KEY are fully masked.
  • Environment variables are logged with the original variable names for clarity.

2. Logging JVM Arguments

JVM arguments provide insight into how the Java Virtual Machine is configured for your application:

import java.lang.management.ManagementFactory;
import java.util.List;

public class JVMArgumentsLogger {
    public static void logJVMArguments() {
        List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
        jvmArgs.forEach(arg -> System.out.println("JVM Argument: " + arg));
    }

    public static void main(String[] args) {
        logJVMArguments();
    }
}

Example Output:

JVM Argument: -Xmx1024m
JVM Argument: -Duser.timezone=UTC
JVM Argument: -XX:+UseG1GC

This output shows:

  • JVM memory settings (-Xmx).
  • System properties like timezone (-Duser.timezone).
  • Garbage collection options (-XX:+UseG1GC).

3. Logging All System Properties

Java’s system properties provide a complete view of the Java runtime and environment settings:

import java.util.Properties;

public class SystemPropertiesLogger {
    public static void logSystemProperties() {
        Properties properties = System.getProperties();
        properties.forEach((key, value) -> System.out.println(key + ": " + value));
    }

    public static void main(String[] args) {
        logSystemProperties();
    }
}

Example Output:

java.version: 11.0.16
java.vendor: Oracle Corporation
os.name: Linux
user.dir: /home/user/app
user.timezone: UTC
java.class.path: /home/user/app/classes:/home/user/app/lib/*

This section logs:

  • Java version (java.version).
  • Operating system information (os.name).
  • The working directory (user.dir).
  • Classpath details (java.class.path).

Logging system properties gives you a comprehensive snapshot of the runtime environment.

4. Comprehensive Logging with Everything Combined

Let’s combine all these details into one comprehensive method to log everything about the Java environment:

import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class ComprehensiveLogger {
    public static void logEverything() {
        logEnvironmentVariables();
        logJVMArguments();
        logSystemProperties();
    }

    private static final List<String> PROTECTED_VARIABLES = Arrays.asList("PASS", "PWD", "KEY");

    private static void logEnvironmentVariables() {
        Map<String, String> env = System.getenv();
        env.forEach((key, value) -> {
            if (isProtectedVariable(key)) {
                value = maskSensitiveData(value);
            }
            System.out.println(key + ": " + value);
        });
    }

    private static void logJVMArguments() {
        List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
        jvmArgs.forEach(arg -> System.out.println("JVM Argument: " + arg));
    }

    private static void logSystemProperties() {
        Properties properties = System.getProperties();
        properties.forEach((key, value) -> System.out.println(key + ": " + value));
    }

    private static String maskSensitiveData(String value) {
        return "********";
    }

    public static void main(String[] args) {
        logEverything();
    }
}

Example Output:

Environment Variables:
JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64
DATABASE_PASS: ********
API_KEY: ********

JVM Arguments:
JVM Argument: -Xmx1024m
JVM Argument: -Duser.timezone=UTC

System Properties:
java.version: 11.0.16
os.name: Linux
user.dir: /home/user/app
java.class.path: /home/user/app/classes:/home/user/app/lib/*

This logger captures:

  • Environment variables with full masking for sensitive data.
  • JVM arguments for runtime configurations.
  • System properties for detailed runtime information, including paths and version details.

Best Practices for Secure and Effective Logging

  • Use Appropriate Logging Levels: Limit detailed logging to DEBUG or lower levels to avoid exposing sensitive information in production logs.
  • Fully Mask Sensitive Information: Always mask values containing sensitive data like passwords, PWDs, or keys.
  • Restrict Access to Logs: Secure your logs to ensure only authorized personnel can access sensitive information.

Conclusion

Comprehensive logging of environment variables, JVM arguments, and system properties gives you full visibility into your Java application’s configuration. By following these steps and best practices, you can create secure and informative logs that aid in debugging and monitoring without compromising security.


What logging techniques have you found most helpful for your Java projects? Let’s share insights in the comments below!