@commercetools/ts-sdk-apm 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # @commercetools/ts-sdk-apm
2
+
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#458](https://github.com/commercetools/commercetools-sdk-typescript/pull/458) [`c2bf0a5`](https://github.com/commercetools/commercetools-sdk-typescript/commit/c2bf0a5cac18fdcf181dacc8c6714eb760704523) Thanks [@ajimae](https://github.com/ajimae)! - - add newrelic `APM` (application performance monitoring) to the sdk
8
+ - add `withTelemetryMiddleware()` middleware creator method to ClientBuilder class
9
+ - add basic `newrelic` application configuration module
10
+ - add examples to repository example folder to demonstrate how this feature can be used
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 commercetools
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # @commercetools/ts-sdk-apm
2
+
3
+ _commercetools Typescript SDK Application Performance Monitoring._
4
+
5
+ This package is used to monitor the application performance of the Typescript SDK. It exposes a middleware creator method that accepts some options for monitoring and tracing the SDK performance.
6
+
7
+ ## Installation
8
+
9
+ To use this package we need to install the package and add the newrelic configuration file.
10
+
11
+ Using npm
12
+
13
+ ```bash
14
+ $ npm install @commercetools/ts-sdk-apm
15
+ ```
16
+
17
+ or using yarn
18
+
19
+ ```bash
20
+ $ yarn add @commercetools/ts-sdk-apm
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Import the package and add it to the SDK client using `withTelemetryMiddleware()` method.
26
+
27
+ ```typescript
28
+ // import the @commercetools/ts-sdk-apm package
29
+ import { ClientBuilder } from '@commercetools/sdk-client-v2'
30
+ import { createTelemetryMiddleware } from '@commercetools/ts-sdk-apm'
31
+
32
+ // newrelic options
33
+ const telemetryOptions = {
34
+ createTelemetryMiddleware
35
+ }
36
+
37
+ // create the client and include the telemetry middleware
38
+ const client = new ClientBuilder()
39
+ .withClientCredentialsFlow(...)
40
+ .withHttpMiddleware(...)
41
+ .withTelemetryMiddleware(telemetryOptions) // telemetry middleware
42
+ ...
43
+ .build()
44
+ ...
45
+
46
+ ```
47
+
48
+ ## Using a custom (user-defined options)
49
+
50
+ All the monitoring and tracing functionality has been implemented by default however, the `telemetryOptions` accepts three function parameters `createTelemetryMiddleware`, `apm` and `tracer`, the `createTelemetryMiddleware` and `tracer` can be custom implemented.
51
+
52
+ ```typescript
53
+ type telemetryOptions = {
54
+ createTelemetryMiddleware: (options: Omit<telemetryOptions, 'createTelemetryMiddleware'>) => Middleware;
55
+ apm?: () => typeof require('newrelic');
56
+ tracer?: () => typeof require('/absolute-path-to-a-tracer(opentelemetry)-module')
57
+ }
58
+ ```
59
+
60
+ Example
61
+
62
+ ```typescript
63
+ const telemetryOptions = {
64
+ createTelemetryMiddleware, // coming from the `@commercetools/ts-sdk-apm or a custom implementation
65
+ tracer: () =>
66
+ require(require('path').join(
67
+ __dirname,
68
+ '..',
69
+ '..',
70
+ 'custom-telemetry-module.js'
71
+ )), // make sure the require takes in an absolute path to the custom tracer module.
72
+ }
73
+ ```
74
+
75
+ The tracer is responsible for tracing `http` calls right from the resource request call to the external commercetools backend service and everything that goes on in-between.
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ /**
6
+ * default newrelic APM and
7
+ * opentelemetry tracer modules
8
+ */
9
+ const defaultOptions = {
10
+ apm: () => require('newrelic'),
11
+ tracer: () => require('../opentelemetry')
12
+ };
13
+ function createTelemetryMiddleware(options) {
14
+ // trace
15
+ function trace() {
16
+ // validate apm and tracer
17
+ if (!(options !== null && options !== void 0 && options.apm && typeof options.apm == 'function')) {
18
+ options.apm = defaultOptions.apm;
19
+ }
20
+ if (!(options !== null && options !== void 0 && options.tracer && typeof options.tracer == 'function')) {
21
+ options.tracer = defaultOptions.tracer;
22
+ }
23
+ options.apm();
24
+ options.tracer();
25
+ }
26
+ trace(); // expose tracing modules
27
+ return next => (request, response) => {
28
+ const nextRequest = {
29
+ ...request,
30
+ ...options
31
+ };
32
+ next(nextRequest, response);
33
+ };
34
+ }
35
+
36
+ exports.createTelemetryMiddleware = createTelemetryMiddleware;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * default newrelic APM and
3
+ * opentelemetry tracer modules
4
+ */
5
+ const defaultOptions = {
6
+ apm: () => require('newrelic'),
7
+ tracer: () => require('../opentelemetry')
8
+ };
9
+ function createTelemetryMiddleware(options) {
10
+ // trace
11
+ function trace() {
12
+ // validate apm and tracer
13
+ if (!(options !== null && options !== void 0 && options.apm && typeof options.apm == 'function')) {
14
+ options.apm = defaultOptions.apm;
15
+ }
16
+ if (!(options !== null && options !== void 0 && options.tracer && typeof options.tracer == 'function')) {
17
+ options.tracer = defaultOptions.tracer;
18
+ }
19
+ options.apm();
20
+ options.tracer();
21
+ }
22
+ trace(); // expose tracing modules
23
+ return next => (request, response) => {
24
+ const nextRequest = {
25
+ ...request,
26
+ ...options
27
+ };
28
+ next(nextRequest, response);
29
+ };
30
+ }
31
+
32
+ export { createTelemetryMiddleware };
@@ -0,0 +1 @@
1
+ export * from "./declarations/src/index";
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ /**
6
+ * default newrelic APM and
7
+ * opentelemetry tracer modules
8
+ */
9
+ const defaultOptions = {
10
+ apm: () => require('newrelic'),
11
+ tracer: () => require('../opentelemetry')
12
+ };
13
+ function createTelemetryMiddleware(options) {
14
+ // trace
15
+ function trace() {
16
+ // validate apm and tracer
17
+ if (!(options !== null && options !== void 0 && options.apm && typeof options.apm == 'function')) {
18
+ options.apm = defaultOptions.apm;
19
+ }
20
+ if (!(options !== null && options !== void 0 && options.tracer && typeof options.tracer == 'function')) {
21
+ options.tracer = defaultOptions.tracer;
22
+ }
23
+ options.apm();
24
+ options.tracer();
25
+ }
26
+ trace(); // expose tracing modules
27
+ return next => (request, response) => {
28
+ const nextRequest = {
29
+ ...request,
30
+ ...options
31
+ };
32
+ next(nextRequest, response);
33
+ };
34
+ }
35
+
36
+ exports.createTelemetryMiddleware = createTelemetryMiddleware;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === "production") {
4
+ module.exports = require("./commercetools-ts-sdk-apm.cjs.prod.js");
5
+ } else {
6
+ module.exports = require("./commercetools-ts-sdk-apm.cjs.dev.js");
7
+ }
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ /**
6
+ * default newrelic APM and
7
+ * opentelemetry tracer modules
8
+ */
9
+ const defaultOptions = {
10
+ apm: () => require('newrelic'),
11
+ tracer: () => require('../opentelemetry')
12
+ };
13
+ function createTelemetryMiddleware(options) {
14
+ // trace
15
+ function trace() {
16
+ // validate apm and tracer
17
+ if (!(options !== null && options !== void 0 && options.apm && typeof options.apm == 'function')) {
18
+ options.apm = defaultOptions.apm;
19
+ }
20
+ if (!(options !== null && options !== void 0 && options.tracer && typeof options.tracer == 'function')) {
21
+ options.tracer = defaultOptions.tracer;
22
+ }
23
+ options.apm();
24
+ options.tracer();
25
+ }
26
+ trace(); // expose tracing modules
27
+ return next => (request, response) => {
28
+ const nextRequest = {
29
+ ...request,
30
+ ...options
31
+ };
32
+ next(nextRequest, response);
33
+ };
34
+ }
35
+
36
+ exports.createTelemetryMiddleware = createTelemetryMiddleware;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * default newrelic APM and
3
+ * opentelemetry tracer modules
4
+ */
5
+ const defaultOptions = {
6
+ apm: () => require('newrelic'),
7
+ tracer: () => require('../opentelemetry')
8
+ };
9
+ function createTelemetryMiddleware(options) {
10
+ // trace
11
+ function trace() {
12
+ // validate apm and tracer
13
+ if (!(options !== null && options !== void 0 && options.apm && typeof options.apm == 'function')) {
14
+ options.apm = defaultOptions.apm;
15
+ }
16
+ if (!(options !== null && options !== void 0 && options.tracer && typeof options.tracer == 'function')) {
17
+ options.tracer = defaultOptions.tracer;
18
+ }
19
+ options.apm();
20
+ options.tracer();
21
+ }
22
+ trace(); // expose tracing modules
23
+ return next => (request, response) => {
24
+ const nextRequest = {
25
+ ...request,
26
+ ...options
27
+ };
28
+ next(nextRequest, response);
29
+ };
30
+ }
31
+
32
+ export { createTelemetryMiddleware };
@@ -0,0 +1,2 @@
1
+ import type { Middleware, OTelemetryMiddlewareOptions } from '../types/types';
2
+ export default function createTelemetryMiddleware(options: OTelemetryMiddlewareOptions): Middleware;
@@ -0,0 +1,2 @@
1
+ export { default as createTelemetryMiddleware } from './apm';
2
+ export * from '../types/types.d';
@@ -0,0 +1,103 @@
1
+ export type JsonObject<T = unknown> = { [key: string]: T }
2
+ export type MiddlewareRequest = ClientRequest
3
+ export type Middleware = (next: Dispatch) => Dispatch
4
+
5
+ export type Dispatch = (
6
+ request: MiddlewareRequest,
7
+ response: MiddlewareResponse
8
+ ) => unknown
9
+
10
+ export type Next = (
11
+ request: MiddlewareRequest,
12
+ response: MiddlewareResponse
13
+ ) => unknown
14
+
15
+ export type MiddlewareResponse = {
16
+ resolve(response: JsonObject): void
17
+ reject(error: JsonObject): void
18
+ body?: JsonObject
19
+ error?: HttpErrorType
20
+ statusCode: number
21
+ headers?: JsonObject<string>
22
+ request?: JsonObject
23
+ }
24
+
25
+ export interface ClientRequest {
26
+ baseUri?: string
27
+ uri?: string
28
+ headers?: VariableMap
29
+ method: MethodType
30
+ uriTemplate?: string
31
+ pathVariables?: VariableMap
32
+ queryParams?: VariableMap
33
+ body?: any,
34
+ }
35
+
36
+ export type HttpErrorType = {
37
+ name: string
38
+ message: string
39
+ code: number
40
+ status: number
41
+ statusCode: number
42
+ originalRequest: ClientRequest
43
+ body?: JsonObject
44
+ retryCount?: number
45
+ headers?: JsonObject<string>
46
+ }
47
+
48
+ export type VariableMap = {
49
+ [key: string]: QueryParam
50
+ }
51
+
52
+ export type QueryParam =
53
+ | string
54
+ | string[]
55
+ | number
56
+ | number[]
57
+ | boolean
58
+ | boolean[]
59
+ | undefined
60
+
61
+ export type MethodType =
62
+ | 'ACL'
63
+ | 'BIND'
64
+ | 'CHECKOUT'
65
+ | 'CONNECT'
66
+ | 'COPY'
67
+ | 'DELETE'
68
+ | 'GET'
69
+ | 'HEAD'
70
+ | 'LINK'
71
+ | 'LOCK'
72
+ | 'M-SEARCH'
73
+ | 'MERGE'
74
+ | 'MKACTIVITY'
75
+ | 'MKCALENDAR'
76
+ | 'MKCOL'
77
+ | 'MOVE'
78
+ | 'NOTIFY'
79
+ | 'OPTIONS'
80
+ | 'PATCH'
81
+ | 'POST'
82
+ | 'PROPFIND'
83
+ | 'PROPPATCH'
84
+ | 'PURGE'
85
+ | 'PUT'
86
+ | 'REBIND'
87
+ | 'REPORT'
88
+ | 'SEARCH'
89
+ | 'SOURCE'
90
+ | 'SUBSCRIBE'
91
+ | 'TRACE'
92
+ | 'UNBIND'
93
+ | 'UNLINK'
94
+ | 'UNLOCK'
95
+ | 'UNSUBSCRIBE'
96
+
97
+ export type TelemetryMiddlewareOptions = {
98
+ apm?: Function,
99
+ tracer?: Function,
100
+ createTelemetryMiddleware: (options?: OTelemetryMiddlewareOptions) => Middleware
101
+ }
102
+
103
+ export type OTelemetryMiddlewareOptions = Omit<TelemetryMiddlewareOptions, 'createTelemetryMiddleware'>
@@ -0,0 +1,90 @@
1
+ 'use strict'
2
+ require('dotenv').config()
3
+
4
+ const process = require('process')
5
+ const { v4: uuidv4 } = require('uuid')
6
+ const opentelemetry = require('@opentelemetry/sdk-node')
7
+ const {
8
+ getNodeAutoInstrumentations,
9
+ } = require('@opentelemetry/auto-instrumentations-node')
10
+ const { Resource } = require('@opentelemetry/resources')
11
+ const {
12
+ SemanticResourceAttributes,
13
+ } = require('@opentelemetry/semantic-conventions')
14
+ const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http')
15
+ const {
16
+ OTLPMetricExporter,
17
+ } = require('@opentelemetry/exporter-metrics-otlp-http')
18
+ const {
19
+ AggregationTemporality,
20
+ PeriodicExportingMetricReader,
21
+ } = require('@opentelemetry/sdk-metrics')
22
+ const api = require('@opentelemetry/api')
23
+
24
+ // enable logging ONLY for developement
25
+ // this is useful for debugging instrumentation issues
26
+ // remove from production after issues (if any) are resolved
27
+ // view more logging levels here: https://github.com/open-telemetry/opentelemetry-js-api/blob/main/src/diag/types.ts#L67
28
+
29
+ if (process.env.NODE_ENV == 'development') {
30
+ api.diag.setLogger(
31
+ new api.DiagConsoleLogger(),
32
+ api.DiagLogLevel.INFO,
33
+ api.DiagLogLevel.ERROR
34
+ )
35
+ }
36
+
37
+ // Declare the resource to be used.
38
+ // A resource represents a collection of attributes describing the
39
+ // service. This collection of attributes will be associated with all
40
+ // telemetry generated from this service (traces, metrics, logs).
41
+ const resource = new Resource({
42
+ [SemanticResourceAttributes.SERVICE_INSTANCE_ID]: uuidv4(),
43
+ [SemanticResourceAttributes.SERVICE_NAME]: process.env['NEW_RELIC_APP_NAME'],
44
+ })
45
+
46
+ // Enable auto-instrumentation from the meta package.
47
+ const instrumentations = [getNodeAutoInstrumentations()]
48
+
49
+ // Configure the OTLP/HTTP exporters.
50
+ // The following assumes you've set the OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OLTP_HEADERS
51
+ // environment variables.
52
+ const traceExporter = new OTLPTraceExporter()
53
+ const metricExporter = new OTLPMetricExporter({
54
+ temporalityPreference: AggregationTemporality.DELTA,
55
+ })
56
+
57
+ // If you haven't set the OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OLTP_HEADERS
58
+ // environment variables, you can configure the OTLP exporter programmatically by
59
+ // uncommenting the following code:
60
+
61
+ // this endpoint contains a path since this exporter is signal specific (traces)
62
+ // see more details here: https://docs.newrelic.com/docs/more-integrations/open-source-telemetry-integrations/opentelemetry/opentelemetry-quick-start/#note-endpoints
63
+ // const url = "https://otlp.nr-data.net:4317/v1/traces";
64
+
65
+ // Configure PeriodicMetricReader
66
+ const metricReader = new PeriodicExportingMetricReader({
67
+ exporter: metricExporter,
68
+ exportIntervalMillis: 5000,
69
+ })
70
+
71
+ // Configure the OpenTelemetry NodeSDK
72
+ const sdk = new opentelemetry.NodeSDK({
73
+ resource,
74
+ traceExporter,
75
+ metricReader,
76
+ instrumentations,
77
+ })
78
+
79
+ // Initialize the SDK and register with the OpenTelemetry API:
80
+ // this enables the API to record telemetry
81
+ sdk.start()
82
+
83
+ // Gracefully shut down the SDK on process exit
84
+ process.on('SIGTERM', () => {
85
+ sdk
86
+ .shutdown()
87
+ .then(() => console.log('OpenTelemetry terminated'))
88
+ .catch((error) => console.log('Error terminating OpenTelemetry', error))
89
+ .finally(() => process.exit(0))
90
+ })
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@commercetools/ts-sdk-apm",
3
+ "version": "1.0.0",
4
+ "description": "commercetools typescript SDK application performance monitoring.",
5
+ "main": "dist/commercetools-ts-sdk-apm.cjs.js",
6
+ "module": "dist/commercetools-ts-sdk-apm.esm.js",
7
+ "author": "Chukwuemeka Ajima <meeky.ae@gmail.com>",
8
+ "license": "MIT",
9
+ "private": false,
10
+ "dependencies": {
11
+ "@opentelemetry/api": "^1.4.1",
12
+ "@opentelemetry/auto-instrumentations-node": "^0.36.6",
13
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.38.0",
14
+ "@opentelemetry/sdk-node": "^0.38.0",
15
+ "newrelic": "^9.15.0",
16
+ "uuid": "9.0.0"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "engines": {
22
+ "node": ">=14"
23
+ },
24
+ "keywords": [
25
+ "commercetools",
26
+ "composable commerce",
27
+ "typescript",
28
+ "sdk",
29
+ "apm",
30
+ "telemetry",
31
+ "opentelemetry",
32
+ "tracing"
33
+ ],
34
+ "homepage": "https://github.com/commercetools/commercetools-typescript-sdks",
35
+ "bugs": "https://github.com/commercetools/commercetools-typescript-sdks/issues",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/commercetools/commercetools-typescript-sdks.git"
39
+ },
40
+ "files": ["dist", "CHANGELOG.md", "opentelemetry.js"],
41
+ "browser": {
42
+ "./dist/commercetools-ts-sdk-apm.cjs.js": "./dist/commercetools-ts-sdk-apm.browser.cjs.js",
43
+ "./dist/commercetools-ts-sdk-apm.esm.js": "./dist/commercetools-ts-sdk-apm.browser.esm.js"
44
+ },
45
+ "scripts": {
46
+ "organize_imports": "find src -type f -name '*.ts' | xargs organize-imports-cli",
47
+ "postbuild": "yarn organize_imports",
48
+ "post_process_generate": "yarn organize_imports"
49
+ }
50
+ }