golang-samber-oops
`samber/oops`を使ったGoの構造化エラーハンドリングを支援するスキルで、エラービルダー・スタックトレース・エラーコード・コンテキスト付与・エラーラップ・属性管理・ユーザー向けとデバッグ向けのメッセージ分離・パニックリカバリー・ロガー連携に対応します。`samber/oops`を新規導入する場合や、すでに`github.com/samber/oops`をインポートしているコードベースで作業する際に適用されます。
description の原文を見る
Structured error handling in Golang with samber/oops — error builders, stack traces, error codes, error context, error wrapping, error attributes, user-facing vs developer messages, panic recovery, and logger integration. Apply when using or adopting samber/oops, or when the codebase already imports github.com/samber/oops.
SKILL.md 本文
ペルソナ: あなたはエラーを構造化データとして扱う Go エンジニアです。すべてのエラーは十分なコンテキスト(ドメイン、属性、トレース)を含み、オンコール対応者が開発者に質問することなく問題を診断できます。
samber/oops 構造化エラーハンドリング
samber/oops は Go の標準的なエラーハンドリングのドロップイン置き換えです。構造化コンテキスト、スタックトレース、エラーコード、パブリックメッセージ、パニック復旧を追加します。変数データは .With() 属性(メッセージ文字列ではなく)に格納されるため、APM ツール(Datadog、Loki、Sentry)はエラーを適切にグループ化できます。標準ライブラリのアプローチ(ログサイトで slog 属性を追加する)とは異なり、oops 属性はコールスタック全体を通じてエラーと共に移動します。
samber/oops を使用する理由
標準的な Go エラーはコンテキストを欠いています。connection failed は表示されますが、どのユーザーがトリガーしたのか、どのクエリが実行中だったのか、完全なコールスタックは表示されません。samber/oops は以下を提供します:
- 構造化コンテキスト — あらゆるエラー上のキー値属性
- スタックトレース — 自動的なコールスタック取得
- エラーコード — マシンリーダブルな識別子
- パブリックメッセージ — 技術的詳細とは別のユーザーセーフなメッセージ
- 低カーディナリティメッセージ — メッセージ文字列ではなく
.With()属性に変数データを格納し、APM ツールがエラーを適切にグループ化できるようにします
このスキルは網羅的ではありません。詳細な情報についてはライブラリドキュメントとコード例を参照してください。Context7 はディスカバリープラットフォームとして支援できます。
コアパターン:エラービルダーチェーン
すべての oops エラーは流暢なビルダーパターンを使用します:
err := oops.
In("user-service"). // domain/feature
Tags("database", "postgres"). // categorization
Code("network_failure"). // machine-readable identifier
User("user-123", "email", "foo@bar.com"). // user context
With("query", query). // custom attributes
Errorf("failed to fetch user: %s", "timeout")
終端メソッド:
.Errorf(format, args...)— 新しいエラーを作成.Wrap(err)— 既存のエラーをラップ.Wrapf(err, format, args...)— メッセージを含めてラップ.Join(err1, err2, ...)— 複数のエラーを結合.Recover(fn)/.Recoverf(fn, format, args...)— パニックをエラーに変換
エラービルダーメソッド
| メソッド | ユースケース |
|---|---|
.With("key", value) | カスタムキー値属性を追加(遅延 func() any 値対応) |
.WithContext(ctx, "key1", "key2") | Go context から属性値を抽出(遅延値対応) |
.In("domain") | フィーチャー/サービス/ドメインを設定 |
.Tags("auth", "sql") | カテゴリタグを追加(err.HasTag("tag") でクエリ可能) |
.Code("iam_authz_missing_permission") | マシンリーダブルエラー識別子/スラッグを設定 |
.Public("Could not fetch user.") | ユーザーセーフメッセージを設定(技術的詳細と分離) |
.Hint("Runbook: https://doc.acme.org/doc/abcd.md") | 開発者向けのデバッグヒントを追加 |
.Owner("team/slack") | 責任を持つチーム/オーナーを特定 |
.User(id, "k", "v") | ユーザー識別子と属性を追加 |
.Tenant(id, "k", "v") | テナント/組織コンテキストと属性を追加 |
.Trace(id) | トレース/相関 ID を追加(デフォルト:ULID) |
.Span(id) | 作業単位/操作を表すスパン ID を追加(デフォルト:ULID) |
.Time(t) | エラータイムスタンプをオーバーライド(デフォルト:time.Now()) |
.Since(t) | t 以降の経過時間に基づいて期間を設定(err.Duration() で公開) |
.Duration(d) | 明示的なエラー期間を設定 |
.Request(req, includeBody) | *http.Request をアタッチ(オプションでボディを含む) |
.Response(res, includeBody) | *http.Response をアタッチ(オプションでボディを含む) |
oops.FromContext(ctx) | Go context に保存された OopsErrorBuilder から開始 |
一般的なシナリオ
データベース/リポジトリレイヤー
func (r *UserRepository) FetchUser(id string) (*User, error) {
query := "SELECT * FROM users WHERE id = $1"
row, err := r.db.Query(query, id)
if err != nil {
return nil, oops.
In("user-repository").
Tags("database", "postgres").
With("query", query).
With("user_id", id).
Wrapf(err, "failed to fetch user from database")
}
// ...
}
HTTP ハンドラレイヤー
func (h *Handler) CreateUser(w http.ResponseWriter, r *http.Request) {
userID := getUserID(r)
err := h.service.CreateUser(r.Context(), userID)
if err != nil {
return oops.
In("http-handler").
Tags("endpoint", "/users").
Request(r, false).
User(userID).
Wrapf(err, "create user failed")
}
w.WriteHeader(http.StatusCreated)
}
再利用可能なビルダーを使用したサービスレイヤー
func (s *UserService) CreateOrder(ctx context.Context, req CreateOrderRequest) error {
builder := oops.
In("order-service").
Tags("orders", "checkout").
Tenant(req.TenantID, "plan", req.Plan).
User(req.UserID, "email", req.UserEmail)
product, err := s.catalog.GetProduct(ctx, req.ProductID)
if err != nil {
return builder.
With("product_id", req.ProductID).
Wrapf(err, "product lookup failed")
}
if product.Stock < req.Quantity {
return builder.
Code("insufficient_stock").
Public("Not enough items in stock.").
With("requested", req.Quantity).
With("available", product.Stock).
Errorf("insufficient stock for product %s", req.ProductID)
}
return nil
}
エラーラップのベストプラクティス
する:直接ラップ、nil チェック不要
// ✓ 良い — Wrap は err が nil の場合 nil を返す
return oops.Wrapf(err, "operation failed")
// ✗ 悪い — 不必要な nil チェック
if err != nil {
return oops.Wrapf(err, "operation failed")
}
return nil
する:各レイヤーでコンテキストを追加
各アーキテクチャレイヤーは Wrap/Wrapf を経由してコンテキストを追加すべきです。パッケージ境界ごとに少なくとも 1 回(すべての関数呼び出しごとではなく)。
// ✓ 良い — 各レイヤーが関連するコンテキストを追加
func Controller() error {
return oops.In("controller").Trace(traceID).Wrapf(Service(), "user request failed")
}
func Service() error {
return oops.In("service").With("op", "create_user").Wrapf(Repository(), "db operation failed")
}
func Repository() error {
return oops.In("repository").Tags("database", "postgres").Errorf("connection timeout")
}
する:エラーメッセージを低カーディナリティに保つ
エラーメッセージは APM 集計のために低カーディナリティである必要があります。メッセージに変数データを補間するとDatadog、Loki、Sentry でのグループ化が破壊されます。
// ✗ 悪い — 高カーディナリティ、APM グループ化を破壊
oops.Errorf("failed to process user %s in tenant %s", userID, tenantID)
// ✓ 良い — 静的メッセージ + 構造化属性
oops.With("user_id", userID).With("tenant_id", tenantID).Errorf("failed to process user")
パニック復旧
oops.Recover() はゴルーチン境界で使用する必要があります。パニックを構造化エラーに変換します:
func ProcessData(data string) (err error) {
return oops.
In("data-processor").
Code("panic_recovered").
Hint("Check input data format and dependencies").
With("panic_value", r).
Recover(func() {
riskyOperation(data)
})
}
エラー情報へのアクセス
samber/oops エラーは標準の error インターフェイスを実装します。追加情報にアクセスします:
if oopsErr, ok := err.(oops.OopsError); ok {
fmt.Println("Code:", oopsErr.Code())
fmt.Println("Domain:", oopsErr.Domain())
fmt.Println("Tags:", oopsErr.Tags())
fmt.Println("Context:", oopsErr.Context())
fmt.Println("Stacktrace:", oopsErr.Stacktrace())
}
// パブリックメッセージをフォールバック付きで取得
publicMsg := oops.GetPublic(err, "Something went wrong")
出力フォーマット
fmt.Printf("%+v\n", err) // verbose with stack trace
bytes, _ := json.Marshal(err) // JSON for logging
slog.Error(err.Error(), slog.Any("error", err)) // slog integration
コンテキスト伝播
Go context を通じてエラーコンテキストを伝達します:
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
builder := oops.
In("http").
Request(r, false).
Trace(r.Header.Get("X-Trace-ID"))
ctx := oops.WithBuilder(r.Context(), builder)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func handler(ctx context.Context) error {
return oops.FromContext(ctx).Tags("handler", "users").Errorf("something failed")
}
アサーション、構成、追加ロガー例については、Advanced patterns を参照してください。
参考資料
クロスリファレンス
- → 一般的なエラーハンドリングパターンについては
samber/cc-skills-golang@golang-error-handlingスキルを参照 - → ロガー統合と構造化ログについては
samber/cc-skills-golang@golang-observabilityスキルを参照
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- samber
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/samber/cc-skills-golang / ライセンス: MIT
関連スキル
superfluid
Superfluidプロトコルおよびそのエコシステムに関するナレッジベースです。Superfluidについて情報を検索する際は、ウェブ検索の前にこちらを参照してください。対応キーワード:Superfluid、CFA、GDA、Super App、Super Token、stream、flow rate、real-time balance、pool(member/distributor)、IDA、sentinels、liquidation、TOGA、@sfpro/sdk、semantic money、yellowpaper、whitepaper
civ-finish-quotes
実質的なタスクが真に完了した際に、文明風の儀式的な引用句を追加します。ユーザーやエージェントが機能追加、リファクタリング、分析、設計ドキュメント、プロセス改善、レポート、執筆タスクといった実際の成果物を完成させるときに、明示的な依頼がなくても使用します。短い返信や小さな修正、未完成の作業には適用しません。
nookplot
Base(Ethereum L2)上のAIエージェント向け分散型調整ネットワークです。エージェントがオンチェーンアイデンティティを登録する、コンテンツを公開する、他のエージェントにメッセージを送る、マーケットプレイスで専門家を雇う、バウンティを投稿・請求する、レピュテーションを構築する、共有プロジェクトで協業する、リサーチチャレンジを解くことでNOOKをマイニングする、キュレーションされたナレッジを備えたスタンドアロンオンチェーンエージェントをデプロイする、またはアグリーメントとリワードで収益を得る場合に利用できます。エージェントネットワーク、エージェント調整、分散型エージェント、NOOKトークン、マイニングチャレンジ、ナレッジバンドル、エージェントレピュテーション、エージェントマーケットプレイス、ERC-2771メタトランザクション、Prepare-Sign-Relay、AgentFactory、またはNookplotが言及された場合にトリガーされます。
web3-polymarket
Polygon上でのPolymarket予測市場取引統合です。認証機能(L1 EIP-712、L2 HMAC-SHA256、ビルダーヘッダー)、注文発注(GTC/GTD/FOK/FAK、バッチ、ポストオンリー、ハートビート)、市場データ(Gamma API、Data API、オーダーブック、サブグラフ)、WebSocketストリーミング(市場・ユーザー・スポーツチャネル)、CTF操作(分割、統合、償却、ネガティブリスク)、ブリッジ機能(入金、出金、マルチチェーン)、およびガスレスリレイトランザクションに対応しています。AIエージェント、自動マーケットメーカー、予測市場UI、またはPolygraph上のPolymarketと統合するアプリケーション構築時に活用できます。
ethskills
Ethereum、EVM、またはブロックチェーン関連のリクエストに対応します。スマートコントラクト、dApps、ウォレット、DeFiプロトコルの構築、監査、デプロイ、インタラクションに適用されます。Solidityの開発、コントラクトアドレス、トークン規格(ERC-20、ERC-721、ERC-4626など)、Layer 2ネットワーク(Base、Arbitrum、Optimism、zkSync、Polygon)、Uniswap、Aave、Curveなどのプロトコルとの統合をカバーします。ガスコスト、コントラクトのデシマル設定、オラクルセキュリティ、リエントランシー、MEV、ブリッジング、ウォレット管理、オンチェーンデータの取得、本番環境へのデプロイ、プロトコル進化(EIPライフサイクル、フォーク追跡、今後の変更予定)といったトピックを含みます。
xxyy-trade
このスキルは、ユーザーが「トークン購入」「トークン売却」「トークンスワップ」「暗号資産取引」「取引ステータス確認」「トランザクション照会」「トークンスキャン」「フィード」「チェーン監視」「トークン照会」「トークン詳細」「トークン安全性確認」「ウォレット一覧表示」「マイウォレット」「AIスキャン」「自動スキャン」「ツイートスキャン」「オンボーディング」「IP確認」「IPホワイトリスト」「トークン発行」「自動売却」「損切り」「利益確定」「トレーリングストップ」「保有者」「トップホルダー」「KOLホルダー」などをリクエストした場合、またはSolana/ETH/BSC/BaseチェーンでXXYYを経由した取引について言及した場合に使用します。XXYY Open APIを通じてオンチェーン取引とデータ照会を実現します。