@reactionary/source 0.0.28 → 0.0.29

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/core/package.json CHANGED
@@ -3,6 +3,7 @@
3
3
  "version": "0.0.1",
4
4
  "dependencies": {
5
5
  "zod": "4.0.0-beta.20250430T185432",
6
- "@upstash/redis": "^1.34.9"
6
+ "@upstash/redis": "^1.34.9",
7
+ "@reactionary/otel": "0.0.1"
7
8
  }
8
9
  }
@@ -3,6 +3,7 @@ import { Session } from '../schemas/session.schema';
3
3
  import { BaseQuery } from '../schemas/queries/base.query';
4
4
  import { BaseMutation } from '../schemas/mutations/base.mutation';
5
5
  import { BaseModel } from '../schemas/models/base.model';
6
+ import { createProviderInstrumentation } from '@reactionary/otel';
6
7
 
7
8
  /**
8
9
  * Base capability provider, responsible for mutations (changes) and queries (fetches)
@@ -13,7 +14,11 @@ export abstract class BaseProvider<
13
14
  Q extends BaseQuery = BaseQuery,
14
15
  M extends BaseMutation = BaseMutation
15
16
  > {
16
- constructor(public readonly schema: z.ZodType<T>, public readonly querySchema: z.ZodType<Q, Q>, public readonly mutationSchema: z.ZodType<M, M>) {}
17
+ private instrumentation: ReturnType<typeof createProviderInstrumentation>;
18
+
19
+ constructor(public readonly schema: z.ZodType<T>, public readonly querySchema: z.ZodType<Q, Q>, public readonly mutationSchema: z.ZodType<M, M>) {
20
+ this.instrumentation = createProviderInstrumentation(this.constructor.name);
21
+ }
17
22
 
18
23
  /**
19
24
  * Validates that the final domain model constructed by the provider
@@ -37,13 +42,21 @@ export abstract class BaseProvider<
37
42
  * of the results will match the order of the queries.
38
43
  */
39
44
  public async query(queries: Q[], session: Session): Promise<T[]> {
40
- const results = await this.fetch(queries, session);
45
+ return this.instrumentation.traceQuery(
46
+ 'query',
47
+ async (span) => {
48
+ span.setAttribute('provider.query.count', queries.length);
49
+ const results = await this.fetch(queries, session);
41
50
 
42
- for (const result of results) {
43
- this.assert(result);
44
- }
51
+ for (const result of results) {
52
+ this.assert(result);
53
+ }
45
54
 
46
- return results;
55
+ span.setAttribute('provider.result.count', results.length);
56
+ return results;
57
+ },
58
+ { queryCount: queries.length }
59
+ );
47
60
  }
48
61
 
49
62
  /**
@@ -51,11 +64,18 @@ export abstract class BaseProvider<
51
64
  * resulting from that set of operations.
52
65
  */
53
66
  public async mutate(mutations: M[], session: Session): Promise<T> {
54
- const result = await this.process(mutations, session);
67
+ return this.instrumentation.traceMutation(
68
+ 'mutate',
69
+ async (span) => {
70
+ span.setAttribute('provider.mutation.count', mutations.length);
71
+ const result = await this.process(mutations, session);
55
72
 
56
- this.assert(result);
73
+ this.assert(result);
57
74
 
58
- return result;
75
+ return result;
76
+ },
77
+ { mutationCount: mutations.length }
78
+ );
59
79
  }
60
80
 
61
81
  /**
@@ -0,0 +1,52 @@
1
+ # OpenTelemetry Configuration (Standard Environment Variables)
2
+ # See: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
3
+ # ============================
4
+
5
+ # Service identification
6
+ OTEL_SERVICE_NAME=reactionary-trpc-example
7
+ OTEL_SERVICE_VERSION=1.0.0
8
+ DEPLOYMENT_ENVIRONMENT=development
9
+
10
+ # Traces exporter: console | otlp | otlp/http | none
11
+ OTEL_TRACES_EXPORTER=console
12
+
13
+ # Metrics exporter: console | otlp | otlp/http | none
14
+ OTEL_METRICS_EXPORTER=console
15
+
16
+ # For OTLP exporters (e.g., Honeycomb, Jaeger, Grafana)
17
+ # OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io
18
+ # OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=your-api-key
19
+
20
+ # Or use specific endpoints for traces/metrics
21
+ # OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.honeycomb.io/v1/traces
22
+ # OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://api.honeycomb.io/v1/metrics
23
+
24
+ # For local Jaeger
25
+ # OTEL_TRACES_EXPORTER=otlp
26
+ # OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
27
+
28
+ # Debug logging
29
+ # OTEL_LOG_LEVEL=debug
30
+
31
+ # Metrics export interval (milliseconds)
32
+ OTEL_METRIC_EXPORT_INTERVAL=60000
33
+
34
+ # Session Store Configuration
35
+ # ============================
36
+ SESSION_STORE_REDIS_CONNECTION=redis://localhost:6379
37
+ SESSION_STORE_SECRET=your-session-secret-here
38
+
39
+ # Provider API Keys
40
+ # ==================
41
+ ALGOLIA_API_KEY=your-algolia-api-key
42
+ ALGOLIA_APP_ID=your-algolia-app-id
43
+ ALGOLIA_INDEX=your-algolia-index
44
+
45
+ COMMERCETOOLS_API_URL=https://api.commercetools.com
46
+ COMMERCETOOLS_AUTH_URL=https://auth.commercetools.com
47
+ COMMERCETOOLS_CLIENT_ID=your-client-id
48
+ COMMERCETOOLS_CLIENT_SECRET=your-client-secret
49
+ COMMERCETOOLS_PROJECT_KEY=your-project-key
50
+
51
+ POSTHOG_API_KEY=your-posthog-api-key
52
+ POSTHOG_HOST=https://app.posthog.com
@@ -52,4 +52,12 @@ app.use(
52
52
  })
53
53
  );
54
54
 
55
- app.listen(3000);
55
+ const server = app.listen(3000, () => {
56
+ console.log('Server started on http://localhost:3000');
57
+ // OTEL auto-initializes based on standard env vars
58
+ if (process.env['OTEL_LOG_LEVEL'] === 'debug') {
59
+ console.log('OTEL traces exporter:', process.env['OTEL_TRACES_EXPORTER'] || 'console');
60
+ console.log('OTEL metrics exporter:', process.env['OTEL_METRICS_EXPORTER'] || 'console');
61
+ }
62
+ });
63
+
package/otel/README.md ADDED
@@ -0,0 +1,248 @@
1
+ # @reactionary/otel
2
+
3
+ Zero-configuration OpenTelemetry instrumentation for Reactionary framework. Automatically instruments tRPC routes and providers using standard OTEL environment variables.
4
+
5
+ ## Features
6
+
7
+ - **Zero Configuration**: Auto-initializes on first use with standard OTEL env vars
8
+ - **Automatic tRPC Route Tracing**: All tRPC procedures automatically traced
9
+ - **Provider Instrumentation**: BaseProvider operations automatically instrumented
10
+ - **Standard Compliance**: Uses official OpenTelemetry environment variables
11
+ - **Multiple Exporters**: Console, OTLP/HTTP, or custom exporters
12
+ - **Metrics Collection**: Request counts, durations, errors automatically tracked
13
+ - **Lazy Initialization**: Only starts when actually used
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add @reactionary/otel
19
+ ```
20
+
21
+ That's it! No initialization code needed.
22
+
23
+ ## How It Works
24
+
25
+ The OTEL package automatically initializes itself on first use, reading configuration from standard OpenTelemetry environment variables. When your code first creates a span or metric, the SDK initializes with your environment configuration.
26
+
27
+ ```typescript
28
+ // No imports or initialization needed!
29
+ // Just use your tRPC router or providers normally
30
+ import { createTRPCRouter } from '@reactionary/trpc';
31
+
32
+ const router = createTRPCRouter(client);
33
+ // ↑ Automatically instrumented when OTEL env vars are set
34
+ ```
35
+
36
+ ## Configuration
37
+
38
+ Use standard OpenTelemetry environment variables. No code changes needed.
39
+
40
+ ### Standard Environment Variables
41
+
42
+ ```bash
43
+ # Service identification
44
+ OTEL_SERVICE_NAME=my-service
45
+ OTEL_SERVICE_VERSION=1.0.0
46
+ DEPLOYMENT_ENVIRONMENT=production
47
+
48
+ # Traces exporter (console | otlp | otlp/http | none)
49
+ OTEL_TRACES_EXPORTER=otlp
50
+
51
+ # Metrics exporter (console | otlp | otlp/http | none)
52
+ OTEL_METRICS_EXPORTER=otlp
53
+
54
+ # OTLP endpoint and headers
55
+ OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io
56
+ OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=your-api-key
57
+
58
+ # Or use specific endpoints
59
+ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.honeycomb.io/v1/traces
60
+ OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://api.honeycomb.io/v1/metrics
61
+
62
+ # Debug logging
63
+ OTEL_LOG_LEVEL=debug
64
+
65
+ # Metrics export interval (milliseconds)
66
+ OTEL_METRIC_EXPORT_INTERVAL=60000
67
+ ```
68
+
69
+ See the [OpenTelemetry specification](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/) for all available options.
70
+
71
+ ## Exporters
72
+
73
+ ### Console (Development)
74
+ ```bash
75
+ OTEL_TRACES_EXPORTER=console
76
+ OTEL_METRICS_EXPORTER=console
77
+ ```
78
+
79
+ ### OTLP (Production)
80
+ Works with any OTLP-compatible backend:
81
+
82
+ ```bash
83
+ OTEL_TRACES_EXPORTER=otlp
84
+ OTEL_METRICS_EXPORTER=otlp
85
+ OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io
86
+ OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=your-api-key
87
+ ```
88
+
89
+ ### Disable
90
+ ```bash
91
+ OTEL_TRACES_EXPORTER=none
92
+ OTEL_METRICS_EXPORTER=none
93
+ ```
94
+
95
+ ## Custom Instrumentation
96
+
97
+ ### Manual Spans
98
+
99
+ Create custom spans for specific operations:
100
+
101
+ ```typescript
102
+ import { withSpan, getTracer } from '@reactionary/otel';
103
+
104
+ // Using withSpan helper
105
+ const result = await withSpan('custom-operation', async (span) => {
106
+ span.setAttribute('custom.attribute', 'value');
107
+ // Your operation here
108
+ return someAsyncOperation();
109
+ });
110
+
111
+ // Using tracer directly
112
+ const tracer = getTracer();
113
+ const span = tracer.startSpan('manual-span');
114
+ try {
115
+ // Your operation
116
+ span.setStatus({ code: SpanStatusCode.OK });
117
+ } catch (error) {
118
+ span.recordException(error);
119
+ span.setStatus({ code: SpanStatusCode.ERROR });
120
+ throw error;
121
+ } finally {
122
+ span.end();
123
+ }
124
+ ```
125
+
126
+ ### Provider Instrumentation
127
+
128
+ Providers are automatically instrumented when extending BaseProvider:
129
+
130
+ ```typescript
131
+ import { BaseProvider } from '@reactionary/core';
132
+
133
+ class MyProvider extends BaseProvider {
134
+ // Automatically traced when OTEL is initialized
135
+ protected async fetch(queries, session) {
136
+ // Your implementation
137
+ }
138
+
139
+ protected async process(mutations, session) {
140
+ // Your implementation
141
+ }
142
+ }
143
+ ```
144
+
145
+ ### Custom Metrics
146
+
147
+ Track custom business metrics:
148
+
149
+ ```typescript
150
+ import { getMetrics } from '@reactionary/otel';
151
+
152
+ const metrics = getMetrics();
153
+
154
+ // Increment counter
155
+ metrics.requestCounter.add(1, {
156
+ 'endpoint': '/api/users',
157
+ 'method': 'GET'
158
+ });
159
+
160
+ // Record histogram
161
+ metrics.requestDuration.record(150, {
162
+ 'endpoint': '/api/users',
163
+ 'status': 'success'
164
+ });
165
+ ```
166
+
167
+ ## Examples
168
+
169
+ ### Honeycomb
170
+
171
+ ```bash
172
+ OTEL_SERVICE_NAME=my-service
173
+ OTEL_TRACES_EXPORTER=otlp
174
+ OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io
175
+ OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=your-api-key
176
+ ```
177
+
178
+ ### Local Development
179
+
180
+ ```bash
181
+ OTEL_SERVICE_NAME=my-service-dev
182
+ OTEL_TRACES_EXPORTER=console
183
+ OTEL_METRICS_EXPORTER=none # Disable metrics in dev
184
+ ```
185
+
186
+ ### Docker Compose with Jaeger
187
+
188
+ ```yaml
189
+ services:
190
+ app:
191
+ environment:
192
+ - OTEL_EXPORTER_TYPE=otlp
193
+ - OTEL_COLLECTOR_ENDPOINT=http://jaeger:4318
194
+ - OTEL_SERVICE_NAME=my-service
195
+
196
+ jaeger:
197
+ image: jaegertracing/all-in-one:latest
198
+ ports:
199
+ - "16686:16686" # Jaeger UI
200
+ - "4318:4318" # OTLP HTTP
201
+ ```
202
+
203
+ ## Metrics Reference
204
+
205
+ The following metrics are automatically collected:
206
+
207
+ | Metric | Type | Description |
208
+ |--------|------|-------------|
209
+ | `reactionary.requests` | Counter | Total number of requests |
210
+ | `reactionary.request.duration` | Histogram | Request duration in milliseconds |
211
+ | `reactionary.requests.active` | UpDownCounter | Number of active requests |
212
+ | `reactionary.errors` | Counter | Total number of errors |
213
+ | `reactionary.provider.calls` | Counter | Total provider calls |
214
+ | `reactionary.provider.duration` | Histogram | Provider call duration |
215
+ | `reactionary.cache.hits` | Counter | Cache hit count |
216
+ | `reactionary.cache.misses` | Counter | Cache miss count |
217
+
218
+ ## Best Practices
219
+
220
+ 1. **Use Standard Variables**: Stick to OpenTelemetry standard environment variables
221
+ 2. **Set Service Name**: Always set `OTEL_SERVICE_NAME` for service identification
222
+ 3. **Environment-based Config**: Use different configs for dev/staging/production
223
+ 4. **Add Context**: Use span attributes to add business context to traces
224
+ 5. **Handle Errors**: Ensure spans are properly closed even on errors
225
+ 6. **Sample Wisely**: Consider sampling strategies for high-volume services
226
+ 7. **Monitor Performance**: Watch for overhead in high-throughput scenarios
227
+
228
+ ## Troubleshooting
229
+
230
+ ### Traces Not Appearing
231
+
232
+ 1. Check OTEL is initialized before other components
233
+ 2. Verify `OTEL_TRACE_ENABLED` is not set to `false`
234
+ 3. Check exporter configuration and endpoint connectivity
235
+ 4. Look for initialization errors in console
236
+
237
+ ### Performance Impact
238
+
239
+ - Use sampling to reduce overhead
240
+ - Disable metrics if not needed
241
+ - Consider using batch exporters
242
+ - Increase export intervals for metrics
243
+
244
+ ### Memory Usage
245
+
246
+ - Monitor span processor queue size
247
+ - Adjust batch size and timeout
248
+ - Consider using sampling for high-volume services
@@ -0,0 +1,23 @@
1
+ import baseConfig from '../eslint.config.mjs';
2
+
3
+ export default [
4
+ ...baseConfig,
5
+ {
6
+ files: ['**/*.json'],
7
+ rules: {
8
+ '@nx/dependency-checks': [
9
+ 'error',
10
+ {
11
+ ignoredFiles: [
12
+ '{projectRoot}/eslint.config.{js,cjs,mjs}',
13
+ '{projectRoot}/esbuild.config.{js,ts,mjs,mts}',
14
+ '{projectRoot}/vite.config.{js,ts,mjs,mts}',
15
+ ],
16
+ },
17
+ ],
18
+ },
19
+ languageOptions: {
20
+ parser: await import('jsonc-eslint-parser'),
21
+ },
22
+ },
23
+ ];
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@reactionary/otel",
3
+ "version": "0.0.1",
4
+ "type": "commonjs",
5
+ "main": "./src/index.js",
6
+ "types": "./src/index.d.ts",
7
+ "dependencies": {
8
+ "@opentelemetry/api": "^1.9.0",
9
+ "@opentelemetry/sdk-node": "^0.203.0",
10
+ "@opentelemetry/sdk-trace-base": "^2.0.1",
11
+ "@opentelemetry/sdk-metrics": "^2.0.1",
12
+ "@opentelemetry/resources": "^2.0.1",
13
+ "@opentelemetry/semantic-conventions": "^1.36.0",
14
+ "@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
15
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.203.0",
16
+ "@opentelemetry/instrumentation-http": "^0.203.0",
17
+ "@opentelemetry/instrumentation-express": "^0.52.0",
18
+ "@trpc/server": "^11.1.2"
19
+ }
20
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "otel",
3
+ "$schema": "../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "otel/src",
5
+ "projectType": "library",
6
+ "release": {
7
+ "version": {
8
+ "manifestRootsToUpdate": ["dist/{projectRoot}"],
9
+ "currentVersionResolver": "git-tag",
10
+ "fallbackCurrentVersionResolver": "disk"
11
+ }
12
+ },
13
+ "tags": [],
14
+ "targets": {
15
+ "build": {
16
+ "executor": "@nx/esbuild:esbuild",
17
+ "outputs": ["{options.outputPath}"],
18
+ "options": {
19
+ "outputPath": "dist/otel",
20
+ "main": "otel/src/index.ts",
21
+ "tsConfig": "otel/tsconfig.lib.json",
22
+ "assets": ["otel/*.md"],
23
+ "format": ["cjs"],
24
+ "generatePackageJson": true
25
+ }
26
+ },
27
+ "nx-release-publish": {
28
+ "options": {
29
+ "packageRoot": "dist/{projectRoot}"
30
+ }
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,139 @@
1
+ import { SpanExporter } from '@opentelemetry/sdk-trace-base';
2
+ import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base';
3
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
4
+ import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
5
+ import { MetricReader, PeriodicExportingMetricReader, ConsoleMetricExporter } from '@opentelemetry/sdk-metrics';
6
+
7
+ // Internal config interface - not exported
8
+ interface OtelConfig {
9
+ serviceName: string;
10
+ serviceVersion?: string;
11
+ environment?: string;
12
+ otlpEndpoint?: string;
13
+ otlpHeaders?: Record<string, string>;
14
+ traceEnabled: boolean;
15
+ metricsEnabled: boolean;
16
+ metricExportIntervalMillis: number;
17
+ }
18
+
19
+ // Detect if we're running in a browser environment
20
+ function isBrowser(): boolean {
21
+ return typeof window !== 'undefined' && typeof process === 'undefined';
22
+ }
23
+
24
+ // Parse config from standard OTEL environment variables
25
+ // https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
26
+ export function getConfigFromEnv(): OtelConfig {
27
+ // In browser environments, return disabled config
28
+ if (isBrowser()) {
29
+ return {
30
+ serviceName: 'browser-service',
31
+ serviceVersion: undefined,
32
+ environment: 'browser',
33
+ otlpEndpoint: undefined,
34
+ otlpHeaders: undefined,
35
+ traceEnabled: false,
36
+ metricsEnabled: false,
37
+ metricExportIntervalMillis: 60000,
38
+ };
39
+ }
40
+
41
+ // Determine exporter type from standard OTEL vars
42
+ const tracesExporter = process.env['OTEL_TRACES_EXPORTER'] || 'console';
43
+ const metricsExporter = process.env['OTEL_METRICS_EXPORTER'] || 'console';
44
+
45
+ return {
46
+ serviceName: process.env['OTEL_SERVICE_NAME'] || 'unknown_service',
47
+ serviceVersion: process.env['OTEL_SERVICE_VERSION'],
48
+ environment: process.env['DEPLOYMENT_ENVIRONMENT'] || process.env['NODE_ENV'] || 'development',
49
+ otlpEndpoint: process.env['OTEL_EXPORTER_OTLP_ENDPOINT'],
50
+ otlpHeaders: process.env['OTEL_EXPORTER_OTLP_HEADERS']
51
+ ? parseHeaders(process.env['OTEL_EXPORTER_OTLP_HEADERS'])
52
+ : undefined,
53
+ traceEnabled: tracesExporter !== 'none',
54
+ metricsEnabled: metricsExporter !== 'none',
55
+ metricExportIntervalMillis: process.env['OTEL_METRIC_EXPORT_INTERVAL']
56
+ ? parseInt(process.env['OTEL_METRIC_EXPORT_INTERVAL'], 10)
57
+ : 60000, // Default 60 seconds per OTEL spec
58
+ };
59
+ }
60
+
61
+ function parseHeaders(headerString: string): Record<string, string> {
62
+ const headers: Record<string, string> = {};
63
+ headerString.split(',').forEach(header => {
64
+ const [key, value] = header.split('=');
65
+ if (key && value) {
66
+ headers[key.trim()] = value.trim();
67
+ }
68
+ });
69
+ return headers;
70
+ }
71
+
72
+ export function createTraceExporter(config: OtelConfig): SpanExporter | undefined {
73
+ if (!config.traceEnabled) {
74
+ return undefined;
75
+ }
76
+
77
+ const tracesExporter = process.env['OTEL_TRACES_EXPORTER'] || 'console';
78
+
79
+ switch (tracesExporter) {
80
+ case 'otlp':
81
+ case 'otlp/http': {
82
+ if (!config.otlpEndpoint) {
83
+ console.warn('OTEL_EXPORTER_OTLP_ENDPOINT not set, falling back to console');
84
+ return new ConsoleSpanExporter();
85
+ }
86
+ // Use standard endpoint resolution
87
+ const tracesEndpoint = process.env['OTEL_EXPORTER_OTLP_TRACES_ENDPOINT'] ||
88
+ `${config.otlpEndpoint}/v1/traces`;
89
+ return new OTLPTraceExporter({
90
+ url: tracesEndpoint,
91
+ headers: config.otlpHeaders,
92
+ });
93
+ }
94
+ case 'console':
95
+ return new ConsoleSpanExporter();
96
+ case 'none':
97
+ default:
98
+ return undefined;
99
+ }
100
+ }
101
+
102
+ export function createMetricReader(config: OtelConfig): MetricReader | undefined {
103
+ if (!config.metricsEnabled) {
104
+ return undefined;
105
+ }
106
+
107
+ const metricsExporter = process.env['OTEL_METRICS_EXPORTER'] || 'console';
108
+ let exporter;
109
+
110
+ switch (metricsExporter) {
111
+ case 'otlp':
112
+ case 'otlp/http': {
113
+ if (!config.otlpEndpoint) {
114
+ console.warn('OTEL_EXPORTER_OTLP_ENDPOINT not set, falling back to console');
115
+ exporter = new ConsoleMetricExporter();
116
+ } else {
117
+ // Use standard endpoint resolution
118
+ const metricsEndpoint = process.env['OTEL_EXPORTER_OTLP_METRICS_ENDPOINT'] ||
119
+ `${config.otlpEndpoint}/v1/metrics`;
120
+ exporter = new OTLPMetricExporter({
121
+ url: metricsEndpoint,
122
+ headers: config.otlpHeaders,
123
+ });
124
+ }
125
+ break;
126
+ }
127
+ case 'console':
128
+ exporter = new ConsoleMetricExporter();
129
+ break;
130
+ case 'none':
131
+ default:
132
+ return undefined;
133
+ }
134
+
135
+ return new PeriodicExportingMetricReader({
136
+ exporter,
137
+ exportIntervalMillis: config.metricExportIntervalMillis,
138
+ });
139
+ }
@@ -0,0 +1,12 @@
1
+ // SDK functions - auto-initialization handled internally
2
+ export { isOtelInitialized, shutdownOtel } from './sdk';
3
+
4
+ // Tracing utilities
5
+ export * from './tracer';
6
+ export * from './metrics';
7
+ export * from './trpc-middleware';
8
+ export * from './provider-instrumentation';
9
+
10
+ // Re-export common OTEL types
11
+ export { trace, context, SpanKind, SpanStatusCode } from '@opentelemetry/api';
12
+ export type { Span, Tracer, SpanOptions, Attributes } from '@opentelemetry/api';