@kabran-tecnologia/kabran-config 1.7.0 → 1.8.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/package.json
CHANGED
|
@@ -27,6 +27,8 @@ NC='\033[0m'
|
|
|
27
27
|
declare -a ERRORS=()
|
|
28
28
|
declare -a STEP_RESULTS=()
|
|
29
29
|
CI_START_TIME=""
|
|
30
|
+
CI_TRACE_ID=""
|
|
31
|
+
CI_SPAN_ID=""
|
|
30
32
|
|
|
31
33
|
# ==============================================================================
|
|
32
34
|
# Logging Functions
|
|
@@ -58,6 +60,128 @@ log_debug() {
|
|
|
58
60
|
fi
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
# ==============================================================================
|
|
64
|
+
# Trace Context Functions (OpenTelemetry W3C Trace Context)
|
|
65
|
+
# ==============================================================================
|
|
66
|
+
|
|
67
|
+
# Generate a random hex string of specified length
|
|
68
|
+
# Usage: generate_hex_string 32
|
|
69
|
+
generate_hex_string() {
|
|
70
|
+
local length="${1:-32}"
|
|
71
|
+
# Try multiple methods for generating random hex
|
|
72
|
+
if command -v openssl &>/dev/null; then
|
|
73
|
+
openssl rand -hex "$((length / 2))" 2>/dev/null
|
|
74
|
+
elif [ -r /dev/urandom ]; then
|
|
75
|
+
head -c "$((length / 2))" /dev/urandom | od -An -tx1 | tr -d ' \n' | head -c "$length"
|
|
76
|
+
else
|
|
77
|
+
# Fallback: use date + process ID + random
|
|
78
|
+
local seed="$$$(date +%s%N 2>/dev/null || date +%s)"
|
|
79
|
+
echo "$seed" | md5sum 2>/dev/null | head -c "$length" || echo "$seed" | head -c "$length"
|
|
80
|
+
fi
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Generate a W3C trace ID (32 hex chars = 128 bits)
|
|
84
|
+
# Usage: generate_trace_id
|
|
85
|
+
generate_trace_id() {
|
|
86
|
+
generate_hex_string 32
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Generate a W3C span ID (16 hex chars = 64 bits)
|
|
90
|
+
# Usage: generate_span_id
|
|
91
|
+
generate_span_id() {
|
|
92
|
+
generate_hex_string 16
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Initialize trace context for the CI run
|
|
96
|
+
# Sets TRACEPARENT env var if not already set
|
|
97
|
+
# Format: 00-{trace_id}-{span_id}-{flags}
|
|
98
|
+
# Usage: setup_trace_context
|
|
99
|
+
setup_trace_context() {
|
|
100
|
+
# Check if trace context already exists from environment
|
|
101
|
+
if [ -n "${TRACEPARENT:-}" ]; then
|
|
102
|
+
log_debug "Using existing TRACEPARENT: $TRACEPARENT"
|
|
103
|
+
# Extract trace_id and span_id from existing TRACEPARENT
|
|
104
|
+
CI_TRACE_ID=$(echo "$TRACEPARENT" | cut -d'-' -f2)
|
|
105
|
+
CI_SPAN_ID=$(echo "$TRACEPARENT" | cut -d'-' -f3)
|
|
106
|
+
return 0
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# Check for direct trace ID from environment
|
|
110
|
+
if [ -n "${OTEL_TRACE_ID:-}" ]; then
|
|
111
|
+
log_debug "Using OTEL_TRACE_ID: $OTEL_TRACE_ID"
|
|
112
|
+
CI_TRACE_ID="$OTEL_TRACE_ID"
|
|
113
|
+
CI_SPAN_ID=$(generate_span_id)
|
|
114
|
+
TRACEPARENT="00-${CI_TRACE_ID}-${CI_SPAN_ID}-01"
|
|
115
|
+
export TRACEPARENT
|
|
116
|
+
return 0
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# Check for GitHub Actions run ID (use as fallback correlation)
|
|
120
|
+
if [ -n "${GITHUB_RUN_ID:-}" ]; then
|
|
121
|
+
log_debug "Using GitHub run ID for trace correlation"
|
|
122
|
+
# Create deterministic trace_id from GitHub run info
|
|
123
|
+
local gh_seed="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT:-1}"
|
|
124
|
+
CI_TRACE_ID=$(echo "$gh_seed" | md5sum | head -c 32)
|
|
125
|
+
CI_SPAN_ID=$(generate_span_id)
|
|
126
|
+
TRACEPARENT="00-${CI_TRACE_ID}-${CI_SPAN_ID}-01"
|
|
127
|
+
export TRACEPARENT
|
|
128
|
+
log_debug "Generated TRACEPARENT from GitHub: $TRACEPARENT"
|
|
129
|
+
return 0
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# Generate new trace context for local execution
|
|
133
|
+
log_debug "Generating new trace context for local CI run"
|
|
134
|
+
CI_TRACE_ID=$(generate_trace_id)
|
|
135
|
+
CI_SPAN_ID=$(generate_span_id)
|
|
136
|
+
TRACEPARENT="00-${CI_TRACE_ID}-${CI_SPAN_ID}-01"
|
|
137
|
+
export TRACEPARENT
|
|
138
|
+
|
|
139
|
+
log_info "Trace ID: ${CI_TRACE_ID:0:8}... (local)"
|
|
140
|
+
log_debug "Full TRACEPARENT: $TRACEPARENT"
|
|
141
|
+
return 0
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Get the current trace ID
|
|
145
|
+
# Usage: get_trace_id
|
|
146
|
+
get_trace_id() {
|
|
147
|
+
echo "${CI_TRACE_ID:-}"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Get trace context info for metadata
|
|
151
|
+
# Usage: get_trace_context_json
|
|
152
|
+
get_trace_context_json() {
|
|
153
|
+
local trace_id="${CI_TRACE_ID:-}"
|
|
154
|
+
local span_id="${CI_SPAN_ID:-}"
|
|
155
|
+
local traceparent="${TRACEPARENT:-}"
|
|
156
|
+
|
|
157
|
+
if [ -z "$trace_id" ]; then
|
|
158
|
+
echo "null"
|
|
159
|
+
return
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# Determine source of trace context
|
|
163
|
+
local source="local"
|
|
164
|
+
if [ -n "${GITHUB_RUN_ID:-}" ]; then
|
|
165
|
+
source="github"
|
|
166
|
+
elif [ -n "${OTEL_TRACE_ID:-}" ]; then
|
|
167
|
+
source="otel_env"
|
|
168
|
+
elif [ -n "${TRACEPARENT:-}" ] && [ "${CI_TRACE_ID:-}" != "$(echo "$TRACEPARENT" | cut -d'-' -f2)" ]; then
|
|
169
|
+
source="external"
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
jq -n \
|
|
173
|
+
--arg trace_id "$trace_id" \
|
|
174
|
+
--arg span_id "$span_id" \
|
|
175
|
+
--arg traceparent "$traceparent" \
|
|
176
|
+
--arg source "$source" \
|
|
177
|
+
'{
|
|
178
|
+
trace_id: $trace_id,
|
|
179
|
+
span_id: $span_id,
|
|
180
|
+
traceparent: $traceparent,
|
|
181
|
+
source: $source
|
|
182
|
+
}'
|
|
183
|
+
}
|
|
184
|
+
|
|
61
185
|
# ==============================================================================
|
|
62
186
|
# Version Compatibility Check
|
|
63
187
|
# ==============================================================================
|
|
@@ -432,6 +556,10 @@ export_ci_data() {
|
|
|
432
556
|
# Get scope
|
|
433
557
|
local scope="${CI_SCOPE:-all}"
|
|
434
558
|
|
|
559
|
+
# Get trace context
|
|
560
|
+
local trace_context
|
|
561
|
+
trace_context=$(get_trace_context_json)
|
|
562
|
+
|
|
435
563
|
# Generate intermediate data file for Node.js generator
|
|
436
564
|
jq -n \
|
|
437
565
|
--argjson steps "$steps_json" \
|
|
@@ -441,6 +569,7 @@ export_ci_data() {
|
|
|
441
569
|
--arg finished_at "$now" \
|
|
442
570
|
--arg project_name "$project_name" \
|
|
443
571
|
--arg scope "$scope" \
|
|
572
|
+
--argjson trace_context "$trace_context" \
|
|
444
573
|
'{
|
|
445
574
|
steps: $steps,
|
|
446
575
|
errors: $errors,
|
|
@@ -454,7 +583,8 @@ export_ci_data() {
|
|
|
454
583
|
},
|
|
455
584
|
metadata: {
|
|
456
585
|
scope: $scope
|
|
457
|
-
}
|
|
586
|
+
},
|
|
587
|
+
trace_context: $trace_context
|
|
458
588
|
}' > "$output_file"
|
|
459
589
|
|
|
460
590
|
log_debug "CI data exported to: $output_file"
|
|
@@ -177,8 +177,9 @@ export function generateCiResult(input) {
|
|
|
177
177
|
// Determine exit code
|
|
178
178
|
const exitCode = executionStats.steps_failed > 0 ? 1 : 0
|
|
179
179
|
|
|
180
|
-
// Get trace ID
|
|
181
|
-
const
|
|
180
|
+
// Get trace ID - prefer trace_context from input (shell-generated) over env vars
|
|
181
|
+
const traceContext = input.trace_context || {}
|
|
182
|
+
const traceId = traceContext.trace_id || getTraceId()
|
|
182
183
|
|
|
183
184
|
// Build meta object
|
|
184
185
|
const meta = {
|
|
@@ -198,7 +199,19 @@ export function generateCiResult(input) {
|
|
|
198
199
|
// Build extensions with telemetry if trace_id exists
|
|
199
200
|
const extensions = { ...(metadata.extensions || {}) }
|
|
200
201
|
if (traceId) {
|
|
201
|
-
|
|
202
|
+
// Count errors from failed steps
|
|
203
|
+
const errorsRecorded = executionStats.steps_failed || 0
|
|
204
|
+
|
|
205
|
+
extensions.telemetry = buildTelemetryExtension(traceId, {
|
|
206
|
+
errorsRecorded,
|
|
207
|
+
// spans_exported remains 0 until we implement actual OTel export (GAP-004/Q12)
|
|
208
|
+
spansExported: 0,
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
// Add trace source info if available
|
|
212
|
+
if (traceContext.source) {
|
|
213
|
+
extensions.telemetry.trace_source = traceContext.source
|
|
214
|
+
}
|
|
202
215
|
}
|
|
203
216
|
|
|
204
217
|
// Build result object
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
# @kabran-tecnologia/kabran-config/telemetry
|
|
2
|
+
|
|
3
|
+
Unified telemetry package for Kabran projects using OpenTelemetry.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-runtime support**: Node.js, Frontend (Browser), Edge/Serverless
|
|
8
|
+
- **OpenTelemetry integration**: W3C Trace Context, OTLP export
|
|
9
|
+
- **Zero-config defaults**: Works out of the box with sensible defaults
|
|
10
|
+
- **Structured logging**: Logger with automatic trace correlation
|
|
11
|
+
- **Tree-shakeable**: Import only what you need
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
The telemetry modules are included in `@kabran-tecnologia/kabran-config`. Install the package and the required OpenTelemetry peer dependencies:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Install kabran-config
|
|
19
|
+
npm install @kabran-tecnologia/kabran-config
|
|
20
|
+
|
|
21
|
+
# Install required peer dependencies (pick based on your runtime)
|
|
22
|
+
|
|
23
|
+
# For Node.js:
|
|
24
|
+
npm install @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources @opentelemetry/semantic-conventions
|
|
25
|
+
|
|
26
|
+
# For Frontend:
|
|
27
|
+
npm install @opentelemetry/api @opentelemetry/sdk-trace-web @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/instrumentation-fetch @opentelemetry/instrumentation-document-load @opentelemetry/instrumentation-user-interaction
|
|
28
|
+
|
|
29
|
+
# For Edge/Serverless:
|
|
30
|
+
npm install @opentelemetry/api @opentelemetry/sdk-trace-base @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources @opentelemetry/semantic-conventions
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### Node.js (Express/Fastify)
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
// instrumentation.js - Import this FIRST in your app
|
|
39
|
+
import { initTelemetry, telemetryMiddleware, shutdownTelemetry } from '@kabran-tecnologia/kabran-config/telemetry/node'
|
|
40
|
+
|
|
41
|
+
// Initialize telemetry
|
|
42
|
+
initTelemetry({
|
|
43
|
+
serviceName: 'my-api',
|
|
44
|
+
serviceVersion: '1.0.0',
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// In your Express app
|
|
48
|
+
import express from 'express'
|
|
49
|
+
const app = express()
|
|
50
|
+
|
|
51
|
+
// Add telemetry middleware (creates spans for each request)
|
|
52
|
+
app.use(telemetryMiddleware())
|
|
53
|
+
|
|
54
|
+
app.get('/api/users', (req, res) => {
|
|
55
|
+
// Your handler - automatically traced
|
|
56
|
+
res.json({ users: [] })
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Graceful shutdown
|
|
60
|
+
process.on('SIGTERM', async () => {
|
|
61
|
+
await shutdownTelemetry()
|
|
62
|
+
process.exit(0)
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Frontend (React/Vite)
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// main.tsx
|
|
70
|
+
import { initTelemetry } from '@kabran-tecnologia/kabran-config/telemetry/frontend'
|
|
71
|
+
|
|
72
|
+
// Initialize before rendering
|
|
73
|
+
initTelemetry({
|
|
74
|
+
serviceName: 'my-frontend',
|
|
75
|
+
serviceVersion: '1.0.0',
|
|
76
|
+
// Optional: customize which events to trace
|
|
77
|
+
instrumentation: {
|
|
78
|
+
userInteractionEvents: ['click', 'submit'],
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Your React app renders normally
|
|
83
|
+
import { createRoot } from 'react-dom/client'
|
|
84
|
+
import App from './App'
|
|
85
|
+
|
|
86
|
+
createRoot(document.getElementById('root')!).render(<App />)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Edge/Serverless (Supabase Functions)
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// supabase/functions/my-function/index.ts
|
|
93
|
+
import { withTelemetry, traceSupabaseQuery } from '@kabran-tecnologia/kabran-config/telemetry/edge'
|
|
94
|
+
import { createClient } from '@supabase/supabase-js'
|
|
95
|
+
|
|
96
|
+
const supabase = createClient(
|
|
97
|
+
Deno.env.get('SUPABASE_URL')!,
|
|
98
|
+
Deno.env.get('SUPABASE_ANON_KEY')!
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
// Wrap your handler with telemetry
|
|
102
|
+
Deno.serve(withTelemetry(
|
|
103
|
+
{
|
|
104
|
+
serviceName: 'my-edge-function',
|
|
105
|
+
serviceVersion: '1.0.0',
|
|
106
|
+
},
|
|
107
|
+
async (req) => {
|
|
108
|
+
// Trace Supabase queries
|
|
109
|
+
const { data, error } = await traceSupabaseQuery(
|
|
110
|
+
'select-users',
|
|
111
|
+
() => supabase.from('users').select('*')
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return new Response(JSON.stringify({ data, error }), {
|
|
115
|
+
headers: { 'Content-Type': 'application/json' },
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
))
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Structured Logging
|
|
122
|
+
|
|
123
|
+
The logger automatically includes trace context in log output:
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
import { createLogger } from '@kabran-tecnologia/kabran-config/telemetry/logger'
|
|
127
|
+
|
|
128
|
+
const log = createLogger()
|
|
129
|
+
|
|
130
|
+
log.info('User logged in', { userId: '123' })
|
|
131
|
+
// Output (JSON in production):
|
|
132
|
+
// {"level":"info","message":"User logged in","userId":"123","trace_id":"abc123...","span_id":"def456...","timestamp":"2024-01-13T..."}
|
|
133
|
+
|
|
134
|
+
// Output (pretty in development):
|
|
135
|
+
// 2024-01-13T12:00:00.000Z [INFO] User logged in [trace:abc123...] {"userId":"123"}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Span-bound Logger
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
import { trace } from '@opentelemetry/api'
|
|
142
|
+
import { createSpanLogger } from '@kabran-tecnologia/kabran-config/telemetry/logger'
|
|
143
|
+
|
|
144
|
+
const span = trace.getActiveSpan()
|
|
145
|
+
const log = createSpanLogger(span)
|
|
146
|
+
|
|
147
|
+
log.info('Processing order', { orderId: '456' })
|
|
148
|
+
// Logs are also added as span events for visibility in traces
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Configuration
|
|
152
|
+
|
|
153
|
+
### Environment Variables
|
|
154
|
+
|
|
155
|
+
All configuration can be set via environment variables:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# Core
|
|
159
|
+
SERVICE_NAME=my-service # Required: Your service name
|
|
160
|
+
SERVICE_VERSION=1.0.0 # Service version (default: 1.0.0)
|
|
161
|
+
ENVIRONMENT=production # Environment name (default: from NODE_ENV)
|
|
162
|
+
OTEL_NAMESPACE=kabran # Service namespace (default: kabran)
|
|
163
|
+
|
|
164
|
+
# OTLP Exporter
|
|
165
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.kabran.com.br # Collector endpoint
|
|
166
|
+
OTEL_EXPORTER_OTLP_TIMEOUT=10000 # Export timeout in ms (default: 10000)
|
|
167
|
+
|
|
168
|
+
# Sampling
|
|
169
|
+
OTEL_SAMPLE_RATE=0.1 # Sampling rate 0.0-1.0 (default: 0.1 = 10%)
|
|
170
|
+
|
|
171
|
+
# Enable/Disable
|
|
172
|
+
OTEL_ENABLED=true # Enable telemetry (default: true in production)
|
|
173
|
+
|
|
174
|
+
# Logger
|
|
175
|
+
OTEL_LOG_TRACE_ID_LENGTH=8 # Trace ID length in logs (default: 8)
|
|
176
|
+
NO_COLOR=1 # Disable ANSI colors in logs
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Programmatic Configuration
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
import { initTelemetry } from '@kabran-tecnologia/kabran-config/telemetry/node'
|
|
183
|
+
|
|
184
|
+
initTelemetry({
|
|
185
|
+
// Required
|
|
186
|
+
serviceName: 'my-service',
|
|
187
|
+
|
|
188
|
+
// Optional (all have sensible defaults)
|
|
189
|
+
serviceVersion: '1.0.0',
|
|
190
|
+
environment: 'production',
|
|
191
|
+
endpoint: 'https://otel.kabran.com.br',
|
|
192
|
+
sampleRate: 0.1,
|
|
193
|
+
enabled: true,
|
|
194
|
+
|
|
195
|
+
// Resource attributes (added to all spans)
|
|
196
|
+
resourceAttributes: {
|
|
197
|
+
'deployment.environment': 'production',
|
|
198
|
+
'service.instance.id': process.env.POD_NAME,
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Batch processor settings (Node.js only)
|
|
202
|
+
batchProcessor: {
|
|
203
|
+
maxQueueSize: 2048,
|
|
204
|
+
maxExportBatchSize: 512,
|
|
205
|
+
scheduledDelayMillis: 5000,
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Module Reference
|
|
211
|
+
|
|
212
|
+
### `telemetry/node`
|
|
213
|
+
|
|
214
|
+
For Node.js servers and long-running processes.
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
import {
|
|
218
|
+
initTelemetry, // Initialize the tracer
|
|
219
|
+
shutdownTelemetry, // Graceful shutdown
|
|
220
|
+
telemetryMiddleware,// Express/Fastify middleware
|
|
221
|
+
isInitialized, // Check if initialized
|
|
222
|
+
getTracer, // Get the tracer instance
|
|
223
|
+
getConfig, // Get resolved config
|
|
224
|
+
} from '@kabran-tecnologia/kabran-config/telemetry/node'
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### `telemetry/frontend`
|
|
228
|
+
|
|
229
|
+
For browser applications (React, Vue, vanilla JS).
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
import {
|
|
233
|
+
initTelemetry, // Initialize the tracer
|
|
234
|
+
shutdownTelemetry, // Flush pending spans
|
|
235
|
+
isInitialized, // Check if initialized
|
|
236
|
+
getTracer, // Get the tracer instance
|
|
237
|
+
getConfig, // Get resolved config
|
|
238
|
+
} from '@kabran-tecnologia/kabran-config/telemetry/frontend'
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### `telemetry/edge`
|
|
242
|
+
|
|
243
|
+
For Edge Functions and serverless (Supabase, Deno Deploy, Cloudflare Workers).
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
import {
|
|
247
|
+
withTelemetry, // Handler wrapper with auto-tracing
|
|
248
|
+
traceSupabaseQuery, // Trace Supabase queries
|
|
249
|
+
shutdownTelemetry, // Flush pending spans
|
|
250
|
+
isInitialized, // Check if initialized
|
|
251
|
+
getConfig, // Get resolved config
|
|
252
|
+
} from '@kabran-tecnologia/kabran-config/telemetry/edge'
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### `telemetry/logger`
|
|
256
|
+
|
|
257
|
+
Structured logger with trace correlation.
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
import {
|
|
261
|
+
createLogger, // Create a logger instance
|
|
262
|
+
createSpanLogger, // Create a span-bound logger
|
|
263
|
+
log, // Default logger instance
|
|
264
|
+
getTraceContext, // Get current trace context
|
|
265
|
+
} from '@kabran-tecnologia/kabran-config/telemetry/logger'
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### `telemetry/config`
|
|
269
|
+
|
|
270
|
+
Configuration utilities.
|
|
271
|
+
|
|
272
|
+
```javascript
|
|
273
|
+
import {
|
|
274
|
+
defineTelemetryConfig, // Create a type-safe config
|
|
275
|
+
resolveConfig, // Resolve config with defaults and env vars
|
|
276
|
+
validateConfig, // Validate config object
|
|
277
|
+
detectEnabled, // Check if telemetry should be enabled
|
|
278
|
+
} from '@kabran-tecnologia/kabran-config/telemetry/config'
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### `telemetry/shared`
|
|
282
|
+
|
|
283
|
+
Shared utilities and types.
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
import {
|
|
287
|
+
setAttributes, // Set multiple span attributes
|
|
288
|
+
formatDuration, // Format milliseconds to human-readable
|
|
289
|
+
generateInvocationId, // Generate unique invocation ID
|
|
290
|
+
safeWarn, // Safe console.warn
|
|
291
|
+
safeLog, // Safe console.log
|
|
292
|
+
} from '@kabran-tecnologia/kabran-config/telemetry/shared'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Integration with Kosmos Observability
|
|
296
|
+
|
|
297
|
+
This package is designed to work with the Kosmos observability stack:
|
|
298
|
+
|
|
299
|
+
- **Traces** → Grafana Tempo
|
|
300
|
+
- **Logs** → Grafana Loki (via stdout/Promtail or direct export)
|
|
301
|
+
- **Metrics** → Prometheus (planned, see [GAP-006])
|
|
302
|
+
|
|
303
|
+
Default endpoint: `https://otel.kabran.com.br`
|
|
304
|
+
|
|
305
|
+
### Viewing Traces
|
|
306
|
+
|
|
307
|
+
1. Open Grafana at your Kosmos instance
|
|
308
|
+
2. Go to Explore → Select Tempo
|
|
309
|
+
3. Search by service name or trace ID
|
|
310
|
+
|
|
311
|
+
## Best Practices
|
|
312
|
+
|
|
313
|
+
### 1. Initialize Early
|
|
314
|
+
|
|
315
|
+
Initialize telemetry as early as possible in your application:
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
// This should be the FIRST import
|
|
319
|
+
import './instrumentation.js'
|
|
320
|
+
|
|
321
|
+
// Then your app code
|
|
322
|
+
import express from 'express'
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### 2. Use Meaningful Span Names
|
|
326
|
+
|
|
327
|
+
```javascript
|
|
328
|
+
// Good
|
|
329
|
+
span.updateName('user.create')
|
|
330
|
+
span.updateName('order.process')
|
|
331
|
+
|
|
332
|
+
// Bad
|
|
333
|
+
span.updateName('handler')
|
|
334
|
+
span.updateName('function1')
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### 3. Add Relevant Attributes
|
|
338
|
+
|
|
339
|
+
```javascript
|
|
340
|
+
import { trace } from '@opentelemetry/api'
|
|
341
|
+
|
|
342
|
+
const span = trace.getActiveSpan()
|
|
343
|
+
span?.setAttributes({
|
|
344
|
+
'user.id': userId,
|
|
345
|
+
'order.id': orderId,
|
|
346
|
+
'order.total': total,
|
|
347
|
+
})
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 4. Handle Errors Properly
|
|
351
|
+
|
|
352
|
+
```javascript
|
|
353
|
+
import { trace, SpanStatusCode } from '@opentelemetry/api'
|
|
354
|
+
|
|
355
|
+
try {
|
|
356
|
+
// Your code
|
|
357
|
+
} catch (error) {
|
|
358
|
+
const span = trace.getActiveSpan()
|
|
359
|
+
span?.recordException(error)
|
|
360
|
+
span?.setStatus({ code: SpanStatusCode.ERROR, message: error.message })
|
|
361
|
+
throw error
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### 5. Graceful Shutdown
|
|
366
|
+
|
|
367
|
+
Always flush pending spans before shutdown:
|
|
368
|
+
|
|
369
|
+
```javascript
|
|
370
|
+
process.on('SIGTERM', async () => {
|
|
371
|
+
await shutdownTelemetry()
|
|
372
|
+
process.exit(0)
|
|
373
|
+
})
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Troubleshooting
|
|
377
|
+
|
|
378
|
+
### Traces not appearing
|
|
379
|
+
|
|
380
|
+
1. Check `OTEL_ENABLED` is not set to `false`
|
|
381
|
+
2. Verify `SERVICE_NAME` is set
|
|
382
|
+
3. Check network connectivity to the collector endpoint
|
|
383
|
+
4. Verify sampling rate (default is 10%)
|
|
384
|
+
|
|
385
|
+
### Missing trace correlation in logs
|
|
386
|
+
|
|
387
|
+
1. Ensure telemetry is initialized before logging
|
|
388
|
+
2. Check that you're within an active span context
|
|
389
|
+
|
|
390
|
+
### High memory usage
|
|
391
|
+
|
|
392
|
+
Reduce batch processor queue size:
|
|
393
|
+
|
|
394
|
+
```javascript
|
|
395
|
+
initTelemetry({
|
|
396
|
+
serviceName: 'my-service',
|
|
397
|
+
batchProcessor: {
|
|
398
|
+
maxQueueSize: 512, // Lower from default 2048
|
|
399
|
+
},
|
|
400
|
+
})
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## Related Documentation
|
|
404
|
+
|
|
405
|
+
- [OpenTelemetry JavaScript](https://opentelemetry.io/docs/languages/js/)
|
|
406
|
+
- [W3C Trace Context](https://www.w3.org/TR/trace-context/)
|
|
407
|
+
- [Kosmos Observability Stack](https://github.com/kabran-owner/kosmos/tree/main/services/observability)
|