Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: enhanced typescript support for currentSpan().span #1370

Merged
merged 1 commit into from
Oct 9, 2024
Merged

Conversation

kirrg001
Copy link
Contributor

@kirrg001 kirrg001 commented Oct 7, 2024

import instana from '@instana/collector';

....

// type error because it was not defined in shared.d.ts
instana.currentSpan().span

FYI: We are aware that we have more any definitions. We can optimize the TS support later.

@kirrg001 kirrg001 marked this pull request as ready for review October 7, 2024 14:09
@kirrg001 kirrg001 requested a review from a team as a code owner October 7, 2024 14:09
Copy link
Contributor

@aryamohanan aryamohanan left a comment

Choose a reason for hiding this comment

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

LGTM

@legehwahn
Copy link

legehwahn commented Oct 7, 2024

Should named / destructured imports also be supported?
By the looks of it, it is currently possbile via the default export type:

export type InitFunction = {
  (config?: CollectorConfig): Init;
  sdk: any;
  core: any;
  currentSpan: { span: InstanaBaseSpan };
  isTracing: boolean;
  isConnected: boolean;;
  setLogger: (logger: GenericLogger): void;
  sharedMetrics: any;
  experimental: any;
  opentracing: any;
}

e.g.

import { currentSpan } from '@instana/collector';
currentSpan().span;

Can add another esm test if need be.

Copy link

@legehwahn legehwahn left a comment

Choose a reason for hiding this comment

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

Though the following feedback is not within the scope of the change, I believe it is worth considering.

For users wanting to access the currentSpan, nesting the InstanaBaseSpan under a span property adds no benefit, it adds an additional property getter call at runtime to get to the desired current span object.
Consider removing this level of the response in the actual implementation unless the intent is to extend the currentSpan response type in future to include more than the InstanaBaseSpan type.

@@ -2,7 +2,7 @@ import { CollectorConfig } from './collector';
import { GenericLogger, InstanaBaseSpan } from '@instana/core/src/core';

export interface Init {
currentSpan(): InstanaBaseSpan;
currentSpan(): { span: InstanaBaseSpan };
Copy link

@legehwahn legehwahn Oct 7, 2024

Choose a reason for hiding this comment

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

This is lacking the type for the scenario when the collector / tracer is not initialized / when the call is not being traced.
Suspect it should be:

currentSpan(): { span: InstanaBaseSpan | undefined };

The above is based on the call chain for currentSpan()

@instana/collector/src/index.js

init.currentSpan = function getHandleForCurrentSpan() {
  return instanaNodeJsCore.tracing.getHandleForCurrentSpan();
};

@instana/core/src/tracing/index.js

exports.getHandleForCurrentSpan = function getHandleForCurrentSpan() {
  return spanHandle.getHandleForCurrentSpan(cls);
};

@instana/core/src/tracing/spanHandle.js

/**
 * @param {import ('./cls')} cls
 * @returns {SpanHandle | NoopSpanHandle}
 */
exports.getHandleForCurrentSpan = function getHandleForCurrentSpan(cls) {
  if (cls && cls.isTracing()) {
    return new SpanHandle(cls.getCurrentSpan());
  } else {
    return new NoopSpanHandle();
  }
};

/**
 * TODO: make it as a class
 * Provides noop operation for the SpanHandle API when automatic tracing is not enabled or no span is currently active.
 */
function NoopSpanHandle() {}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I understand what you mean in general, but the differentiation if the span is available happens on runtime and should not affect the TS type checking.

If you are running into a TS error, please share here with us 👍

Copy link

@legehwahn legehwahn Oct 8, 2024

Choose a reason for hiding this comment

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

Thanks for acknowledging.
I'm in agreement, it does not affect TS type checking - TS would indicate the code should run without any type errors.
This is not necessarily the truth if there is misalignment between the type definition and the actual JS implementation.

Here's an example of a code snippet that would produce a runtime error, without producing a warning / error during tsc build.

The TypeScript compiler has no issue with this code as it does not know the value can actually be undefined at runtime and therefore does not warn the developer during tsc build.

import { type InstanaBaseSpan } from '@instana/core/src/core.js';
import instana from '@instana/collector';

// Re-usable function to get subset of fields from the span to use through-out the application.
function getLogDetailFromSpan(span: InstanaBaseSpan) {
  return span.t; // TypeError: Cannot read properties of undefined (reading 't')
}

// Explicitly specifying the type here, but this is the type which TS would derive anyway.
const currentSpan: { span: InstanaBaseSpan } = instana.currentSpan();
const traceId = getLogDetailFromSpan(currentSpan);

The code produces an error at runtime when the collector is not initialized (which is the case for me during local development) or when the current call isn't traced by the collector.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Your code snippet is not right.

instana.currentSpan();

Either returns a SpanHandle or NoopSpanHandle.

NoopSpanHandle {} or SpanHandle { span }

You could protect your code like this:

...

const spanHandle = instana.currentSpan();

// Option 1
if (spanHandle.constructor.name === 'NoopSpanHandle') {}

// Option 2
if (!spanHandle.span) {}

Keep in mind: There should be always an active span in an async context.

app.get('something', (req) => {
   const span = instana.currentSpan();
   // span should exist
})

Either returns a SpanHandle or NoopSpanHandle.

We definitely can improve our SDK. It would be much better if customers could simply do !instana.currentSpan(). A PR is welcome to improve this without breaking the old notation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@legehwahn See also #1374. Another improvement, which makes it easier to access the information of a span.

packages/collector/src/types/shared.d.ts Show resolved Hide resolved
@kirrg001
Copy link
Contributor Author

kirrg001 commented Oct 8, 2024

For users wanting to access the currentSpan, nesting the InstanaBaseSpan under a span property adds no benefit, it adds an additional property getter call at runtime to get to the desired current span object.
Consider removing this level of the response in the actual implementation unless the intent is to extend the currentSpan response type in future to include more than the InstanaBaseSpan type.

We are aware of that. This is on our list of breaking changes for a future major release. Outside of this PR scope.

@kirrg001
Copy link
Contributor Author

kirrg001 commented Oct 8, 2024

Should named / destructured imports also be supported?
By the looks of it, it is currently possbile via the default export type:

export type InitFunction = {
(config?: CollectorConfig): Init;
sdk: any;
core: any;
currentSpan: { span: InstanaBaseSpan };
isTracing: boolean;
isConnected: boolean;;
setLogger: (logger: GenericLogger): void;
sharedMetrics: any;
experimental: any;
opentracing: any;
}
e.g.

import { currentSpan } from '@instana/collector';
currentSpan().span;
Can add another esm test if need be.

In general: yes, we definitely could enhance our TS support in many ways. But again, outside of this PR.

We are happy for any help & contribution pull requests :)

@kirrg001 kirrg001 force-pushed the fix-ts-span branch 2 times, most recently from 2832067 to 4f5aa5e Compare October 9, 2024 08:20
@kirrg001 kirrg001 merged commit 762af17 into main Oct 9, 2024
1 check passed
@kirrg001 kirrg001 deleted the fix-ts-span branch October 9, 2024 08:52
aryamohanan pushed a commit that referenced this pull request Oct 14, 2024
- typing showed an error when accessing `instana.currentSpan().span`
- defined the inner span attribute via TS
- more TS & SDK improvements are coming soon
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.

4 participants