Skip to content

[eas-cli] Add observe:logs command for viewing custom events#3638

Merged
douglowder merged 16 commits intomainfrom
doug/eng-20725-cli-add-eas-cli-commands-for-inspecting-custom-events
May 4, 2026
Merged

[eas-cli] Add observe:logs command for viewing custom events#3638
douglowder merged 16 commits intomainfrom
doug/eng-20725-cli-add-eas-cli-commands-for-inspecting-custom-events

Conversation

@douglowder
Copy link
Copy Markdown
Contributor

@douglowder douglowder commented Apr 27, 2026

Why

Add new command to retrieve custom events for Expo Observe.

USAGE
  $ eas observe:logs [EVENTNAME] [--platform android|ios] [--after <value>] [--limit <value>] [--start <value> |
    --days <value>] [--end <value> | ] [--app-version <value>] [--update-id <value>] [--session-id <value>]
    [--all-events] [--project-id <value>] [--json] [--non-interactive]

ARGUMENTS
  [EVENTNAME]  Custom event name to filter by

FLAGS
  --after=<value>        Cursor for pagination. Use the endCursor from a previous query to fetch the next page.
  --all-events           When no event name argument is provided, list all events across all event names instead of a
                         summary of event names + counts.
  --app-version=<value>  Filter by app version
  --days=<value>         Show events from the last N days (mutually exclusive with --start/--end)
  --end=<value>          End of time range (ISO date)
  --json                 Enable JSON output, non-JSON messages will be printed to stderr. Implies --non-interactive.
  --limit=<value>        The number of items to fetch each query. Defaults to 10 and is capped at 100.
  --non-interactive      Run the command in non-interactive mode.
  --platform=<option>    Filter by platform
                         <options: android|ios>
  --project-id=<value>   EAS project ID (defaults to the project ID of the current directory)
  --session-id=<value>   Filter by session ID
  --start=<value>        Start of time range (ISO date)
  --update-id=<value>    Filter by EAS update ID

DESCRIPTION
  display individual custom events (logs) emitted by the app, filtered by the event name in the argument. With no
  arguments, a list of the available event names and associated event counts is returned.


How

  • Add new GraphQL query and fragment, and new formatter for the retrieved custom events
  • New command observe:logs, designed to be similar to observe:events
  • Did some refactoring so that observe commands reuse methods for formatting results

The command can return a list of the different event names found and their counts, a list of events for a specific name, or a list of all events found:

  ┌───────────────────────────┬───────────────────────────────────────────┐                                             
  │        invocation         │                  result                   │                                             
  ├───────────────────────────┼───────────────────────────────────────────┤                                             
  │ observe:logs              │ event-name summary table (names + counts) │                                             
  ├───────────────────────────┼───────────────────────────────────────────┤                                             
  │ observe:logs --all-events │ full list of events across all names      │                                             
  ├───────────────────────────┼───────────────────────────────────────────┤                                             
  │ observe:logs <event>      │ full list of events filtered by name      │                                             
  └───────────────────────────┴───────────────────────────────────────────┘  

Test Plan

  • New unit tests added
  • Will test against Kadi's ingestion endpoint data

@linear
Copy link
Copy Markdown

linear Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@douglowder douglowder requested a review from kadikraman April 27, 2026 20:58
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

❌ Patch coverage is 93.47826% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.51%. Comparing base (51311f1) to head (3f390fa).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...ckages/eas-cli/src/graphql/queries/ObserveQuery.ts 0.00% 10 Missing ⚠️
packages/eas-cli/src/observe/formatCustomEvents.ts 96.48% 3 Missing ⚠️
packages/eas-cli/src/observe/platforms.ts 91.67% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3638      +/-   ##
==========================================
+ Coverage   56.31%   56.51%   +0.21%     
==========================================
  Files         879      886       +7     
  Lines       38004    38201     +197     
  Branches     7902     7950      +48     
==========================================
+ Hits        21397    21587     +190     
- Misses      16509    16516       +7     
  Partials       98       98              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@douglowder douglowder force-pushed the doug/eng-20725-cli-add-eas-cli-commands-for-inspecting-custom-events branch from e4ca37b to a83cd5b Compare April 29, 2026 23:12
@douglowder douglowder marked this pull request as ready for review April 29, 2026 23:26
@douglowder douglowder force-pushed the doug/eng-20725-cli-add-eas-cli-commands-for-inspecting-custom-events branch from a83cd5b to a97a24f Compare April 29, 2026 23:46
Copy link
Copy Markdown
Contributor

@kadikraman kadikraman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice thanks for the PR, it works well!

I also added a resolver for observe.customEventNames which returns { eventName, count } for the custom events for the given time period. I think we should include this because it will help with discoverability since the event names are user-chosen. Otherwise right now you need to run observe:logs with no event name to get an idea of what event names exist, but this is less useful if the event you're looking for is not in the first few pages.

So I would expect the actual usage to be more like: "list all the event names and counts in the given time period" and then you can dig deeper into the events you're interested in.

@douglowder douglowder force-pushed the doug/eng-20725-cli-add-eas-cli-commands-for-inspecting-custom-events branch from a97a24f to e876a24 Compare May 2, 2026 00:24
Copy link
Copy Markdown
Contributor Author

So I would expect the actual usage to be more like: "list all the event names and counts in the given time period" and then you can dig deeper into the events you're interested in.

Thanks for the new GraphQL! I've adjusted the command to emit event names and counts by default. See the PR description for the new help menu and a table of the different behaviors.

@douglowder douglowder requested a review from kadikraman May 2, 2026 00:36
@douglowder douglowder force-pushed the doug/eng-20725-cli-add-eas-cli-commands-for-inspecting-custom-events branch from 3b222b0 to 956de0a Compare May 2, 2026 01:34
@douglowder douglowder requested a review from tsapeta May 4, 2026 17:07
Comment thread packages/eas-cli/src/observe/formatUtils.ts
Comment thread packages/eas-cli/src/observe/formatUtils.ts Outdated
Comment thread packages/eas-cli/src/commands/observe/logs.ts
Comment thread packages/eas-cli/src/commands/observe/logs.ts Outdated
Comment thread packages/eas-cli/src/observe/formatCustomEvents.ts Outdated
Comment thread packages/eas-cli/src/observe/formatCustomEvents.ts
Comment thread packages/eas-cli/src/observe/fetchCustomEvents.ts
Comment thread packages/eas-cli/src/commands/observe/logs.ts
douglowder and others added 16 commits May 4, 2026 11:11
New hidden command that queries the customEventList GraphQL
field and displays user-emitted custom events. The eventName is
an optional positional arg; when omitted, all custom events are
returned. Filter flags mirror observe:events: --platform,
--start/--end/--days, --app-version, --update-id, --session-id,
plus pagination via --limit/--after.

Includes:
- AppObserveCustomEventFragment in types/Observe.ts
- customEventListAsync method on ObserveQuery
- observe/fetchCustomEvents.ts wrapping the query
- observe/formatCustomEvents.ts with table and JSON builders
- observe:logs hidden command + unit tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate duplicated formatTimestamp, formatDate, and time-range
description helpers from formatEvents, formatCustomEvents,
formatVersions, and formatMetrics into a single formatUtils module.
Switch formatEvents and formatCustomEvents to use the shared
renderTextTable utility for table rendering.

The events table now shows seconds and milliseconds in the
Timestamp column (matching custom events) since both formatters
share the same formatTimestamp helper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Sort fragment imports alphabetically in ObserveQuery
- Sort imports in formatMetrics.test (type MetricValues first)
- Add explicit AppObserveAppVersion return type on test helper
- Add explicit id field on customEventList edge node selection
  to satisfy the GraphQL required-fields rule

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…t name

When no positional event name is provided, query the new
customEventNames field instead of listing all events. The
result is rendered as a two-column table (Event Name, Count)
or as JSON ({ names, isTruncated }) when --json is set.
Also flags the truncated case in the table footer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When no event name positional arg is provided, the new
--all-events flag restores the previous behavior of listing all
events across all event names. Default behavior (no flag, no arg)
remains the event-name summary view.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the duplicated project ID + graphqlClient resolution logic
from observe:logs, observe:events, observe:metrics, and
observe:versions into a single helper at
src/observe/resolveProjectContext.ts. Each command now calls
the helper with its own context definitions and the resolved
flags, removing ~15 lines of duplicated branching code from
each command.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the platform flag handling out of the individual observe
commands into src/observe/platforms.ts:

- allowedPlatformFlagValues: derived from AppObservePlatform so
  new platforms added to the enum are automatically allowed
- appObservePlatformFromFlag: string flag → AppObservePlatform
  (used by observe:events and observe:logs as a single filter)
- appPlatformsFromFlag: string flag → AppPlatform[] (used by
  observe:events, observe:metrics, observe:versions when iterating
  per-platform queries)

All four observe commands now use the shared helpers, replacing
inline ternary chains with single function calls. The explicit
switch-based conversion makes adding new platforms easier in the
future.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When both a positional event name and --all-events were passed,
--all-events was silently ignored. Throw a descriptive error
instead so the user knows the combination is not supported.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover the filter-mapping logic in fetchObserveCustomEventsAsync,
mirroring the structure of fetchMetrics.test.ts. Includes a
test ensuring appUpdateId is not sent when updateId is
undefined (so the server isn't called with an empty filter
field).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The hasSeverity guard previously checked only severityText, so an
event with severityNumber and no text would have its severity
data hidden. Include severityNumber in the check, and render
the number as a fallback in the cell when the text is missing.

Add tests covering the four cases:
- text only
- number only (renders as a number)
- both (text wins)
- neither (column hidden)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add tests covering the same cases formatEvents.test.ts covers:
- Snapshot of the rendered table with multiple rows
- Subject lines: with event name, generic "Custom events"
- Date range header
- Total event count appended to summary
- Null countryCode rendering
- Next-page hint shown / hidden
- Event column hidden when filtering by event name
- Full JSON shape including all custom-event-specific fields
  (severity, properties, environment, appEasBuildId)
- Null optional JSON fields
- Empty events array

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass undefined as the locale argument to toLocaleDateString in
formatTimestamp and formatDate so the user's runtime locale is
used instead of forcing en-US output.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…stamp for logs

Revert formatTimestamp to minute precision so observe:events
and other performance-metric event tables don't show noisy
seconds + milliseconds. Add a separate formatLogTimestamp for
the observe:logs custom-events table where sub-minute resolution
matters.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… no events

When the user passes an event name to observe:logs and the filtered query
returns zero events, fall back to the customEventNames query for the same
time/platform window so the user can see which event names actually exist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@douglowder douglowder force-pushed the doug/eng-20725-cli-add-eas-cli-commands-for-inspecting-custom-events branch from 956de0a to 3f390fa Compare May 4, 2026 19:52
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

✅ Thank you for adding the changelog entry!

Copy link
Copy Markdown
Contributor Author

@tsapeta I addressed the issues you found. I also added a usability improvement -- if no events are returned by observe:logs, the command does the event names/counts query so the user can see what event names actually exist in the data (this allows correction of spelling errors, etc.).

$ easd observe:logs debug_warn_button_pressed
EAS Observe is in preview and subject to breaking changes.

No events found matching "debug_warn_button_pressed" for the last 60 days.

Available event names in this time range:

Event Name                  Count
--------------------------  -----
debug.warn_button_pressed   13   
debug.info_button_pressed   8    
debug.error_button_pressed  7    
debug.debug_button_pressed  6    
debug.fatal_button_pressed  6    
debug.trace_button_pressed  3    

@douglowder douglowder requested a review from tsapeta May 4, 2026 20:02
Copy link
Copy Markdown
Contributor Author

douglowder commented May 4, 2026

Merge activity

  • May 4, 8:24 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 4, 8:24 PM UTC: @douglowder merged this pull request with Graphite.

@douglowder douglowder merged commit 3318392 into main May 4, 2026
11 checks passed
@douglowder douglowder deleted the doug/eng-20725-cli-add-eas-cli-commands-for-inspecting-custom-events branch May 4, 2026 20:24
douglowder added a commit to expo/skills that referenced this pull request May 5, 2026
Adds the new `eas observe:logs` command (custom event names summary,
--all-events, name-filtered listings, --session-id filter, empty-result
suggestions) and reflects recent changes to `observe:metrics`: update IDs
are excluded from the table but exposed in JSON output as a `updateIds`
array per version.

Sources:
- expo/eas-cli#3638 (observe:logs)
- expo/eas-cli#3609 (metrics update ID handling)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

3 participants