Conversation
Browsers can't reach the Databricks Apps OTel Collector, which listens
only on localhost. The server plugin now answers POST /_analytics/v1/logs,
the default endpoint of @databricks/app-analytics, and forwards the
OTLP/HTTP JSON body unchanged to the Collector, resolved from
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT.
The route has its own 64 KiB JSON parser and answers bad input itself:
415 for a non-JSON content type, 413 for oversized bodies, and 400 for
malformed JSON or a body without a resourceLogs array. Otherwise it
returns the Collector's status. An unreachable or slow Collector yields
502 and logs one warning. When App telemetry is off the relay answers
204 and warns once.
server({ appAnalytics: false }) removes the route. Paths under
/_analytics/ are excluded from spans, wide-event logs, and request
metrics.
Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: ditadi <victordperd@gmail.com>
ditadi
added this pull request to stack #616
September 26, 2026 21:42
Contributor
📦 Bundle size reportCompared against
|
| dist | raw | gzip |
|---|---|---|
| JS (runtime) | 1.2 MB (+6.1 KB) | 427 KB (+2.4 KB) |
| Type declarations | 444 KB (+513 B) | 161 KB (+241 B) |
| Source maps | 2.4 MB (+9.4 KB) | 801 KB (+3.6 KB) |
| Other | 11 KB | 3.7 KB |
| Total | 4.0 MB (+16 KB) | 1.4 MB (+6.3 KB) |
Per-entry composition (own code — deps external (as shipped))
| Entry | Initial (gz) | Lazy (gz) | Total (gz) | node_modules (min) | Own code (min) |
|---|---|---|---|---|---|
. |
97 KB (+743 B) | 2.5 KB (-1 B) | 99 KB (+742 B) | external | 316 KB (+1.9 KB) |
./beta |
93 KB (+5 B) | 456 B (-1 B) | 93 KB (+4 B) | external | 281 KB (+15 B) |
./testing |
38 KB (+8 B) | 31 KB (+738 B) | 69 KB (+746 B) | external | 203 KB (+1.9 KB) |
./tsdown |
520 B | 0 B | 520 B | external | 813 B |
./type-generator |
23 KB (+4 B) | 0 B | 23 KB (+4 B) | external | 65 KB (+15 B) |
Chunks:
| Entry | Chunk | Load | Size (gz) |
|---|---|---|---|
. |
index.js |
initial | 93 KB |
. |
utils.js |
initial | 4.0 KB |
. |
remote-tunnel-manager.js |
lazy | 2.5 KB |
./beta |
beta.js |
initial | 77 KB |
./beta |
stream-manager.js |
initial | 5.8 KB |
./beta |
wide-event-emitter.js |
initial | 3.2 KB |
./beta |
databricks.js |
initial | 3.2 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 | 192 B |
./beta |
databricks.js |
lazy | 141 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 | 2.9 KB |
./testing |
index.js |
lazy | 27 KB |
./testing |
remote-tunnel-manager.js |
lazy | 2.5 KB |
./testing |
utils.js |
lazy | 1.2 KB |
./tsdown |
index.js |
initial | 520 B |
./type-generator |
index.js |
initial | 23 KB |
@databricks/appkit-ui
npm tarball (packed): 350 KB (-4 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).
| dist | raw | gzip |
|---|---|---|
| JS (runtime) | 395 KB | 132 KB |
| Type declarations | 229 KB | 84 KB (-1 B) |
| Source maps | 766 KB | 253 KB |
| CSS | 16 KB | 3.2 KB |
| Total | 1.4 MB | 472 KB (-1 B) |
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 | 49 KB | 55 KB | 208 KB | 14 KB |
./js/beta |
20 B | 0 B | 20 B | 0 B | 0 B |
./react |
432 KB | 49 KB | 481 KB | 1.3 MB | 177 KB |
./react/beta |
1.0 KB | 0 B | 1.0 KB | 0 B | 1.9 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 | 20 B |
./react |
index.js |
initial | 430 KB |
./react |
tslib |
initial | 2.1 KB |
./react |
apache-arrow |
lazy | 49 KB |
./react/beta |
beta.js |
initial | 1.0 KB |
This was referenced Sep 26, 2026
Contributor
🤖 AppKit PR bot🔬 Run evalsStart an eval for this PR from the evals-monitor app: Go to Evals Monitor → 📦 Try this PR's app templateScaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh run download 36273883393 -R databricks/appkit -n appkit-template-0.78.0-pr.07c9567-stack-app-analytics-02-relay-612 -D appkit-pr-612 \
&& unzip -o "appkit-pr-612/appkit-template-0.78.0-pr.07c9567-stack-app-analytics-02-relay-612.zip" -d "appkit-pr-612" \
&& databricks apps init --template "appkit-pr-612"The template pins |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why the change
Browsers can't reach the Databricks Apps OTel Collector, which listens only on localhost, so the server plugin now relays App Analytics records from
POST /_analytics/v1/logsto the Collector, and AppKit apps need no server code to collect them.Special things to note
server({ appAnalytics: false })removes it. It adds no auth of its own and relies on the Databricks Apps proxy in front of the app, like the app's other routes.bodyLimitdoesn't apply to it. It checks only the outer shape (resourceLogsis an array). The Collector decides what to accept, and its status is passed back./_analytics/are kept out of AppKit's own spans, wide-event logs, and request metrics, so browser traffic doesn't flood the app's server telemetry.Change outline
The server plugin mounts one guarded route and marks its prefix as excluded:
packages/appkit/src/ ├── plugins/server/ +│ ├── app-analytics-relay.ts # endpoint resolution, guard, relay handler │ ├── index.ts # mounts the route unless appAnalytics === false │ ├── manifest.json # appAnalytics: boolean, default true │ └── types.ts # ServerConfig.appAnalytics └── utils/ └── path-exclusions.ts # + "/_analytics/"The request path, including every answer the route can give:
Every answer has an empty body. The
204when telemetry is off keeps local development quiet, because the browser SDK treats it as delivered and neither retries nor reports a failure.Wiring in
ServerPlugin:this.registerEndpoint("health", "/health"); +if (this.config.appAnalytics !== false) { + this.rawBodyPaths.add(APP_ANALYTICS_PATH); // global JSON parser skips it + this.serverApplication.post(APP_ANALYTICS_PATH, appAnalyticsGuard(), appAnalyticsRelay()); +} requestMetricsMiddleware(req, res, next) + if (req.path.startsWith("/_analytics/")) return next();@databricks/app-analyticsbecomes a devDependency of@databricks/appkit, used only by the integration test. That test drives the real SDK (#611) through the relay into a fake Collector.This pull request and its description were written by Isaac.