Skip to content

feat(appkit): start App Analytics automatically when App telemetry is on - #614

Draft
ditadi wants to merge 2 commits into
stack/app-analytics/03-uifrom
stack/app-analytics/04-auto-start
Draft

ditadi wants to merge 2 commits into
stack/app-analytics/03-uifrom
stack/app-analytics/04-auto-start

Conversation

@ditadi

@ditadi ditadi commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Why the change

Mounting <AppAnalytics /> still takes a code change in every app, so when App telemetry is on the server plugin now injects a self-contained build of the SDK into index.html, and page views flow with no client code.

Special things to note

  • Behavior change on upgrade: apps with App telemetry enabled start collecting page views without any change on their side. server({ appAnalytics: false }) turns off both the relay and the script. Apps without OTEL_EXPORTER_OTLP_ENDPOINT (including local development) get no script and no traffic.
  • An app's own appAnalytics.init() or <AppAnalytics /> always wins, in either order. The injected script skips init() when the app has already called it, and a later app call replaces the injected options. Both use the one shared client from feat(appkit-ui): export App Analytics from the beta barrels #613, so page views aren't recorded twice.
  • @databricks/appkit now ships dist/plugins/server/app-analytics/sdk.js, copied from the app-analytics build. The app-analytics package stays a devDependency and is stripped at pack time, so appkit has no new npm dependency. Its build fails if the file is missing.

Change outline

The new config shape. true (the default) keeps the relay from #612 and adds the script when telemetry is on:

 interface ServerConfig {
-  appAnalytics?: boolean;
+  appAnalytics?: boolean | AppAnalyticsBrowserOptions;
 }
+interface AppAnalyticsBrowserOptions {
+  webVitals?: boolean;    // default false
+  autocapture?: boolean;  // default false
+  sampleRate?: number;    // 0..1, default 1
+}

How the page gets the script and its options:

ServerPlugin
  appAnalytics !== false
    POST /_analytics/v1/logs  → relay (#612)
+   GET  /_analytics/v1/sdk.js → serveSdk()   # read once, text/javascript, no-cache, 404 + one warning if missing
  getInjectedAppAnalytics()
+   shouldInjectSdk: appAnalytics !== false && OTLP logs endpoint resolved
+   appAnalyticsBrowserOptions: only known keys with the right type
  StaticServer / ViteDevServer(…, appAnalytics)
    index.html <body>
      <script id="__appkit__">{ …, appAnalytics: { webVitals, autocapture, sampleRate } }</script>
+     <script type="module" src="/_analytics/v1/sdk.js"></script>

In ViteDevServer, the tag is added after transformIndexHtml. Otherwise Vite would prefix the src with its base and try to pre-transform the file as a client module.

The browser side is a new entry point in app-analytics:

src/auto.ts  → dist/browser/sdk.js (ESM, minified, web-vitals inlined, license banner)
  startFromAppKitConfig()
    options = window.__appkit__.appAnalytics   # none → do nothing
    isDefaultClientConfigured()                # app already called init() → do nothing
    appAnalytics.init(options)
 export const appAnalytics: AppAnalyticsClient = {
-  init: (options) => sharedClient().init(options),
+  init: (options) => {
+    globalThis[CLIENT_CONFIGURED_KEY] = true;   // read by isDefaultClientConfigured()
+    sharedClient().init(options);
+  },

Build wiring:

 packages/app-analytics/tsdown.config.ts
+  second config: entry { sdk: "src/auto.ts" } → dist/browser, platform browser, noExternal web-vitals
 packages/appkit/tsdown.config.ts
+  copy ../app-analytics/dist/browser/sdk.js → dist/plugins/server/app-analytics/
 tools/dist-appkit.ts
+  delete devDependencies["@databricks/app-analytics"]

This pull request and its description were written by Isaac.

@ditadi
ditadi added this pull request to stack #616 September 26, 2026 21:42
@github-actions

github-actions Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

@databricks/appkit

npm tarball (packed): 1.2 MB (+37 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 1.2 MB (+64 KB) 447 KB (+21 KB)
Type declarations 448 KB (+4.6 KB) 162 KB (+569 B)
Source maps 2.4 MB (+42 KB) 813 KB (+17 KB)
Other 11 KB 3.7 KB (+16 B)
Total 4.1 MB (+111 KB) 1.4 MB (+38 KB)
Per-entry composition (own code — deps external (as shipped))
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
. 99 KB (+3.0 KB) 2.5 KB (-7 B) 102 KB (+3.0 KB) external 322 KB (+7.3 KB)
./beta 93 KB (+239 B) 481 B (+24 B) 94 KB (+263 B) external 282 KB (+618 B)
./testing 39 KB (+66 B) 34 KB (+3.0 KB) 72 KB (+3.1 KB) external 209 KB (+7.2 KB)
./tsdown 520 B (+1 B) 0 B 520 B (+1 B) external 813 B
./type-generator 23 KB (+125 B) 0 B 23 KB (+125 B) external 65 KB (+82 B)

Chunks:

Entry Chunk Load Size (gz)
. index.js initial 92 KB
. app-analytics-relay.js initial 6.8 KB
. remote-tunnel-manager.js lazy 2.5 KB
./beta beta.js initial 77 KB
./beta stream-manager.js initial 5.8 KB
./beta databricks.js initial 3.3 KB
./beta wide-event-emitter.js initial 3.3 KB
./beta configuration.js initial 2.3 KB
./beta service-context.js initial 1.3 KB
./beta client.js initial 434 B
./beta client-options.js initial 219 B
./beta supervisor-api.js lazy 193 B
./beta databricks.js lazy 165 B
./beta index.js lazy 123 B
./testing manifest.js initial 26 KB
./testing index.js initial 10.0 KB
./testing wide-event-emitter.js initial 3.0 KB
./testing index.js lazy 27 KB
./testing app-analytics-relay.js lazy 4.1 KB
./testing remote-tunnel-manager.js lazy 2.5 KB
./tsdown index.js initial 520 B
./type-generator index.js initial 23 KB

@databricks/appkit-ui

npm tarball (packed): 409 KB (+14 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 464 KB (+11 KB) 156 KB (+4.9 KB)
Type declarations 240 KB (+5.4 KB) 88 KB (+2.0 KB)
Source maps 883 KB (+17 KB) 292 KB (+8.4 KB)
CSS 16 KB 3.2 KB (-10 B)
Total 1.6 MB (+33 KB) 540 KB (+15 KB)
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
./js 5.3 KB (+9 B) 49 KB (+49 B) 55 KB (+58 B) 208 KB 14 KB
./js/beta 13 KB (+1.2 KB) 0 B 13 KB (+1.2 KB) 8.5 KB (+4 B) 32 KB (+3.4 KB)
./react 432 KB (+1.8 KB) 49 KB (+32 B) 481 KB (+1.8 KB) 1.3 MB 177 KB
./react/beta 14 KB (+1.2 KB) 0 B 14 KB (+1.2 KB) 8.5 KB 34 KB (+3.5 KB)

Chunks:

Entry Chunk Load Size (gz)
./js index.js initial 5.2 KB
./js chunk initial 120 B
./js apache-arrow lazy 49 KB
./js/beta beta.js initial 13 KB
./react index.js initial 430 KB
./react tslib initial 2.1 KB
./react apache-arrow lazy 49 KB
./react/beta beta.js initial 14 KB

@github-actions

github-actions Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

🤖 AppKit PR bot

🔬 Run evals

Start an eval for this PR from the evals-monitor app: Go to Evals Monitor →

📦 Try this PR's app template

Scaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh auth login — and the Databricks CLI):

gh run download 36460968511 -R databricks/appkit -n appkit-template-0.78.0-pr.054b29d-stack-app-analytics-04-auto-start-614 -D appkit-pr-614 \
  && unzip -o "appkit-pr-614/appkit-template-0.78.0-pr.054b29d-stack-app-analytics-04-auto-start-614.zip" -d "appkit-pr-614" \
  && databricks apps init --template "appkit-pr-614"

The template pins @databricks/appkit and @databricks/appkit-ui to tarballs built from this branch, so the scaffolded app runs against this PR's code.

@ditadi
ditadi force-pushed the stack/app-analytics/04-auto-start branch from f42f213 to 29c77d6 Compare September 28, 2026 13:55
ditadi and others added 2 commits September 28, 2026 18:34
When the relay is enabled and App telemetry is configured, the server
plugin adds a <script src="/_analytics/v1/sdk.js"> tag next to the
__appkit__ runtime config and serves a self-contained, minified build of
the App Analytics SDK that ships inside @databricks/appkit. The script
reads window.__appkit__.appAnalytics and starts the shared client, so
records flow with no client code.

server({ appAnalytics: { webVitals, autocapture, sampleRate } }) sets the
browser options; appAnalytics: false turns both the relay and the script
off. An app's own appAnalytics.init() or <AppAnalytics /> still wins, and
both share one client, so page views are not recorded twice.

In ViteDevServer the tag is appended after transformIndexHtml so Vite
does not try to resolve /_analytics/v1/sdk.js as a local module.

Release note: apps with App telemetry enabled start collecting page views
after they upgrade. Set server({ appAnalytics: false }) to opt out.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: ditadi <victordperd@gmail.com>
Signed-off-by: ditadi <victordperd@gmail.com>
@ditadi
ditadi force-pushed the stack/app-analytics/04-auto-start branch from 29c77d6 to 2e1a357 Compare September 28, 2026 17:50

This branch has not been deployed

No deployments
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.

1 participant