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

chore: Semantic Conventions export individual strings #4298

Merged
merged 6 commits into from
Feb 23, 2024

Conversation

MSNev
Copy link
Contributor

@MSNev MSNev commented Nov 16, 2023

Which problem is this PR solving?

This PR emits individual strings independently for the SemanticAttributes and SemanticResourceAttributes along with the existing namespaced object (for backward compatibility). It also "constructs" the static strings at runtime rather than including all of them, to aid in minification of the bundle .

While this initial PR is just introducing these individual strings (and provides better type checking of the values), because all of the existing packages are continuing to use the namespaces it is not expected that this PR will provide a hug minification advantage, it (may) even initial cause the total bundle size to increase (because of the additional exported names).

This is the initial portion of #4185, further effort is needed to complete that issue.

Short description of the changes

  • Updates the SemanticAttributes template
  • Causes the generation of individual strings for the conventions
  • Creates a stronger "type" for all of the SemanticAttributes, SemanticResourceAttributes and their enum values.
  • Fixes a test issue which was not previously caught because of the reduced typing enforcement.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Tested and validated by manually reviewing the generated output (looking for repeated strings and added as needed) and ran all tests locally (on Windows)

Checklist:

  • Followed the style guidelines of this project

@MSNev MSNev requested a review from a team November 16, 2023 00:52
name: SDK_INFO.NAME,
language: SDK_INFO.LANGUAGE,
version: SDK_INFO.VERSION,
name: SDK_INFO[SEMRESATTRS_TELEMETRY_SDK_NAME],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This change is needed because the previous test (trying to use the SDK_INFO.NAME etc) was referencing values that where effectively undefined (this ends up cause the test to always check the "default" values and not the real set values.

@@ -317,14 +322,17 @@ const assertHasOneLabel = (prefix: string, resource: Resource): void => {

assert.ok(
hasOne,
'Resource must have one of the following attributes: ' +
'Must have one node Resource(s) starting with [' +
prefix +
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added extra context to the test to aid in debugging, as this test was failing after my change -- identified and fixed the issue. But found the additional context was useful -- so left it here.

Object.entries(SemanticResourceAttributes)
.reduce((result, [key, value]) => {
if (key.startsWith(prefix)) {
result.push(value);
}
return result;
})
.join(', ')
.join(', ') +
JSON.stringify(Object.keys(SemanticResourceAttributes))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As above additional debugging context when the test was failing that was useful.

[SemanticResourceAttributes.WEBENGINE_NAME]: 'chromium',
[SemanticResourceAttributes.WEBENGINE_VERSION]: '99',
[SemanticResourceAttributes.WEBENGINE_DESCRIPTION]: 'Chromium',
name: 'chromium',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Setting the "Real" (valid) defined properties of the SDK_INFO type.

* to a constant map value will result in all strings being included into your bundle.
* @deprecated Use the SEMRESATTRS_XXXXX constants rather than the SemanticResourceAttributes.XXXXX for bundle minification.
*/
export type SemanticResourceAttributes = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Creating a strong type definition of the existing class to make sure that everything is correctly defined and passed, without this strong type it would actually be possible to pass the wrong (or missing) strings to the namespace object construction on line 1164.

Now if you add or remove something that is expected TypeScript is very unhappy.

* Create exported Value Map for SemanticResourceAttributes values
* @deprecated Use the SEMRESATTRS_XXXXX constants rather than the SemanticResourceAttributes.XXXXX for bundle minification
*/
export const SemanticResourceAttributes: SemanticResourceAttributes =
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This doesn't need to redefine the documentation as it's referencing the interface which will provide it for this object.

* @deprecated Use the SEMRESATTRS_XXXXX constants rather than the SemanticResourceAttributes.XXXXX for bundle minification
*/
export const SemanticResourceAttributes: SemanticResourceAttributes =
createConstMap<SemanticResourceAttributes>([
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This "helper" effective creates the original object (enforced by the type above) with the converted name and the value.

So by passing the "value" defined by the variable SEMRESATTR_CLOUD_PROVIDER, this is effectively expanded to

CLOUD_PROVIDER: 'cloud.provider'

without requiring both the "fixed" (unminificable) property name CLOUD_PROVIDER or the string value (except for the previous declaration of said variable holding the fixed string value of course), so this alone reduces the total "size" of this definition.

* The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html).
*/
export const SEMRESATTRS_AWS_ECS_CONTAINER_ARN = (AWS +
DOT +
Copy link
Contributor Author

Choose a reason for hiding this comment

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

While the usage of the DOT and later UNDERSCORE variables seem like a waste because at the very least they cause all "usages" to expand from 1 character . to at least 2 (when minified) -- not counting the declaration of the variable in the first place.

This does allow "common" repeated strings to be reused more than once just because there is a leading / trailing . or _ which taken in totality reduces the overall size of the static strings.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is also rationalized across both the SemanticAttributes.ts and SemanticResourceAttributes.ts, the "strings" where also chosen by hand and not automatically parsed to identify the longest common variants, which would have required some post-processing of the jinga template conversion or a LOT more work.

@@ -17,7 +17,12 @@
import { SDK_INFO } from '@opentelemetry/core';
import * as assert from 'assert';
import { IResource } from '../../src/IResource';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import {
SEMRESATTRS_TELEMETRY_SDK_LANGUAGE,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As the previous names where wrong, just took this opportunity to start using the new exported values rather than re-using the namespaced version.

@@ -28,7 +32,8 @@ docker run --rm \
code \
--template /templates/SemanticAttributes.ts.j2 \
--output /output/SemanticAttributes.ts \
-Dclass=SemanticAttributes
-Dclass=SemanticAttributes \
-Dcls_prefix=SEMATTRS
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Passing the "prefix" for the constants here but not the individual "constant" strings as (at least on windows with git bash shell) the command line gets exceeded and the tool (via docker) fails to run 😢

Copy link

codecov bot commented Nov 17, 2023

Codecov Report

Merging #4298 (e09afed) into main (aff48a1) will increase coverage by 0.01%.
The diff coverage is n/a.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4298      +/-   ##
==========================================
+ Coverage   92.39%   92.40%   +0.01%     
==========================================
  Files         330      330              
  Lines        9531     9531              
  Branches     2036     2036              
==========================================
+ Hits         8806     8807       +1     
+ Misses        725      724       -1     

see 1 file with indirect coverage changes

@MSNev
Copy link
Contributor Author

MSNev commented Jan 26, 2024

Output from the included size-limit tests using the full strings, while calculating the imports via the autoImports.ts which includes the "individual" strings as well as any possible enumerating "types" for the values for each "group".

The named groups are defined in the autoImports as part of the theImports (starting at line 37)

Simplistic description of the names

  • *-all = Both the namespaced objects and the individual strings
  • *-namespace = Just the namespaced objects
  • *-allStrings = Just the individual strings (and enumerations for the values of the string)
  • *-http = Just http.* strings and the strings for the enumeration values for the HTTP*
  • *-net= Just net.* strings and the strings for the enumeration values
  • *-messaging= Just messaging.* strings and the strings for the enumeration values
  • *-tememetry= Just telemetry.* strings and the strings for the enumeration values

If you run the npm run size-check it runs all of the "size" tests, and it will produce the following in the build/size-limt folder

  • A results.csv with the values
  • A results.json (with internal details on what is imported -- used mostly for debugging the tests)
  • A folder for each listed combination with the minified output of each combination (so you can go and unminify to figure out what it did) -- this is how I found the issue with NOT including an temporary local variable.
Bundle NameFullGZipBrotli
Full Size Load TimeRun TimeTotal TimeGZip SizeLoad TimeRun TimeTotal TimeBrotli SizeLoad TimeRun TimeTotal Time
cjs-all 51.98 kB 1.1 s 226 ms 1.3 s 11.78 kB 230 ms 171 ms 401 ms 8.31 kB 163 ms 181 ms 343 ms
cjs-namespace 39.05 kB 763 ms 185 ms 947 ms 9.76 kB 191 ms 158 ms 348 ms 7.98 kB 156 ms 175 ms 330 ms
cjs-allStrings 51.46 kB 1.1 s 192 ms 1.2 s 11.64 kB 228 ms 171 ms 398 ms 8.42 kB 165 ms 173 ms 338 ms
cjs-http 39.58 kB 774 ms 153 ms 926 ms 9.83 kB 193 ms 169 ms 361 ms 8.01 kB 157 ms 163 ms 319 ms
cjs-net 40.57 kB 793 ms 149 ms 941 ms 9.93 kB 194 ms 148 ms 342 ms 8.02 kB 157 ms 168 ms 325 ms
cjs-messaging 39.85 kB 779 ms 150 ms 928 ms 9.85 kB 193 ms 196 ms 388 ms 8.04 kB 157 ms 186 ms 343 ms
cjs-telemetry 39.49 kB 772 ms 156 ms 927 ms 9.82 kB 192 ms 156 ms 348 ms 8 kB 157 ms 161 ms 317 ms
                         
esm-all 13.3 kB 260 ms 178 ms 437 ms 6.85 kB 134 ms 156 ms 290 ms 5.07 kB 100 ms 158 ms 257 ms
esm-namespace 4.69 kB 92 ms 165 ms 256 ms 1.42 kB 28 ms 146 ms 174 ms 1.23 kB 24 ms 157 ms 181 ms
esm-allStrings 6.2 kB 122 ms 159 ms 280 ms 2.06 kB 41 ms 171 ms 211 ms 1.83 kB 36 ms 178 ms 214 ms
esm-http 344 B 10 ms 153 ms 163 ms 148 B 10 ms 167 ms 177 ms 119 B 10 ms 178 ms 188 ms
esm-net 528 B 11 ms 152 ms 162 ms 213 B 10 ms 157 ms 167 ms 213 B 10 ms 177 ms 187 ms
esm-messaging 568 B 12 ms 157 ms 168 ms 185 B 10 ms 153 ms 163 ms 168 B 10 ms 147 ms 157 ms
esm-telemetry 169 B 10 ms 149 ms 159 ms 91 B 10 ms 163 ms 173 ms 92 B 10 ms 169 ms 179 ms
                       
esnext-all 13.31 kB 260 ms 162 ms 422 ms 6.85 kB 134 ms 152 ms 286 ms 5.08 kB 100 ms 178 ms 277 ms
esnext-namespace 4.7 kB 92 ms 150 ms 242 ms 1.43 kB 28 ms 144 ms 172 ms 1.23 kB 25 ms 173 ms 197 ms
esnext-allStrings 6.2 kB 122 ms 149 ms 270 ms 2.06 kB 41 ms 144 ms 184 ms 1.83 kB 36 ms 173 ms 209 ms
esnext-http 344 B 10 ms 154 ms 164 ms 148 B 10 ms 163 ms 173 ms 119 B 10 ms 166 ms 176 ms
esnext-net 528 B 11 ms 143 ms 153 ms 213 B 10 ms 160 ms 170 ms 213 B 10 ms 148 ms 158 ms
esnext-messaging 568 B 12 ms 160 ms 171 ms 185 B 10 ms 149 ms 159 ms 168 B 10 ms 157 ms 167 ms
esnext-telemetry 169 B 10 ms 148 ms 158 ms 91 B 10 ms 166 ms 176 ms 92 B 10 ms 152 ms 162 ms

@MSNev
Copy link
Contributor Author

MSNev commented Jan 31, 2024

BundlePhobia: @opentelemetry/semantic-conventions v1.21.0 ❘ Bundlephobia (12.9kb minified), so this is the original size with just the namespaces so this is (probably) equivalent to the esm-namespace and/or esnext-namespace and/or esnext-namespace which are both ~4.7Kb so the different is (probably) related to the helper for the namespace construction as the namespace name (key) and value are only listed once

eg original includes

SemanticConventions {
   EXPORTED_NAME1: "exported.name1"
   EXPORTED_NAME2: "exported.name2"
   EXPORTED_NAME3: "exported.name3"
}

where now the name (key) is derived at runtime and so the bundle only lists the value.

Copy link
Contributor

@david-luna david-luna left a comment

Choose a reason for hiding this comment

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

I guess we could use autoImports.ts to print results somewhere and keep track of the gain/loss when updating further the semantic conventions.
The changes on other packages are quite straight forward which is good but I'd suggest that next step should be to think on how to update the specs in a way that enables migrations like the one explained here https://opentelemetry.io/blog/2023/http-conventions-declared-stable/

@MSNev
Copy link
Contributor Author

MSNev commented Feb 14, 2024

Updated sizes after adding using creatConstMap for the additional enum type values as well.

Group Full Size GZip Size Brotli Size
cjs-all 51.09 kB 11.25 kB 8.12 kB
cjs-namespace 38.16 kB 9.24 kB 7.67 kB
cjs-allStrings 50.57 kB 11.12 kB 8.11 kB
cjs-http 38.69 kB 9.31 kB 7.75 kB
cjs-net 39.68 kB 9.41 kB 7.79 kB
cjs-messaging 38.96 kB 9.33 kB 7.75 kB
cjs-telemetry 38.6 kB 9.29 kB 7.7 kB
esm-all 12.08 kB 6 kB 4.39 kB
esm-namespace 4.69 kB 1.42 kB 1.23 kB
esm-allStrings 6.2 kB 2.06 kB 1.83 kB
esm-http 344 B 148 B 119 B
esm-net 528 B 213 B 213 B
esm-messaging 568 B 185 B 168 B
esm-telemetry 169 B 91 B 92 B
esnext-all 12.09 kB 6 kB 4.37 kB
esnext-namespace 4.7 kB 1.43 kB 1.23 kB
esnext-allStrings 6.2 kB 2.06 kB 1.83 kB
esnext-http 344 B 148 B 119 B
esnext-net 528 B 213 B 213 B
esnext-messaging 568 B 185 B 168 B
esnext-telemetry 169 B 91 B 92 B

Copy link
Member

@pichlermarc pichlermarc left a comment

Choose a reason for hiding this comment

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

Looks good % changelog nit
Thanks for working on this and thanks for addressing the feedback after the first attempt 🙂

In the future we could consider adding the bundle size test results to the benchmark data at https://opentelemetry.io/docs/languages/js/benchmarks/ as it might be good to have that tracked over time.

CHANGELOG.md Outdated Show resolved Hide resolved
@pichlermarc
Copy link
Member

@open-telemetry/javascript-approvers last call for comments. Planning to merge this tomorrow (Feb 23, 13:00 CET)

@pichlermarc pichlermarc merged commit 5637e2a into open-telemetry:main Feb 23, 2024
20 checks passed
pkanal added a commit to honeycombio/honeycomb-opentelemetry-web that referenced this pull request May 16, 2024
## Which problem is this PR solving?
While doing some bundle size analysis, noticed that the
`@opentelemetry/semantic-conventions` library is a significant
contributor to the bundle size because it has a lot of unminified
constants. There was a [PR merged that also exports each constant by
itself](open-telemetry/opentelemetry-js#4298)
instead of on an object which allows tree shaking to only import that
constant instead of a whole object. This PR uses the new constants from
the `@opentelemetry/semantic-conventions` instead of the deprecated
`SemanticConventions` object.

## Short description of the changes
- Use exported constant for service name.

## How to verify that this has the expected result
- Smoke tests pass
- Service name is still respected, run the example app locally and check
that service name is set to the value expected.
Zirak pushed a commit to Zirak/opentelemetry-js that referenced this pull request Sep 14, 2024
…#4298)

* chore: Semantic Conventions export individual strings

* Reduce to just emit full strings and add size-limit test output to review the results

* Update generation to use createConstMap for enums where possible

* Move changelog back to Unreleased -- merge shifted it

---------

Co-authored-by: Marc Pichler <[email protected]>
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