> Add or review structured error logging in EdenFS daemon (C++) — the ErrorLogger / EdenErrorInfo system that feeds the edenfs_errors table. Use when exception_wrapper / .thenError), instrumenting a failure path, or debugging a wrong stack trace or wrong component. Covers throw-site trace rules, component choice, errorType, noise filtering, gating, and testing.
npx skills add https://github.com/facebook/sapling --skill structured-error-logging
EdenFS records daemon-side failures as structured rows in the edenfs_errors
table (Hive + Scuba) via the ErrorLogger. Unlike XLOG(ERR, ...) — which only
writes to the local log file — structured logging gives every error queryable
columns (component, error code/name, mount point, inode, stack trace, …) so we
can aggregate failures across the fleet, alert on them, and debug them after the
fact.
Logging *correctly* matters: the two most common mistakes — a **wrong stack
trace and a mislabeled component** — make the data actively misleading rather
than just incomplete. The rules below prevent both. (If you'd XLOG(ERR, ...) an
unexpected failure, consider a structured error too.)
| Component | Location |
|-----------|----------|
| Logger entry point | eden/fs/telemetry/ErrorLogger.h (log(), isEnabled()) |
| Builder + factories | eden/fs/telemetry/EdenErrorInfo.h, EdenErrorInfoBuilder.h |
| Exception wrapper | eden/fs/telemetry/ErrorArg.h |
| Field → column mapping | eden/fs/telemetry/DaemonError.h (populate()) |
| Component enum | eden/fs/telemetry/EdenComponent.h |
| Access from a handler | serverState_->getErrorLogger() |
try {
doSomethingThatMayThrow();
} catch (const std::exception& ex) {
serverState_->getErrorLogger().log(
EdenErrorInfo::backingStore(ex) // 1. pick the component, pass the error
.withMountPoint(mountPath.asString()) // 2. chain optional context
.withErrorType("blob_import_failed")); // 3. a stable, queryable subtype
}
log() consumes the builder — you never call create() yourself.
EdenFS hooks the C++ throw path so that **every throw records a backtrace into a
thread-local slot**. Two properties of that slot decide correctness:
log() runs) consumes it.
ErrorArg(const std::exception&) opts into reading that slot. So the trace
attached to your error matches your exception **only if the most recent throw on
this thread was the one you're logging, and nothing has thrown since.**
The one valid case — pass ex directly:
try {
importBlob(id); // throws here, on this thread
} catch (const std::exception& ex) {
// nothing else throws between the catch and the log
logger.log(EdenErrorInfo::backingStore(ex).withErrorType("blob_import"));
}
Watch the "nothing thrown since" part: any throw between the catch and the
log() — even one thrown-and-caught internally by a helper, a folly::tryTo, a
map/fmt op — overwrites the slot, so do the log() before such work.
ex's trace is wrong → use fromExceptionWithoutTraceIn all these cases the slot holds a *different* throw (or none), so passing ex
attaches a confidently wrong trace (worse than none) — use
ErrorArg::fromExceptionWithoutTrace(ex):
folly::Try / exception_wrapper, or .thenError / .thenTry /with_exception.
newEdenError(ex), std::rethrow_exception (a newthrow overwrites the slot).
// Visiting a boxed exception (here a folly::exception_wrapper) away from its
// throw site — strip the mismatched trace:
ew.with_exception([&](const std::exception& e) {
logger.log(EdenErrorInfo::objectStore(ErrorArg::fromExceptionWithoutTrace(e))
.withErrorType("checkout_update_error"));
});
The component column is how errors are bucketed. EdenErrorInfo has one factory
per component — see EdenErrorInfo.h for the factories/signatures and
EdenComponent.h for the component list.
The non-obvious part is *which* to pick: use the factory for the subsystem where
the failure actually originated, which is not always the subsystem whose code
you're editing. E.g. an inode *load* failure surfaces in InodeMap, but the data
came from the object store, so it's objectStore(...), not overlay(...). Ask
"where did the thing that failed live?", not "what file am I editing?".
withErrorType("...") distinguishes failure modes within a component so you can
filter/alert on them. Use descriptive, stable lower_snake_case strings (e.g.
blob_import_failed). Treat them like enum values — don't reword an existing one
casually; dashboards and alerts key off them.
Chain whatever .with*() context is useful (mount point, inode, file path, …);
all are optional and return the builder — see EdenErrorInfoBuilder.h. One thing
not visible from the signatures: .withErrorCode() / .withErrorName() are
auto-filled from std::system_error, so you don't set them yourself.
Skip errors that are normal control flow (e.g. a FUSE/NFS ENOENT on a missing
path) — logging them buries the real failures. Where an errno is available,
filter on it.
Behavior is controlled by config flags (see EdenConfig.h); log() no-ops when
disabled, so just call it from the catch:
telemetry:enable-error-logging — master on/off.telemetry:error-scribe-category — Scribe category for the legacy path.telemetry:enable-stack-trace-upload — upload captured stack traces to Manifold.telemetry:enable-xplatlogger-errors — route errors via XplatLogger to theLogger (Hive).
Components that take an injectable ErrorLogger&/ErrorLogger* (e.g.
SaplingBackingStore, FuseChannel, TestMount) are unit-testable directly:
inject a capturing logger and assert on what was logged.
eden/fs/telemetry/test/CapturingScribeLogger.h to capture emitted events.TestMount accepts an injectable ErrorLogger.debugLogError Thrift endpoint throws andlogs a test error: eden debug thrift debugLogError.
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Use when implementing any feature or bugfix, before writing implementation code
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
Expert guidance for systematic backtesting of trading strategies. Use when developing, testing, stress-testing, or validating quantitative trading strategies. Covers "beating ideas to death" methodology, parameter robustness testing, slippage modeling, bias prevention, and interpreting backtest results. Applicable when user asks about backtesting, strategy validation, robustness testing, avoiding overfitting, or systematic trading development.
Cloud laboratory platform for automated protein testing and validation. Use when designing proteins and needing experimental validation including binding assays, expression testing, thermostability measurements, enzyme activity assays, or protein sequence optimization. Also use for submitting experiments via API, tracking experiment status, downloading results, optimizing protein sequences for better expression using computational tools (NetSolP, SoluProt, SolubleMPNN, ESM), or managing protein design workflows with wet-lab validation.
This skill should be used for time series machine learning tasks including classification, regression, clustering, forecasting, anomaly detection, segmentation, and similarity search. Use when working with temporal data, sequential patterns, or time-indexed observations requiring specialized algorithms beyond standard ML approaches. Particularly suited for univariate and multivariate time series analysis with scikit-learn compatible APIs.
Take facebook/structured-error-logging from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.