container-superposition 0.1.1
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/README.md +843 -0
- package/dist/scripts/init.d.ts +3 -0
- package/dist/scripts/init.d.ts.map +1 -0
- package/dist/scripts/init.js +1190 -0
- package/dist/scripts/init.js.map +1 -0
- package/dist/scripts/migrate-to-manifests.d.ts +12 -0
- package/dist/scripts/migrate-to-manifests.d.ts.map +1 -0
- package/dist/scripts/migrate-to-manifests.js +230 -0
- package/dist/scripts/migrate-to-manifests.js.map +1 -0
- package/dist/tool/questionnaire/composer.d.ts +6 -0
- package/dist/tool/questionnaire/composer.d.ts.map +1 -0
- package/dist/tool/questionnaire/composer.js +1232 -0
- package/dist/tool/questionnaire/composer.js.map +1 -0
- package/dist/tool/readme/markdown-parser.d.ts +30 -0
- package/dist/tool/readme/markdown-parser.d.ts.map +1 -0
- package/dist/tool/readme/markdown-parser.js +139 -0
- package/dist/tool/readme/markdown-parser.js.map +1 -0
- package/dist/tool/readme/readme-generator.d.ts +9 -0
- package/dist/tool/readme/readme-generator.d.ts.map +1 -0
- package/dist/tool/readme/readme-generator.js +422 -0
- package/dist/tool/readme/readme-generator.js.map +1 -0
- package/dist/tool/schema/custom-loader.d.ts +17 -0
- package/dist/tool/schema/custom-loader.d.ts.map +1 -0
- package/dist/tool/schema/custom-loader.js +149 -0
- package/dist/tool/schema/custom-loader.js.map +1 -0
- package/dist/tool/schema/overlay-loader.d.ts +47 -0
- package/dist/tool/schema/overlay-loader.d.ts.map +1 -0
- package/dist/tool/schema/overlay-loader.js +252 -0
- package/dist/tool/schema/overlay-loader.js.map +1 -0
- package/dist/tool/schema/types.d.ts +212 -0
- package/dist/tool/schema/types.d.ts.map +1 -0
- package/dist/tool/schema/types.js +5 -0
- package/dist/tool/schema/types.js.map +1 -0
- package/docs/README.md +308 -0
- package/docs/architecture.md +233 -0
- package/docs/creating-overlays.md +549 -0
- package/docs/custom-patches.md +540 -0
- package/docs/dependencies.md +279 -0
- package/docs/examples/custom-patches-example.md +85 -0
- package/docs/examples.md +576 -0
- package/docs/messaging-comparison.md +265 -0
- package/docs/messaging-quick-start.md +385 -0
- package/docs/observability-workflow.md +537 -0
- package/docs/overlay-manifest-refactoring.md +214 -0
- package/docs/overlay-metadata-archive.md +54 -0
- package/docs/overlays.md +523 -0
- package/docs/presets-architecture.md +498 -0
- package/docs/presets.md +366 -0
- package/docs/publishing.md +476 -0
- package/docs/quick-reference.md +326 -0
- package/docs/ux.md +170 -0
- package/features/README.md +85 -0
- package/features/cross-distro-packages/README.md +146 -0
- package/features/cross-distro-packages/devcontainer-feature.json +20 -0
- package/features/cross-distro-packages/install.sh +58 -0
- package/features/local-secrets-manager/devcontainer-feature.json +18 -0
- package/features/local-secrets-manager/install.sh +127 -0
- package/features/project-scaffolder/devcontainer-feature.json +24 -0
- package/features/project-scaffolder/install.sh +100 -0
- package/features/team-conventions/devcontainer-feature.json +24 -0
- package/features/team-conventions/install.sh +93 -0
- package/overlays/.registry/README.md +14 -0
- package/overlays/.registry/base-images.yml +26 -0
- package/overlays/.registry/base-templates.yml +7 -0
- package/overlays/README.md +155 -0
- package/overlays/alertmanager/.env.example +5 -0
- package/overlays/alertmanager/README.md +465 -0
- package/overlays/alertmanager/alert-rules.yml +56 -0
- package/overlays/alertmanager/alertmanager.yml +42 -0
- package/overlays/alertmanager/devcontainer.patch.json +12 -0
- package/overlays/alertmanager/docker-compose.yml +20 -0
- package/overlays/alertmanager/overlay.yml +17 -0
- package/overlays/alertmanager/setup.sh +53 -0
- package/overlays/alertmanager/verify.sh +31 -0
- package/overlays/aws-cli/README.md +473 -0
- package/overlays/aws-cli/devcontainer.patch.json +13 -0
- package/overlays/aws-cli/overlay.yml +13 -0
- package/overlays/azure-cli/README.md +551 -0
- package/overlays/azure-cli/devcontainer.patch.json +8 -0
- package/overlays/azure-cli/overlay.yml +13 -0
- package/overlays/bun/README.md +312 -0
- package/overlays/bun/devcontainer.patch.json +41 -0
- package/overlays/bun/overlay.yml +16 -0
- package/overlays/bun/setup.sh +79 -0
- package/overlays/bun/verify.sh +30 -0
- package/overlays/codex/README.md +128 -0
- package/overlays/codex/devcontainer.patch.json +3 -0
- package/overlays/codex/overlay.yml +14 -0
- package/overlays/codex/setup.sh +24 -0
- package/overlays/codex/verify.sh +30 -0
- package/overlays/commitlint/README.md +333 -0
- package/overlays/commitlint/devcontainer.patch.json +8 -0
- package/overlays/commitlint/overlay.yml +16 -0
- package/overlays/commitlint/setup.sh +234 -0
- package/overlays/direnv/README.md +504 -0
- package/overlays/direnv/devcontainer.patch.json +6 -0
- package/overlays/direnv/overlay.yml +13 -0
- package/overlays/direnv/setup.sh +139 -0
- package/overlays/docker-in-docker/README.md +534 -0
- package/overlays/docker-in-docker/devcontainer.patch.json +10 -0
- package/overlays/docker-in-docker/overlay.yml +13 -0
- package/overlays/docker-sock/README.md +256 -0
- package/overlays/docker-sock/devcontainer.patch.json +9 -0
- package/overlays/docker-sock/docker-compose.yml +8 -0
- package/overlays/docker-sock/overlay.yml +13 -0
- package/overlays/dotnet/README.md +147 -0
- package/overlays/dotnet/devcontainer.patch.json +51 -0
- package/overlays/dotnet/global-tools.txt +24 -0
- package/overlays/dotnet/overlay.yml +13 -0
- package/overlays/dotnet/setup.sh +51 -0
- package/overlays/dotnet/verify.sh +26 -0
- package/overlays/gcloud/README.md +269 -0
- package/overlays/gcloud/devcontainer.patch.json +14 -0
- package/overlays/gcloud/overlay.yml +14 -0
- package/overlays/gcloud/verify.sh +52 -0
- package/overlays/git-helpers/README.md +168 -0
- package/overlays/git-helpers/devcontainer.patch.json +33 -0
- package/overlays/git-helpers/overlay.yml +15 -0
- package/overlays/git-helpers/setup.sh +91 -0
- package/overlays/go/README.md +293 -0
- package/overlays/go/devcontainer.patch.json +43 -0
- package/overlays/go/overlay.yml +15 -0
- package/overlays/go/setup.sh +33 -0
- package/overlays/go/verify.sh +40 -0
- package/overlays/grafana/.env.example +9 -0
- package/overlays/grafana/README.md +462 -0
- package/overlays/grafana/dashboard-provider.yml +11 -0
- package/overlays/grafana/dashboards/observability-overview.json +263 -0
- package/overlays/grafana/devcontainer.patch.json +12 -0
- package/overlays/grafana/docker-compose.yml +27 -0
- package/overlays/grafana/grafana-datasources.yml +57 -0
- package/overlays/grafana/overlay.yml +21 -0
- package/overlays/grafana/verify.sh +34 -0
- package/overlays/jaeger/.env.example +7 -0
- package/overlays/jaeger/README.md +867 -0
- package/overlays/jaeger/devcontainer.patch.json +12 -0
- package/overlays/jaeger/docker-compose.yml +17 -0
- package/overlays/jaeger/overlay.yml +19 -0
- package/overlays/java/README.md +267 -0
- package/overlays/java/devcontainer.patch.json +44 -0
- package/overlays/java/overlay.yml +16 -0
- package/overlays/java/setup.sh +41 -0
- package/overlays/java/verify.sh +42 -0
- package/overlays/just/README.md +443 -0
- package/overlays/just/devcontainer.patch.json +3 -0
- package/overlays/just/overlay.yml +13 -0
- package/overlays/just/setup.sh +182 -0
- package/overlays/kubectl-helm/README.md +660 -0
- package/overlays/kubectl-helm/devcontainer.patch.json +10 -0
- package/overlays/kubectl-helm/overlay.yml +13 -0
- package/overlays/loki/.env.example +5 -0
- package/overlays/loki/README.md +1156 -0
- package/overlays/loki/devcontainer.patch.json +12 -0
- package/overlays/loki/docker-compose.yml +18 -0
- package/overlays/loki/loki-config.yaml +45 -0
- package/overlays/loki/overlay.yml +17 -0
- package/overlays/minio/.env.example +9 -0
- package/overlays/minio/README.md +639 -0
- package/overlays/minio/devcontainer.patch.json +30 -0
- package/overlays/minio/docker-compose.yml +28 -0
- package/overlays/minio/overlay.yml +18 -0
- package/overlays/minio/setup.sh +61 -0
- package/overlays/minio/verify.sh +64 -0
- package/overlays/mkdocs/README.md +309 -0
- package/overlays/mkdocs/devcontainer.patch.json +24 -0
- package/overlays/mkdocs/overlay.yml +15 -0
- package/overlays/modern-cli-tools/README.md +556 -0
- package/overlays/modern-cli-tools/devcontainer.patch.json +3 -0
- package/overlays/modern-cli-tools/overlay.yml +13 -0
- package/overlays/modern-cli-tools/setup.sh +153 -0
- package/overlays/mongodb/.env.example +9 -0
- package/overlays/mongodb/README.md +481 -0
- package/overlays/mongodb/devcontainer.patch.json +32 -0
- package/overlays/mongodb/docker-compose.yml +44 -0
- package/overlays/mongodb/overlay.yml +17 -0
- package/overlays/mongodb/verify.sh +48 -0
- package/overlays/mysql/.env.example +11 -0
- package/overlays/mysql/README.md +542 -0
- package/overlays/mysql/devcontainer.patch.json +34 -0
- package/overlays/mysql/docker-compose.yml +55 -0
- package/overlays/mysql/overlay.yml +16 -0
- package/overlays/mysql/verify.sh +48 -0
- package/overlays/nats/.env.example +5 -0
- package/overlays/nats/README.md +762 -0
- package/overlays/nats/devcontainer.patch.json +24 -0
- package/overlays/nats/docker-compose.yml +31 -0
- package/overlays/nats/overlay.yml +18 -0
- package/overlays/nats/verify.sh +50 -0
- package/overlays/ngrok/README.md +503 -0
- package/overlays/ngrok/devcontainer.patch.json +3 -0
- package/overlays/ngrok/overlay.yml +14 -0
- package/overlays/ngrok/setup.sh +125 -0
- package/overlays/nodejs/README.md +192 -0
- package/overlays/nodejs/devcontainer.patch.json +49 -0
- package/overlays/nodejs/global-packages.txt +16 -0
- package/overlays/nodejs/overlay.yml +14 -0
- package/overlays/nodejs/setup.sh +46 -0
- package/overlays/nodejs/verify.sh +32 -0
- package/overlays/otel-collector/.env.example +9 -0
- package/overlays/otel-collector/README.md +1257 -0
- package/overlays/otel-collector/devcontainer.patch.json +28 -0
- package/overlays/otel-collector/docker-compose.yml +22 -0
- package/overlays/otel-collector/otel-collector-config.yaml +68 -0
- package/overlays/otel-collector/overlay.yml +21 -0
- package/overlays/otel-collector/setup.sh +49 -0
- package/overlays/otel-demo-nodejs/.env.example +2 -0
- package/overlays/otel-demo-nodejs/Dockerfile-otel-demo-nodejs +17 -0
- package/overlays/otel-demo-nodejs/README.md +409 -0
- package/overlays/otel-demo-nodejs/devcontainer.patch.json +12 -0
- package/overlays/otel-demo-nodejs/docker-compose.yml +19 -0
- package/overlays/otel-demo-nodejs/overlay.yml +23 -0
- package/overlays/otel-demo-nodejs/package-otel-demo-nodejs.json +20 -0
- package/overlays/otel-demo-nodejs/server-otel-demo-nodejs.js +259 -0
- package/overlays/otel-demo-nodejs/tracing-otel-demo-nodejs.js +57 -0
- package/overlays/otel-demo-nodejs/verify.sh +31 -0
- package/overlays/otel-demo-python/.env.example +2 -0
- package/overlays/otel-demo-python/Dockerfile-otel-demo-python +16 -0
- package/overlays/otel-demo-python/README.md +82 -0
- package/overlays/otel-demo-python/app-otel-demo-python.py +208 -0
- package/overlays/otel-demo-python/devcontainer.patch.json +12 -0
- package/overlays/otel-demo-python/docker-compose.yml +19 -0
- package/overlays/otel-demo-python/overlay.yml +23 -0
- package/overlays/otel-demo-python/requirements-otel-demo-python.txt +4 -0
- package/overlays/otel-demo-python/verify.sh +31 -0
- package/overlays/playwright/README.md +629 -0
- package/overlays/playwright/devcontainer.patch.json +9 -0
- package/overlays/playwright/overlay.yml +13 -0
- package/overlays/postgres/.env.example +6 -0
- package/overlays/postgres/README.md +602 -0
- package/overlays/postgres/devcontainer.patch.json +21 -0
- package/overlays/postgres/docker-compose.yml +22 -0
- package/overlays/postgres/overlay.yml +15 -0
- package/overlays/postgres/verify.sh +45 -0
- package/overlays/powershell/README.md +314 -0
- package/overlays/powershell/devcontainer.patch.json +22 -0
- package/overlays/powershell/overlay.yml +13 -0
- package/overlays/powershell/setup.sh +29 -0
- package/overlays/powershell/verify.sh +38 -0
- package/overlays/pre-commit/README.md +263 -0
- package/overlays/pre-commit/devcontainer.patch.json +9 -0
- package/overlays/pre-commit/overlay.yml +16 -0
- package/overlays/pre-commit/setup.sh +129 -0
- package/overlays/presets/docs-site.yml +118 -0
- package/overlays/presets/fullstack.yml +181 -0
- package/overlays/presets/microservice.yml +118 -0
- package/overlays/presets/web-api.yml +109 -0
- package/overlays/prometheus/.env.example +5 -0
- package/overlays/prometheus/README.md +1246 -0
- package/overlays/prometheus/devcontainer.patch.json +12 -0
- package/overlays/prometheus/docker-compose.yml +22 -0
- package/overlays/prometheus/overlay.yml +17 -0
- package/overlays/prometheus/prometheus.yml +12 -0
- package/overlays/prometheus/verify.sh +34 -0
- package/overlays/promtail/.env.example +2 -0
- package/overlays/promtail/README.md +357 -0
- package/overlays/promtail/devcontainer.patch.json +5 -0
- package/overlays/promtail/docker-compose.yml +16 -0
- package/overlays/promtail/overlay.yml +17 -0
- package/overlays/promtail/promtail-config.yaml +60 -0
- package/overlays/promtail/verify.sh +31 -0
- package/overlays/pulumi/README.md +472 -0
- package/overlays/pulumi/devcontainer.patch.json +13 -0
- package/overlays/pulumi/overlay.yml +14 -0
- package/overlays/pulumi/verify.sh +31 -0
- package/overlays/python/README.md +919 -0
- package/overlays/python/devcontainer.patch.json +41 -0
- package/overlays/python/overlay.yml +12 -0
- package/overlays/python/requirements-overlay.txt +13 -0
- package/overlays/python/setup.sh +47 -0
- package/overlays/python/verify.sh +32 -0
- package/overlays/rabbitmq/.env.example +7 -0
- package/overlays/rabbitmq/README.md +680 -0
- package/overlays/rabbitmq/devcontainer.patch.json +28 -0
- package/overlays/rabbitmq/docker-compose.yml +30 -0
- package/overlays/rabbitmq/overlay.yml +18 -0
- package/overlays/rabbitmq/verify.sh +41 -0
- package/overlays/redis/.env.example +4 -0
- package/overlays/redis/README.md +776 -0
- package/overlays/redis/devcontainer.patch.json +21 -0
- package/overlays/redis/docker-compose.yml +21 -0
- package/overlays/redis/overlay.yml +15 -0
- package/overlays/redis/verify.sh +41 -0
- package/overlays/redpanda/.env.example +10 -0
- package/overlays/redpanda/README.md +703 -0
- package/overlays/redpanda/devcontainer.patch.json +37 -0
- package/overlays/redpanda/docker-compose.yml +67 -0
- package/overlays/redpanda/overlay.yml +21 -0
- package/overlays/redpanda/verify.sh +48 -0
- package/overlays/rust/README.md +299 -0
- package/overlays/rust/devcontainer.patch.json +39 -0
- package/overlays/rust/overlay.yml +15 -0
- package/overlays/rust/setup.sh +36 -0
- package/overlays/rust/verify.sh +51 -0
- package/overlays/sqlite/README.md +584 -0
- package/overlays/sqlite/devcontainer.patch.json +14 -0
- package/overlays/sqlite/overlay.yml +15 -0
- package/overlays/sqlite/setup.sh +27 -0
- package/overlays/sqlite/verify.sh +43 -0
- package/overlays/sqlserver/.env.example +6 -0
- package/overlays/sqlserver/README.md +592 -0
- package/overlays/sqlserver/devcontainer.patch.json +22 -0
- package/overlays/sqlserver/docker-compose.yml +32 -0
- package/overlays/sqlserver/overlay.yml +17 -0
- package/overlays/sqlserver/verify.sh +30 -0
- package/overlays/tempo/.env.example +5 -0
- package/overlays/tempo/README.md +273 -0
- package/overlays/tempo/devcontainer.patch.json +12 -0
- package/overlays/tempo/docker-compose.yml +20 -0
- package/overlays/tempo/overlay.yml +20 -0
- package/overlays/tempo/tempo-config.yaml +32 -0
- package/overlays/tempo/verify.sh +31 -0
- package/overlays/terraform/README.md +389 -0
- package/overlays/terraform/devcontainer.patch.json +15 -0
- package/overlays/terraform/overlay.yml +14 -0
- package/overlays/terraform/verify.sh +63 -0
- package/package.json +74 -0
- package/templates/README.md +285 -0
- package/templates/compose/.devcontainer/devcontainer.json +46 -0
- package/templates/compose/.devcontainer/docker-compose.yml +12 -0
- package/templates/compose/README.md +20 -0
- package/templates/plain/.devcontainer/devcontainer.json +35 -0
- package/templates/plain/README.md +21 -0
- package/tool/README.md +281 -0
- package/tool/schema/base-images.schema.json +43 -0
- package/tool/schema/base-templates.schema.json +34 -0
- package/tool/schema/config.schema.json +71 -0
- package/tool/schema/overlay-manifest.schema.json +86 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
const http = require('http');
|
|
2
|
+
const pino = require('pino');
|
|
3
|
+
const { trace, metrics, context } = require('@opentelemetry/api');
|
|
4
|
+
|
|
5
|
+
// Create JSON logger
|
|
6
|
+
const logger = pino({
|
|
7
|
+
level: 'info',
|
|
8
|
+
formatters: {
|
|
9
|
+
level: (label) => {
|
|
10
|
+
return { level: label };
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Get tracer and meter
|
|
16
|
+
const tracer = trace.getTracer('otel-demo-nodejs', '1.0.0');
|
|
17
|
+
const meter = metrics.getMeter('otel-demo-nodejs', '1.0.0');
|
|
18
|
+
|
|
19
|
+
// Create custom metrics
|
|
20
|
+
const requestCounter = meter.createCounter('http_requests_total', {
|
|
21
|
+
description: 'Total number of HTTP requests',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const requestDuration = meter.createHistogram('http_request_duration_seconds', {
|
|
25
|
+
description: 'HTTP request duration in seconds',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const activeRequests = meter.createUpDownCounter('http_requests_active', {
|
|
29
|
+
description: 'Number of active HTTP requests',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Request handler
|
|
33
|
+
function handleRequest(req, res) {
|
|
34
|
+
const startTime = Date.now();
|
|
35
|
+
|
|
36
|
+
// Increment active requests
|
|
37
|
+
activeRequests.add(1, { method: req.method, route: req.url });
|
|
38
|
+
|
|
39
|
+
// Create span for request
|
|
40
|
+
const span = tracer.startSpan(`${req.method} ${req.url}`, {
|
|
41
|
+
kind: 1, // SERVER
|
|
42
|
+
attributes: {
|
|
43
|
+
'http.method': req.method,
|
|
44
|
+
'http.url': req.url,
|
|
45
|
+
'http.target': req.url,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
context.with(trace.setSpan(context.active(), span), () => {
|
|
50
|
+
// Log request
|
|
51
|
+
logger.info({
|
|
52
|
+
msg: 'HTTP request received',
|
|
53
|
+
method: req.method,
|
|
54
|
+
url: req.url,
|
|
55
|
+
'trace.id': span.spanContext().traceId,
|
|
56
|
+
'span.id': span.spanContext().spanId,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Route handling
|
|
60
|
+
if (req.url === '/') {
|
|
61
|
+
handleHome(req, res, span);
|
|
62
|
+
} else if (req.url === '/api/data') {
|
|
63
|
+
handleData(req, res, span);
|
|
64
|
+
} else if (req.url === '/api/slow') {
|
|
65
|
+
handleSlow(req, res, span);
|
|
66
|
+
} else if (req.url === '/api/error') {
|
|
67
|
+
handleError(req, res, span);
|
|
68
|
+
} else if (req.url === '/health') {
|
|
69
|
+
handleHealth(req, res, span);
|
|
70
|
+
} else {
|
|
71
|
+
handleNotFound(req, res, span);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Record metrics
|
|
75
|
+
const duration = (Date.now() - startTime) / 1000;
|
|
76
|
+
requestCounter.add(1, {
|
|
77
|
+
method: req.method,
|
|
78
|
+
route: req.url,
|
|
79
|
+
status: res.statusCode,
|
|
80
|
+
});
|
|
81
|
+
requestDuration.record(duration, {
|
|
82
|
+
method: req.method,
|
|
83
|
+
route: req.url,
|
|
84
|
+
status: res.statusCode,
|
|
85
|
+
});
|
|
86
|
+
activeRequests.add(-1, { method: req.method, route: req.url });
|
|
87
|
+
|
|
88
|
+
// Set span status and end
|
|
89
|
+
span.setAttribute('http.status_code', res.statusCode);
|
|
90
|
+
if (res.statusCode >= 400) {
|
|
91
|
+
span.setStatus({ code: 2, message: 'Error' }); // ERROR
|
|
92
|
+
} else {
|
|
93
|
+
span.setStatus({ code: 1 }); // OK
|
|
94
|
+
}
|
|
95
|
+
span.end();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function handleHome(req, res, span) {
|
|
100
|
+
span.addEvent('Rendering home page');
|
|
101
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
102
|
+
res.end(`
|
|
103
|
+
<!DOCTYPE html>
|
|
104
|
+
<html>
|
|
105
|
+
<head>
|
|
106
|
+
<title>OpenTelemetry Demo - Node.js</title>
|
|
107
|
+
<style>
|
|
108
|
+
body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
|
|
109
|
+
h1 { color: #333; }
|
|
110
|
+
.endpoint { background: #f4f4f4; padding: 10px; margin: 10px 0; border-left: 4px solid #007acc; }
|
|
111
|
+
.endpoint code { background: #e0e0e0; padding: 2px 6px; border-radius: 3px; }
|
|
112
|
+
.success { color: #28a745; }
|
|
113
|
+
.warning { color: #ffc107; }
|
|
114
|
+
.error { color: #dc3545; }
|
|
115
|
+
</style>
|
|
116
|
+
</head>
|
|
117
|
+
<body>
|
|
118
|
+
<h1>🔭 OpenTelemetry Demo - Node.js</h1>
|
|
119
|
+
<p>This is a sample application instrumented with OpenTelemetry.</p>
|
|
120
|
+
|
|
121
|
+
<h2>Available Endpoints:</h2>
|
|
122
|
+
|
|
123
|
+
<div class="endpoint">
|
|
124
|
+
<strong class="success">GET /</strong><br>
|
|
125
|
+
This page - application home
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div class="endpoint">
|
|
129
|
+
<strong class="success">GET /api/data</strong><br>
|
|
130
|
+
Returns sample JSON data with nested spans
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<div class="endpoint">
|
|
134
|
+
<strong class="warning">GET /api/slow</strong><br>
|
|
135
|
+
Simulates slow request (2 second delay)
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<div class="endpoint">
|
|
139
|
+
<strong class="error">GET /api/error</strong><br>
|
|
140
|
+
Simulates an error (500 status)
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div class="endpoint">
|
|
144
|
+
<strong class="success">GET /health</strong><br>
|
|
145
|
+
Health check endpoint
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<h2>Observability Data:</h2>
|
|
149
|
+
<ul>
|
|
150
|
+
<li><strong>Traces:</strong> Sent to OTLP endpoint (view in Jaeger/Tempo via Grafana)</li>
|
|
151
|
+
<li><strong>Metrics:</strong> Exported every 5 seconds (view in Prometheus/Grafana)</li>
|
|
152
|
+
<li><strong>Logs:</strong> JSON formatted to stdout (view in Loki via Grafana)</li>
|
|
153
|
+
</ul>
|
|
154
|
+
|
|
155
|
+
<h2>Try It:</h2>
|
|
156
|
+
<ul>
|
|
157
|
+
<li><a href="/api/data">Call /api/data</a></li>
|
|
158
|
+
<li><a href="/api/slow">Call /api/slow (slow)</a></li>
|
|
159
|
+
<li><a href="/api/error">Call /api/error (error)</a></li>
|
|
160
|
+
<li><a href="/health">Call /health</a></li>
|
|
161
|
+
</ul>
|
|
162
|
+
</body>
|
|
163
|
+
</html>
|
|
164
|
+
`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function handleData(req, res, span) {
|
|
168
|
+
// Create child span for data processing
|
|
169
|
+
const childSpan = tracer.startSpan('process_data', {
|
|
170
|
+
parent: span,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
childSpan.addEvent('Fetching data');
|
|
174
|
+
|
|
175
|
+
// Simulate data processing
|
|
176
|
+
const data = {
|
|
177
|
+
timestamp: new Date().toISOString(),
|
|
178
|
+
items: [
|
|
179
|
+
{ id: 1, name: 'Item 1', value: Math.random() * 100 },
|
|
180
|
+
{ id: 2, name: 'Item 2', value: Math.random() * 100 },
|
|
181
|
+
{ id: 3, name: 'Item 3', value: Math.random() * 100 },
|
|
182
|
+
],
|
|
183
|
+
trace: {
|
|
184
|
+
traceId: span.spanContext().traceId,
|
|
185
|
+
spanId: span.spanContext().spanId,
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
childSpan.setAttribute('data.items', data.items.length);
|
|
190
|
+
childSpan.end();
|
|
191
|
+
|
|
192
|
+
logger.info({ msg: 'Data fetched successfully', itemCount: data.items.length });
|
|
193
|
+
|
|
194
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
195
|
+
res.end(JSON.stringify(data, null, 2));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function handleSlow(req, res, span) {
|
|
199
|
+
span.addEvent('Simulating slow operation');
|
|
200
|
+
logger.warn({ msg: 'Slow endpoint called', expectedDelay: '2s' });
|
|
201
|
+
|
|
202
|
+
// Simulate slow operation
|
|
203
|
+
setTimeout(() => {
|
|
204
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
205
|
+
res.end(
|
|
206
|
+
JSON.stringify({
|
|
207
|
+
message: 'This was a slow request',
|
|
208
|
+
duration: '2 seconds',
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
}, 2000);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function handleError(req, res, span) {
|
|
215
|
+
span.addEvent('Error occurred');
|
|
216
|
+
logger.error({ msg: 'Error endpoint called', error: 'Simulated error' });
|
|
217
|
+
|
|
218
|
+
span.recordException(new Error('Simulated error'));
|
|
219
|
+
|
|
220
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
221
|
+
res.end(
|
|
222
|
+
JSON.stringify({
|
|
223
|
+
error: 'Internal Server Error',
|
|
224
|
+
message: 'This is a simulated error',
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function handleHealth(req, res, span) {
|
|
230
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
231
|
+
res.end(
|
|
232
|
+
JSON.stringify({
|
|
233
|
+
status: 'healthy',
|
|
234
|
+
timestamp: new Date().toISOString(),
|
|
235
|
+
})
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function handleNotFound(req, res, span) {
|
|
240
|
+
logger.warn({ msg: 'Not found', url: req.url });
|
|
241
|
+
|
|
242
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
243
|
+
res.end(
|
|
244
|
+
JSON.stringify({
|
|
245
|
+
error: 'Not Found',
|
|
246
|
+
path: req.url,
|
|
247
|
+
})
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Create HTTP server
|
|
252
|
+
const server = http.createServer(handleRequest);
|
|
253
|
+
|
|
254
|
+
const PORT = process.env.PORT || 8080;
|
|
255
|
+
|
|
256
|
+
server.listen(PORT, () => {
|
|
257
|
+
logger.info({ msg: 'Server started', port: PORT });
|
|
258
|
+
console.log(`🚀 Server running on http://localhost:${PORT}`);
|
|
259
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { NodeSDK } = require('@opentelemetry/sdk-node');
|
|
2
|
+
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
|
|
3
|
+
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
|
|
4
|
+
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc');
|
|
5
|
+
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
|
|
6
|
+
const { Resource } = require('@opentelemetry/resources');
|
|
7
|
+
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
|
|
8
|
+
|
|
9
|
+
// Configure resource with service information
|
|
10
|
+
const resource = Resource.default().merge(
|
|
11
|
+
new Resource({
|
|
12
|
+
[SemanticResourceAttributes.SERVICE_NAME]:
|
|
13
|
+
process.env.OTEL_SERVICE_NAME || 'otel-demo-nodejs',
|
|
14
|
+
[SemanticResourceAttributes.SERVICE_VERSION]: process.env.OTEL_SERVICE_VERSION || '1.0.0',
|
|
15
|
+
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]:
|
|
16
|
+
process.env.OTEL_DEPLOYMENT_ENVIRONMENT || 'dev',
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Configure trace exporter
|
|
21
|
+
const traceExporter = new OTLPTraceExporter({
|
|
22
|
+
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://otel-collector:4317',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Configure metrics exporter
|
|
26
|
+
const metricReader = new PeriodicExportingMetricReader({
|
|
27
|
+
exporter: new OTLPMetricExporter({
|
|
28
|
+
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://otel-collector:4317',
|
|
29
|
+
}),
|
|
30
|
+
exportIntervalMillis: 5000, // Export every 5 seconds
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Initialize OpenTelemetry SDK
|
|
34
|
+
const sdk = new NodeSDK({
|
|
35
|
+
resource: resource,
|
|
36
|
+
traceExporter: traceExporter,
|
|
37
|
+
metricReader: metricReader,
|
|
38
|
+
instrumentations: [
|
|
39
|
+
getNodeAutoInstrumentations({
|
|
40
|
+
'@opentelemetry/instrumentation-fs': {
|
|
41
|
+
enabled: false, // Disable filesystem instrumentation (noisy)
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Start the SDK
|
|
48
|
+
sdk.start();
|
|
49
|
+
console.log('OpenTelemetry instrumentation initialized');
|
|
50
|
+
|
|
51
|
+
// Gracefully shutdown on exit
|
|
52
|
+
process.on('SIGTERM', () => {
|
|
53
|
+
sdk.shutdown()
|
|
54
|
+
.then(() => console.log('OpenTelemetry SDK shut down successfully'))
|
|
55
|
+
.catch((error) => console.log('Error shutting down OpenTelemetry SDK', error))
|
|
56
|
+
.finally(() => process.exit(0));
|
|
57
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# OTel Demo Node.js verification script
|
|
3
|
+
|
|
4
|
+
echo "🔍 Verifying OTel Demo (Node.js) installation..."
|
|
5
|
+
|
|
6
|
+
# Track overall success
|
|
7
|
+
ALL_CHECKS_PASSED=true
|
|
8
|
+
|
|
9
|
+
# Check if service is running
|
|
10
|
+
if docker ps --format '{{.Names}}' | grep -q otel-demo-nodejs; then
|
|
11
|
+
echo "✓ OTel Demo (Node.js) service is running"
|
|
12
|
+
else
|
|
13
|
+
echo "✗ OTel Demo (Node.js) service is not running"
|
|
14
|
+
ALL_CHECKS_PASSED=false
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Check if HTTP endpoint is accessible
|
|
18
|
+
if curl -s -o /dev/null -w "%{http_code}" http://otel-demo-nodejs:8080/health 2>/dev/null | grep -q "200"; then
|
|
19
|
+
echo "✓ Demo app HTTP endpoint is accessible"
|
|
20
|
+
else
|
|
21
|
+
echo "⚠️ Demo app HTTP endpoint not responding yet (may still be starting)"
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Final result
|
|
25
|
+
if [ "$ALL_CHECKS_PASSED" = true ]; then
|
|
26
|
+
echo "✓ All critical checks passed"
|
|
27
|
+
exit 0
|
|
28
|
+
else
|
|
29
|
+
echo "✗ Some checks failed"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Copy requirements and app files
|
|
6
|
+
COPY ./requirements-otel-demo-python.txt requirements.txt
|
|
7
|
+
COPY ./app-otel-demo-python.py app.py
|
|
8
|
+
|
|
9
|
+
# Install dependencies
|
|
10
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
11
|
+
|
|
12
|
+
# Expose port
|
|
13
|
+
EXPOSE 8081
|
|
14
|
+
|
|
15
|
+
# Run with auto-instrumentation
|
|
16
|
+
CMD ["opentelemetry-instrument", "python", "app.py"]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# OpenTelemetry Demo - Python
|
|
2
|
+
|
|
3
|
+
Sample Python Flask application fully instrumented with OpenTelemetry to demonstrate distributed tracing, metrics, and logging.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Auto-instrumentation** - Automatic Flask request tracing
|
|
8
|
+
- **Custom metrics** - Request counters and duration histograms
|
|
9
|
+
- **Structured logs** - JSON formatted logs with trace context
|
|
10
|
+
- **Multiple endpoints** - Various scenarios (normal, slow, error)
|
|
11
|
+
- **OTLP export** - Sends traces and metrics to OpenTelemetry Collector
|
|
12
|
+
- **Zero-config** - Works out-of-box with the observability stack
|
|
13
|
+
|
|
14
|
+
## How It Works
|
|
15
|
+
|
|
16
|
+
This Flask application demonstrates the three pillars of observability using OpenTelemetry's Python SDK with auto-instrumentation.
|
|
17
|
+
|
|
18
|
+
**Key Components:**
|
|
19
|
+
|
|
20
|
+
- `opentelemetry-instrument` - Auto-instruments Flask
|
|
21
|
+
- Flask application - HTTP server with sample endpoints
|
|
22
|
+
- JSON logging - Structured logs with trace correlation
|
|
23
|
+
- Custom spans and metrics - Business logic instrumentation
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
### Ports
|
|
28
|
+
|
|
29
|
+
- `8081` - HTTP API (web interface and endpoints)
|
|
30
|
+
|
|
31
|
+
### Environment Variables
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# OTLP endpoint (OpenTelemetry Collector)
|
|
35
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
|
|
36
|
+
|
|
37
|
+
# Service identification
|
|
38
|
+
OTEL_SERVICE_NAME=otel-demo-python
|
|
39
|
+
OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=dev
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Dependencies
|
|
43
|
+
|
|
44
|
+
Requires:
|
|
45
|
+
|
|
46
|
+
- **otel-collector** - Required to receive and route telemetry
|
|
47
|
+
- **jaeger** or **tempo** - Recommended for trace visualization
|
|
48
|
+
- **prometheus** - Recommended for metrics visualization
|
|
49
|
+
- **loki** - Recommended for log aggregation
|
|
50
|
+
- **grafana** - Recommended for unified visualization
|
|
51
|
+
|
|
52
|
+
## Available Endpoints
|
|
53
|
+
|
|
54
|
+
Same as Node.js demo:
|
|
55
|
+
|
|
56
|
+
- `GET /` - Home page with documentation
|
|
57
|
+
- `GET /api/data` - Returns JSON data with trace context
|
|
58
|
+
- `GET /api/slow` - Simulates 2-second delay
|
|
59
|
+
- `GET /api/error` - Returns 500 error
|
|
60
|
+
- `GET /health` - Health check
|
|
61
|
+
|
|
62
|
+
## Use Cases
|
|
63
|
+
|
|
64
|
+
- **Learning OpenTelemetry** - Python/Flask implementation
|
|
65
|
+
- **Testing observability stack** - Verify traces, metrics, and logs work
|
|
66
|
+
- **Multi-language tracing** - See trace correlation across services
|
|
67
|
+
- **Dashboard development** - Real data for creating Grafana dashboards
|
|
68
|
+
|
|
69
|
+
## References
|
|
70
|
+
|
|
71
|
+
- [OpenTelemetry Python](https://opentelemetry.io/docs/instrumentation/python/)
|
|
72
|
+
- [Flask Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/flask/flask.html)
|
|
73
|
+
- [Auto-instrumentation](https://opentelemetry.io/docs/instrumentation/python/automatic/)
|
|
74
|
+
|
|
75
|
+
**Related Overlays:**
|
|
76
|
+
|
|
77
|
+
- `otel-collector` - Required for receiving telemetry
|
|
78
|
+
- `otel-demo-nodejs` - Node.js demo app for multi-language testing
|
|
79
|
+
- `jaeger` or `tempo` - Trace visualization
|
|
80
|
+
- `prometheus` - Metrics storage
|
|
81
|
+
- `loki` - Log aggregation
|
|
82
|
+
- `grafana` - Unified observability UI
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import random
|
|
3
|
+
import logging
|
|
4
|
+
import json
|
|
5
|
+
from flask import Flask, jsonify, request
|
|
6
|
+
from opentelemetry import trace, metrics
|
|
7
|
+
from opentelemetry.trace import Status, StatusCode
|
|
8
|
+
|
|
9
|
+
# Configure JSON logging
|
|
10
|
+
class JSONFormatter(logging.Formatter):
|
|
11
|
+
def format(self, record):
|
|
12
|
+
log_data = {
|
|
13
|
+
'timestamp': self.formatTime(record),
|
|
14
|
+
'level': record.levelname.lower(),
|
|
15
|
+
'message': record.getMessage(),
|
|
16
|
+
'logger': record.name,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Add trace context if available
|
|
20
|
+
span = trace.get_current_span()
|
|
21
|
+
if span and span.get_span_context().is_valid:
|
|
22
|
+
ctx = span.get_span_context()
|
|
23
|
+
log_data['trace.id'] = format(ctx.trace_id, '032x')
|
|
24
|
+
log_data['span.id'] = format(ctx.span_id, '016x')
|
|
25
|
+
|
|
26
|
+
return json.dumps(log_data)
|
|
27
|
+
|
|
28
|
+
# Setup logging
|
|
29
|
+
handler = logging.StreamHandler()
|
|
30
|
+
handler.setFormatter(JSONFormatter())
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
logger.addHandler(handler)
|
|
33
|
+
logger.setLevel(logging.INFO)
|
|
34
|
+
|
|
35
|
+
# Create Flask app
|
|
36
|
+
app = Flask(__name__)
|
|
37
|
+
|
|
38
|
+
# Get tracer and meter
|
|
39
|
+
tracer = trace.get_tracer(__name__)
|
|
40
|
+
meter = metrics.get_meter(__name__)
|
|
41
|
+
|
|
42
|
+
# Create custom metrics
|
|
43
|
+
request_counter = meter.create_counter(
|
|
44
|
+
name="http_requests_total",
|
|
45
|
+
description="Total number of HTTP requests",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
request_duration = meter.create_histogram(
|
|
49
|
+
name="http_request_duration_seconds",
|
|
50
|
+
description="HTTP request duration in seconds",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@app.route('/')
|
|
54
|
+
def home():
|
|
55
|
+
"""Home page with documentation"""
|
|
56
|
+
span = trace.get_current_span()
|
|
57
|
+
span.add_event("Rendering home page")
|
|
58
|
+
|
|
59
|
+
logger.info("Home page requested")
|
|
60
|
+
|
|
61
|
+
return """
|
|
62
|
+
<!DOCTYPE html>
|
|
63
|
+
<html>
|
|
64
|
+
<head>
|
|
65
|
+
<title>OpenTelemetry Demo - Python</title>
|
|
66
|
+
<style>
|
|
67
|
+
body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
|
|
68
|
+
h1 { color: #333; }
|
|
69
|
+
.endpoint { background: #f4f4f4; padding: 10px; margin: 10px 0; border-left: 4px solid #007acc; }
|
|
70
|
+
.success { color: #28a745; }
|
|
71
|
+
.warning { color: #ffc107; }
|
|
72
|
+
.error { color: #dc3545; }
|
|
73
|
+
</style>
|
|
74
|
+
</head>
|
|
75
|
+
<body>
|
|
76
|
+
<h1>🐍 OpenTelemetry Demo - Python</h1>
|
|
77
|
+
<p>Flask application instrumented with OpenTelemetry.</p>
|
|
78
|
+
|
|
79
|
+
<h2>Available Endpoints:</h2>
|
|
80
|
+
|
|
81
|
+
<div class="endpoint">
|
|
82
|
+
<strong class="success">GET /</strong><br>
|
|
83
|
+
This page - application home
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="endpoint">
|
|
87
|
+
<strong class="success">GET /api/data</strong><br>
|
|
88
|
+
Returns sample JSON data with nested spans
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div class="endpoint">
|
|
92
|
+
<strong class="warning">GET /api/slow</strong><br>
|
|
93
|
+
Simulates slow request (2 second delay)
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="endpoint">
|
|
97
|
+
<strong class="error">GET /api/error</strong><br>
|
|
98
|
+
Simulates an error (500 status)
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<div class="endpoint">
|
|
102
|
+
<strong class="success">GET /health</strong><br>
|
|
103
|
+
Health check endpoint
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<h2>Try It:</h2>
|
|
107
|
+
<ul>
|
|
108
|
+
<li><a href="/api/data">Call /api/data</a></li>
|
|
109
|
+
<li><a href="/api/slow">Call /api/slow (slow)</a></li>
|
|
110
|
+
<li><a href="/api/error">Call /api/error (error)</a></li>
|
|
111
|
+
<li><a href="/health">Call /health</a></li>
|
|
112
|
+
</ul>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
@app.route('/api/data')
|
|
118
|
+
def get_data():
|
|
119
|
+
"""Returns sample data with child span"""
|
|
120
|
+
with tracer.start_as_current_span("process_data") as span:
|
|
121
|
+
span.add_event("Fetching data")
|
|
122
|
+
|
|
123
|
+
# Simulate data processing
|
|
124
|
+
data = {
|
|
125
|
+
'timestamp': time.time(),
|
|
126
|
+
'items': [
|
|
127
|
+
{'id': i, 'name': f'Item {i}', 'value': random.random() * 100}
|
|
128
|
+
for i in range(1, 4)
|
|
129
|
+
],
|
|
130
|
+
'trace': {
|
|
131
|
+
'traceId': format(span.get_span_context().trace_id, '032x'),
|
|
132
|
+
'spanId': format(span.get_span_context().span_id, '016x'),
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
span.set_attribute("data.items", len(data['items']))
|
|
137
|
+
logger.info(f"Data fetched successfully, itemCount={len(data['items'])}")
|
|
138
|
+
|
|
139
|
+
return jsonify(data)
|
|
140
|
+
|
|
141
|
+
@app.route('/api/slow')
|
|
142
|
+
def slow_endpoint():
|
|
143
|
+
"""Simulates slow request"""
|
|
144
|
+
span = trace.get_current_span()
|
|
145
|
+
span.add_event("Simulating slow operation")
|
|
146
|
+
logger.warning("Slow endpoint called, expectedDelay=2s")
|
|
147
|
+
|
|
148
|
+
time.sleep(2)
|
|
149
|
+
|
|
150
|
+
return jsonify({
|
|
151
|
+
'message': 'This was a slow request',
|
|
152
|
+
'duration': '2 seconds'
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
@app.route('/api/error')
|
|
156
|
+
def error_endpoint():
|
|
157
|
+
"""Simulates error"""
|
|
158
|
+
span = trace.get_current_span()
|
|
159
|
+
span.add_event("Error occurred")
|
|
160
|
+
logger.error("Error endpoint called")
|
|
161
|
+
|
|
162
|
+
# Record exception in span
|
|
163
|
+
try:
|
|
164
|
+
raise ValueError("Simulated error")
|
|
165
|
+
except ValueError as e:
|
|
166
|
+
span.record_exception(e)
|
|
167
|
+
span.set_status(Status(StatusCode.ERROR, "Simulated error"))
|
|
168
|
+
|
|
169
|
+
return jsonify({
|
|
170
|
+
'error': 'Internal Server Error',
|
|
171
|
+
'message': 'This is a simulated error'
|
|
172
|
+
}), 500
|
|
173
|
+
|
|
174
|
+
@app.route('/health')
|
|
175
|
+
def health():
|
|
176
|
+
"""Health check"""
|
|
177
|
+
return jsonify({
|
|
178
|
+
'status': 'healthy',
|
|
179
|
+
'timestamp': time.time()
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
# Middleware to record metrics
|
|
183
|
+
@app.before_request
|
|
184
|
+
def before_request():
|
|
185
|
+
request.start_time = time.time()
|
|
186
|
+
|
|
187
|
+
@app.after_request
|
|
188
|
+
def after_request(response):
|
|
189
|
+
duration = time.time() - request.start_time
|
|
190
|
+
|
|
191
|
+
# Record metrics
|
|
192
|
+
request_counter.add(1, {
|
|
193
|
+
'method': request.method,
|
|
194
|
+
'route': request.path,
|
|
195
|
+
'status': response.status_code,
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
request_duration.record(duration, {
|
|
199
|
+
'method': request.method,
|
|
200
|
+
'route': request.path,
|
|
201
|
+
'status': response.status_code,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
return response
|
|
205
|
+
|
|
206
|
+
if __name__ == '__main__':
|
|
207
|
+
logger.info("Server starting on port 8081")
|
|
208
|
+
app.run(host='0.0.0.0', port=8081, debug=False)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.base.schema.json",
|
|
3
|
+
"runServices": ["otel-demo-python"],
|
|
4
|
+
"_serviceOrder": 3,
|
|
5
|
+
"forwardPorts": [8081],
|
|
6
|
+
"portsAttributes": {
|
|
7
|
+
"8081": {
|
|
8
|
+
"label": "OTel Demo (Python)",
|
|
9
|
+
"onAutoForward": "openBrowser"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
services:
|
|
3
|
+
otel-demo-python:
|
|
4
|
+
build:
|
|
5
|
+
context: .
|
|
6
|
+
dockerfile: ./Dockerfile-otel-demo-python
|
|
7
|
+
environment:
|
|
8
|
+
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
|
|
9
|
+
- OTEL_SERVICE_NAME=otel-demo-python
|
|
10
|
+
- OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=dev
|
|
11
|
+
ports:
|
|
12
|
+
- '${OTEL_DEMO_PYTHON_PORT:-8081}:8081'
|
|
13
|
+
depends_on:
|
|
14
|
+
- otel-collector
|
|
15
|
+
networks:
|
|
16
|
+
- devnet
|
|
17
|
+
|
|
18
|
+
networks:
|
|
19
|
+
devnet:
|