Skip to content

Commit

Permalink
add Effect.functionWithSpan
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart committed May 1, 2024
1 parent 02c3f03 commit 5d6d9b7
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 180 deletions.
19 changes: 19 additions & 0 deletions .changeset/lovely-frogs-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"effect": minor
---

add Effect.functionWithSpan

Allows you to define an effectful function that is wrapped with a span.

```ts
import { Effect } from "effect"

const getTodo: (id: number) => Effect.Effect<string> = Effect.functionWithSpan({
body: (id: number) => Effect.succeed(`Got todo ${id}!`),
options: (id) => ({
name: `getTodo-${id}`,
attributes: { id }
})
})
```
34 changes: 34 additions & 0 deletions packages/effect/src/Effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5274,6 +5274,40 @@ export const withSpan: {
): Effect<A, E, Exclude<R, Tracer.ParentSpan>>
} = effect.withSpan

/**
* Wraps a function that returns an effect with a new span for tracing.
*
* @since 3.2.0
* @category models
*/
export type FunctionWithSpanOptions<Params extends ReadonlyArray<any>> =
| (Omit<Tracer.SpanOptions, "captureStackTrace"> & { readonly name: string })
| ((...args: Params) => Omit<Tracer.SpanOptions, "captureStackTrace"> & { readonly name: string })

/**
* Wraps a function that returns an effect with a new span for tracing.
*
* @since 3.2.0
* @category tracing
* @example
* import { Effect } from "effect"
*
* const getTodo: (id: number) => Effect.Effect<string, never, never> = Effect.functionWithSpan({
* body: (id: number) => Effect.succeed(`Got todo ${id}!`),
* options: (id) => ({
* name: `getTodo-${id}`,
* attributes: { id }
* })
* })
*/
export const functionWithSpan: <Fn extends (...args: ReadonlyArray<any>) => Effect<any, any, any>>(
options: {
readonly body: Fn
readonly options: FunctionWithSpanOptions<Parameters<Fn>>
readonly captureStackTrace?: boolean | undefined
}
) => Fn = effect.functionWithSpan

/**
* Wraps the effect with a new span for tracing.
*
Expand Down
31 changes: 31 additions & 0 deletions packages/effect/src/internal/core-effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2162,6 +2162,37 @@ export const withSpan: {
return (self: Effect.Effect<any, any, any>) => useSpan(name, options, (span) => withParentSpan(self, span))
} as any

export const functionWithSpan = <Fn extends (...args: ReadonlyArray<any>) => Effect.Effect<any, any, any>>(
options: {
readonly body: Fn
readonly options: Effect.FunctionWithSpanOptions<Parameters<Fn>>
readonly captureStackTrace?: boolean | undefined
}
): Fn =>
(function(this: any) {
let captureStackTrace: Error | boolean = options.captureStackTrace ?? false
if (options.captureStackTrace !== false) {
const limit = Error.stackTraceLimit
Error.stackTraceLimit = 2
const error = new Error()
Error.stackTraceLimit = limit
if (error.stack !== undefined) {
const stack = error.stack.trim().split("\n")
error.stack = stack.slice(2).join("\n").trim()
captureStackTrace = error
}
}
return core.suspend(() => {
const opts = typeof options.options === "function"
? options.options.apply(null, arguments as any)
: options.options
return withSpan(options.body.apply(this, arguments as any), opts.name, {
...opts,
captureStackTrace
})
})
}) as any

// -------------------------------------------------------------------------------------
// optionality
// -------------------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit 5d6d9b7

Please sign in to comment.