@kabran-tecnologia/kabran-config 1.6.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.
@@ -79,6 +79,7 @@ export function parseArgs(args) {
79
79
  skipQualityStandard: false,
80
80
  syncWorkflows: false,
81
81
  syncHusky: false,
82
+ telemetryEnv: false,
82
83
  force: false,
83
84
  dryRun: false,
84
85
  help: false,
@@ -97,6 +98,8 @@ export function parseArgs(args) {
97
98
  options.syncWorkflows = true;
98
99
  } else if (arg === '--sync-husky') {
99
100
  options.syncHusky = true;
101
+ } else if (arg === '--telemetry-env') {
102
+ options.telemetryEnv = true;
100
103
  } else if (arg === '--force') {
101
104
  options.force = true;
102
105
  } else if (arg === '--dry-run') {
@@ -132,6 +135,7 @@ ${colors.yellow}OPTIONS:${colors.reset}
132
135
  --skip-quality-standard Don't create quality-standard.md
133
136
  --sync-workflows Overwrite existing workflow files
134
137
  --sync-husky Overwrite existing husky hooks
138
+ --telemetry-env Generate .env.example with telemetry variables
135
139
  --force Overwrite all existing files
136
140
  --dry-run Preview changes without modifying files
137
141
  --help, -h Show this help message
@@ -146,6 +150,9 @@ ${colors.yellow}EXAMPLES:${colors.reset}
146
150
  # Update workflows only
147
151
  npx kabran-setup --sync-workflows
148
152
 
153
+ # Generate telemetry .env.example
154
+ npx kabran-setup --telemetry-env
155
+
149
156
  # Preview changes without modifying
150
157
  npx kabran-setup --dry-run
151
158
 
@@ -547,6 +554,79 @@ export function setupQualityStandard(projectDir, templatesDir, options) {
547
554
  return results;
548
555
  }
549
556
 
557
+ /**
558
+ * Setup telemetry .env.example
559
+ * @param {string} projectDir - Project directory
560
+ * @param {string} templatesDir - Templates directory
561
+ * @param {object} options - Setup options
562
+ * @returns {object} Results
563
+ */
564
+ export function setupTelemetryEnv(projectDir, templatesDir, options) {
565
+ const {force = false, dryRun = false} = options;
566
+
567
+ const results = {
568
+ created: 0,
569
+ overwritten: 0,
570
+ skipped: 0,
571
+ };
572
+
573
+ logInfo('Setting up telemetry .env.example...');
574
+
575
+ const src = join(templatesDir, 'telemetry', '.env.telemetry.example');
576
+ const dest = join(projectDir, '.env.example');
577
+
578
+ // Check if source template exists
579
+ if (!existsSync(src)) {
580
+ logWarn('Template not found: templates/telemetry/.env.telemetry.example');
581
+ return results;
582
+ }
583
+
584
+ // Check if destination exists
585
+ const destExists = existsSync(dest);
586
+
587
+ if (destExists && !force) {
588
+ // If .env.example exists, append telemetry section if not present
589
+ const existingContent = readFileSync(dest, 'utf-8');
590
+
591
+ if (existingContent.includes('Kabran Telemetry Configuration')) {
592
+ if (dryRun) {
593
+ logDry('Would skip .env.example (telemetry section already present)');
594
+ } else {
595
+ logSkip('.env.example (telemetry section already present)');
596
+ }
597
+ results.skipped = 1;
598
+ return results;
599
+ }
600
+
601
+ // Append telemetry section
602
+ const telemetryContent = readFileSync(src, 'utf-8');
603
+
604
+ if (dryRun) {
605
+ logDry('Would append telemetry section to .env.example');
606
+ results.overwritten = 1;
607
+ return results;
608
+ }
609
+
610
+ const newContent = existingContent.trimEnd() + '\n\n' + telemetryContent;
611
+ writeFileSync(dest, newContent, 'utf-8');
612
+ logSuccess('Appended telemetry section to: .env.example');
613
+ results.overwritten = 1;
614
+ return results;
615
+ }
616
+
617
+ const status = copyFile(src, dest, {overwrite: force, dryRun});
618
+
619
+ if (status === 'created' || status === 'would_create') {
620
+ results.created = 1;
621
+ } else if (status === 'overwritten' || status === 'would_overwrite') {
622
+ results.overwritten = 1;
623
+ } else {
624
+ results.skipped = 1;
625
+ }
626
+
627
+ return results;
628
+ }
629
+
550
630
  /**
551
631
  * Run setup
552
632
  * @param {string} projectDir - Project directory
@@ -561,6 +641,7 @@ export function runSetup(projectDir, options) {
561
641
  husky: {created: 0, overwritten: 0, skipped: 0},
562
642
  configs: {created: 0, overwritten: 0, skipped: 0},
563
643
  qualityStandard: {created: 0, overwritten: 0, skipped: 0},
644
+ telemetryEnv: {created: 0, overwritten: 0, skipped: 0},
564
645
  };
565
646
 
566
647
  console.log('');
@@ -592,11 +673,17 @@ export function runSetup(projectDir, options) {
592
673
  }
593
674
 
594
675
  // Setup quality-standard.md (unless skipped or in sync mode)
595
- if (!options.skipQualityStandard && !isSyncMode) {
676
+ if (!options.skipQualityStandard && !isSyncMode && !options.telemetryEnv) {
596
677
  summary.qualityStandard = setupQualityStandard(projectDir, templatesDir, options);
597
678
  console.log('');
598
679
  }
599
680
 
681
+ // Setup telemetry .env.example (only when explicitly requested)
682
+ if (options.telemetryEnv) {
683
+ summary.telemetryEnv = setupTelemetryEnv(projectDir, templatesDir, options);
684
+ console.log('');
685
+ }
686
+
600
687
  return summary;
601
688
  }
602
689
 
@@ -609,10 +696,10 @@ export function printSummary(summary) {
609
696
  console.log(`${colors.cyan}=== Setup Summary ===${colors.reset}`);
610
697
 
611
698
  const total = {
612
- created: summary.workflows.created + summary.husky.created + summary.configs.created + summary.qualityStandard.created,
699
+ created: summary.workflows.created + summary.husky.created + summary.configs.created + summary.qualityStandard.created + summary.telemetryEnv.created,
613
700
  overwritten:
614
- summary.workflows.overwritten + summary.husky.overwritten + summary.configs.overwritten + summary.qualityStandard.overwritten,
615
- skipped: summary.workflows.skipped + summary.husky.skipped + summary.configs.skipped + summary.qualityStandard.skipped,
701
+ summary.workflows.overwritten + summary.husky.overwritten + summary.configs.overwritten + summary.qualityStandard.overwritten + summary.telemetryEnv.overwritten,
702
+ skipped: summary.workflows.skipped + summary.husky.skipped + summary.configs.skipped + summary.qualityStandard.skipped + summary.telemetryEnv.skipped,
616
703
  };
617
704
 
618
705
  if (total.created > 0) {
@@ -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)