-
Notifications
You must be signed in to change notification settings - Fork 807
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
chore: avoid leaking type definitions in instrumentations #2256
Conversation
Currently instrumentations leak the type of their host modules via the protected init() function required by generic base class InstrumentationAbstract. Actually the instrumentation module has no need for a concrete type here. Therefore replaced the generic type by unknown. In the instrumentation the return type of init() is set explicit to InstrumentationNodeModuleDefinition<any>[] to avoid leaking of types. The creation of the InstrumentationNodeModuleDefinition instances is still using the concrete types to ensure proper type checking within the implementation of the instrumentations. Besides that I made some protected funcitons of HTTP instrumentation private since they are no longer needed as http and https is combined meanwhile.
Codecov Report
@@ Coverage Diff @@
## main #2256 +/- ##
=======================================
Coverage 92.77% 92.77%
=======================================
Files 145 145
Lines 5216 5216
Branches 1068 1068
=======================================
Hits 4839 4839
Misses 377 377
|
@@ -23,8 +23,7 @@ import * as types from './types'; | |||
/** | |||
* Base abstract internal class for instrumenting node and web plugins | |||
*/ | |||
export abstract class InstrumentationAbstract<T = any> | |||
implements types.Instrumentation { | |||
export abstract class InstrumentationAbstract implements types.Instrumentation { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My Preference would be to keep the type so that any user can retain the type safety.
It seems to me that what we should do is (where necessary) to have the instrumentation export a "public" interface, any if the specific instrumentation doesn't want to do that, it can use "any" or "unknown".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type which was used here had no impact on the concrete instrumentation. The concrete type to use was (and still is) specified in the concrete implementation to instantiate the InstrumentationNodeModuleDefinition
s.
Maybe we could keep the <T = any>
here but I see no value in it. Just take a look at e.g. xhr instrumenation. It has to specifies a type which is not used anywhere afterwards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But, for any instrumentation where they want to support their own extensions (properties or functions) this would provide additional type safety and they can choose to still hide the actual internal workings (from type safety / code completion / documentation / exported types) but provide some sort of public API etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, that specifying any
doesn't have any advantages for what I said above. As you are basically saying that there is no type safety and do what you want. (and yes I know that is pure JS you can anyway)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Users can (and should) still use concrete types when creating a InstrumentationNodeModuleDefinition
.
They can even make their class extending InstrumentationAbstract
a generic and supply a type if this helps anywhere.
But within the @opentelemetry/instrumentation module the type has no value for type safety to my understanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree the type has Zero benefit for the internal workings, it's purely for consumers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally the types were added to keep consistency to what we had previously - plugins had defined type too. Some of the instrumentations already uses mixed types and quite often the last thing you can do is to use any. If that would also help to resolve issue with having to include types for the package in dependencies I would be fully into removing this anyway then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me this conversation is resolved no?
@@ -70,7 +70,8 @@ export class GrpcJsInstrumentation extends InstrumentationBase { | |||
this._config = Object.assign({}, config); | |||
} | |||
|
|||
init() { | |||
// use InstrumentationNodeModuleDefinition<any>[] to avoid leaking grpc types | |||
protected init(): InstrumentationNodeModuleDefinition<any>[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually this is the change really required to avoid leaking grpc types here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this is where the "generic" (interface) should be used (I may be over simplifying)
protected init(): InstrumentationNodeModuleDefinition<T>[] {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
init()
is called by base class constructor (and this is the only one who should call init) and base class has no need for the types.
But generated type definitions hold full types for public and protected methods therefore this causes that types leak and we have to add the @types packages as production dependencies (see open-telemetry/opentelemetry-js-contrib#460 for more details).
Moving init()
to private would solve this also but this is not allowed in typescript (it would be in C++...).
Looking into e.g. open-telemetry/opentelemetry-js-contrib#527 I fear it's anyway not easily possible to avoid that we leak types in general. Even without this PR a concrete instrumentation can use return type |
I'm not sure to understand the status of the PR then, do we want to continue with this ? |
Closing as leaking types happens also because of other reasons (request/response hooks,...). |
Currently instrumentations leak the type of their host modules via the protected
init()
function required by generic base classInstrumentationAbstract
.Actually the instrumentation module has no need for a concrete type here. Therefore replaced the generic type by
unknown
.In the instrumentation the return type of
init()
is set explicit toInstrumentationNodeModuleDefinition<any>[]
to avoid leaking of types. The creation of theInstrumentationNodeModuleDefinition
instances is still using the concrete types to ensure proper type checking within the implementation of the instrumentations.Besides that I made some protected functions of HTTP instrumentation private since they are no longer needed as http and https is combined meanwhile.
Leaking type definition required to add @types packages as dependencies. These @types packages in turn may require real packages (for example installing
@opentelemetry/instrumentation-graphql
results in graphql installed as@types/graphql
depends on graphql itself).Refs: open-telemetry/opentelemetry-js-contrib#460
Refs: open-telemetry/opentelemetry-js-contrib#468