Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ensure Redactable works with Console.log #4153

Draft
wants to merge 15 commits into
base: next-minor
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add span annotation to disable propagation to the tracer (#4123)
tim-smart authored and effect-bot committed Dec 19, 2024
commit 7a1d1955967d67ab616ef5ae4d608aff33e73031
5 changes: 5 additions & 0 deletions .changeset/shiny-vans-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": minor
---

add span annotation to disable propagation to the tracer
14 changes: 14 additions & 0 deletions packages/effect/src/Tracer.ts
Original file line number Diff line number Diff line change
@@ -164,3 +164,17 @@ export const externalSpan: (
*/
export const tracerWith: <A, E, R>(f: (tracer: Tracer) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> =
defaultServices.tracerWith

/**
* @since 3.12.0
* @category annotations
*/
export interface DisablePropagation {
readonly _: unique symbol
}

/**
* @since 3.12.0
* @category annotations
*/
export const DisablePropagation: Context.Reference<DisablePropagation, boolean> = internal.DisablePropagation
96 changes: 55 additions & 41 deletions packages/effect/src/internal/core-effect.ts
Original file line number Diff line number Diff line change
@@ -2020,61 +2020,75 @@ export const linkSpans = dual<

const bigint0 = BigInt(0)

const filterDisablePropagation: (self: Option.Option<Tracer.AnySpan>) => Option.Option<Tracer.AnySpan> = Option.flatMap(
(span) =>
Context.get(span.context, internalTracer.DisablePropagation)
? span._tag === "Span" ? filterDisablePropagation(span.parent) : Option.none()
: Option.some(span)
)

/** @internal */
export const unsafeMakeSpan = <XA, XE>(
fiber: FiberRuntime<XA, XE>,
name: string,
options: Tracer.SpanOptions
) => {
const enabled = fiber.getFiberRef(core.currentTracerEnabled)
if (enabled === false) {
return core.noopSpan(name)
}

const disablePropagation = !fiber.getFiberRef(core.currentTracerEnabled) ||
(options.context && Context.get(options.context, internalTracer.DisablePropagation))
const context = fiber.getFiberRef(core.currentContext)
const services = fiber.getFiberRef(defaultServices.currentServices)

const tracer = Context.get(services, internalTracer.tracerTag)
const clock = Context.get(services, Clock.Clock)
const timingEnabled = fiber.getFiberRef(core.currentTracerTimingEnabled)

const fiberRefs = fiber.getFiberRefs()
const annotationsFromEnv = FiberRefs.get(fiberRefs, core.currentTracerSpanAnnotations)
const linksFromEnv = FiberRefs.get(fiberRefs, core.currentTracerSpanLinks)

const parent = options.parent
? Option.some(options.parent)
: options.root
? Option.none()
: Context.getOption(context, internalTracer.spanTag)

const links = linksFromEnv._tag === "Some" ?
options.links !== undefined ?
[
...Chunk.toReadonlyArray(linksFromEnv.value),
...(options.links ?? [])
] :
Chunk.toReadonlyArray(linksFromEnv.value) :
options.links ?? Arr.empty()

const span = tracer.span(
name,
parent,
options.context ?? Context.empty(),
links,
timingEnabled ? clock.unsafeCurrentTimeNanos() : bigint0,
options.kind ?? "internal"
)
: filterDisablePropagation(Context.getOption(context, internalTracer.spanTag))

if (typeof options.captureStackTrace === "function") {
internalCause.spanToTrace.set(span, options.captureStackTrace)
}
let span: Tracer.Span

if (annotationsFromEnv._tag === "Some") {
HashMap.forEach(annotationsFromEnv.value, (value, key) => span.attribute(key, value))
if (disablePropagation) {
span = core.noopSpan({
name,
parent,
context: Context.add(options.context ?? Context.empty(), internalTracer.DisablePropagation, true)
})
} else {
const services = fiber.getFiberRef(defaultServices.currentServices)

const tracer = Context.get(services, internalTracer.tracerTag)
const clock = Context.get(services, Clock.Clock)
const timingEnabled = fiber.getFiberRef(core.currentTracerTimingEnabled)

const fiberRefs = fiber.getFiberRefs()
const annotationsFromEnv = FiberRefs.get(fiberRefs, core.currentTracerSpanAnnotations)
const linksFromEnv = FiberRefs.get(fiberRefs, core.currentTracerSpanLinks)

const links = linksFromEnv._tag === "Some" ?
options.links !== undefined ?
[
...Chunk.toReadonlyArray(linksFromEnv.value),
...(options.links ?? [])
] :
Chunk.toReadonlyArray(linksFromEnv.value) :
options.links ?? Arr.empty()

span = tracer.span(
name,
parent,
options.context ?? Context.empty(),
links,
timingEnabled ? clock.unsafeCurrentTimeNanos() : bigint0,
options.kind ?? "internal"
)

if (annotationsFromEnv._tag === "Some") {
HashMap.forEach(annotationsFromEnv.value, (value, key) => span.attribute(key, value))
}
if (options.attributes !== undefined) {
Object.entries(options.attributes).forEach(([k, v]) => span.attribute(k, v))
}
}
if (options.attributes !== undefined) {
Object.entries(options.attributes).forEach(([k, v]) => span.attribute(k, v))

if (typeof options.captureStackTrace === "function") {
internalCause.spanToTrace.set(span, options.captureStackTrace)
}

return span
15 changes: 6 additions & 9 deletions packages/effect/src/internal/core.ts
Original file line number Diff line number Diff line change
@@ -3063,14 +3063,11 @@ export const currentSpanFromFiber = <A, E>(fiber: Fiber.RuntimeFiber<A, E>): Opt
return span !== undefined && span._tag === "Span" ? Option.some(span) : Option.none()
}

const NoopSpanProto: Tracer.Span = {
const NoopSpanProto: Omit<Tracer.Span, "parent" | "name" | "context"> = {
_tag: "Span",
spanId: "noop",
traceId: "noop",
name: "noop",
sampled: false,
parent: Option.none(),
context: Context.empty(),
status: {
_tag: "Ended",
startTime: BigInt(0),
@@ -3086,8 +3083,8 @@ const NoopSpanProto: Tracer.Span = {
}

/** @internal */
export const noopSpan = (name: string): Tracer.Span => {
const span = Object.create(NoopSpanProto)
span.name = name
return span
}
export const noopSpan = (options: {
readonly name: string
readonly parent: Option.Option<Tracer.AnySpan>
readonly context: Context.Context<never>
}): Tracer.Span => Object.assign(Object.create(NoopSpanProto), options)
6 changes: 6 additions & 0 deletions packages/effect/src/internal/tracer.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
*/
import * as Context from "../Context.js"
import type * as Exit from "../Exit.js"
import { constFalse } from "../Function.js"
import type * as Option from "../Option.js"
import type * as Tracer from "../Tracer.js"

@@ -135,3 +136,8 @@ export const addSpanStackTrace = (options: Tracer.SpanOptions | undefined): Trac
}
}
}

/** @internal */
export const DisablePropagation = Context.Reference<Tracer.DisablePropagation>()("effect/Tracer/DisablePropagation", {
defaultValue: constFalse
})
41 changes: 40 additions & 1 deletion packages/effect/test/Tracer.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Cause, Tracer } from "effect"
import * as Context from "effect/Context"
import { millis, seconds } from "effect/Duration"
import * as Effect from "effect/Effect"
@@ -274,6 +275,44 @@ it.effect("withTracerEnabled", () =>
assert.deepEqual(spanB.name, "B")
}))

describe("Tracer.DisablePropagation", () => {
it.effect("creates noop span", () =>
Effect.gen(function*() {
const span = yield* Effect.currentSpan.pipe(
Effect.withSpan("A", { context: Tracer.DisablePropagation.context(true) })
)
const spanB = yield* Effect.currentSpan.pipe(
Effect.withSpan("B")
)

assert.deepEqual(span.name, "A")
assert.deepEqual(span.spanId, "noop")
assert.deepEqual(spanB.name, "B")
}))

it.effect("captures stack", () =>
Effect.gen(function*() {
const cause = yield* Effect.die(new Error("boom")).pipe(
Effect.withSpan("C", { context: Tracer.DisablePropagation.context(true) }),
Effect.sandbox,
Effect.flip
)
assert.include(Cause.pretty(cause), "Tracer.test.ts:295")
}))

it.effect("isnt used as parent span", () =>
Effect.gen(function*() {
const span = yield* Effect.currentSpan.pipe(
Effect.withSpan("child"),
Effect.withSpan("disabled", { context: Tracer.DisablePropagation.context(true) }),
Effect.withSpan("parent")
)
assert.strictEqual(span.name, "child")
assert(span.parent._tag === "Some" && span.parent.value._tag === "Span")
assert.strictEqual(span.parent.value.name, "parent")
}))
})

it.effect("includes trace when errored", () =>
Effect.gen(function*() {
let maybeSpan: undefined | Span
@@ -290,7 +329,7 @@ it.effect("includes trace when errored", () =>
})
yield* Effect.flip(getSpan("fail"))
assert.isDefined(maybeSpan)
assert.include(maybeSpan!.attributes.get("code.stacktrace"), "Tracer.test.ts:291:24")
assert.include(maybeSpan!.attributes.get("code.stacktrace"), "Tracer.test.ts:330:24")
}))

describe("functionWithSpan", () => {