EF

effect-patterns-observability

Provides curated patterns for implementing observability within Effect-TS code.

Install

mkdir -p .claude/skills/effect-patterns-observability && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/2805" && unzip -o skill.zip -d .claude/skills/effect-patterns-observability && rm skill.zip

Installs to .claude/skills/effect-patterns-observability

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Effect-TS patterns for Observability. Use when working with observability in Effect-TS applications.
100 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Injects tap loggers for data inspection
  • Traces pipeline execution with logging
  • Inspects values during runtime
  • Implements error boundary debugging

How it works

Instruments monadic pipelines with side-effect tapping to expose internal state without mutating the data flow.

Inputs & outputs

You give it
Effect pipeline code
You get back
Annotated pipeline with debug logging

When to use effect-patterns-observability

  • Debugging program pipelines
  • Logging telemetry in Effect-TS
  • Tracing data flow

About this skill

Effect-TS Patterns: Observability

This skill provides 13 curated Effect-TS patterns for observability. Use this skill when working on tasks related to:

  • observability
  • Best practices in Effect-TS applications
  • Real-world patterns and solutions

🟢 Beginner Patterns

Debug Effect Programs

Rule: Use Effect.tap and logging to inspect values without changing program flow.

Good Example:

import { Effect, pipe } from "effect"

// ============================================
// 1. Using tap to inspect values
// ============================================

const fetchUser = (id: string) =>
  Effect.succeed({ id, name: "Alice", email: "[email protected]" })

const processUser = (id: string) =>
  fetchUser(id).pipe(
    // tap runs an effect for its side effect, then continues with original value
    Effect.tap((user) => Effect.log(`Fetched user: ${user.name}`)),
    Effect.map((user) => ({ ...user, processed: true })),
    Effect.tap((user) => Effect.log(`Processed: ${JSON.stringify(user)}`))
  )

// ============================================
// 2. Debug a pipeline
// ============================================

const numbers = [1, 2, 3, 4, 5]

const pipeline = Effect.gen(function* () {
  yield* Effect.log("Starting pipeline")

  const step1 = numbers.filter((n) => n % 2 === 0)
  yield* Effect.log(`After filter (even): ${JSON.stringify(step1)}`)

  const step2 = step1.map((n) => n * 10)
  yield* Effect.log(`After map (*10): ${JSON.stringify(step2)}`)

  const step3 = step2.reduce((a, b) => a + b, 0)
  yield* Effect.log(`After reduce (sum): ${step3}`)

  return step3
})

// ============================================
// 3. Debug errors
// ============================================

const riskyOperation = (shouldFail: boolean) =>
  Effect.gen(function* () {
    yield* Effect.log("Starting risky operation")

    if (shouldFail) {
      yield* Effect.log("About to fail...")
      return yield* Effect.fail(new Error("Something went wrong"))
    }

    yield* Effect.log("Success!")
    return "result"
  })

const debugErrors = riskyOperation(true).pipe(
  // Log when operation fails
  Effect.tapError((error) => Effect.log(`Operation failed: ${error.message}`)),

  // Provide a fallback
  Effect.catchAll((error) => {
    return Effect.succeed(`Recovered from: ${error.message}`)
  })
)

// ============================================
// 4. Trace execution flow
// ============================================

const step = (name: string, value: number) =>
  Effect.gen(function* () {
    yield* Effect.log(`[${name}] Input: ${value}`)
    const result = value * 2
    yield* Effect.log(`[${name}] Output: ${result}`)
    return result
  })

const tracedWorkflow = Effect.gen(function* () {
  const a = yield* step("Step 1", 5)
  const b = yield* step("Step 2", a)
  const c = yield* step("Step 3", b)
  yield* Effect.log(`Final result: ${c}`)
  return c
})

// ============================================
// 5. Quick debug with console
// ============================================

// Sometimes you just need console.log
const quickDebug = Effect.gen(function* () {
  const value = yield* Effect.succeed(42)
  
  // Effect.sync wraps side effects
  yield* Effect.sync(() => console.log("Quick debug:", value))
  
  return value
})

// ============================================
// 6. Run examples
// ============================================

const program = Effect.gen(function* () {
  yield* Effect.log("=== Tap Example ===")
  yield* processUser("123")

  yield* Effect.log("\n=== Pipeline Debug ===")
  yield* pipeline

  yield* Effect.log("\n=== Error Debug ===")
  yield* debugErrors

  yield* Effect.log("\n=== Traced Workflow ===")
  yield* tracedWorkflow
})

Effect.runPromise(program)

Rationale:

Use Effect.tap to inspect values and Effect.log to trace execution without changing program behavior.


Debugging Effect code differs from imperative code:

  1. No breakpoints - Effects are descriptions, not executions
  2. Lazy evaluation - Code runs later when you call runPromise
  3. Composition - Effects chain together

tap and logging let you see inside without breaking the chain.



Your First Logs

Rule: Use Effect.log and related functions for structured, contextual logging.

Good Example:

import { Effect, Logger, LogLevel } from "effect"

// ============================================
// 1. Basic logging
// ============================================

const basicLogging = Effect.gen(function* () {
  // Different log levels
  yield* Effect.logDebug("Debug message - for development")
  yield* Effect.logInfo("Info message - normal operation")
  yield* Effect.log("Default log - same as logInfo")
  yield* Effect.logWarning("Warning - something unusual")
  yield* Effect.logError("Error - something went wrong")
})

// ============================================
// 2. Logging with context
// ============================================

const withContext = Effect.gen(function* () {
  // Add structured data to logs
  yield* Effect.log("User logged in").pipe(
    Effect.annotateLogs({
      userId: "user-123",
      action: "login",
      ipAddress: "192.168.1.1",
    })
  )

  // Add a single annotation
  yield* Effect.log("Processing request").pipe(
    Effect.annotateLogs("requestId", "req-456")
  )
})

// ============================================
// 3. Log spans for timing
// ============================================

const withTiming = Effect.gen(function* () {
  yield* Effect.log("Starting operation")

  // withLogSpan adds timing information
  yield* Effect.sleep("100 millis").pipe(
    Effect.withLogSpan("database-query")
  )

  yield* Effect.log("Operation complete")
})

// ============================================
// 4. Practical example
// ============================================

interface User {
  id: string
  email: string
}

const processOrder = (orderId: string, userId: string) =>
  Effect.gen(function* () {
    yield* Effect.logInfo("Processing order").pipe(
      Effect.annotateLogs({ orderId, userId })
    )

    // Simulate work
    yield* Effect.sleep("50 millis")

    yield* Effect.logInfo("Order processed successfully").pipe(
      Effect.annotateLogs({ orderId, status: "completed" })
    )

    return { orderId, status: "completed" }
  }).pipe(
    Effect.withLogSpan("processOrder")
  )

// ============================================
// 5. Configure log level
// ============================================

const debugProgram = basicLogging.pipe(
  // Show all logs including debug
  Logger.withMinimumLogLevel(LogLevel.Debug)
)

const productionProgram = basicLogging.pipe(
  // Only show warnings and errors
  Logger.withMinimumLogLevel(LogLevel.Warning)
)

// ============================================
// 6. Run
// ============================================

const program = Effect.gen(function* () {
  yield* Effect.log("=== Basic Logging ===")
  yield* basicLogging

  yield* Effect.log("\n=== With Context ===")
  yield* withContext

  yield* Effect.log("\n=== With Timing ===")
  yield* withTiming

  yield* Effect.log("\n=== Process Order ===")
  yield* processOrder("order-789", "user-123")
})

Effect.runPromise(program)

Rationale:

Use Effect's built-in logging functions for structured, contextual logging that works with any logging backend.


Effect's logging is superior to console.log:

  1. Structured - Logs are data, not just strings
  2. Contextual - Automatically includes fiber info, timestamps
  3. Configurable - Change log levels, formats, destinations
  4. Type-safe - Part of the Effect type system


🟡 Intermediate Patterns

Instrument and Observe Function Calls with Effect.fn

Rule: Use Effect.fn to wrap functions with effectful instrumentation, such as logging, metrics, or tracing, in a composable and type-safe way.

Good Example:

import { Effect } from "effect";

// A simple function to instrument
function add(a: number, b: number): number {
  return a + b;
}

// Use Effect.fn to instrument the function with observability
const addWithLogging = Effect.fn("add")(add).pipe(
  Effect.withSpan("add", { attributes: { "fn.name": "add" } })
);

// Use the instrumented function in an Effect workflow
const program = Effect.gen(function* () {
  yield* Effect.logInfo("Calling add function");
  const sum = yield* addWithLogging(2, 3);
  yield* Effect.logInfo(`Sum is ${sum}`);
  return sum;
});

// Run the program
Effect.runPromise(program);

Explanation:

  • Effect.fn("name")(fn) wraps a function with instrumentation capabilities, enabling observability.
  • You can add tracing spans, logging, metrics, and other observability logic to function boundaries.
  • Keeps instrumentation separate from business logic and fully composable.
  • The wrapped function integrates seamlessly with Effect's observability and tracing infrastructure.

Anti-Pattern:

Scattering logging, metrics, or tracing logic directly inside business functions, making code harder to test, maintain, and compose.

Rationale:

Use Effect.fn to wrap and instrument function calls with effectful logic, such as logging, metrics, or tracing.
This enables you to observe, monitor, and debug function boundaries in a composable, type-safe way.

Instrumenting function calls is essential for observability, especially in complex or critical code paths.
Effect.fn lets you add effectful logic (logging, metrics, tracing, etc.) before, after, or around any function call, without changing the function’s core logic.


Leverage Effect's Built-in Structured Logging

Rule: Use Effect.log, Effect.logInfo, and Effect.logError to add structured, context-aware logging to your Effect code.

Good Example:

import { Effect } from "effect";

// Log a simple message
const program = Effect.gen(function* () {
  yield* Effect.log("Starting the ap

---

*Content truncated.*

When not to use it

  • Performance-critical code paths where logging overhead is unacceptable
  • Simple pure functions

Prerequisites

Effect-TS library

Limitations

  • Logging adds noise to trace logs
  • Cannot inspect private closures inside functions

How it compares

Allows observation of declarative program state without breaking the compositionality of the Effect-TS pipeline.

Compared to similar skills

effect-patterns-observability side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
effect-patterns-observability (this skill)17moNo flagsIntermediate
agentation65moReviewBeginner
langfuse-local-dev-loop127dReviewIntermediate
chrome-devtools417moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

agentation

benjitaylor

Add Agentation visual feedback toolbar to a Next.js project

69

langfuse-local-dev-loop

jeremylongshore

Set up Langfuse local development workflow with hot reload and debugging. Use when developing LLM applications locally, debugging traces, or setting up a fast iteration loop with Langfuse. Trigger with phrases like "langfuse local dev", "langfuse development", "debug langfuse traces", "langfuse hot reload", "langfuse dev workflow".

10

chrome-devtools

mrgoonie

Browser automation, debugging, and performance analysis using Puppeteer CLI scripts. Use for automating browsers, taking screenshots, analyzing performance, monitoring network traffic, web scraping, form automation, and JavaScript debugging.

41157

langfuse

davila7

Expert in Langfuse - the open-source LLM observability platform. Covers tracing, prompt management, evaluation, datasets, and integration with LangChain, LlamaIndex, and OpenAI. Essential for debugging, monitoring, and improving LLM applications in production. Use when: langfuse, llm observability, llm tracing, prompt management, llm evaluation.

743

obsidian-performance-tuning

jeremylongshore

Optimize Obsidian plugin performance for smooth operation. Use when experiencing lag, memory issues, or slow startup, or when optimizing plugin code for large vaults. Trigger with phrases like "obsidian performance", "obsidian slow", "optimize obsidian plugin", "obsidian memory usage".

640

obsidian-observability

jeremylongshore

Set up comprehensive logging and monitoring for Obsidian plugins. Use when implementing debug logging, tracking plugin performance, or setting up error reporting for your Obsidian plugin. Trigger with phrases like "obsidian logging", "obsidian monitoring", "obsidian debug", "track obsidian plugin".

534

Search skills

Search the agent skills registry