Skip to content

Prototype for sessions as entities#1

Draft
martinkuba wants to merge 3 commits intopackages-prototypefrom
entity-prototype
Draft

Prototype for sessions as entities#1
martinkuba wants to merge 3 commits intopackages-prototypefrom
entity-prototype

Conversation

@martinkuba
Copy link
Copy Markdown
Owner

@martinkuba martinkuba commented Mar 1, 2026

This is intended as a prototype of modeling browser sessions using entities based on this proposal:

OTEP: Allow multiple Resources in an SDK by jsuereth · Pull Request #4665 · open-telemetry/opentelemetry-specification.

The proposal introduces a forEntity() method on providers. I think this works when a single module (e.g. single instrumentation) is in control of the entity-bound provider.

However, the session entity should be applied to all telemetry. When any instrumentation (and user code) emits a signal, it should include the session attributes. And when a session rotates, this needs to propagate to all.

This prototype includes an EntityAwareLoggerProvider that delegates to an internal LoggerProvider and allows updating the entity via the setEntity() method. Internally, the delegate LoggerProvider is constructed by using the forEntity() method as described in the proposal.

@martinkuba martinkuba changed the title Prototype for sessions as entities: SessionAwareLoggerProvider Prototype for sessions as entities Mar 1, 2026
// Rebind on session rotation
sessionManager.addObserver({
onSessionStarted(session) {
loggerProvider.setEntity(createSessionEntity(session.id));
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This shows user code updating the global logger provider with a new session entity.

* route telemetry through the new entity-bound provider.
*/
setEntity(entity: Entity): void {
this._currentProvider = this.forEntity(entity);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If we are replacing the current provider I wonder if its necessary to shut it down 1st 🤔

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Prototype implementation for modeling browser sessions as Entity-bound Resources, aligned with the proposed forEntity() provider API, plus a runnable example demonstrating dynamic session rebinding for logs.

Changes:

  • Export new entity utilities from @opentelemetry/sdk-browser (Entity types, session entity creator, resource merge helper, entity-aware logger provider).
  • Introduce EntityAwareLoggerProvider and mergeEntityIntoResource() to bind (and rebind) log telemetry to a session entity via Resource attributes.
  • Add a new examples/session-entity webpack demo and update Biome config to ignore generated bundle.js.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/sdk-browser/src/index.ts Re-exports new entity/session APIs from the SDK package entrypoint.
packages/sdk-browser/src/entity/Entity.ts Defines the Entity interface used by the prototype.
packages/sdk-browser/src/entity/createSessionEntity.ts Adds helper to create a browser.session entity with session.id.
packages/sdk-browser/src/entity/mergeEntityIntoResource.ts Merges entity attributes into a Resource to model entity-bound providers.
packages/sdk-browser/src/entity/EntityAwareLoggerProvider.ts Adds an entity-rebindable logger provider using dynamic delegation.
packages/sdk-browser/src/configuration.ts Switches logs session tracking to use EntityAwareLoggerProvider when enabled.
examples/session-entity/* Adds a runnable demo showcasing rebinding behavior across session rotation.
biome.jsonc Ignores generated bundle.js.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"allowImportingTsExtensions": false,
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

tsconfig.json disables allowImportingTsExtensions, but this example imports local modules using ".ts" extensions (e.g., "./otel-config.ts"). This will fail type-checking with TS5097 under common TypeScript configurations. Either set allowImportingTsExtensions to true (and ensure the bundler supports it) or remove the ".ts" extensions from local imports.

Suggested change
"allowImportingTsExtensions": false,
"allowImportingTsExtensions": true,

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +73
// For logs: use EntityAwareLoggerProvider when session tracking is enabled.
// This models the session as an Entity on the Resource (per the Entity Provider OTEP)
// instead of injecting session.id as an attribute via a processor.
let loggerProvider: LoggerProvider | EntityAwareLoggerProvider;
if (sessionManager) {
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

configureBrowserSDK has existing Vitest coverage (configuration.test.ts), but the newly introduced session-tracking log path (EntityAwareLoggerProvider + entity binding/rebinding) isn't tested. Adding a test that enables session tracking and asserts logs include session.id on the Resource (and that it changes after rotation) would help prevent regressions.

Copilot uses AI. Check for mistakes.
entityLoggerProvider.setEntity(createSessionEntity(session.id));
},
onSessionEnded() {
entityLoggerProvider.forceFlush();
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

forceFlush() returns a Promise, but it's called without awaiting or handling rejection. This can cause unhandled promise rejections and also race with shutdown (sessionManager.shutdown() may trigger this observer right before loggerProvider.shutdown()). Consider using void ...forceFlush().catch(...) or restructuring so flush is awaited by an async caller.

Suggested change
entityLoggerProvider.forceFlush();
void entityLoggerProvider.forceFlush().catch(() => {
// Intentionally ignore errors to avoid unhandled rejections on session end.
});

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +30
const entityAttributes: Record<string, string | number | boolean> = {
...entity.identifier,
};

if (entity.attributes) {
for (const [key, value] of Object.entries(entity.attributes)) {
// Descriptive attributes don't override existing values
if (!(key in entityAttributes)) {
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

Descriptive entity attributes are supposed to be included without overriding existing base Resource attributes, but this loop only prevents overriding identifier attributes (entityAttributes). Because the merged Resource is later created and merged into baseResource, descriptive keys that already exist on baseResource will still override. Filter descriptive keys against baseResource's existing attributes (or merge in two steps) to match the documented behavior.

Suggested change
const entityAttributes: Record<string, string | number | boolean> = {
...entity.identifier,
};
if (entity.attributes) {
for (const [key, value] of Object.entries(entity.attributes)) {
// Descriptive attributes don't override existing values
if (!(key in entityAttributes)) {
const baseAttributes = baseResource.attributes ?? {};
const entityAttributes: Record<string, string | number | boolean> = {
...entity.identifier,
};
if (entity.attributes) {
for (const [key, value] of Object.entries(entity.attributes)) {
// Descriptive attributes don't override existing values or base resource attributes
if (!(key in entityAttributes) && !(key in baseAttributes)) {

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +44
resource ??
({
attributes: {},
merge: (r: Resource | null) => r ?? this._baseResource,
getRawAttributes: () => [],
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

This fallback Resource is a hand-rolled stub and likely does not satisfy the full Resource contract (e.g., missing standard methods and potentially different merge semantics). Since EntityAwareLoggerProvider is exported publicly, construct a real empty Resource instead (e.g., resourceFromAttributes({}) or Resource.empty() if available) rather than using a type assertion.

Copilot uses AI. Check for mistakes.
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