Releases: dezsirazvan/ez_logs_agent
v0.2.1 — bulk capture polish
Polish pass on 0.2.0's bulk-database capture, plus hot-path perf work across all capturers.
Fixed
- Bulk-op row counts on Postgres. The PG adapter does not populate
payload[:row_count]for plainDELETE/UPDATEnotifications, so 0.2.0 shippedrow_count: 0for the cases that matter most.BulkDatabaseCapturernow prepends a tiny shim ontoActiveRecord::Relation#delete_all/#update_allthat stashes the returned row count and back-fills it onto the just-pushed event viaBuffer.peek_last. No wire-shape change. - Model resolution in Rails dev mode.
ActiveRecord::Base.descendantsonly sees eager-loaded models. The resolver now falls through tosafe_constantizeof the classified table name and verifies the reconstructed class actually owns that table, so bulk ops on lazy-autoloaded models in dev / test no longer silently drop. - Rails Query Log Tags noise in
where_template. The parser strips/*application='X',action='Y'*/comments before extracting the WHERE clause, so the humanized filter line reads cleanly instead of leaking instrumentation tags.
Changed
- Framework-rewrite filter narrowed. The 0.2.0 filter that swallowed Rails-generated
SET col = NULLwrites was too aggressive — it also hid deliberate "null this column out" operations the customer wrote. The filter now only dropsCOALESCE-shaped counter bumps and empty-SETshells; honestSET col = NULLwrites are captured. - Hot-path performance.
capture_jobs,capture_database, the excluded-tables / excluded-job-classes / display-name maps, and the user-extended sensitive-key patterns are now memoized at install time acrossActiveJobCapturer,DatabaseCapturer,BulkDatabaseCapturer, andSensitivePatterns. Thesql.active_recordsubscriber uses a 5-arity block (no splat allocation) and anend_with?name-prefilter ahead of any parsing. Bulk capture overhead measured below the noise floor on a 10 ms reference query.
\xf0\x9f\x92\x8e Install: `gem "ez_logs_agent", "~> 0.2.1"`
v0.2.0
[0.2.0] — 2026-06-05
Added
-
Bulk database operations are now captured. Adds a fourth event
source_type—bulk_database— for the four ActiveRecord operations
that bypass per-row callbacks:delete_all,update_all,insert_all,
upsert_all. Implemented via a narrowly-filtered
ActiveSupport::Notifications.subscribe("sql.active_record")subscription
(Capturers::BulkDatabaseCapturer) — not a replacement for the existing
callback-based DatabaseCapturer; the two run side by side under the
samecapture_databaseconfig flag.The new wire shape carries
model_class,operation,row_count,
where_template+ sanitizedwhere_binds, plus an operation-specific
field (setfor update_all,columnsfor insert_all/upsert_all).
Insert/upsert ship column NAMES only — no values, per the product
decision that bulk-row PII shouldn't ride the wire.Cascade case (
dependent: :delete_allon a parent destroy) is
captured automatically, since it produces the same SQL shape — the
reader sees the parent destroy AND the cascade as sibling rows on the
timeline.Resource attribution uses a
resource_id: "bulk:<row_count>"sentinel,
since individual row IDs are not knowable from the SQL without
changing the customer's operation (which would violate the read-only
principle).
Changed
Sanitizer::SENSITIVE_PATTERNSand the previous in-class
DatabaseCapturer::SENSITIVE_PATTERNS(which had drifted) are now a
single source of truth:EzLogsAgent::SensitivePatterns::PATTERNS.
Both capturers + the new BulkDatabaseCapturer consult the same list.
The merged list is the UNION of the previous two — no patterns
removed;passwd,pwd,cvv,cvc,pem,cipher,nonce,
salt,digest,signature,hmacall continue to be masked.encrypts :foointrospection (Rails 7+model.class.encrypted_attributes)
is now exposed as the standaloneEzLogsAgent::EncryptedAttributes
module, so both the per-row and bulk capturers consult it via the
same path. Behavior unchanged for per-row.
Limits (documented)
- Raw
connection.execute(sql)calls are not captured (no
notifications fire undersql.active_recordwith a recognizable
shape). Use the typed bulk methods to get visibility. - Specific row IDs affected by a bulk op are not captured — only the
filter rule (WHERE columns + values) and row count. For per-row
detail, usefind_each(&:destroy)style which fires per-row
callbacks.
v0.1.10
[0.1.10] — 2026-06-05
Fixed
Sanitizerno longer collapses ActiveJob keyword-argument hashes
(those tagged with_aj_ruby2_keywords) to"[Object]"at the
depth-3 cap. ActionMailer puts kwargs at two wrapper layers — an
outer{"args" => [kwargs_hash], "_aj_ruby2_keywords" => ["args"]}
payload and the kwargs hash itself, also marked. Each layer is
framework noise; the depth budget now skips them so real kwargs
survive the wire (e.g.CompanyMailer.deleted(admin_email:, ...)
now shipsadmin_email/company_name/deleted_atinstead of a
single"[Object]").
The carve-out is narrow: only hashes that actually carry the
_aj_ruby2_keywords marker are exempt. Customer-data hashes
without the marker still hit the depth cap unchanged, and
sensitive-key filtering (passwords, tokens, …) still runs on the
real kwargs entries — the wrapper is free to descend into, but
nothing inside it is exempt from masking. No wire-format change.
v0.1.9
Fixed
Sanitizerno longer collapses ActiveJob record references ({"_aj_globalid" => "gid://app/Model/id"}) to"[Object]"at the depth-3 cap. These one-key wrapper hashes pass through verbatim so the server can display which record a job ran on (e.g. ActionMailer'sRecord 1field now readsUser #42instead of[Object]).
The carve-out is narrow: only the exact {"_aj_globalid" => "gid://..."} shape is exempt. Multi-key hashes, non-GID values, and any other nested structure still hit the existing graph-protection rules (depth cap, array truncation, non-primitive collapse) unchanged. No wire-format change.
v0.1.8
Changed
- Install template (
rails generate ez_logs_agent:install) now pre-fillsconfig.server_urlwith the real SaaS endpoint (https://app.ezlogs.io) andconfig.project_tokenwithENV["EZLOGS_API_KEY"], uncommented. Self-hosters override viaENV["EZLOGS_SERVER_URL"]. New customers only need to set one env var (the API key); they no longer have to know to uncomment the URL line or guess the right value. - Documentation (
QUICKSTART.md,CONFIGURATION.md,FAQ.md) uses the real product URLs (app.ezlogs.io,ezlogs.io/pricing) instead ofyour-ezlogs-server.complaceholders.
No wire-format or runtime-behavior changes. Existing customers' already-generated initializer files are untouched — the generator doesn't rewrite them. Only fresh installs see the new defaults.
v0.1.7
Changed
- Gem
homepagenow points at the product site (https://ezlogs.io/)
instead of the GitHub mirror, so the RubyGems "Homepage" link sends
installers to the product.source_code_uristill points at the
mirror. Metadata-only release; no code or wire change.
v0.1.6
Added
- Optional
actor.principalsub-field ({ id:, label? }) on the wire,
carrying the human akind: "agent"orkind: "hybrid"actor is
acting on behalf of. Drops silently if malformed; existing callers
unaffected. Lets the server narrate agent actions as
"Claude updated employee, on behalf of Razvan" instead of
attributing the change to either party alone.
Internal
- Dropped a stray gemspec bump to
sidekiq ~> 8.1that landed via an
auto-merged dependabot PR without re-resolvingGemfile.lock. Dev
deps are back in sync with the lockfile (rails ~> 7.0,
sqlite3 ~> 1.6,sidekiq ~> 7.0); no runtime change for customers.
v0.1.5
Security
DatabaseCapturerno longer captures columns the host app declared
encrypts :fooon. Rails 7+ decrypts attributes in memory before
saved_changesfires, so without this guard the plaintext of every
encrypted column was landing on the wire and in the EZLogs UI on
every create / update. The new policy is declarative: at capture
time we readrecord.class.encrypted_attributes(Rails 7+) and drop
every name in that set, regardless of column name. If the host app
encrypted it, we never capture it. Upgrade is strongly recommended
for any deployment whose models useencrypts. Customers running
0.1.4 or earlier should also scrub historical events for the
affected column names — the data leaked in the past will stay in
the event store until masked.SENSITIVE_PATTERNS(the secondary name-based denylist) now also
matchesprivate_key,public_key,signing_key,pem,cipher,
nonce,salt,digest,signature,hmac. Belt-and-suspenders
for columns that carry sensitive material but weren't declared
encrypts(legacy code, manual hashing, externally-generated
material).
v0.1.4
Fixed
ActiveJobCapturer#sidekiq_adapter?no longer triggers Rails to
autoloadActiveJob::QueueAdapters::SidekiqAdapter. Rails registers
that constant for lazy autoload even when sidekiq isn't in the host's
bundle; the oldis_a?check forced the adapter file to load, which
requires sidekiq and raisedGem::LoadError: sidekiq is not part of the bundleon every job run for hosts using SolidQueue, GoodJob, or
any other non-Sidekiq adapter. The check now compares the adapter
class name string and never references the constant.
v0.1.3
Fixed
- Include
lib/tasks/ez_logs_agent.rakein the published gem. 0.1.2's
gemspec usedlib/**/*.{rb,tt}which didn't match.rakefiles; the
railtie'srake_tasks do; load 'tasks/...'; endthen raised
LoadErroron boot, breakingassets:precompilein any host app's
Docker build. 0.1.2 yanked.
Note
- 0.1.0, 0.1.1, 0.1.2 are all yanked. Use 0.1.3 or later.