# Introduction to Logging in ZIO

> ZIO's lightweight built-in logging facade with support for log levels, spans, and contextual annotations.

ZIO supports a lightweight built-in logging facade that standardizes the interface to logging functionality. So it doesn't replace existing logging libraries, but also we can plug it into one of the existing logging backends.

We can easily log using the `ZIO.log` function:

```scala

val app =
  for {
    _ <- ZIO.log("Application started!")
    name <- Console.readLine("Please enter your name: ")
    _ <- ZIO.log("User entered its name: $name")
    _ <- Console.printLine("Hello, $name")
  } yield ()
```

## Logging Levels

To log with a specific log-level, we can use the `ZIO.logLevel` combinator:

```scala
ZIO.logLevel(LogLevel.Warning) {
  ZIO.log("The response time exceeded its threshold!")
}
```

Or we can use the following functions directly:

* `ZIO.logDebug`
* `ZIO.logError`
* `ZIO.logFatal`
* `ZIO.logInfo`
* `ZIO.logWarning`

For example, for log with the error level, we can use `ZIO.logError` like this:

```scala
ZIO.logError("File does not exist: ~/var/www/favicon.ico")
```

## Spans

It also supports spans:

```scala
ZIO.logSpan("myspan") {
  ZIO.sleep(1.second) *> ZIO.log("The job is finished!")
}
```

ZIO Logging calculates the running duration of that span and includes that in the logging data corresponding to its span
label.

## Log Annotations

ZIO by default adds some contextual information to the log messages, like the timestamp, log level, [fiber ID](../fiber/index.md), and source location. Sometimes these default contextual information are not sufficient to understand the circumstances under which they were generated. In such cases, we need to add custom contextual information to the log messages. We can do this using log annotations.

### ZIO's Built-in Log Annotation

For example, in microservice environments, we might have several services that are communicating with each other. In such cases, we might want to correlate the logs generated by different services. We can do this by adding a log annotation called `correlation_id`. This log annotation can be very simple, just a string, that is passed along with requests and responses. So, when we log a message, we know which request or response it is related to. ZIO has a built-in log annotation API that allows us to add such custom contextual information to the log messages:

```scala

object MainApp extends ZIOAppDefault {
  def randomDelay = Random.nextIntBounded(1000).flatMap(t => ZIO.sleep(t.millis))

  def run =
    ZIO.foreachParDiscard(Chunk("UserA", "UserB", "UserC")) { user =>
      ZIO.logAnnotate("correlation_id", user) {
        for {
          _ <- ZIO.log("fetching user from database") *> randomDelay
          _ <- ZIO.log("downloading user's profile picture") *> randomDelay
        } yield ()
      }
    }
}
```

Here is an example of the log messages generated by the above code, each log message contains the `correlation_id` log annotation:

```
timestamp=2024-05-14T15:44:50.734129Z level=INFO thread=#zio-fiber-851563977 message="fetching user from database" location=zio.examples.MainApp.run file=MainApp.scala line=12 correlation_id=UserC
timestamp=2024-05-14T15:44:50.734127Z level=INFO thread=#zio-fiber-41969365 message="fetching user from database" location=zio.examples.MainApp.run file=MainApp.scala line=12 correlation_id=UserA
timestamp=2024-05-14T15:44:50.734123Z level=INFO thread=#zio-fiber-1775966732 message="fetching user from database" location=zio.examples.MainApp.run file=MainApp.scala line=12 correlation_id=UserB
timestamp=2024-05-14T15:44:50.928248Z level=INFO thread=#zio-fiber-851563977 message="downloading user's profile picture" location=zio.examples.MainApp.run file=MainApp.scala line=13 correlation_id=UserC
timestamp=2024-05-14T15:44:51.054287Z level=INFO thread=#zio-fiber-41969365 message="downloading user's profile picture" location=zio.examples.MainApp.run file=MainApp.scala line=13 correlation_id=UserA
timestamp=2024-05-14T15:44:51.534263Z level=INFO thread=#zio-fiber-1775966732 message="downloading user's profile picture" location=zio.examples.MainApp.run file=MainApp.scala line=13 correlation_id=UserB
```

### Typed Log Annotations

In more complex scenarios, we might want to add more structured information to the log messages. For example, we might want to add the user information to the log messages. In such cases, we need a typed log annotation that supports structured information, e.g. a `User` case class that contains the user's id, name, email, etc. 

Using [ZIO Logging](https://zio.dev/zio-logging), we can define typed log annotations using the `LogAnnotation` class. So let's add required dependencies to the `build.sbt` file:

```scala
libraryDependencies ++= Seq(
  "dev.zio" %% "zio-logging" % "4.0.2",
  "dev.zio" %% "zio-json"    % "0.7.38"
)
```

Now, let's assume we have a `User` case class:

```scala
case class User(firstName: String, lastName: String)
```

We can define a typed log annotation for the `User` case class like this:

```scala

object TypedLogAnnotationExample extends ZIOAppDefault {

  case class User(firstName: String, lastName: String)

  object User {
    implicit val encoder = DeriveJsonEncoder.gen[User]
  }

  private val userLogAnnotation = LogAnnotation[User]("user", (_, u) => u, _.toJson)
  
  private val logConfig = ConsoleLoggerConfig.default.copy(
    format = LogFormat.default + LogFormat.annotation(LogAnnotation.TraceId) + LogFormat.annotation(userLogAnnotation)
  )

  override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] =
    Runtime.removeDefaultLoggers >>> consoleJsonLogger(logConfig)

  def run =
    for {
      _ <- ZIO.foreachPar(List(User("John", "Doe"), User("Jane", "Doe"))) { user =>
        {
          ZIO.logInfo("Starting operation") *>
            ZIO.sleep(500.millis) *>
            ZIO.logInfo("Stopping operation")
        } @@ userLogAnnotation(user)
      }
      _ <- ZIO.logInfo("Done")
    } yield ()

}
```

The log messages generated by the above code will contain the `user` log annotation:

```
{"timestamp":"2024-05-14T20:37:33.744171+04:30","level":"INFO","thread":"zio-fiber-6","message":"Starting operation","user":{"firstName":"Jane","lastName":"Doe"}}
{"timestamp":"2024-05-14T20:37:33.744166+04:30","level":"INFO","thread":"zio-fiber-5","message":"Starting operation","user":{"firstName":"John","lastName":"Doe"}}
{"timestamp":"2024-05-14T20:37:34.334837+04:30","level":"INFO","thread":"zio-fiber-5","message":"Stopping operation","user":{"firstName":"John","lastName":"Doe"}}
{"timestamp":"2024-05-14T20:37:34.334848+04:30","level":"INFO","thread":"zio-fiber-6","message":"Stopping operation","user":{"firstName":"Jane","lastName":"Doe"}}
{"timestamp":"2024-05-14T20:37:34.337953+04:30","level":"INFO","thread":"zio-fiber-4","message":"Done"}
```

## See Also

* [ZIO Logging](https://zio.dev/zio-logging)
- [Tutorial: How to Debug a ZIO Application?](../../guides/tutorials/debug-a-zio-application.md)
- [Tutorial: How to Enable Logging in a ZIO Application](../../guides/tutorials/enable-logging-in-a-zio-application.md)
- [Tutorial: How to Create a Custom Logger for a ZIO Application?](../../guides/tutorials/create-custom-logger-for-a-zio-application.md)
