xiaohongshu
xiaohongshu
effect-logging-discipline
Enforce Effect.log over console.log in TMNL. Effect-native logging provides structured output, dynamic log levels, annotations, spans, and observability integration.
Full skill instructions
BANNED: console.log, console.error, console.warn, console.debug
REQUIRED: Effect.log, Effect.logError, Effect.logWarning, Effect.logDebug
| Feature | console.log | Effect.log |
|---|---|---|
| Dynamic log levels | ❌ | ✅ Per-effect control |
| Structured output | ❌ | ✅ timestamp, level, fiber |
| Custom destinations | ❌ | ✅ File, service, etc. |
| Environment-based | ❌ | ✅ Different levels per env |
| Annotations | ❌ | ✅ Custom metadata |
| Spans | ❌ | ✅ Duration tracking |
| Disable in tests | ❌ Pollutes output | ✅ LogLevel.None |
import { Effect } from "effect"
// INFO level (default, always shown)
yield* Effect.log("Application started")
yield* Effect.log("Processing", "user", userId)
// Multiple messages
yield* Effect.log("message1", "message2", "message3")
// DEBUG - Hidden by default, enable with Logger.withMinimumLogLevel(LogLevel.Debug)
yield* Effect.logDebug("Verbose debug info")
// INFO - Default, always shown
yield* Effect.logInfo("Operation completed")
yield* Effect.log("Same as logInfo")
// WARN - For potential issues
yield* Effect.logWarning("Deprecated API called")
// ERROR - For failures
yield* Effect.logError("Request failed", cause)
// FATAL - Unrecoverable
yield* Effect.logFatal("System shutdown required")
timestamp=2024-01-15T10:30:00.000Z level=INFO fiber=#0 message="Application started"
Add metadata to all logs within a scope:
const program = Effect.gen(function* () {
yield* Effect.log("Processing request")
yield* Effect.log("Request complete")
}).pipe(
Effect.annotateLogs("requestId", "req-123"),
Effect.annotateLogs({ userId: "user-456", service: "auth" })
)
// Output:
// level=INFO message="Processing request" requestId=req-123 userId=user-456 service=auth
// level=INFO message="Request complete" requestId=req-123 userId=user-456 service=auth
Measure operation duration:
const operation = Effect.gen(function* () {
yield* Effect.sleep("1 second")
yield* Effect.log("The job is finished!")
}).pipe(
Effect.withLogSpan("myOperation")
)
// Output:
// level=INFO message="The job is finished!" myOperation=1011ms
class SearchService extends Effect.Service<SearchService>()("app/SearchService", {
effect: Effect.gen(function* () {
const search = (query: string) =>
Effect.gen(function* () {
yield* Effect.logDebug(`Starting search for: ${query}`)
const results = yield* performSearch(query)
yield* Effect.log(`Found ${results.length} results`)
return results
}).pipe(
Effect.annotateLogs("query", query),
Effect.withLogSpan("SearchService.search")
)
return { search } as const
}),
}) {}
For React components that can't use Effect.gen, create helper functions:
import { Effect } from "effect"
// Module-level helper
const logInfo = (message: string, ...args: unknown[]) =>
Effect.runFork(Effect.log(message, ...args.map(String)))
const logDebug = (message: string, ...args: unknown[]) =>
Effect.runFork(Effect.logDebug(message, ...args.map(String)))
const logError = (message: string, error?: unknown) =>
Effect.runFork(Effect.logError(message, error ? String(error) : undefined))
// Usage in React
function MyComponent() {
useEffect(() => {
logInfo("[MyComponent] Mounted")
return () => logInfo("[MyComponent] Unmounted")
}, [])
}
import { runtimeAtom } from "./atoms"
// Log within atom operations where you have Effect context
export const ops = {
doSomething: runtimeAtom.fn<string>()((input, ctx) =>
Effect.gen(function* () {
yield* Effect.logDebug(`Processing: ${input}`)
// ... work
yield* Effect.log("Complete")
})
),
}
import { Logger, LogLevel } from "effect"
// Method 1: Per-effect
Effect.runFork(
program.pipe(Logger.withMinimumLogLevel(LogLevel.None))
)
// Method 2: Via layer
const silentLayer = Logger.minimumLogLevel(LogLevel.None)
Effect.runFork(program.pipe(Effect.provide(silentLayer)))
// Method 3: In vitest setup
// vitest.setup.ts
import { Logger, LogLevel, Effect } from "effect"
beforeAll(() => {
// Global silent logger for tests
})
Debug logs are hidden by default:
import { Logger, LogLevel } from "effect"
// Enable for specific effect
const debuggedEffect = myEffect.pipe(
Logger.withMinimumLogLevel(LogLevel.Debug)
)
// Enable via layer
const debugLayer = Logger.minimumLogLevel(LogLevel.Debug)
console.log(`[GenerativeContainer] MOUNT depth=${depth}`)
console.log(`[GenerativeContainer] prompt="${prompt?.substring(0, 60)}..."`)
console.error(`[GenerativeContainer] ERROR:`, err)
// If in Effect.gen context:
yield* Effect.log(`[GenerativeContainer] MOUNT depth=${depth}`)
yield* Effect.log(`[GenerativeContainer] prompt="${prompt?.substring(0, 60)}..."`)
yield* Effect.logError(`[GenerativeContainer] ERROR: ${err}`)
// If in React callback (fire-and-forget):
Effect.runFork(Effect.log(`[GenerativeContainer] MOUNT depth=${depth}`))
Effect.runFork(Effect.logError(`[GenerativeContainer] ERROR: ${err}`))
// Better: with annotations
Effect.runFork(
Effect.log("MOUNT").pipe(
Effect.annotateLogs({ component: "GenerativeContainer", depth })
)
)
// BANNED
console.log("Debug:", value)
console.error("Error:", err)
console.warn("Warning")
console.debug("Trace")
// WRONG - Effect.log returns Effect, doesn't execute
Effect.log("This does nothing")
// CORRECT - Fire and forget
Effect.runFork(Effect.log("This executes"))
// BANNED - Inconsistent
yield* Effect.log("Step 1")
console.log("Step 2") // NO!
yield* Effect.log("Step 3")
| Pattern | File |
|---|---|
| Service logging | src/lib/data-manager/v1/DataManager.ts |
| Span usage | src/lib/slider/v1/services/SliderService.ts |
| Annotation usage | src/lib/geoint/services/SearchService.ts |
console.log callsconsole.error callsconsole.warn callsconsole.debug callsxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience