@ogcio/o11y-sdk-node 0.4.1 → 0.4.2
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 +7 -0
- package/dist/lib/config-manager.d.ts +1 -1
- package/dist/lib/config-manager.js +1 -4
- package/dist/lib/exporter/pii-exporter-decorator.d.ts +1 -1
- package/dist/lib/exporter/pii-exporter-decorator.js +1 -1
- package/dist/lib/traces.js +1 -1
- package/dist/package.json +1 -1
- package/dist/vitest.config.js +1 -1
- package/lib/config-manager.ts +2 -6
- package/lib/exporter/pii-exporter-decorator.ts +2 -2
- package/lib/traces.ts +2 -2
- package/package.json +1 -1
- package/test/config-manager.test.ts +2 -2
- package/test/integration/README.md +59 -11
- package/test/integration/docker-utils.sh +214 -0
- package/test/integration/main.sh +52 -0
- package/test/integration/teardown.sh +7 -0
- package/test/integration/{http-tracing.integration.test.ts → test_fastify-o11y-pii-enabled/http-tracing.integration.test.ts} +1 -1
- package/test/integration/{pii.integration.test.ts → test_fastify-o11y-pii-enabled/pii.integration.test.ts} +1 -1
- package/test/integration/test_fastify-o11y-pii-enabled/run.sh +42 -0
- package/test/integration/test_without-o11y/run.sh +30 -0
- package/test/integration/test_without-o11y/verify-status.integration.test.ts +32 -0
- package/test/traces/active-span.test.ts +2 -4
- package/test/traces/with-span.test.ts +16 -0
- package/vitest.config.ts +1 -1
- package/test/integration/run.sh +0 -88
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.2](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@v0.4.1...@ogcio/o11y-sdk-node@v0.4.2) (2025-09-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* withSpan function throws error if sdk is not initialized AB[#30828](https://github.com/ogcio/o11y/issues/30828) ([#200](https://github.com/ogcio/o11y/issues/200)) ([407b616](https://github.com/ogcio/o11y/commit/407b616590024ca4131610c7a69bacd9699f23c5))
|
|
9
|
+
|
|
3
10
|
## [0.4.1](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@v0.4.0...@ogcio/o11y-sdk-node@v0.4.1) (2025-09-03)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -3,9 +3,6 @@ export const setNodeSdkConfig = (config) => {
|
|
|
3
3
|
nodeSDKConfig = config;
|
|
4
4
|
};
|
|
5
5
|
export const getNodeSdkConfig = () => {
|
|
6
|
-
if (!nodeSDKConfig) {
|
|
7
|
-
throw new Error("Node SDK Config was not initialized.");
|
|
8
|
-
}
|
|
9
6
|
// Ensure getters do not edit config.
|
|
10
|
-
return JSON.parse(JSON.stringify(nodeSDKConfig));
|
|
7
|
+
return nodeSDKConfig ? JSON.parse(JSON.stringify(nodeSDKConfig)) : undefined;
|
|
11
8
|
};
|
|
@@ -8,7 +8,7 @@ export declare class PIIExporterDecorator extends OTLPExporterBase<(ReadableSpan
|
|
|
8
8
|
private readonly _exporter;
|
|
9
9
|
private readonly _config;
|
|
10
10
|
private readonly _redactors;
|
|
11
|
-
constructor(exporter: OTLPExporterBase<(ReadableSpan | ReadableLogRecord)[] | ResourceMetrics>, config: NodeSDKConfig);
|
|
11
|
+
constructor(exporter: OTLPExporterBase<(ReadableSpan | ReadableLogRecord)[] | ResourceMetrics>, config: NodeSDKConfig | undefined);
|
|
12
12
|
forceFlush(): Promise<void>;
|
|
13
13
|
shutdown(): Promise<void>;
|
|
14
14
|
export(items: (ReadableSpan | ReadableLogRecord)[] | ResourceMetrics, resultCallback: (result: ExportResult) => void): void;
|
|
@@ -9,7 +9,7 @@ export class PIIExporterDecorator extends OTLPExporterBase {
|
|
|
9
9
|
super(exporter["_delegate"]);
|
|
10
10
|
this._exporter = exporter;
|
|
11
11
|
this._config = config;
|
|
12
|
-
this._redactors = this._buildRedactors(config
|
|
12
|
+
this._redactors = this._buildRedactors(config?.detection);
|
|
13
13
|
}
|
|
14
14
|
forceFlush() {
|
|
15
15
|
return this._exporter.forceFlush();
|
package/dist/lib/traces.js
CHANGED
|
@@ -45,6 +45,6 @@ export function getActiveSpan() {
|
|
|
45
45
|
}
|
|
46
46
|
export function withSpan({ traceName, spanName, spanOptions = {}, fn, }) {
|
|
47
47
|
const sdkConfig = getNodeSdkConfig();
|
|
48
|
-
const tracer = trace.getTracer(traceName ?? sdkConfig
|
|
48
|
+
const tracer = trace.getTracer(traceName ?? sdkConfig?.serviceName ?? "o11y-sdk", sdkConfig?.serviceVersion);
|
|
49
49
|
return tracer.startActiveSpan(spanName, spanOptions, selfContainedSpanHandlerGenerator(fn));
|
|
50
50
|
}
|
package/dist/package.json
CHANGED
package/dist/vitest.config.js
CHANGED
package/lib/config-manager.ts
CHANGED
|
@@ -6,11 +6,7 @@ export const setNodeSdkConfig = (config: NodeSDKConfig) => {
|
|
|
6
6
|
nodeSDKConfig = config;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
export const getNodeSdkConfig = (): NodeSDKConfig => {
|
|
10
|
-
if (!nodeSDKConfig) {
|
|
11
|
-
throw new Error("Node SDK Config was not initialized.");
|
|
12
|
-
}
|
|
13
|
-
|
|
9
|
+
export const getNodeSdkConfig = (): NodeSDKConfig | undefined => {
|
|
14
10
|
// Ensure getters do not edit config.
|
|
15
|
-
return JSON.parse(JSON.stringify(nodeSDKConfig));
|
|
11
|
+
return nodeSDKConfig ? JSON.parse(JSON.stringify(nodeSDKConfig)) : undefined;
|
|
16
12
|
};
|
|
@@ -31,12 +31,12 @@ export class PIIExporterDecorator
|
|
|
31
31
|
exporter: OTLPExporterBase<
|
|
32
32
|
(ReadableSpan | ReadableLogRecord)[] | ResourceMetrics
|
|
33
33
|
>,
|
|
34
|
-
config: NodeSDKConfig,
|
|
34
|
+
config: NodeSDKConfig | undefined,
|
|
35
35
|
) {
|
|
36
36
|
super(exporter["_delegate"]);
|
|
37
37
|
this._exporter = exporter;
|
|
38
38
|
this._config = config;
|
|
39
|
-
this._redactors = this._buildRedactors(config
|
|
39
|
+
this._redactors = this._buildRedactors(config?.detection);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
forceFlush(): Promise<void> {
|
package/lib/traces.ts
CHANGED
|
@@ -67,8 +67,8 @@ export function withSpan<T>({
|
|
|
67
67
|
}: WithSpanParams<T>) {
|
|
68
68
|
const sdkConfig = getNodeSdkConfig();
|
|
69
69
|
const tracer = trace.getTracer(
|
|
70
|
-
traceName ?? sdkConfig
|
|
71
|
-
sdkConfig
|
|
70
|
+
traceName ?? sdkConfig?.serviceName ?? "o11y-sdk",
|
|
71
|
+
sdkConfig?.serviceVersion,
|
|
72
72
|
);
|
|
73
73
|
return tracer.startActiveSpan(
|
|
74
74
|
spanName,
|
package/package.json
CHANGED
|
@@ -3,8 +3,8 @@ import { getNodeSdkConfig, setNodeSdkConfig } from "../lib/config-manager";
|
|
|
3
3
|
import { NodeSDKConfig } from "../lib";
|
|
4
4
|
|
|
5
5
|
describe("Config Manager", () => {
|
|
6
|
-
it("
|
|
7
|
-
expect(() => getNodeSdkConfig()).toThrow();
|
|
6
|
+
it("does not throw if getConfig is called before initialization", () => {
|
|
7
|
+
expect(() => getNodeSdkConfig()).not.toThrow();
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
it("sdk defined config is not pollutable", () => {
|
|
@@ -1,26 +1,74 @@
|
|
|
1
|
-
# Integration Test
|
|
1
|
+
# Integration Test Runner
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This script is a **Bash-based test runner** that discovers and executes integration test scripts for the Node SDK (`./packages/sdk-node/test/integration`).
|
|
4
|
+
It ensures tests run inside an isolated Docker network and provides a summary of results.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
---
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
- Run the sh script `sh ./packages/sdk-node/test/integration/run.sh 1 .` from project root with following params
|
|
9
|
-
1. pipeline build number, for local development, any number or string is fine
|
|
10
|
-
2. root folder for docker context
|
|
11
|
-
- Change dir to `packages/sdk-node/`
|
|
12
|
-
- Run full test suite with `pnpm test`
|
|
8
|
+
## Usage
|
|
13
9
|
|
|
14
|
-
|
|
10
|
+
```bash
|
|
11
|
+
./run-tests.sh <build-id> <root-path> <is-ci?>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Arguments**
|
|
15
|
+
|
|
16
|
+
<build-id>
|
|
17
|
+
A unique identifier for the test run. Used to name the Docker network (<build-id>_testnetwork).
|
|
18
|
+
|
|
19
|
+
<root-path>
|
|
20
|
+
The root path for the project or build environment.
|
|
21
|
+
|
|
22
|
+
[ci-flag] (optional)
|
|
23
|
+
Defaults to false.
|
|
24
|
+
If set to true, the script will remove all Docker images after the tests complete (intended for CI pipelines to save disk space).
|
|
25
|
+
|
|
26
|
+
## How It Works
|
|
27
|
+
|
|
28
|
+
- Ensures at least two arguments are provided. Exits with usage instructions otherwise.
|
|
29
|
+
- Creates a temporary Docker network named <build-id>\_testnetwork.
|
|
30
|
+
- Finds all integration test scripts under: ./packages/sdk-node/test/integration/test\*/run.sh
|
|
31
|
+
- Runs each test script.
|
|
32
|
+
- Captures exit codes and tracks failures.
|
|
33
|
+
- Prints results per test.
|
|
34
|
+
- Removes the Docker network after all tests finish.
|
|
35
|
+
- If running in CI mode (ci-flag = true), removes all Docker images.
|
|
36
|
+
- Reports whether all tests passed or how many failed.
|
|
37
|
+
Exits with:
|
|
38
|
+
- 0 if all tests succeeded
|
|
39
|
+
- 1 if one or more tests failed
|
|
40
|
+
|
|
41
|
+
**Example**
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
# Run tests locally
|
|
45
|
+
./run-tests.sh my-build-id /path/to/project
|
|
46
|
+
|
|
47
|
+
# Run tests in CI mode (cleanup Docker images after)
|
|
48
|
+
./run-tests.sh my-build-id /path/to/project true
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Tests
|
|
52
|
+
|
|
53
|
+
### test_fastify-o11y-pii-enabled
|
|
15
54
|
|
|
16
55
|
The `run.sh` script performs the following steps:
|
|
17
56
|
|
|
18
57
|
- build a docker image of a fastify app `/examples/fastify`
|
|
19
|
-
- setup an temporary test docker network
|
|
20
58
|
- run grafana alloy inside a docker container with a test configuration `/alloy/integration-test.alloy`
|
|
21
59
|
- ensure is running otherwise exit process
|
|
22
60
|
- run fastify app in a docker container
|
|
23
61
|
- ensure is running otherwise exit process
|
|
24
62
|
- execute some curl to the fastify microservice
|
|
25
63
|
- persist alloy log to a file and save to following path `/packages/sdk-node/test/integration/`
|
|
64
|
+
- verify pii data are removed and metrics are increased
|
|
26
65
|
- docker turn down process (containers/network/image)
|
|
66
|
+
|
|
67
|
+
### test_without-o11y
|
|
68
|
+
|
|
69
|
+
The `run.sh` script performs the following steps:
|
|
70
|
+
|
|
71
|
+
- build a docker image of a fastify app `/examples/fastify`
|
|
72
|
+
- verify service execution with o11y sdk disabled (otel collector empty)
|
|
73
|
+
- execute http requests for dummy route with otel trace customization function calls
|
|
74
|
+
- assert 200 http status without any error
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
: '
|
|
4
|
+
Build a new docker image of a given Dockerfile, if already exists skip build.
|
|
5
|
+
|
|
6
|
+
@param container_name docker container name
|
|
7
|
+
|
|
8
|
+
@return int error code
|
|
9
|
+
0. success
|
|
10
|
+
1. validation error
|
|
11
|
+
'
|
|
12
|
+
build_image() {
|
|
13
|
+
if [ "$#" -lt 3 ] || [ "$#" -gt 4 ]; then
|
|
14
|
+
echo "Error: build_image expected 3 or 4 parameters, got $#." >&2
|
|
15
|
+
echo "Usage: build_image <image_name> <dockerfile_path> <context_path> [max_age_minutes]" >&2
|
|
16
|
+
return 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
local image_name="$1"
|
|
21
|
+
local dockerfile_path="$2"
|
|
22
|
+
local context_path="$3"
|
|
23
|
+
local max_age_minutes="${4:-30}" # default threshold = 30 minutes
|
|
24
|
+
|
|
25
|
+
if docker image inspect "$image_name" > /dev/null 2>&1; then
|
|
26
|
+
echo "Image $image_name already exists. Checking age..."
|
|
27
|
+
|
|
28
|
+
# Extract the Created timestamp from docker inspect (ISO 8601 format)
|
|
29
|
+
local created_at
|
|
30
|
+
created_at=$(docker image inspect --format '{{.Created}}' "$image_name")
|
|
31
|
+
if [ -z "$created_at" ]; then
|
|
32
|
+
echo "Warning: Could not determine creation date. Proceeding without rebuild check." >&2
|
|
33
|
+
return 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Convert created_at to epoch seconds
|
|
37
|
+
local created_epoch
|
|
38
|
+
created_epoch=$(date -d "$created_at" +%s 2>/dev/null)
|
|
39
|
+
|
|
40
|
+
# Current time in epoch seconds
|
|
41
|
+
local now_epoch
|
|
42
|
+
now_epoch=$(date +%s)
|
|
43
|
+
|
|
44
|
+
# Calculate age in minutes
|
|
45
|
+
local age_minutes=$(( (now_epoch - created_epoch) / 60 ))
|
|
46
|
+
|
|
47
|
+
echo "Image $image_name is $age_minutes minutes old (threshold: $max_age_minutes minutes)."
|
|
48
|
+
|
|
49
|
+
if [ "$age_minutes" -ge "$max_age_minutes" ]; then
|
|
50
|
+
echo "Image is too old. Rebuilding..."
|
|
51
|
+
docker build -t $image_name -f $dockerfile_path $context_path
|
|
52
|
+
return $?
|
|
53
|
+
else
|
|
54
|
+
echo "Image is recent. Skipping rebuild."
|
|
55
|
+
return 0
|
|
56
|
+
fi
|
|
57
|
+
else
|
|
58
|
+
echo "Image $image_name not found. Building..."
|
|
59
|
+
docker build -t "$image_name" -f "$dockerfile_path" "$context_path"
|
|
60
|
+
return $?
|
|
61
|
+
fi
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
: '
|
|
65
|
+
Stop and remove a docker container for the given input container name.
|
|
66
|
+
|
|
67
|
+
@param container_name docker container name
|
|
68
|
+
|
|
69
|
+
@return int error code
|
|
70
|
+
0. success
|
|
71
|
+
1. validation error
|
|
72
|
+
'
|
|
73
|
+
container_stop_and_rm() {
|
|
74
|
+
local container_name="$1"
|
|
75
|
+
|
|
76
|
+
if [[ -z "$container_name" ]]; then
|
|
77
|
+
echo "Error: container_name must have a value."
|
|
78
|
+
return 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
docker container stop $container_name
|
|
82
|
+
docker container rm -f $container_name
|
|
83
|
+
|
|
84
|
+
return $?
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
: '
|
|
88
|
+
Run a new alloy container and check if the status is running.
|
|
89
|
+
|
|
90
|
+
@param root_path volume root path for alloy file config
|
|
91
|
+
@param network_name docker network name
|
|
92
|
+
@param container_name docker container name
|
|
93
|
+
|
|
94
|
+
@optional max_retries
|
|
95
|
+
@default 10
|
|
96
|
+
|
|
97
|
+
@return int error code
|
|
98
|
+
0. success
|
|
99
|
+
1. validation error
|
|
100
|
+
2. execution failed
|
|
101
|
+
'
|
|
102
|
+
run_and_check_alloy() {
|
|
103
|
+
|
|
104
|
+
if [ "$#" -lt 3 ]; then
|
|
105
|
+
echo "Error: run_and_check_alloy expected 3 parameters, got $#." >&2
|
|
106
|
+
return 1
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
local root_path="$1"
|
|
110
|
+
local network_name="$2"
|
|
111
|
+
local container_name="$3"
|
|
112
|
+
local max_retries="${4:-10}"
|
|
113
|
+
local counter_retries=0
|
|
114
|
+
|
|
115
|
+
if [[ -z "$root_path" || -z "$network_name" || -z "$container_name" ]]; then
|
|
116
|
+
echo "Error: root_path, network_name, and container_name must all have values."
|
|
117
|
+
return 1
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
if ! [[ "$max_retries" =~ ^[0-9]+$ ]]; then
|
|
121
|
+
echo "Error: max_retries parameter must be a number." >&2
|
|
122
|
+
return 1
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
docker run -d \
|
|
126
|
+
-v "$root_path/alloy/integration-test.alloy:/etc/alloy/config.alloy:ro" \
|
|
127
|
+
--network $network_name \
|
|
128
|
+
--name $container_name \
|
|
129
|
+
-p 4317:4317 \
|
|
130
|
+
-p 4318:4318 \
|
|
131
|
+
grafana/alloy:v1.9.2 \
|
|
132
|
+
run --server.http.listen-addr=0.0.0.0:12345 --stability.level=experimental /etc/alloy/config.alloy
|
|
133
|
+
|
|
134
|
+
echo "$container_name container status"
|
|
135
|
+
until [ "$(docker inspect -f {{.State.Running}} $container_name)" = true ]; do
|
|
136
|
+
sleep 2
|
|
137
|
+
docker inspect -f {{.State.Running}} $container_name
|
|
138
|
+
counter_retries=$((counter_retries + 1))
|
|
139
|
+
if [ $counter_retries -ge $max_retries ]; then
|
|
140
|
+
echo "Exceeded maximum retries. Exiting."
|
|
141
|
+
return 2
|
|
142
|
+
fi
|
|
143
|
+
done
|
|
144
|
+
|
|
145
|
+
return 0
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
: '
|
|
149
|
+
Run a new fastify microservice container and check if the status is running.
|
|
150
|
+
|
|
151
|
+
@param network_name docker network name
|
|
152
|
+
@param collector_url otel collector grpc endpoint
|
|
153
|
+
@param container_name docker container name
|
|
154
|
+
@param build_id current pipeline build id
|
|
155
|
+
|
|
156
|
+
@optional max_retries
|
|
157
|
+
@default 10
|
|
158
|
+
|
|
159
|
+
@return int error code
|
|
160
|
+
0. success
|
|
161
|
+
1. validation error
|
|
162
|
+
2. execution failed
|
|
163
|
+
'
|
|
164
|
+
run_and_check_fastify() {
|
|
165
|
+
|
|
166
|
+
if [ "$#" -lt 4 ]; then
|
|
167
|
+
echo "Error: run_and_check_alloy expected 3 parameters, got $#." >&2
|
|
168
|
+
return 1
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
local network_name="$1"
|
|
172
|
+
local collector_url="$2"
|
|
173
|
+
local container_name="$3"
|
|
174
|
+
local build_id="$4"
|
|
175
|
+
local max_retries="${5:-10}"
|
|
176
|
+
local counter_retries=0
|
|
177
|
+
|
|
178
|
+
if [[ -z "$build_id" || -z "$network_name" || -z "$container_name" ]]; then
|
|
179
|
+
echo "Error: build_id, network_name, and container_name must all have values."
|
|
180
|
+
return 1
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
if ! [[ "$max_retries" =~ ^[0-9]+$ ]]; then
|
|
184
|
+
echo "Error: max_retries parameter must be a number." >&2
|
|
185
|
+
return 1
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
docker run --detach \
|
|
189
|
+
--network $network_name \
|
|
190
|
+
--name $container_name \
|
|
191
|
+
-e DB_DISABLED="true" \
|
|
192
|
+
-e SERVER_HOST="0.0.0.0" \
|
|
193
|
+
-e OTEL_COLLECTOR_URL=$collector_url \
|
|
194
|
+
--health-cmd="curl -f http://${container_name}:9091/api/health > /dev/null || exit 1" \
|
|
195
|
+
--health-start-period=1s \
|
|
196
|
+
--health-retries=10 \
|
|
197
|
+
--health-interval=1s \
|
|
198
|
+
-p 9091:9091 \
|
|
199
|
+
$container_name:$build_id
|
|
200
|
+
|
|
201
|
+
counter_retries=0
|
|
202
|
+
echo "$container_name container status"
|
|
203
|
+
until [ "$(docker inspect -f {{.State.Health.Status}} $container_name)" = "healthy" ]; do
|
|
204
|
+
sleep 1
|
|
205
|
+
docker inspect -f {{.State.Health.Status}} $container_name
|
|
206
|
+
counter_retries=$((counter_retries + 1))
|
|
207
|
+
if [ $counter_retries -ge $max_retries ]; then
|
|
208
|
+
echo "Exceeded maximum retries. Exiting."
|
|
209
|
+
return 2
|
|
210
|
+
fi
|
|
211
|
+
done
|
|
212
|
+
|
|
213
|
+
return 0
|
|
214
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
set -o pipefail
|
|
5
|
+
|
|
6
|
+
if [ $# -lt 2 ]; then
|
|
7
|
+
echo "Usage: $0 <build-id> <root-path>"
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
BUILD_ID=$1
|
|
12
|
+
ROOT_PATH=$2
|
|
13
|
+
|
|
14
|
+
NETWORK_NAME="${BUILD_ID}_testnetwork"
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
|
|
17
|
+
docker network create $NETWORK_NAME
|
|
18
|
+
|
|
19
|
+
TEST_SCRIPTS=( $(find . -type f -path "./packages/sdk-node/test/integration/test*/run.sh" | sort) )
|
|
20
|
+
|
|
21
|
+
FAILURES=0
|
|
22
|
+
|
|
23
|
+
for test_script in "${TEST_SCRIPTS[@]}"; do
|
|
24
|
+
TEST_DIR=$(dirname "$test_script")
|
|
25
|
+
TEST_NAME=$(basename "$TEST_DIR")
|
|
26
|
+
|
|
27
|
+
echo "Running test: $TEST_NAME ($test_script)"
|
|
28
|
+
|
|
29
|
+
# Run the test
|
|
30
|
+
bash "$test_script" "$BUILD_ID" "$ROOT_PATH" "$NETWORK_NAME"
|
|
31
|
+
EXIT_CODE=$?
|
|
32
|
+
|
|
33
|
+
if [ $EXIT_CODE -eq 0 ]; then
|
|
34
|
+
echo "$test_script completed"
|
|
35
|
+
else
|
|
36
|
+
echo "❌ $test_script failed with exit code $EXIT_CODE"
|
|
37
|
+
FAILURES=$((FAILURES + 1))
|
|
38
|
+
fi
|
|
39
|
+
echo "------------------------------"
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
# Remove network and images from docker host
|
|
43
|
+
bash "$SCRIPT_DIR/teardown.sh" "$BUILD_ID" "$NETWORK_NAME"
|
|
44
|
+
|
|
45
|
+
# Final summary
|
|
46
|
+
if [ $FAILURES -eq 0 ]; then
|
|
47
|
+
echo "All tests ready!"
|
|
48
|
+
exit 0
|
|
49
|
+
else
|
|
50
|
+
echo "$FAILURES test(s) failed."
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# @docs: packages/sdk-node/test/integration/README.md#test_fastify-o11y-pii-enabled
|
|
3
|
+
BUILD_ID=$1
|
|
4
|
+
ROOT_PATH=$2
|
|
5
|
+
NETWORK_NAME=$3
|
|
6
|
+
|
|
7
|
+
ALLOY_CONTAINER_NAME="integrationalloy"
|
|
8
|
+
NODE_CONTAINER_NAME="${BUILD_ID}_fastify_app"
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
LOG_FILE="$SCRIPT_DIR/logs.txt"
|
|
11
|
+
ERROR_CODE=0
|
|
12
|
+
|
|
13
|
+
source "$SCRIPT_DIR/../docker-utils.sh"
|
|
14
|
+
|
|
15
|
+
build_image "${NODE_CONTAINER_NAME}:${BUILD_ID}" "$ROOT_PATH/examples/fastify/Dockerfile" "$ROOT_PATH/"
|
|
16
|
+
|
|
17
|
+
run_and_check_alloy $ROOT_PATH $NETWORK_NAME $ALLOY_CONTAINER_NAME
|
|
18
|
+
ERROR_CODE=$?
|
|
19
|
+
|
|
20
|
+
if [[ $ERROR_CODE -eq 0 ]]; then
|
|
21
|
+
run_and_check_fastify $NETWORK_NAME "http://integrationalloy:4317" $NODE_CONTAINER_NAME $BUILD_ID
|
|
22
|
+
ERROR_CODE=$?
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
if [[ $ERROR_CODE -eq 0 ]]; then
|
|
26
|
+
sleep 2
|
|
27
|
+
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
28
|
+
sleep 2
|
|
29
|
+
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# sleep N seconds to await instrumentation flow send and receiving signals
|
|
33
|
+
sleep 1
|
|
34
|
+
|
|
35
|
+
# Copy logs from container to file
|
|
36
|
+
docker container logs $ALLOY_CONTAINER_NAME >&$LOG_FILE
|
|
37
|
+
echo "log file at $LOG_FILE"
|
|
38
|
+
|
|
39
|
+
container_stop_and_rm $ALLOY_CONTAINER_NAME
|
|
40
|
+
container_stop_and_rm $NODE_CONTAINER_NAME
|
|
41
|
+
|
|
42
|
+
exit $ERROR_CODE
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# @docs: packages/sdk-node/test/integration/README.md#test_without-o11y
|
|
3
|
+
BUILD_ID=$1
|
|
4
|
+
ROOT_PATH=$2
|
|
5
|
+
NETWORK_NAME=$3
|
|
6
|
+
|
|
7
|
+
NODE_CONTAINER_NAME="${BUILD_ID}_fastify_app"
|
|
8
|
+
ERROR_CODE=0
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
LOG_FILE="$SCRIPT_DIR/logs.txt"
|
|
11
|
+
|
|
12
|
+
source "$SCRIPT_DIR/../docker-utils.sh"
|
|
13
|
+
|
|
14
|
+
build_image "${NODE_CONTAINER_NAME}:${BUILD_ID}" "$ROOT_PATH/examples/fastify/Dockerfile" "$ROOT_PATH/"
|
|
15
|
+
|
|
16
|
+
run_and_check_fastify $NETWORK_NAME "" $NODE_CONTAINER_NAME $BUILD_ID
|
|
17
|
+
ERROR_CODE=$?
|
|
18
|
+
|
|
19
|
+
if [[ $ERROR_CODE -eq 0 ]]; then
|
|
20
|
+
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
21
|
+
sleep 1
|
|
22
|
+
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
docker container logs $NODE_CONTAINER_NAME >&$LOG_FILE
|
|
26
|
+
echo "log file at $LOG_FILE"
|
|
27
|
+
|
|
28
|
+
container_stop_and_rm $NODE_CONTAINER_NAME
|
|
29
|
+
|
|
30
|
+
exit $ERROR_CODE
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
describe("Log validation for /api/dummy with instrumentation disabled", async () => {
|
|
6
|
+
const log = await readFile(join(__dirname, "logs.txt"), "utf-8");
|
|
7
|
+
|
|
8
|
+
it("should contain message that collector URL is not set", () => {
|
|
9
|
+
expect(log).toContain(
|
|
10
|
+
"collectorUrl not set. Skipping NodeJS OpenTelemetry instrumentation.",
|
|
11
|
+
);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should have all /api/dummy responses with status code 200", () => {
|
|
15
|
+
// Extract all request IDs for /api/dummy requests
|
|
16
|
+
const dummyRequestIds = [
|
|
17
|
+
...log.matchAll(/"reqId":"(.*?)","req":\{[^}]*"url":"\/api\/dummy"/g),
|
|
18
|
+
].map((match) => match[1]);
|
|
19
|
+
|
|
20
|
+
expect(dummyRequestIds.length).toBeGreaterThan(0);
|
|
21
|
+
|
|
22
|
+
// For each /api/dummy request ID, verify that its response status is 200
|
|
23
|
+
const non200Responses = dummyRequestIds.filter((reqId) => {
|
|
24
|
+
const responseMatch = log.match(
|
|
25
|
+
new RegExp(`"reqId":"${reqId}".*"statusCode":(\\d+)`),
|
|
26
|
+
);
|
|
27
|
+
return responseMatch && Number(responseMatch[1]) !== 200;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(non200Responses).toEqual([]); // No non-200 responses allowed
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as piiDetection from "../../lib/internals/redaction/pii-detection.js";
|
|
3
|
-
import { getActiveSpan } from "../../lib/traces.js";
|
|
4
|
-
import { MockSpan } from "../utils/mock-signals.js";
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
2
|
import { setNodeSdkConfig } from "../../lib/config-manager.js";
|
|
3
|
+
import { getActiveSpan } from "../../lib/traces.js";
|
|
6
4
|
|
|
7
5
|
describe("getActiveSpan", () => {
|
|
8
6
|
it("returns undefined if no active span", async () => {
|
|
@@ -10,6 +10,22 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
|
|
|
10
10
|
import { setNodeSdkConfig } from "../../lib/config-manager.js";
|
|
11
11
|
import { getActiveSpan, withSpan } from "../../lib/traces.js";
|
|
12
12
|
|
|
13
|
+
describe("withSpan with opentelemetry not initialized", () => {
|
|
14
|
+
it("should handle noop span", async ({}) => {
|
|
15
|
+
let capturedSpan: Span;
|
|
16
|
+
|
|
17
|
+
await withSpan({
|
|
18
|
+
spanName: "test-sync-span",
|
|
19
|
+
fn: (span: Span) => {
|
|
20
|
+
capturedSpan = span;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(capturedSpan).not.toBeNull();
|
|
25
|
+
expect(capturedSpan.constructor.name).toBe("NonRecordingSpan");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
13
29
|
describe("withSpan", () => {
|
|
14
30
|
let memoryExporter: InMemorySpanExporter;
|
|
15
31
|
let spanProcessor: SpanProcessor;
|
package/vitest.config.ts
CHANGED
package/test/integration/run.sh
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
BUILD_ID=$1
|
|
2
|
-
ROOT_PATH=$2
|
|
3
|
-
|
|
4
|
-
NETWORK_NAME="${BUILD_ID}_testnetwork"
|
|
5
|
-
ALLOY_CONTAINER_NAME="integrationalloy"
|
|
6
|
-
NODE_CONTAINER_NAME="${BUILD_ID}_fastify_app"
|
|
7
|
-
ERROR_CODE=0
|
|
8
|
-
|
|
9
|
-
docker build -t ${NODE_CONTAINER_NAME}:${BUILD_ID} -f $ROOT_PATH/examples/fastify/Dockerfile $ROOT_PATH/
|
|
10
|
-
|
|
11
|
-
docker network create $NETWORK_NAME
|
|
12
|
-
|
|
13
|
-
docker run -d \
|
|
14
|
-
-v "$ROOT_PATH/alloy/integration-test.alloy:/etc/alloy/config.alloy:ro" \
|
|
15
|
-
--network $NETWORK_NAME \
|
|
16
|
-
--name $ALLOY_CONTAINER_NAME \
|
|
17
|
-
-p 4317:4317 \
|
|
18
|
-
-p 4318:4318 \
|
|
19
|
-
grafana/alloy:v1.9.2 \
|
|
20
|
-
run --server.http.listen-addr=0.0.0.0:12345 --stability.level=experimental /etc/alloy/config.alloy
|
|
21
|
-
|
|
22
|
-
MAX_RETRIES=10
|
|
23
|
-
COUNTER=0
|
|
24
|
-
echo "$ALLOY_CONTAINER_NAME container status"
|
|
25
|
-
until [ "$(docker inspect -f {{.State.Running}} $ALLOY_CONTAINER_NAME)" = true ]; do
|
|
26
|
-
sleep 2
|
|
27
|
-
docker inspect -f {{.State.Running}} $ALLOY_CONTAINER_NAME
|
|
28
|
-
COUNTER=$((COUNTER + 1))
|
|
29
|
-
if [ $COUNTER -ge $MAX_RETRIES ]; then
|
|
30
|
-
echo "Exceeded maximum retries. Exiting."
|
|
31
|
-
ERROR_CODE=1
|
|
32
|
-
break
|
|
33
|
-
fi
|
|
34
|
-
done
|
|
35
|
-
|
|
36
|
-
if [[ $ERROR_CODE -eq 0 ]]; then
|
|
37
|
-
docker run --detach \
|
|
38
|
-
--network $NETWORK_NAME \
|
|
39
|
-
--name $NODE_CONTAINER_NAME \
|
|
40
|
-
-e DB_DISABLED="true" \
|
|
41
|
-
-e SERVER_HOST="0.0.0.0" \
|
|
42
|
-
-e OTEL_COLLECTOR_URL="http://integrationalloy:4317" \
|
|
43
|
-
--health-cmd="curl -f http://${NODE_CONTAINER_NAME}:9091/api/health > /dev/null || exit 1" \
|
|
44
|
-
--health-start-period=1s \
|
|
45
|
-
--health-retries=10 \
|
|
46
|
-
--health-interval=1s \
|
|
47
|
-
-p 9091:9091 \
|
|
48
|
-
${NODE_CONTAINER_NAME}:${BUILD_ID}
|
|
49
|
-
|
|
50
|
-
COUNTER=0
|
|
51
|
-
echo "$NODE_CONTAINER_NAME container status"
|
|
52
|
-
until [ "$(docker inspect -f {{.State.Health.Status}} $NODE_CONTAINER_NAME)" = "healthy" ]; do
|
|
53
|
-
sleep 1
|
|
54
|
-
docker inspect -f {{.State.Health.Status}} $NODE_CONTAINER_NAME
|
|
55
|
-
COUNTER=$((COUNTER + 1))
|
|
56
|
-
if [ $COUNTER -ge $MAX_RETRIES ]; then
|
|
57
|
-
echo "Exceeded maximum retries. Exiting."
|
|
58
|
-
ERROR_CODE=1
|
|
59
|
-
break
|
|
60
|
-
fi
|
|
61
|
-
done
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
if [[ $ERROR_CODE -eq 0 ]]; then
|
|
65
|
-
sleep 2
|
|
66
|
-
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
67
|
-
sleep 2
|
|
68
|
-
curl -X GET -f http://localhost:9091/api/dummy -H 'Content-Type: application/json'
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
|
-
# sleep N seconds to await instrumentation flow send and receiving signals
|
|
72
|
-
sleep 1
|
|
73
|
-
|
|
74
|
-
# Copy logs from container to file
|
|
75
|
-
docker container logs $ALLOY_CONTAINER_NAME >&$ROOT_PATH/packages/sdk-node/test/integration/logs.txt
|
|
76
|
-
echo "log file at $ROOT_PATH/packages/sdk-node/test/integration/logs.txt"
|
|
77
|
-
|
|
78
|
-
docker container stop $ALLOY_CONTAINER_NAME
|
|
79
|
-
docker container stop $NODE_CONTAINER_NAME
|
|
80
|
-
|
|
81
|
-
docker container rm -f $ALLOY_CONTAINER_NAME
|
|
82
|
-
docker container rm -f $NODE_CONTAINER_NAME
|
|
83
|
-
|
|
84
|
-
docker image rm ${NODE_CONTAINER_NAME}:${BUILD_ID}
|
|
85
|
-
|
|
86
|
-
docker network rm $NETWORK_NAME
|
|
87
|
-
|
|
88
|
-
exit $ERROR_CODE
|