All Downloads are FREE. Search and download functionalities are using the official Maven repository.

zio.logging.LogAnnotation.scala Maven / Gradle / Ivy

/*
 * Copyright 2019-2024 John A. De Goes and the ZIO Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package zio.logging

import zio._

import java.{ util => ju }

/**
 * A `LogAnnotation` describes a particular type of statically-typed log
 * annotation applied to log lines. Log annotations combine in user-defined
 * ways, which means they can have arbitrary structure. In the end, however,
 * it must be possible to render each log annotation as a string.
 * {{{
 * myEffect @@ UserId("jdoe")
 * }}}
 */
final case class LogAnnotation[A: Tag](name: String, combine: (A, A) => A, render: A => String) {
  self =>
  type Id
  type Type = A

  def apply(value: A): ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] =
    LogAnnotation.LogAnnotationAspect(Map(self -> value))

  def id: Id = (name, tag).asInstanceOf[Id]

  /**
   * The class tag of the annotation type, used for disambiguation purposes only.
   */
  def tag: Tag[A] = implicitly[Tag[A]]

  override def hashCode: Int = id.hashCode

  override def equals(that: Any): Boolean =
    that match {
      case that: LogAnnotation[_] => self.id == that.id
      case _                      => false
    }

  override def toString: String = s"LogAnnotation($name, $tag)"
}

object LogAnnotation {

  private[logging] final case class LogAnnotationAspect(annotations: Map[LogAnnotation[_], Any])
      extends ZIOAspect[Nothing, Any, Nothing, Any, Nothing, Any] {

    override def >>>[LowerR, UpperR, LowerE, UpperE, LowerA, UpperA](
      that: ZIOAspect[LowerR, UpperR, LowerE, UpperE, LowerA, UpperA]
    ): ZIOAspect[LowerR, UpperR, LowerE, UpperE, LowerA, UpperA] =
      that match {
        case LogAnnotationAspect(thatAnnotations) => LogAnnotationAspect(annotations ++ thatAnnotations)
        case that                                 => super.andThen(that)
      }

    final def apply[R, E, A](zio: ZIO[R, E, A])(implicit trace: Trace): ZIO[R, E, A] =
      logContext.getWith { context =>
        logContext.locally {
          annotations.foldLeft(context) { case (context, (annotation, value)) =>
            context.annotate(annotation.asInstanceOf[LogAnnotation[Any]], value)
          }
        }(zio)
      }
  }

  /**
   * The `TraceId` annotation keeps track of distributed trace id.
   */
  val TraceId: LogAnnotation[ju.UUID] = LogAnnotation[java.util.UUID](
    name = "trace_id",
    combine = (_: ju.UUID, r: ju.UUID) => r,
    render = _.toString
  )

  /**
   * The `TraceSpans` annotation keeps track of distributed spans.
   */
  val TraceSpans: LogAnnotation[List[String]] = LogAnnotation[List[String]](
    name = "trace_spans",
    combine = (l, r) => l ++ r,
    render = _.mkString(":")
  )

  /**
   * The `UserId` annotation keeps track of user id.
   */
  val UserId: LogAnnotation[String] = LogAnnotation[String](
    name = "user_id",
    combine = (_: String, r: String) => r,
    render = _.toString
  )
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy