Logging Observability
getsentry/toolkit
Review code for correct logging and error handling patterns.
Adds logging to fp-go functional pipelines with Tap operators, entry and exit logs and error context, so that logging never changes the value or error flowing through.
$ npx skills add IBM/fp-go --skill fp-go-logging -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install IBM/fp-go fp-go-logging --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/fp-go-logging .claude/skills/fp-go-logging && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "fp-go-logging" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-logging into .claude/skills/fp-go-logging/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-logging", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/IBM/fp-go/tree/main/skills/fp-go-loggingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add IBM/fp-go --skill fp-go-logging -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install IBM/fp-go fp-go-logging --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/fp-go-logging .agents/skills/fp-go-logging && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "fp-go-logging" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-logging into .agents/skills/fp-go-logging/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-logging", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add IBM/fp-go --skill fp-go-logging -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install IBM/fp-go fp-go-logging --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/fp-go-logging .cursor/skills/fp-go-logging && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "fp-go-logging" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-logging into .cursor/skills/fp-go-logging/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-logging", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/IBM/fp-go.git --path skills/fp-go-logging--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add IBM/fp-go --skill fp-go-logging -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install IBM/fp-go fp-go-logging --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/fp-go-logging .gemini/skills/fp-go-logging && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "fp-go-logging" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-logging into .gemini/skills/fp-go-logging/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-logging", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install IBM/fp-go fp-go-loggingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add IBM/fp-go --skill fp-go-logging -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/fp-go-logging .github/skills/fp-go-logging && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "fp-go-logging" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-logging into .github/skills/fp-go-logging/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-logging", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add IBM/fp-go --skill fp-go-logging -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install IBM/fp-go fp-go-logging --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/IBM/fp-go.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/fp-go-logging .opencode/skills/fp-go-logging && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "fp-go-logging" agent skill from https://github.com/IBM/fp-go/tree/main/skills/fp-go-logging into .opencode/skills/fp-go-logging/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "fp-go-logging", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
fp-go-loggingAdds logging to fp-go functional pipelines with Tap operators, entry and exit logs and error context, so that logging never changes the value or error flowing through.
Logging is treated as a side effect: a `Tap` runs an effect on the current value or error and passes the original computation on unchanged. The skill covers three tasks: a simple log statement with `TapSLog`, `TapIOK`, `TapReaderIOK` or `TapLeft`, entry and exit logs with `LogEntryExit`, and adding context to the error channel with `MapLeft` and `errors.OnError` and then logging once. A quick reference table maps goals to operators on a `ReaderIOResult`, including debug-level variants, success-only and error-only logging, printf-style output, custom attributes, timing with a correlation ID, and choosing the logger for a pipeline with `logging.WithLogger`.
Because fp-go signatures are easy to misremember, the agent looks up unfamiliar combinators through the fp-go MCP server's `search_examples` and `get_example` tools, and after writing code runs `go build` and `go vet` across the module to catch type-parameter and argument-order errors. Runnable examples live in a logging example test file in the library's `readerioresult` package.
3 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 1c4245d. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
goFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
fp-go Logging Patterns loads about 3.7k tokens when it runs. Until then it costs about 130 tokens; SKILL.md has 1,095 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from IBM/fp-go at commit 1c4245d, republished under its Apache-2.0 licence (© IBM). 1,095 words, ~3,723 tokens.
.claude/skills/fp-go-logging/SKILL.md (or your agent's skills folder).Logging is a side effect: it must never change the value or the error flowing through a
pipeline. fp-go expresses this with the Tap* family of operators. A Tap runs an effect on
the current value (or error) and hands the original computation downstream unchanged.
This skill covers three tasks:
TapSLog, TapIOK, TapReaderIOK, TapLeftLogEntryExitMapLeft + errors.OnError, then log onceExamples assume these imports; all live under github.com/IBM/fp-go/v2:
import (
"log/slog"
RIO "github.com/IBM/fp-go/v2/context/readerioresult"
ER "github.com/IBM/fp-go/v2/errors"
F "github.com/IBM/fp-go/v2/function"
IO "github.com/IBM/fp-go/v2/io"
"github.com/IBM/fp-go/v2/logging"
R "github.com/IBM/fp-go/v2/result"
)Runnable versions of the snippets are in v2/context/readerioresult/logging_example_test.go
(ExampleTapSLog, ExampleTapIOK_logging, ExampleTapReaderIOK_logging, ExampleSLogRight,
ExampleSLogLeft, ExampleLogEntryExit, ExampleLogEntryExit_nested,
ExampleLogEntryExitWithCallback, ExampleMapLeft, ExampleMapLeft_logging).
fp-go is low-frequency in training data, so signatures are easy to misremember.
For any combinator not shown below, look it up via the fp-go MCP server's
search_examples / get_example tools (see the fp-go-mcp skill) instead of
guessing. After writing code, run go build ./... and go vet ./... and fix any
type-parameter or argument-order errors before presenting it.
| Goal | Operator (in F.Pipe on a ReaderIOResult[A]) |
|---|---|
| Log value or error, structured | RIO.TapSLog[A]("msg") |
| Same at DEBUG level | RIO.TapSLogDebug[A]("msg") |
| Log success only | RIO.Tap(RIO.SLogRight[A]("msg")) |
| Log error only (ERROR level) | RIO.TapLeft[A](RIO.SLogLeft("msg")) |
| printf-style log of the value | RIO.TapIOK(IO.Logf[A]("msg: %v")) |
| Custom attributes / custom message | RIO.TapReaderIOK(logX) with logX: func(A) ReaderIO[Void] |
| Entry + exit logs with timing and correlation ID | RIO.LogEntryExit[A]("name") |
| Entry + exit at another level | RIO.LogEntryExitWithCallback[A](slog.LevelDebug, logging.GetLoggerFromContext, "name") |
| Add context to an error | RIO.MapLeft[A](ER.OnError("loading user %d", id)) |
| Choose the logger for a pipeline | RIO.Local[A](logging.WithLogger(l)) |
Tap* and ChainFirst* are aliases (TapIOK = ChainFirstIOK, TapLeft = ChainFirstLeft, …).
Prefer the Tap names for logging; they state the intent.
TapTapSLogTapSLog[A](msg) is an Operator[A, A]. Put it in the pipe after the step you want to
observe. It logs value=<A> on success or error=<err> on failure at INFO level, using the
logger from the context, and returns the computation unchanged.
func greeting(id int) RIO.ReaderIOResult[string] {
return F.Pipe2(
fetchUser(id),
RIO.TapSLog[User]("fetched user"),
RIO.Map(getName),
)
}
// level=INFO msg="fetched user" value="{ID:42 Name:Alice}"
// level=INFO msg="fetched user" error="not found"User here, not string).TapSLogInfo / TapSLogDebug choose the level explicitly. Debug logs cost nothing when the
logger's level filters them out.context/readerresult and context/readerio have their own TapSLog[A] with the same usage.TapIOK with IO.LogfAny io.Kleisli[A, B] (func(A) IO[B]) can be tapped with TapIOK. The io package
has ready-made ones:
RIO.TapIOK(IO.Logf[User]("fetched user %+v")) // log.Printf
RIO.TapIOK(IO.LogGo[User]("user {{.Name}}")) // text/template, log.Println
RIO.TapIOK(IO.Printf[User]("fetched user %+v\n")) // fmt.Printf (stdout, no timestamp)
RIO.TapIOK(IO.Logger[User](myStdLogger)("user")) // custom *log.LoggerThese use the standard log package, not slog, and log only on success; an error skips
the tap. Prefer TapSLog when the application uses structured logging.
The same operator exists for the other monads: IOR.TapIOK (ioresult), RR.TapIOK
(context/readerresult), IO.ChainFirst (plain IO).
TapReaderIOKWhen you want a specific message and specific attributes, write a small named
func(A) ReaderIO[Void] that reads the logger from the context. Reading from the context means
the log uses the request-scoped logger and inherits the correlation ID of an enclosing
LogEntryExit.
import RIOC "github.com/IBM/fp-go/v2/context/readerio"
func logUserLoaded(u User) RIOC.ReaderIO[F.Void] {
return func(ctx context.Context) IO.IO[F.Void] {
return func() F.Void {
logging.GetLoggerFromContext(ctx).InfoContext(ctx, "user loaded", "id", u.ID, "name", u.Name)
return F.VOID
}
}
}
pipeline := F.Pipe2(
fetchUser(42),
RIO.TapReaderIOK(logUserLoaded),
RIO.Map(getName),
)
// level=INFO msg="user loaded" id=42 name=AliceDo not call slog.Info(...) from inside RIO.Map: Map is for pure functions, and the global
logger ignores the logger scoped to the context.
SLogRight and SLogLeft are Kleisli arrows, so they go through Tap / TapLeft:
RIO.Tap(RIO.SLogRight[User]("fetched user")) // INFO, success only
RIO.TapLeft[User](RIO.SLogLeft("fetching user failed")) // ERROR, error only; the error still propagatesTapLeft needs the explicit [A] because A cannot be inferred from the error handler.
// ❌ SLog is Kleisli[Result[A], Void]: its input is the Result, not A, so this does not compile
RIO.Chain(RIO.SLog[User]("fetched"))
// ✅
RIO.TapSLog[User]("fetched")
// ❌ Chain replaces the value with Void
RIO.Chain(RIO.SLogRight[User]("fetched"))
// ✅
RIO.Tap(RIO.SLogRight[User]("fetched"))
// ❌ side effect inside a pure Map; bypasses the context logger
RIO.Map(func(u User) User { slog.Info("fetched", "user", u); return u })
// ✅
RIO.TapSLog[User]("fetched")LogEntryExit[A](name) is an Operator[A, A] that wraps the computation it is applied to.
It logs [entering] before that computation starts and [exiting ] (success) or [throwing]
(failure, with the error) after it ends. Both records carry the same correlation ID, and the
exit record carries the duration.
func loadUser(id int) RIO.ReaderIOResult[User] {
return F.Pipe1(
fetchUser(id),
RIO.LogEntryExit[User]("loadUser"),
)
}
// level=INFO msg=[entering] ID=7 name=loadUser
// level=INFO msg="[exiting ]" ID=7 name=loadUser duration=1.2ms
//
// on failure:
// level=INFO msg=[entering] ID=8 name=loadUser
// level=INFO msg=[throwing] ID=8 name=loadUser duration=0.4ms error="not found"In a F.Pipe, LogEntryExit wraps everything above it. Put it last to time a whole
function, or apply it to a single step to time just that step:
// Whole pipeline: the TapSLog runs between entry and exit and is logged with the same ID
loadName := F.Pipe3(
fetchUser(42),
RIO.TapSLog[User]("fetched user"),
RIO.Map(getName),
RIO.LogEntryExit[string]("loadName"),
)
// level=INFO msg=[entering] ID=3 name=loadName
// level=INFO msg="fetched user" ID=3 value="{ID:42 Name:Alice}"
// level=INFO msg="[exiting ]" ID=3 name=loadName duration=…
// One step inside a larger pipeline: wrap the Kleisli, not the pipeline
F.Pipe2(
fetchUser(42),
RIO.Chain(F.Flow2(fetchOrders, RIO.LogEntryExit[[]Order]("fetchOrders"))),
RIO.Map(summarize),
)Nested LogEntryExit calls each get their own ID. The ID is installed on the context logger
for the wrapped computation, so every context-aware log inside it (TapSLog, SLogLeft,
TapReaderIOK using GetLoggerFromContext) is correlated automatically.
TapSLog("entering")?A Tap placed before a step runs after the previous step. It can mark "about to start",
but it cannot log the end of a failed step, measure the duration or correlate the two records.
Use LogEntryExit for entry/exit logging.
RIO.LogEntryExitWithCallback[User](slog.LevelDebug, logging.GetLoggerFromContext, "loadUser")If the logger does not have the level enabled, the whole instrumentation (ID, timer, both
records) is skipped. The second argument is any func(context.Context) *slog.Logger.
An error usually surfaces far from where it happened. Logging it at every layer produces duplicate records that lack context. The idiomatic approach:
MapLeft and errors.OnError.TapLeft(SLogLeft(...)),
TapSLog, or the [throwing] record of LogEntryExit.MapLeft + errors.OnErrorRIO.MapLeft[A](f Endomorphism[error]) rewrites the error and leaves successes untouched.
ER.OnError(format, args...) builds such an endomorphism. It wraps the original error with
%w, so errors.Is / errors.As keep working.
func loadGreeting(id int) RIO.ReaderIOResult[string] {
return F.Pipe2(
fetchUser(id),
RIO.Map(getName),
RIO.MapLeft[string](ER.OnError("loading greeting for user %d", id)),
)
}
// err.Error() == "loading greeting for user 7, Caused By: not found"
// errors.Is(err, ErrNotFound) == trueCapture the inputs that explain the failure (id above) from the enclosing Kleisli's
parameters. The error then says which call failed, not just that one failed.
func handle(id int) RIO.ReaderIOResult[string] {
return F.Pipe2(
loadGreeting(id),
RIO.MapLeft[string](ER.OnError("handling request")),
RIO.TapLeft[string](RIO.SLogLeft("request failed")),
)
}
// level=ERROR msg="request failed" error="handling request, Caused By: loading greeting for user 7, Caused By: not found"LogEntryExit[throwing] logs the error as it leaves the wrapped computation. Put MapLeft above
LogEntryExit so the exit record includes the context:
F.Pipe2(
fetchUser(id),
RIO.MapLeft[User](ER.OnError("loading user %d", id)), // enrich first …
RIO.LogEntryExit[User]("loadUser"), // … then [throwing] logs the enriched error
)MapLeft to change the error. Do not use OrElse/ChainLeft with RIO.Left just to
rewrap; those are for recovery.%w (which ER.OnError does). fmt.Errorf("…: %v", err) breaks errors.Is.TapLeft log in every layer
duplicates the record.MapLeft is available in context/readerioresult, context/readerresult,
context/readerreaderioresult, readerresult, result and ioresult. In result/ioresult
the function has type func(error) E; ER.OnError(...) fits with E = error.All context-aware operators (TapSLog*, SLogLeft/Right, LogEntryExit*) read the logger with
logging.GetLoggerFromContext, which falls back to the global logger.
// Global logger (defaults to slog.Default())
old := logging.SetLogger(slog.New(slog.NewJSONHandler(os.Stdout, nil)))
defer logging.SetLogger(old)
// Request-scoped logger: install it around the whole pipeline with Local.
// logging.WithLogger(l) is the ctx → Pair[CancelFunc, ctx] arrow that Local expects.
reqLogger := logging.GetLogger().With("requestID", requestID)
value, err := R.Unwrap(F.Pipe1(
pipeline,
RIO.Local[Data](logging.WithLogger(reqLogger)),
)(ctx)())Local exists for context/readerio, context/readerresult and context/statereaderioresult
as well. To get a plain enriched context outside a pipeline, use
_, ctx2 := pair.Unpack(logging.WithLogger(reqLogger)(ctx)). For a logger stored under your
own key, build the callback with
F.Flow2(CR.AskValue[*slog.Logger](myKey), O.GetOrElse(slog.Default)). For general context
handling see the fp-go-context skill.
| Package | Logging API |
|---|---|
context/readerioresult | TapSLog, TapSLogInfo, TapSLogDebug, SLog*, SLogLeft, SLogRight, SLogWithCallback, LogEntryExit, LogEntryExitWithCallback, MapLeft, Tap*, Local |
context/readerresult | TapSLog, SLog (Kleisli[Result[A], A]), TapIOK, MapLeft, Local |
context/readerio | TapSLog, SLog, Tap, TapIOK, Local |
io | Logf, LogGo, Printf, PrintGo, Logger: func(A) IO[A], used with TapIOK |
readerio | Logf, LogGo, Printf, PrintGo for the generic ReaderIO |
logging | GetLogger, SetLogger, WithLogger, GetLoggerFromContext, LoggingCallbacks |
errors | OnError (wrap with context), OnNone, OnSome (build errors) |
SLogWithCallback[A](level, cb, msg) has the shape of SLog. Turn it into an operator with
RIOC.Tap(...) (context/readerio) when you need TapSLog with a custom level and logger.
func loadUser(id int) RIO.ReaderIOResult[User] {
return F.Pipe3(
fetchUser(id),
RIO.TapSLogDebug[User]("fetched user"), // simple log statement
RIO.MapLeft[User](ER.OnError("loading user %d", id)), // add context to the error
RIO.LogEntryExit[User]("loadUser"), // entry/exit around the three steps
)
}
func handleGreeting(reqLogger *slog.Logger, id int) RIO.ReaderIOResult[string] {
return F.Pipe3(
loadUser(id),
RIO.Map(getName),
RIO.TapLeft[string](RIO.SLogLeft("greeting failed")), // log the error once, at the boundary
RIO.Local[string](logging.WithLogger(reqLogger)), // every log above uses reqLogger
)
}
name, err := R.Unwrap(handleGreeting(reqLogger, 7)(ctx)())© IBM, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/fp-go-logging of IBM/fp-go.
Open the folder on GitHubat commit 1c4245d
fp-go Logging Patterns next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| fp-go Logging Patterns this skillIBM/fp-go | 2k | — | ~3.7k | Automated safety check: Pass | Apache-2.0 | |
| Logging Observabilitygetsentry/toolkit | 917 | — | ~2.6k | Automated safety check: Pass | Custom licence | |
| Golang Error Handlingunxed/f4 | 241 | 3 repos | ~1.8k | Automated safety check: Pass | MIT | |
| Deploy Observabilityaliyun/alibabacloud-observability-mcp-server | 166 | — | ~2.6k | Automated safety check: Notes | None | |
| Zodjasonjgardner/blockbench-mcp-plugin | 491 | 3 repos | ~1.4k | Automated safety check: Pass | GPL-3.0 | |
| Grepaiyoanbernabeu/grepai | 1.9k | — | ~1.1k | Automated safety check: Pass | MIT |
getsentry/toolkit
Review code for correct logging and error handling patterns.
unxed/f4
Idiomatic Golang error handling — creation, wrapping with %w, errors.Is/As, errors.Join, custom error types, sentinel errors, panic/recover, the single handling rule, structured logging with slog…
aliyun/alibabacloud-observability-mcp-server
Deploy, start, and update the Alibaba Cloud Observability MCP Server (阿里云可观测 MCP Server).
jasonjgardner/blockbench-mcp-plugin
Zod schema validation best practices for type safety, parsing, and error handling.
yoanbernabeu/grepai
Semantic code search and call-graph tracing. An agent skill from yoanbernabeu/grepai.
yoanbernabeu/grepai
Create a new release for grepai. An agent skill from yoanbernabeu/grepai.
IBM/fp-go
Covers handling Go's context.Context idiomatically in fp-go code: reading and scoping context through operators, timeouts, cancellation and converting ctx-first functions.
IBM/fp-go
Teaches an agent to write fp-go v2 services with the Effect type, carrying dependencies in its type parameter instead of in context.Context or parameters.
IBM/fp-go
Shows how to build composable, context-aware HTTP pipelines in Go using fp-go's ReaderIOResult monad instead of raw net/http calls.
IBM/fp-go
Generates or hand-writes composable lenses for the fp-go library so nested Go structs can be read and updated immutably.
IBM/fp-go
Configures and queries the fp-go MCP server so Claude Code, Claude Desktop or another MCP client can search fp-go's examples and skills directly.
IBM/fp-go
Guides writing or reviewing point-free, first-match-wins case lists in fp-go v2 Go code instead of switch statements and if-else chains.
Works with
Categories
Adds logging to fp-go functional pipelines with Tap operators, entry and exit logs and error context, so that logging never changes the value or error flowing through. Logging is treated as a side effect: a `Tap` runs an effect on the current value or error and passes the original computation on unchanged.OnError` and then logging once.
fp-go Logging Patterns fits situations like: adding structured slog logging inside an fp-go Pipe or Flow; logging entry and exit of a ReaderIOResult step with timing; attaching context to an error before it is logged once; putting a request-scoped logger into the context for a pipeline.
Run `npx skills add IBM/fp-go --skill fp-go-logging -a claude-code`. Or copy the skill folder (skills/fp-go-logging in IBM/fp-go) into .claude/skills/fp-go-logging in your project. Claude Code loads it when a task matches its description.
Run `npx skills add IBM/fp-go --skill fp-go-logging -a codex`. Or copy the skill folder (skills/fp-go-logging in IBM/fp-go) into .agents/skills/fp-go-logging in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add IBM/fp-go --skill fp-go-logging -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/fp-go-logging, .gemini/skills/fp-go-logging, .github/skills/fp-go-logging and .opencode/skills/fp-go-logging in your project.
Going by SKILL.md and its folder, fp-go Logging Patterns needs the command-line tools its instructions call (go). Our summary lists: Go with the fp-go v2 library; The fp-go MCP server, for looking up combinators.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
fp-go Logging Patterns is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 3.7k tokens (SKILL.md is roughly 15k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with fp-go Logging Patterns: Logging Observability (getsentry/toolkit, 917 stars), Golang Error Handling (unxed/f4, 241 stars), Deploy Observability (aliyun/alibabacloud-observability-mcp-server, 166 stars) and Zod (jasonjgardner/blockbench-mcp-plugin, 491 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
IBM (a GitHub organization, an official publisher) maintains it in IBM/fp-go, which has 2,029 GitHub stars. The repository holds 10 skills in this directory. The repository was last updated on October 7, 2026.
Source: IBM/fp-go on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.