@kabran-tecnologia/kabran-config 1.7.0 → 1.9.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 +5 -2
- package/src/scripts/ci/ci-core.sh +131 -1
- package/src/scripts/ci/ci-runner.sh +3 -0
- package/src/scripts/generate-ci-result.mjs +16 -3
- package/src/scripts/traceability/coverage-report.sh +222 -0
- package/src/scripts/traceability/traceability-core.sh +122 -0
- package/src/scripts/traceability/validate-traceability.sh +150 -0
- package/src/telemetry/README.md +407 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kabran-tecnologia/kabran-config",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Shared quality configurations, enforcement scripts, and CI/CD tooling for Kabran projects",
|
|
5
5
|
"author": "Kabran",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"bin": {
|
|
12
12
|
"kabran-setup": "src/scripts/setup.mjs",
|
|
13
13
|
"kabran-ci": "src/scripts/ci/ci-runner.sh",
|
|
14
|
-
"kabran-pr-comment": "src/scripts/pr-quality-comment.mjs"
|
|
14
|
+
"kabran-pr-comment": "src/scripts/pr-quality-comment.mjs",
|
|
15
|
+
"kabran-traceability": "src/scripts/traceability/validate-traceability.sh",
|
|
16
|
+
"kabran-coverage": "src/scripts/traceability/coverage-report.sh"
|
|
15
17
|
},
|
|
16
18
|
"exports": {
|
|
17
19
|
"./eslint": "./src/eslint.mjs",
|
|
@@ -29,6 +31,7 @@
|
|
|
29
31
|
"./scripts/env-validator": "./src/scripts/env-validator.mjs",
|
|
30
32
|
"./scripts/ci/*": "./src/scripts/ci/*",
|
|
31
33
|
"./scripts/deploy/*": "./src/scripts/deploy/*",
|
|
34
|
+
"./scripts/traceability/*": "./src/scripts/traceability/*",
|
|
32
35
|
"./scripts/setup": "./src/scripts/setup.mjs",
|
|
33
36
|
"./scripts/quality-standard-validator": "./src/scripts/quality-standard-validator.mjs",
|
|
34
37
|
"./scripts/generate-ci-result": "./src/scripts/generate-ci-result.mjs",
|
|
@@ -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,222 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ==============================================================================
|
|
3
|
+
# Kabran Traceability Coverage Report
|
|
4
|
+
# Part of @kabran-owner/kabran-config
|
|
5
|
+
# Implements PROP-006: JSDoc Traceability Tags
|
|
6
|
+
#
|
|
7
|
+
# Generates a report of spec coverage in the codebase.
|
|
8
|
+
# Shows which specs have code implementing them and which ACs are covered.
|
|
9
|
+
# ==============================================================================
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
# Source core functions
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
# shellcheck source=traceability-core.sh
|
|
16
|
+
source "$SCRIPT_DIR/traceability-core.sh"
|
|
17
|
+
|
|
18
|
+
# ==============================================================================
|
|
19
|
+
# Configuration
|
|
20
|
+
# ==============================================================================
|
|
21
|
+
|
|
22
|
+
SEARCH_PATH="${1:-.}"
|
|
23
|
+
SPEC_FILTER="${2:-}"
|
|
24
|
+
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}"
|
|
25
|
+
|
|
26
|
+
# ==============================================================================
|
|
27
|
+
# Report Functions
|
|
28
|
+
# ==============================================================================
|
|
29
|
+
|
|
30
|
+
# Generate report for a specific spec
|
|
31
|
+
report_spec() {
|
|
32
|
+
local spec="$1"
|
|
33
|
+
local files
|
|
34
|
+
|
|
35
|
+
files=$(grep -rl "@spec $spec" --include="*.ts" --include="*.tsx" "$SEARCH_PATH" 2>/dev/null || true)
|
|
36
|
+
|
|
37
|
+
if [ -z "$files" ]; then
|
|
38
|
+
return
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
echo ""
|
|
42
|
+
echo "=== $spec ==="
|
|
43
|
+
echo ""
|
|
44
|
+
echo "Files:"
|
|
45
|
+
echo "$files" | while read -r file; do
|
|
46
|
+
[ -n "$file" ] && echo " - $file"
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
echo ""
|
|
50
|
+
echo "ACs Implemented:"
|
|
51
|
+
echo "$files" | while read -r file; do
|
|
52
|
+
[ -n "$file" ] && extract_implements "$file"
|
|
53
|
+
done | sort -u | while read -r ac; do
|
|
54
|
+
[ -n "$ac" ] && echo " - $ac"
|
|
55
|
+
done
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Generate summary report
|
|
59
|
+
report_summary() {
|
|
60
|
+
log_section "Traceability Coverage Report v${TRACEABILITY_VERSION}"
|
|
61
|
+
log_info "Search path: $SEARCH_PATH"
|
|
62
|
+
echo ""
|
|
63
|
+
|
|
64
|
+
local total_files
|
|
65
|
+
local tagged_files
|
|
66
|
+
local specs
|
|
67
|
+
|
|
68
|
+
total_files=$(count_total_files "$SEARCH_PATH")
|
|
69
|
+
tagged_files=$(count_tagged_files "$SEARCH_PATH")
|
|
70
|
+
|
|
71
|
+
echo "## Overview"
|
|
72
|
+
echo ""
|
|
73
|
+
echo "| Metric | Value |"
|
|
74
|
+
echo "|--------|-------|"
|
|
75
|
+
echo "| Total TypeScript files | $total_files |"
|
|
76
|
+
echo "| Files with @spec | $tagged_files |"
|
|
77
|
+
|
|
78
|
+
if [ "$total_files" -gt 0 ]; then
|
|
79
|
+
local coverage=$((tagged_files * 100 / total_files))
|
|
80
|
+
echo "| Coverage | $coverage% |"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
echo ""
|
|
84
|
+
echo "## Specs Found"
|
|
85
|
+
echo ""
|
|
86
|
+
|
|
87
|
+
# Find all unique specs
|
|
88
|
+
specs=$(grep -roh '@spec S[0-9]*' --include="*.ts" --include="*.tsx" "$SEARCH_PATH" 2>/dev/null | sed 's/@spec //' | sort -u || true)
|
|
89
|
+
|
|
90
|
+
if [ -z "$specs" ]; then
|
|
91
|
+
echo "No @spec tags found in codebase."
|
|
92
|
+
return
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
echo "$specs" | while read -r spec; do
|
|
96
|
+
if [ -n "$spec" ]; then
|
|
97
|
+
local file_count
|
|
98
|
+
file_count=$(grep -rl "@spec $spec" --include="*.ts" --include="*.tsx" "$SEARCH_PATH" 2>/dev/null | wc -l)
|
|
99
|
+
echo "- **$spec**: $file_count files"
|
|
100
|
+
fi
|
|
101
|
+
done
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Generate detailed report for all specs
|
|
105
|
+
report_detailed() {
|
|
106
|
+
report_summary
|
|
107
|
+
|
|
108
|
+
echo ""
|
|
109
|
+
echo "## Detailed Coverage"
|
|
110
|
+
|
|
111
|
+
local specs
|
|
112
|
+
specs=$(grep -roh '@spec S[0-9]*' --include="*.ts" --include="*.tsx" "$SEARCH_PATH" 2>/dev/null | sed 's/@spec //' | sort -u || true)
|
|
113
|
+
|
|
114
|
+
if [ -n "$specs" ]; then
|
|
115
|
+
echo "$specs" | while read -r spec; do
|
|
116
|
+
[ -n "$spec" ] && report_spec "$spec"
|
|
117
|
+
done
|
|
118
|
+
fi
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Generate JSON report
|
|
122
|
+
report_json() {
|
|
123
|
+
local total_files
|
|
124
|
+
local tagged_files
|
|
125
|
+
local specs
|
|
126
|
+
|
|
127
|
+
total_files=$(count_total_files "$SEARCH_PATH")
|
|
128
|
+
tagged_files=$(count_tagged_files "$SEARCH_PATH")
|
|
129
|
+
specs=$(grep -roh '@spec S[0-9]*' --include="*.ts" --include="*.tsx" "$SEARCH_PATH" 2>/dev/null | sed 's/@spec //' | sort -u || true)
|
|
130
|
+
|
|
131
|
+
local coverage=0
|
|
132
|
+
if [ "$total_files" -gt 0 ]; then
|
|
133
|
+
coverage=$((tagged_files * 100 / total_files))
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
echo "{"
|
|
137
|
+
echo " \"version\": \"${TRACEABILITY_VERSION}\","
|
|
138
|
+
echo " \"searchPath\": \"$SEARCH_PATH\","
|
|
139
|
+
echo " \"totalFiles\": $total_files,"
|
|
140
|
+
echo " \"taggedFiles\": $tagged_files,"
|
|
141
|
+
echo " \"coverage\": $coverage,"
|
|
142
|
+
echo " \"specs\": ["
|
|
143
|
+
|
|
144
|
+
local first=true
|
|
145
|
+
if [ -n "$specs" ]; then
|
|
146
|
+
echo "$specs" | while read -r spec; do
|
|
147
|
+
if [ -n "$spec" ]; then
|
|
148
|
+
local file_count
|
|
149
|
+
file_count=$(grep -rl "@spec $spec" --include="*.ts" --include="*.tsx" "$SEARCH_PATH" 2>/dev/null | wc -l)
|
|
150
|
+
if [ "$first" = true ]; then
|
|
151
|
+
first=false
|
|
152
|
+
else
|
|
153
|
+
echo ","
|
|
154
|
+
fi
|
|
155
|
+
echo -n " { \"id\": \"$spec\", \"files\": $file_count }"
|
|
156
|
+
fi
|
|
157
|
+
done
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
echo ""
|
|
161
|
+
echo " ]"
|
|
162
|
+
echo "}"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# ==============================================================================
|
|
166
|
+
# Help
|
|
167
|
+
# ==============================================================================
|
|
168
|
+
|
|
169
|
+
show_help() {
|
|
170
|
+
cat << EOF
|
|
171
|
+
Kabran Traceability Coverage Report v${TRACEABILITY_VERSION}
|
|
172
|
+
|
|
173
|
+
Usage: coverage-report.sh [path] [spec] [options]
|
|
174
|
+
|
|
175
|
+
Arguments:
|
|
176
|
+
path Directory to scan (default: current directory)
|
|
177
|
+
spec Filter by specific spec ID (e.g., S25)
|
|
178
|
+
|
|
179
|
+
Environment Variables:
|
|
180
|
+
OUTPUT_FORMAT Output format: text, detailed, json (default: text)
|
|
181
|
+
|
|
182
|
+
Examples:
|
|
183
|
+
coverage-report.sh ./src
|
|
184
|
+
coverage-report.sh ./src S25
|
|
185
|
+
OUTPUT_FORMAT=json coverage-report.sh ./src
|
|
186
|
+
OUTPUT_FORMAT=detailed coverage-report.sh ./src
|
|
187
|
+
|
|
188
|
+
Output Formats:
|
|
189
|
+
text Summary with spec counts
|
|
190
|
+
detailed Full breakdown with files and ACs per spec
|
|
191
|
+
json Machine-readable JSON output
|
|
192
|
+
|
|
193
|
+
EOF
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# ==============================================================================
|
|
197
|
+
# Entry Point
|
|
198
|
+
# ==============================================================================
|
|
199
|
+
|
|
200
|
+
if [[ "${1:-}" == "--help" ]] || [[ "${1:-}" == "-h" ]]; then
|
|
201
|
+
show_help
|
|
202
|
+
exit 0
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
# Handle specific spec filter
|
|
206
|
+
if [ -n "$SPEC_FILTER" ]; then
|
|
207
|
+
report_spec "$SPEC_FILTER"
|
|
208
|
+
exit 0
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
# Generate report based on format
|
|
212
|
+
case "$OUTPUT_FORMAT" in
|
|
213
|
+
json)
|
|
214
|
+
report_json
|
|
215
|
+
;;
|
|
216
|
+
detailed)
|
|
217
|
+
report_detailed
|
|
218
|
+
;;
|
|
219
|
+
*)
|
|
220
|
+
report_summary
|
|
221
|
+
;;
|
|
222
|
+
esac
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ==============================================================================
|
|
3
|
+
# Kabran Traceability Core - Shared Functions
|
|
4
|
+
# Part of @kabran-owner/kabran-config
|
|
5
|
+
# Implements PROP-006: JSDoc Traceability Tags
|
|
6
|
+
# ==============================================================================
|
|
7
|
+
|
|
8
|
+
# Version - Dynamically resolve from package.json
|
|
9
|
+
_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
_PKG_JSON="$_SCRIPT_DIR/../../../package.json"
|
|
11
|
+
if [ -f "$_PKG_JSON" ]; then
|
|
12
|
+
TRACEABILITY_VERSION=$(grep '"version":' "$_PKG_JSON" | head -1 | sed -E 's/.*"version": "([^"]+)".*/\1/')
|
|
13
|
+
else
|
|
14
|
+
TRACEABILITY_VERSION="unknown"
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Colors
|
|
18
|
+
RED='\033[0;31m'
|
|
19
|
+
GREEN='\033[0;32m'
|
|
20
|
+
YELLOW='\033[1;33m'
|
|
21
|
+
BLUE='\033[0;34m'
|
|
22
|
+
GRAY='\033[0;90m'
|
|
23
|
+
NC='\033[0m'
|
|
24
|
+
|
|
25
|
+
# ==============================================================================
|
|
26
|
+
# Logging Functions
|
|
27
|
+
# ==============================================================================
|
|
28
|
+
|
|
29
|
+
log_info() {
|
|
30
|
+
echo -e "${BLUE}[INFO]${NC} $1"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
log_success() {
|
|
34
|
+
echo -e "${GREEN}[PASS]${NC} $1"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
log_error() {
|
|
38
|
+
echo -e "${RED}[FAIL]${NC} $1"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
log_warn() {
|
|
42
|
+
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
log_section() {
|
|
46
|
+
echo -e "\n${BLUE}====${NC} $1 ${BLUE}====${NC}"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# ==============================================================================
|
|
50
|
+
# Tag Patterns (PROP-006 Standard)
|
|
51
|
+
# ==============================================================================
|
|
52
|
+
|
|
53
|
+
# Valid @spec format: @spec SXX (ID only, not full name)
|
|
54
|
+
PATTERN_SPEC_VALID='@spec S[0-9]+'
|
|
55
|
+
PATTERN_SPEC_INVALID='@spec S[0-9]+-'
|
|
56
|
+
|
|
57
|
+
# Valid @implements format: @implements AC-XX or @implements SXX:AC-XX
|
|
58
|
+
PATTERN_IMPLEMENTS='@implements'
|
|
59
|
+
|
|
60
|
+
# Valid @task format: @task XXX-NNN
|
|
61
|
+
PATTERN_TASK='@task [A-Z]+-[0-9]+'
|
|
62
|
+
|
|
63
|
+
# Valid @prd format: @prd RF-XXX or @prd RNF-XXX
|
|
64
|
+
PATTERN_PRD='@prd R(N)?F-[0-9]+'
|
|
65
|
+
|
|
66
|
+
# ==============================================================================
|
|
67
|
+
# Validation Functions
|
|
68
|
+
# ==============================================================================
|
|
69
|
+
|
|
70
|
+
# Check if a file has @implements without @spec (integrity error)
|
|
71
|
+
# Usage: check_orphan_implements "path/to/file.ts"
|
|
72
|
+
# Returns: 0 if valid, 1 if orphan found
|
|
73
|
+
check_orphan_implements() {
|
|
74
|
+
local file="$1"
|
|
75
|
+
|
|
76
|
+
if grep -qE "$PATTERN_IMPLEMENTS" "$file" 2>/dev/null; then
|
|
77
|
+
if ! grep -qE "$PATTERN_SPEC_VALID" "$file" 2>/dev/null; then
|
|
78
|
+
return 1 # Orphan found
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
81
|
+
return 0 # Valid
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Check if a file uses invalid @spec format (full name instead of ID)
|
|
85
|
+
# Usage: check_spec_format "path/to/file.ts"
|
|
86
|
+
# Returns: 0 if valid, 1 if invalid format found
|
|
87
|
+
check_spec_format() {
|
|
88
|
+
local file="$1"
|
|
89
|
+
|
|
90
|
+
if grep -qE "$PATTERN_SPEC_INVALID" "$file" 2>/dev/null; then
|
|
91
|
+
return 1 # Invalid format
|
|
92
|
+
fi
|
|
93
|
+
return 0 # Valid
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Extract all @spec tags from a file
|
|
97
|
+
# Usage: extract_specs "path/to/file.ts"
|
|
98
|
+
extract_specs() {
|
|
99
|
+
local file="$1"
|
|
100
|
+
grep -oE '@spec S[0-9]+' "$file" 2>/dev/null | sed 's/@spec //' | sort -u
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Extract all @implements tags from a file
|
|
104
|
+
# Usage: extract_implements "path/to/file.ts"
|
|
105
|
+
extract_implements() {
|
|
106
|
+
local file="$1"
|
|
107
|
+
grep -oE '@implements [^*]+' "$file" 2>/dev/null | sed 's/@implements //' | tr ',' '\n' | sed 's/^ *//' | sort -u
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Count files with traceability tags
|
|
111
|
+
# Usage: count_tagged_files "path/to/search"
|
|
112
|
+
count_tagged_files() {
|
|
113
|
+
local search_path="${1:-.}"
|
|
114
|
+
grep -rl "@spec" --include="*.ts" --include="*.tsx" "$search_path" 2>/dev/null | wc -l
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Count total TypeScript files
|
|
118
|
+
# Usage: count_total_files "path/to/search"
|
|
119
|
+
count_total_files() {
|
|
120
|
+
local search_path="${1:-.}"
|
|
121
|
+
find "$search_path" -type f \( -name "*.ts" -o -name "*.tsx" \) 2>/dev/null | wc -l
|
|
122
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ==============================================================================
|
|
3
|
+
# Kabran Traceability Validator
|
|
4
|
+
# Part of @kabran-owner/kabran-config
|
|
5
|
+
# Implements PROP-006: JSDoc Traceability Tags
|
|
6
|
+
#
|
|
7
|
+
# Validates FORMAT of traceability tags (not presence).
|
|
8
|
+
# Tags are optional - this script only checks:
|
|
9
|
+
# 1. @implements without @spec (integrity error)
|
|
10
|
+
# 2. @spec with full name instead of ID (format warning)
|
|
11
|
+
# ==============================================================================
|
|
12
|
+
|
|
13
|
+
set -uo pipefail
|
|
14
|
+
|
|
15
|
+
# Source core functions
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
# shellcheck source=traceability-core.sh
|
|
18
|
+
source "$SCRIPT_DIR/traceability-core.sh"
|
|
19
|
+
|
|
20
|
+
# ==============================================================================
|
|
21
|
+
# Configuration
|
|
22
|
+
# ==============================================================================
|
|
23
|
+
|
|
24
|
+
SEARCH_PATH="${1:-.}"
|
|
25
|
+
STRICT_MODE="${STRICT_MODE:-false}"
|
|
26
|
+
EXIT_CODE=0
|
|
27
|
+
|
|
28
|
+
# ==============================================================================
|
|
29
|
+
# Main Validation
|
|
30
|
+
# ==============================================================================
|
|
31
|
+
|
|
32
|
+
main() {
|
|
33
|
+
log_section "Traceability Validation v${TRACEABILITY_VERSION}"
|
|
34
|
+
log_info "Search path: $SEARCH_PATH"
|
|
35
|
+
log_info "Strict mode: $STRICT_MODE"
|
|
36
|
+
|
|
37
|
+
local orphan_count=0
|
|
38
|
+
local format_count=0
|
|
39
|
+
local files_checked=0
|
|
40
|
+
|
|
41
|
+
# Find all TypeScript files
|
|
42
|
+
local files
|
|
43
|
+
files=$(find "$SEARCH_PATH" -type f \( -name "*.ts" -o -name "*.tsx" \) 2>/dev/null || true)
|
|
44
|
+
|
|
45
|
+
if [ -z "$files" ]; then
|
|
46
|
+
log_info "No TypeScript files found"
|
|
47
|
+
return 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
while IFS= read -r file; do
|
|
51
|
+
[ -z "$file" ] && continue
|
|
52
|
+
files_checked=$((files_checked + 1))
|
|
53
|
+
|
|
54
|
+
# Check for orphan @implements (ERROR)
|
|
55
|
+
if ! check_orphan_implements "$file"; then
|
|
56
|
+
log_error "Orphan @implements (no @spec): $file"
|
|
57
|
+
orphan_count=$((orphan_count + 1))
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Check for invalid @spec format (WARNING)
|
|
61
|
+
if ! check_spec_format "$file"; then
|
|
62
|
+
log_warn "Invalid @spec format (use ID only): $file"
|
|
63
|
+
format_count=$((format_count + 1))
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
done <<< "$files"
|
|
67
|
+
|
|
68
|
+
# Summary
|
|
69
|
+
log_section "Validation Summary"
|
|
70
|
+
log_info "Files checked: $files_checked"
|
|
71
|
+
|
|
72
|
+
if [ "$orphan_count" -gt 0 ]; then
|
|
73
|
+
log_error "Orphan @implements found: $orphan_count"
|
|
74
|
+
EXIT_CODE=1
|
|
75
|
+
else
|
|
76
|
+
log_success "No orphan @implements found"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
if [ "$format_count" -gt 0 ]; then
|
|
80
|
+
log_warn "Invalid @spec format: $format_count"
|
|
81
|
+
if [ "$STRICT_MODE" = "true" ]; then
|
|
82
|
+
EXIT_CODE=1
|
|
83
|
+
fi
|
|
84
|
+
else
|
|
85
|
+
log_success "All @spec tags use correct format"
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Coverage stats (informational)
|
|
89
|
+
local tagged_files
|
|
90
|
+
local total_files
|
|
91
|
+
tagged_files=$(count_tagged_files "$SEARCH_PATH")
|
|
92
|
+
total_files=$(count_total_files "$SEARCH_PATH")
|
|
93
|
+
|
|
94
|
+
if [ "$total_files" -gt 0 ]; then
|
|
95
|
+
local coverage=$((tagged_files * 100 / total_files))
|
|
96
|
+
log_info "Traceability coverage: $tagged_files/$total_files files ($coverage%)"
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
return $EXIT_CODE
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# ==============================================================================
|
|
103
|
+
# Help
|
|
104
|
+
# ==============================================================================
|
|
105
|
+
|
|
106
|
+
show_help() {
|
|
107
|
+
cat << EOF
|
|
108
|
+
Kabran Traceability Validator v${TRACEABILITY_VERSION}
|
|
109
|
+
|
|
110
|
+
Usage: validate-traceability.sh [path] [options]
|
|
111
|
+
|
|
112
|
+
Arguments:
|
|
113
|
+
path Directory to validate (default: current directory)
|
|
114
|
+
|
|
115
|
+
Environment Variables:
|
|
116
|
+
STRICT_MODE If "true", format warnings become errors (default: false)
|
|
117
|
+
|
|
118
|
+
Exit Codes:
|
|
119
|
+
0 Validation passed
|
|
120
|
+
1 Validation failed (orphan @implements or strict mode violations)
|
|
121
|
+
|
|
122
|
+
Examples:
|
|
123
|
+
validate-traceability.sh ./src
|
|
124
|
+
STRICT_MODE=true validate-traceability.sh ./src
|
|
125
|
+
|
|
126
|
+
Tags Validated (PROP-006):
|
|
127
|
+
@spec S25 Link to spec (ID only, not full name)
|
|
128
|
+
@implements AC-01 Acceptance criteria implemented
|
|
129
|
+
@task AGT-123 Link to Linear task
|
|
130
|
+
@prd RF-007 Link to PRD requirement
|
|
131
|
+
|
|
132
|
+
Rules:
|
|
133
|
+
- All tags are OPTIONAL
|
|
134
|
+
- @implements WITHOUT @spec is an ERROR
|
|
135
|
+
- @spec with full name (S25-name) is a WARNING
|
|
136
|
+
|
|
137
|
+
EOF
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# ==============================================================================
|
|
141
|
+
# Entry Point
|
|
142
|
+
# ==============================================================================
|
|
143
|
+
|
|
144
|
+
if [[ "${1:-}" == "--help" ]] || [[ "${1:-}" == "-h" ]]; then
|
|
145
|
+
show_help
|
|
146
|
+
exit 0
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
main
|
|
150
|
+
exit $EXIT_CODE
|
|
@@ -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)
|