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.zipInstalls 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.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
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:
- No breakpoints - Effects are descriptions, not executions
- Lazy evaluation - Code runs later when you call
runPromise - 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:
- Structured - Logs are data, not just strings
- Contextual - Automatically includes fiber info, timestamps
- Configurable - Change log levels, formats, destinations
- 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
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.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| effect-patterns-observability (this skill) | 1 | 7mo | No flags | Intermediate |
| agentation | 6 | 5mo | Review | Beginner |
| langfuse-local-dev-loop | 1 | 27d | Review | Intermediate |
| chrome-devtools | 41 | 7mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by PaulJPhilp
View all by PaulJPhilp →You might also like
agentation
benjitaylor
Add Agentation visual feedback toolbar to a Next.js project
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".
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.
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.
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".
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".