Skip to content

feat: opt-in tracing instrumentation on generated entity methods#119

Merged
RAprogramm merged 2 commits into
mainfrom
118-tracing-instrumentation
May 11, 2026
Merged

feat: opt-in tracing instrumentation on generated entity methods#119
RAprogramm merged 2 commits into
mainfrom
118-tracing-instrumentation

Conversation

@RAprogramm
Copy link
Copy Markdown
Owner

Closes #118

Summary

Make logger output pinpoint which entity failed without per-call boilerplate. Every generated async method (CRUD on `Repository`, transaction adapter, projection lookups, save, stream subscriber) is now wrapped with:

```rust
#[cfg_attr(
feature = "tracing",
::tracing::instrument(
skip_all,
fields(entity = "User", op = "create"),
err(Debug),
)
)]
```

Opt-in via Cargo feature → zero cost when off, identical bits to today.

What's covered

Source Methods Op label
`sql/postgres/crud.rs` create / find_by_id / update / delete / list `create` / `find_by_id` / `update` / `delete` (or `soft_delete`) / `list`
`sql/postgres/lookup.rs` find_by_ / exists_by_ dynamic
`sql/postgres/projections.rs` find_by_id_ dynamic
`sql/postgres/save.rs` save (aggregate root) `save`
`transaction.rs` (proc-macro) tx.create / tx.find_by_id / tx.update / tx.delete / tx.list `tx.`
`streams/subscriber.rs` new / recv / try_recv `stream.subscribe` / `stream.recv` / `stream.try_recv`
`entity-core::Transaction` (manual) run / run_with_commit `tx.run` / `tx.run_with_commit`

Helper

`crates/entity-derive-impl/src/utils/tracing.rs` — single source for the attribute emission. 5 unit tests lock the shape:

  • emits `#[cfg_attr(feature = "tracing", …)]`
  • carries `entity` and `op` fields
  • enables `err(Debug)`
  • uses `skip_all`
  • references `::tracing::instrument` via absolute path

Cargo wiring

  • `entity-core` — feature `tracing = ["dep:tracing"]` + optional `tracing` dep.
  • `entity-derive` — feature `tracing = ["entity-core/tracing"]`. Added `tracing` as dev-dep so the test crate compiles with `--all-features`.
  • All 10 example crates declare a no-op `tracing = []` feature to silence `unexpected cfg condition value: "tracing"` warnings under Rust 1.80+.

Version bump (new feature → minor)

Crate Old New
entity-core 0.5.4 0.6.0
entity-derive-impl 0.5.2 0.6.0
entity-derive 0.7.3 0.8.0

Breaking change in entity-core

`Transaction::run` and `run_with_commit` gain a `core::fmt::Debug` bound on the closure's error type `E`. `sqlx::Error` and all standard error types already satisfy it, so most users won't notice — but technically it is breaking. Justifies the minor bump from 0.5 to 0.6.

Usage

```toml
[dependencies]
entity-derive = { version = "0.8", features = ["tracing"] }
tracing = "0.1"
tracing-subscriber = "0.3"
```

Initialize a subscriber and you get logs like:

```
ERROR entity.User.create: error=database error: duplicate key value violates unique constraint
in entity.User.create with entity="User" op="create"
```

Test plan

  • `cargo test --all-features` — 543 + 9 + 45 + 2 tests pass
  • `cargo clippy --all-targets --all-features -- -D warnings` — clean
  • `cargo +nightly fmt --all -- --check` — clean
  • All 10 examples compile cleanly without deprecation/cfg warnings
  • Codecov green before merge

Make logger output pinpoint *which entity* failed without any per-call
boilerplate. Every generated async method on the repository, transaction
adapter, projection lookups, and stream subscriber is now wrapped with
`#[cfg_attr(feature = "tracing", ::tracing::instrument(...))]` carrying:

- `entity = "<EntityName>"` field
- `op = "<operation>"` field (stable label, e.g. `create`, `find_by_id`,
  `find_by_email`, `tx.update`, `stream.recv`)
- `skip_all` so DTOs and pool handles stay out of the span
- `err(Debug)` to auto-emit an ERROR event on `Err` with the formatted
  source error

The attribute is gated on the consumer crate's `tracing` feature, so
users who don't enable it pay zero cost — the generated code is
bit-for-bit identical to before this PR.

Coverage (18 generator sites + 2 hand-written `entity-core` methods):

- crates/entity-derive-impl/src/utils/tracing.rs — new helper
  `instrument(entity, op)` with 5 unit tests covering shape, fields,
  err mode, and absolute-path resolution.
- crates/entity-derive-impl/src/entity/sql/postgres/crud.rs — create,
  find_by_id, update, delete (soft + hard), list.
- crates/entity-derive-impl/src/entity/sql/postgres/lookup.rs —
  find_by_<field>, exists_by_<field> (dynamic op names).
- crates/entity-derive-impl/src/entity/sql/postgres/projections.rs —
  find_by_id_<projection> (dynamic op names).
- crates/entity-derive-impl/src/entity/sql/postgres/save.rs — save
  (aggregate root).
- crates/entity-derive-impl/src/entity/transaction.rs — tx.create,
  tx.find_by_id, tx.update, tx.delete / tx.soft_delete, tx.list.
- crates/entity-derive-impl/src/entity/streams/subscriber.rs —
  stream.subscribe, stream.recv, stream.try_recv.
- crates/entity-core/src/transaction.rs — `Transaction::run` and
  `run_with_commit` carry `op = "tx.run"` / `op = "tx.run_with_commit"`
  spans (manually, since these are not generated). Generic `E` gains a
  `core::fmt::Debug` bound so `err(Debug)` can format it.

Cargo wiring:
- `entity-core` gains feature `tracing = ["dep:tracing"]` + optional
  `tracing` dep.
- `entity-derive` gains feature `tracing = ["entity-core/tracing"]`.
- `entity-derive` adds `tracing` as a dev-dep so the local test crate
  resolves `::tracing::instrument` when `--all-features` is set.
- All 10 example crates declare `tracing = []` to acknowledge the
  cfg-check and stay warning-free in Rust 1.80+.

Version bump (new feature → minor):
- entity-core: 0.5.4 -> 0.6.0
- entity-derive-impl: 0.5.2 -> 0.6.0
- entity-derive: 0.7.3 -> 0.8.0

Usage:
```toml
[dependencies]
entity-derive = { version = "0.8", features = ["tracing"] }
tracing = "0.1"
tracing-subscriber = "0.3"
```
No code changes required beyond initializing a subscriber. Logs of the
shape `entity=User op=create error=duplicate key …` appear automatically.

Closes #118
@codecov
Copy link
Copy Markdown

codecov Bot commented May 11, 2026

Codecov Report

❌ Patch coverage is 96.66667% with 2 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
crates/entity-core/src/transaction.rs 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@RAprogramm RAprogramm merged commit 1b4b1b6 into main May 11, 2026
17 checks passed
@RAprogramm RAprogramm deleted the 118-tracing-instrumentation branch May 11, 2026 08:45
RAprogramm added a commit that referenced this pull request May 11, 2026
…ctions example (#121)

README had drifted from the current crates:

- Installation block still pinned `entity-derive = "0.4"`. Updated to `0.8`
  (latest published).
- Added a "Feature flags" section enumerating every Cargo feature:
  `postgres`, `clickhouse`, `mongodb`, `streams`, `api`, `validate`,
  `tracing`. Each gets a one-line description so users can pick what they
  need without reading the source.
- Added a "Transactions" subsection under Quick Reference with a current,
  copy-pasteable `.run(async |ctx| { ... })` example. The previous text
  mentioned transactions only as a feature-table bullet; the API changed
  in 0.7.0 (#103) and the README never demonstrated the fixed signature.
  Cross-links to `run_with_commit` for conditional-commit use cases.
- Added a "Tracing" subsection showing the feature flag, expected
  dependencies, sample log output, and the zero-cost guarantee when the
  flag is off — the feature was added in 0.8.0 (#118 / #119) and was
  previously undocumented at the README level.
- Added "Structured Logging" to the Features table.

Closes #120
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: opt-in tracing instrumentation on generated entity methods

1 participant