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

com.comcast.money.akka.SpanContextWithStack.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012 Comcast Cable Communications Management, LLC
 *
 * 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 com.comcast.money.akka

import com.comcast.money.api.Span
import com.comcast.money.core.internal.SpanContext
import io.opentelemetry.context.{ Context, Scope }
import org.slf4j.{ Logger, LoggerFactory }

/**
 * A [[SpanContext]] that carries with it a stack of [[Span]] enables explicitly passing of the [[SpanContext]].
 *
 * A [[com.comcast.money.core.internal.SpanLocal]] would not be appropriate as it is tied to a single thread.
 *
 * BEWARE: The below is intended to be used through [[com.comcast.money.core.Tracer]] not to be called directly
 *
 * DO NOT DIRECTLY MUTATE THE STACK IF YOU WANT THE SPAN TO BE CORRECTLY TRACKED
 *
 */

private[akka] object SpanContextWithStack {
  private val log: Logger = LoggerFactory.getLogger(classOf[SpanContextWithStack])

  def logSpanMismatch(expected: Span, actual: Span): Unit = {
    log.error("Unexpected span scope being closed.  Expected={} Actual={}", expected, actual, new Throwable().fillInStackTrace())
  }
}

class SpanContextWithStack() extends SpanContext {
  /**
   * stack is a mutable [[List]] of Spans.
   */

  private var stack: List[Span] = Nil

  /**
   * Returns a fresh copy of this [[SpanContextWithStack]].
   *
   * @return SpanContextWithStack
   */

  def copy: SpanContextWithStack = {
    val freshSpanContext = new SpanContextWithStack
    freshSpanContext.setAll(replacementSpans = this.getAll)
  }

  /**
   * INTERNAL API
   *
   * replaces all spans in the [[SpanContextWithStack]] necessary only for the copy method to correctly copy.
   *
   * @param replacementSpans spans to replace existing spans
   * @return this SpanContextWithStack
   */

  private def setAll(replacementSpans: List[Span]): SpanContextWithStack = {
    stack = replacementSpans
    this
  }

  /**
   * Returns all spans currently stored `spans`.
   * @return List[Span]
   */

  def getAll: List[Span] = stack

  /**
   * CAUTION THIS METHOD WILL NOT TRACK THE SPAN
   *
   * Returns Unit
   *
   * Adds a [[Span]] to the top of the Stack
   *
   * @param span the [[Span]] to be prepended
   */

  override def push(span: Span): Scope = {
    import com.comcast.money.akka.SpanContextWithStack.logSpanMismatch

    // capture the previous stack of spans
    val previousStack = stack
    // push the new span to the head of the stack
    stack = span :: previousStack

    () => stack match {
      // happy path, the remaining stack of spans matches the captured stack of spans
      case _ :: rest if rest == previousStack =>
        stack = rest
      // unbalanced state, log the expected/actual head span and restore the stack to the captured stack of spans
      case head :: _ =>
        logSpanMismatch(span, head)
        stack = previousStack
      // unbalanced state, log the expected head span and leave the stack of spans empty
      case Nil =>
        logSpanMismatch(span, null)
    }
  }

  /**
   * Returns the last element inserted
   *
   * [[Some]] when Stack has at least one element
   *
   * [[None]] when Stack is empty
   *
   * @return
   */

  override def current: Option[Span] = stack.headOption

  override def fromContext(context: Context): Option[Span] = current
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy