Skip to content

Commit

Permalink
Merge pull request #2147 from djspiewak/feature/reconfigure-tracing
Browse files Browse the repository at this point in the history
Adjust tracing to use `IORuntimeConfig`
  • Loading branch information
vasilmkd authored Jul 24, 2021
2 parents c8616ff + 2f3e984 commit 5fb8e10
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 50 deletions.
25 changes: 1 addition & 24 deletions core/jvm/src/main/java/cats/effect/tracing/TracingConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,12 @@ class TracingConstants {
* Sets stack tracing mode for a JVM process, which controls how much stack
* trace information is captured. Acceptable values are: NONE, CACHED, FULL.
*/
private static final String stackTracingMode = Optional.ofNullable(System.getProperty("cats.effect.stackTracingMode"))
private static final String stackTracingMode = Optional.ofNullable(System.getProperty("cats.effect.tracing.mode"))
.filter(x -> !x.isEmpty()).orElse("cached");

static final boolean isCachedStackTracing = stackTracingMode.equalsIgnoreCase("cached");

static final boolean isFullStackTracing = stackTracingMode.equalsIgnoreCase("full");

static final boolean isStackTracing = isFullStackTracing || isCachedStackTracing;

/**
* The number of trace lines to retain during tracing. If more trace lines are
* produced, then the oldest trace lines will be discarded. Automatically
* rounded up to the nearest power of 2.
*/
static final int traceBufferLogSize = Optional.ofNullable(System.getProperty("cats.effect.traceBufferLogSize"))
.filter(x -> !x.isEmpty()).flatMap(x -> {
try {
return Optional.of(Integer.valueOf(x));
} catch (Exception e) {
return Optional.empty();
}
}).orElse(4);

/**
* Sets the enhanced exceptions flag, which controls whether or not the stack
* traces of IO exceptions are augmented to include async stack trace
* information. Stack tracing must be enabled in order to use this feature. This
* flag is enabled by default.
*/
static final boolean enhancedExceptions = Optional.ofNullable(System.getProperty("cats.effect.enhancedExceptions"))
.map(Boolean::valueOf).orElse(true);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,27 @@
package cats.effect
package unsafe

import scala.util.Try

private[unsafe] abstract class IORuntimeConfigCompanionPlatform { this: IORuntimeConfig.type =>

// TODO make the cancelation and auto-yield properties have saner names
protected final val Default: IORuntimeConfig = {
val cancelationCheckThreshold =
System.getProperty("cats.effect.cancelation.check.threshold", "512").toInt
Try(System.getProperty("cats.effect.cancelation.check.threshold").toInt).getOrElse(512)

val autoYieldThreshold =
Try(System.getProperty("cats.effect.auto.yield.threshold.multiplier").toInt)
.getOrElse(2) * cancelationCheckThreshold

val enhancedExceptions =
Try(System.getProperty("cats.effect.tracing.exceptions.enhanced").toBoolean)
.getOrElse(DefaultEnhancedExceptions)

val traceBufferSize =
Try(System.getProperty("cats.effect.tracing.buffer.size").toInt)
.getOrElse(DefaultTraceBufferSize)

apply(
cancelationCheckThreshold,
System
.getProperty("cats.effect.auto.yield.threshold.multiplier", "2")
.toInt * cancelationCheckThreshold)
apply(cancelationCheckThreshold, autoYieldThreshold, enhancedExceptions, traceBufferSize)
}
}
5 changes: 3 additions & 2 deletions core/shared/src/main/scala/cats/effect/IOFiber.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ private final class IOFiber[A](
private[this] val cancelationCheckThreshold: Int = runtime.config.cancelationCheckThreshold
private[this] val autoYieldThreshold: Int = runtime.config.autoYieldThreshold

private[this] val tracingEvents: RingBuffer = RingBuffer.empty
private[this] val tracingEvents: RingBuffer =
RingBuffer.empty(runtime.config.traceBufferLogSize)

override def run(): Unit = {
// insert a read barrier after every async boundary
Expand Down Expand Up @@ -1266,7 +1267,7 @@ private final class IOFiber[A](
}

private[this] def runTerminusFailureK(t: Throwable): IO[Any] = {
Tracing.augmentThrowable(t, tracingEvents)
Tracing.augmentThrowable(runtime.config.enhancedExceptions, t, tracingEvents)
done(Outcome.Errored(t))
IOEndFiber
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,5 @@ private[effect] final class RingBuffer private (logSize: Int) {
}

private[effect] object RingBuffer {
import TracingConstants.traceBufferLogSize

def empty: RingBuffer = new RingBuffer(traceBufferLogSize)
def empty(logSize: Int): RingBuffer = new RingBuffer(logSize)
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private[effect] object Tracing extends ClassValue[TracingEvent] {
"scala."
)

def augmentThrowable(t: Throwable, events: RingBuffer): Unit = {
def augmentThrowable(enhancedExceptions: Boolean, t: Throwable, events: RingBuffer): Unit = {
def applyRunLoopFilter(ste: StackTraceElement): Boolean = {
val name = ste.getClassName
var i = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,68 @@ package unsafe

final case class IORuntimeConfig private (
val cancelationCheckThreshold: Int,
val autoYieldThreshold: Int)
val autoYieldThreshold: Int,
val enhancedExceptions: Boolean,
val traceBufferSize: Int) {

def this(cancelationCheckThreshold: Int, autoYieldThreshold: Int) =
this(
cancelationCheckThreshold,
autoYieldThreshold,
IORuntimeConfig.DefaultEnhancedExceptions,
IORuntimeConfig.DefaultTraceBufferSize)

def copy(
cancelationCheckThreshold: Int = this.cancelationCheckThreshold,
autoYieldThreshold: Int = this.autoYieldThreshold,
enhancedExceptions: Boolean = this.enhancedExceptions,
traceBufferSize: Int = this.traceBufferSize): IORuntimeConfig =
new IORuntimeConfig(
cancelationCheckThreshold,
autoYieldThreshold,
enhancedExceptions,
traceBufferSize)

// shim for binary compat
private[unsafe] def copy(
cancelationCheckThreshold: Int,
autoYieldThreshold: Int): IORuntimeConfig =
new IORuntimeConfig(
cancelationCheckThreshold,
autoYieldThreshold,
enhancedExceptions,
traceBufferSize)

private[effect] val traceBufferLogSize: Int =
Math.round(Math.log(traceBufferSize.toDouble) / Math.log(2)).toInt
}

object IORuntimeConfig extends IORuntimeConfigCompanionPlatform {

// these have to be defs because we forward-reference them from the companion platform
private[unsafe] def DefaultEnhancedExceptions = true
private[unsafe] def DefaultTraceBufferSize = 16

def apply(): IORuntimeConfig = Default

def apply(cancelationCheckThreshold: Int, autoYieldThreshold: Int): IORuntimeConfig = {
def apply(cancelationCheckThreshold: Int, autoYieldThreshold: Int): IORuntimeConfig =
apply(
cancelationCheckThreshold,
autoYieldThreshold,
DefaultEnhancedExceptions,
DefaultTraceBufferSize)

def apply(
cancelationCheckThreshold: Int,
autoYieldThreshold: Int,
enhancedExceptions: Boolean,
traceBufferSize: Int): IORuntimeConfig = {
if (autoYieldThreshold % cancelationCheckThreshold == 0)
new IORuntimeConfig(cancelationCheckThreshold, autoYieldThreshold)
new IORuntimeConfig(
cancelationCheckThreshold,
autoYieldThreshold,
enhancedExceptions,
1 << Math.round(Math.log(traceBufferSize.toDouble) / Math.log(2)).toInt)
else
throw new AssertionError(
s"Auto yield threshold $autoYieldThreshold must be a multiple of cancelation check threshold $cancelationCheckThreshold")
Expand Down
21 changes: 10 additions & 11 deletions docs/tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ enabled before deploying it to a production environment!

### Configuration
The stack tracing mode of an application is configured by the system property
`cats.effect.stackTracingMode`. There are 3 stack tracing modes: `none`,
`cats.effect.tracing.mode`. There are 3 stack tracing modes: `none`,
`cached` and `full`. These values are case-insensitive. The default tracing mode
is `cached`, which uses a global cache to avoid expensive recomputation of stack
tracing information.
Expand All @@ -91,15 +91,14 @@ To prevent unbounded memory usage, stack tracing information for a given fiber
is accumulated in an internal buffer as execution proceeds. If more traces are
collected than the buffer can retain, the oldest trace information will be
overwritten. The default size of the buffer is 16. This value can be configured
via the system property `cats.effect.traceBufferLogSize`. Keep in mind that the
provided value is a logarithm of the actual size of the buffer (i.e. the actual
size of the tracing information buffer is
2<sup>`cats.effect.traceBufferLogSize`</sup>).
via the system property `cats.effect.tracing.buffer.size`. Keep in mind that the
value configured by the system property must be a power of two and will be rounded to the nearest power if not.

For example, to enable full stack tracing and a trace buffer of size 1024,
specify the following system properties:

```
-Dcats.effect.stackTracingMode=full -Dcats.effect.traceBufferLogSize=10
-Dcats.effect.tracing.mode=full -Dcats.effect.tracing.buffer.size=1024
```

## Stack tracing modes
Expand Down Expand Up @@ -239,23 +238,23 @@ deeply nested recursive `IO` programs like the one on this page, even with a
fairly small buffer. The example was generated using the following stack tracing
configuration:
```
-Dcats.effect.stackTracingMode=full -Dcats.effect.traceBufferLogSize=6
-Dcats.effect.tracing.mode=full -Dcats.effect.tracing.buffer.size=64
```

The enhanced exceptions feature is controlled by the system property
`cats.effect.enhancedExceptions`. It is enabled by default.
`cats.effect.tracing.exceptions.enhanced`. It is enabled by default.

It can be disabled with the following configuration:
```
-Dcats.effect.enhancedExceptions=false
-Dcats.effect.tracing.exceptions.enhanced=false
```

### Complete code
Here is the code snippet that was used to generate the above examples:
```scala mdoc
// Pass the following system property to your JVM:
// -Dcats.effect.stackTracingMode=full
// -Dcats.effect.traceBufferLogSize=6
// -Dcats.effect.tracing.mode=full
// -Dcats.effect.tracing.buffer.size=64

import cats.effect.{IO, IOApp}

Expand Down

0 comments on commit 5fb8e10

Please sign in to comment.