Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions .changeset/fair-pandas-trace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fluent-effect": patch
---

Document logging and tracing helpers, including structured log metadata and span attributes.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,10 @@ fx.eachDiscard(items, fn, { concurrency: 5 });
Omit `concurrency` for sequential work, use `true` to turn parallelism on without a limit, or pass a number to bound parallelism.
Use `eachDiscard` for fire-and-discard traversal over large collections when you need the effects but not the collected result array.

### Retry, Timeout, Tracing
### Retry, Timeout, Logging, Tracing

See [docs/retry-timeout.md](./docs/retry-timeout.md) for retry attempt counting, backoff behavior, native schedules, and timeout failures.
See [docs/logging-tracing.md](./docs/logging-tracing.md) for structured log metadata, span attributes, and native Effect instrumentation boundaries.

```ts
fx.retry(task, { times: 3 });
Expand All @@ -214,6 +215,8 @@ fx.retry(task, {

fx.timeout(task, "5 seconds", () => AppError.Timeout({ operation }));

fx.log("Loading user", { userId });

fx.trace(task, "load-user", {
attributes: { userId },
});
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ that differ from raw Effect defaults.
when to use the native Effect escape hatch.
- [Retry and Timeout](./retry-timeout.md) covers retry attempt counting,
backoff options, native schedules, and timeout failure behavior.
- [Logging and Tracing](./logging-tracing.md) covers structured log metadata,
span attributes, and native Effect instrumentation boundaries.
- [Package Exports](./package-exports.md) covers the package entrypoints and
import guarantees.
4 changes: 2 additions & 2 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ import { fx } from "fluent-effect";
| `fx.retryBackoff` | Retry with exponential backoff. |
| `fx.timeout` | Apply a timeout, optionally converting timeout to a typed failure. |
| `fx.timeoutFail` | Apply a timeout that fails with an application error. |
| `fx.log`, `fx.logWarn`, `fx.logError` | Log through Effect. |
| `fx.trace`, `fx.span` | Add a tracing span. |
| `fx.log`, `fx.logWarn`, `fx.logError` | Log through Effect with optional structured metadata. |
| `fx.trace`, `fx.span` | Wrap a task in an Effect tracing span with optional span metadata. |

## Dependencies and Layers

Expand Down
97 changes: 97 additions & 0 deletions docs/logging-tracing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Logging and Tracing

`fluent-effect` exposes small logging and tracing helpers for common
application instrumentation while keeping native Effect behavior available.

## Log Helpers

Use `fx.log`, `fx.logWarn`, and `fx.logError` inside tasks to emit Effect logs.
They return `Task<void>`, so they compose with generator syntax and preserve the
success, failure, and dependency types of the surrounding task.

```ts
const loadUser = (id: string) =>
fx.task(function* () {
yield* fx.log("Loading user", { userId: id });

const user = yield* Users.findById(id);

yield* fx.log("Loaded user", { userId: user.id, plan: user.plan });
return user;
});
```

The optional second argument is a `Record<string, unknown>` and is forwarded as
structured log data to Effect:

```
yield* fx.log("Cache hit", { key, ttlMs: 30_000 });
yield* fx.logWarn("Slow user lookup", { userId, elapsedMs });
yield* fx.logError("Payment provider failed", { provider, cause });
```

Choose the helper by severity:

| Helper | Effect primitive | Intended level |
| ------------- | ------------------- | -------------- |
| `fx.log` | `Effect.logInfo` | info |
| `fx.logWarn` | `Effect.logWarning` | warning |
| `fx.logError` | `Effect.logError` | error |

These helpers do not install a logger, change log formatting, or alter Effect's
runtime logging configuration. Configure those concerns with native Effect
layers or services at the application boundary.

## Spans and Traces

Use `fx.span(task, name, options)` to wrap a task in an Effect tracing span.
`fx.trace` is an alias for `fx.span`; use whichever reads better in the calling
code.

```ts
const loadUser = (id: string) =>
fx.span(
fx.task(function* () {
yield* fx.log("Loading user", { userId: id });
return yield* Users.findById(id);
}),
"load-user",
{
attributes: { userId: id },
kind: "internal",
},
);
```

Span options are native `Tracer.SpanOptions` and are forwarded directly to
`Effect.withSpan`. Common options include:

| Option | Purpose |
| ------------ | --------------------------------------------------------------------- |
| `attributes` | Structured span attributes such as IDs and counts. |
| `kind` | Span kind: `internal`, `server`, `client`, `producer`, or `consumer`. |
| `links` | Native Effect span links. |
| `parent` | Explicit parent span. |
| `root` | Start a root span instead of inheriting a parent. |

Prefer span attributes for stable, queryable dimensions such as entity IDs,
operation names, counts, and external system names. Prefer log metadata for
point-in-time details that explain a specific event.

## Relationship to Native Effect

Logging and tracing helpers are intentionally thin:

- `fx.log(message, data?)` delegates to `Effect.logInfo`.
- `fx.logWarn(message, data?)` delegates to `Effect.logWarning`.
- `fx.logError(message, data?)` delegates to `Effect.logError`.
- `fx.span(task, name, options?)` delegates to `Effect.withSpan`.
- `fx.trace(task, name, options?)` is the same helper as `fx.span`.

Use `fluent-effect/effect` when you need lower-level Effect instrumentation
APIs, custom loggers, tracer providers, scoped spans, span events, or direct
access to the current span.

```ts
import { Effect, Logger } from "fluent-effect/effect";
```
Loading