Skip to main content
Version: 2.x

System

System service contains several useful functions related to system environments and properties. Both of system environments and system properties are key/value pairs. They are used to pass user-defined information to our application. It follows the shared service pattern common to all of ZIO's built-in services: a System trait, a Live implementation, and a synchronous UnsafeAPI for interop.

Environment variables are global operating system level variables available to all applications running on the same machine, while properties are application-level variables provided to our application. The two kinds of access fail differently: environment variable methods fail with SecurityException if a security manager denies access, while system property methods fail with the broader, unrefined Throwable. lineSeparator cannot fail at all (UIO).

System Environment​

The env function retrieves the value of an environment variable:

import zio._

for {
user <- System.env("USER")
_ <- user match {
case Some(value) =>
Console.printLine(s"The USER env is: $value")
case None =>
Console.printLine("Oops! The USER env is not set")
}
} yield ()

envOrElse and envOrOption provide a fallback instead of an Option, and envs reads every environment variable as a Map:

import zio._

for {
logLevel <- System.envOrElse("LOG_LEVEL", "INFO")
logLevelOp <- System.envOrOption("LOG_LEVEL", Some("INFO"))
all <- System.envs
} yield ()

System Property​

Also, the System service has a property function to retrieve the value of a system property:

import zio._

for {
user <- System.property("LOG_LEVEL")
_ <- user match {
case Some(value) =>
Console.printLine(s"The LOG_LEVEL property is: $value")
case None =>
Console.printLine("Oops! The LOG_LEVEL property is not set")
}
} yield ()

propertyOrElse and propertyOrOption provide a fallback instead of an Option, and properties reads every system property as a Map:

import zio._

for {
logLevel <- System.propertyOrElse("log.level", "INFO")
logLevelOp <- System.propertyOrOption("log.level", Some("INFO"))
all <- System.properties
} yield ()

Operating System Detection​

System.os reports which operating system the JVM is running on as a lazily-evaluated, cached OS value, derived from the os.name system property:

sealed trait OS {
def isWindows: Boolean
def isMac: Boolean
def isUnix: Boolean
def isSolaris: Boolean
def isUnknown: Boolean
}

OS is a sealed trait with cases OS.Windows, OS.Mac, OS.Unix, OS.Solaris, and OS.Unknown, and each is* predicate simply checks equality against the corresponding case:

import zio._

val describeOS: UIO[String] =
ZIO.succeed(System.os).map {
case os if os.isWindows => "Windows"
case os if os.isMac => "Mac"
case os if os.isUnix => "Unix"
case os if os.isSolaris => "Solaris"
case _ => "Unknown"
}

Miscellaneous​

With the lineSeparator method, we can determine the line separator for the underlying platform:

System.lineSeparator
// res5: String = """
// """

Synchronous Access (unsafe)​

System also exposes a synchronous UnsafeAPI, following the pattern described in Anatomy of a Built-in Service. SystemLive's unsafe implementation delegates directly to java.lang.System.getenv/getenv() for env/envs, java.lang.System.getProperty/getProperties() for property/properties, and java.lang.System.lineSeparator for lineSeparator, without running an effect:

import zio._

Unsafe.unsafe { implicit unsafe =>
val user: Option[String] = System.SystemLive.unsafe.env("USER")
}

Prefer the ordinary System.env/System.property-style accessors everywhere else.

Testing Environment/System Access​

TestSystem lets tests set and clear in-memory environment variables and properties instead of touching the real process environment. See Testing System for its putEnv/putProperty/clearEnv/clearProperty/setLineSeparator API.

See Also​

  • Built-in services — Guide to ZIO's built-in services: Console, Clock, Random, and System with automatic environment management.