@elto/telemetry
v0.1.2
Published
OpenTelemetry integration for Effect with Node.js and browser support
Maintainers
Readme
@elto/telemetry
OpenTelemetry integration for Effect with Node.js and browser support.
Features
- Node.js Support: Full OpenTelemetry integration with traces, metrics, and logs
- Browser Support: Web-optimized telemetry with traces and logs
- Dual Mode: Browser apps can send telemetry directly to collector or proxy through server
- Effect Integration: Built on top of
@effect/opentelemetryfor seamless Effect usage - TypeScript: Full type safety and excellent IDE support
- Simple API: Clean, declarative configuration
Installation
pnpm add @elto/telemetry effectQuick Start
Node.js
import { createNodeOtelLayer, createEndpoints } from "@elto/telemetry/node";
const OtelLive = createNodeOtelLayer({
resource: {
serviceName: "api-server",
serviceVersion: "1.0.0",
},
endpoints: createEndpoints("http://localhost:4318"),
});
// Use with your Effect program
const program = Effect.gen(function* () {
// Your telemetry-enabled code here
}).pipe(Effect.provide(OtelLive));Browser
import { createWebOtelLayer, createModeBasedEndpoints } from "@elto/telemetry/web";
const OtelLive = createWebOtelLayer({
resource: {
serviceName: "web-client",
serviceVersion: "1.0.0",
},
endpoints: { traces: "", logs: "" }, // fallback
getEndpoints: createModeBasedEndpoints(
"http://localhost:4318",
{ serverUrl: "https://api.example.com" }
),
});
// Use with your Effect program or RPC client
import { withOtel } from "@elto/telemetry/web";
const ClientLive = withOtel(OtelLive, RpcClientLayer);Span Processing
@elto/telemetry applies a built-in custom span processor by default. Both
createNodeOtelLayer and createWebOtelLayer wrap the batch span processor
with an opinionated sanitizer before export.
Behavior:
- Redacts
user.email,note.id, andprofile.idand adds<key>_hash - Redacts
db.connection_stringand addsdb.connection_string_redacted - Shortens
db.statementwhen it exceeds the internal length threshold - Strips query strings from
http.url - Adds
http.status_categorywhenhttp.status_codeis present - Adds
telemetry.redactionsandtelemetry.transformationscounters - Drops spans when
telemetry.dropistrueor"true"
Not redacted or hashed: user.id, cache.key, request.id, request.client_id.
To explicitly drop a span, annotate it:
Effect.annotateCurrentSpan({
"telemetry.drop": true,
"telemetry.drop_reason": "debug-only",
});If you need full control, build your own OpenTelemetry layer using
@effect/opentelemetry or wrap a processor with createCustomSpanProcessor
from @elto/telemetry.
Browser Logger (Non-Effect)
For browser contexts where Effect is not available:
import { createBrowserLogger, getOtelMode } from "@elto/telemetry/web";
const getEndpoint = () =>
getOtelMode() === "proxy"
? "https://api.example.com/api/signals"
: "http://localhost:4318/v1/logs";
const logger = createBrowserLogger({
resource: {
serviceName: "web-client",
serviceVersion: "1.0.0",
},
logsEndpoint: getEndpoint,
});
logger.info("User logged in", { userId: "123" });
logger.error("Operation failed", { operation: "checkout", error: "timeout" });
await logger.flush(); // Flush before page unloadAPI Reference
Common (@elto/telemetry)
createCustomSpanProcessor(delegate: SpanProcessor)
Wraps a delegate span processor (for example, BatchSpanProcessor) with the
sanitization rules described above.
Node.js (@elto/telemetry/node)
createNodeOtelLayer(config: NodeSdkConfig)
Creates an Effect Layer with OpenTelemetry configured for Node.js.
Config:
resource.serviceName- Service name for telemetryresource.serviceVersion- Service versionendpoints- Endpoint URLs (usecreateEndpoints()helper)metricExportIntervalMs- Optional, default 10000ms
Returns: Effect Layer for OpenTelemetry
createEndpoints(baseUrl: string)
Generates OTLP endpoint URLs for traces, logs, and metrics.
Example:
createEndpoints("http://localhost:4318")
// Returns:
// {
// traces: "http://localhost:4318/v1/traces",
// logs: "http://localhost:4318/v1/logs",
// metrics: "http://localhost:4318/v1/metrics"
// }DEFAULT_OTEL_ENDPOINT
Default OpenTelemetry collector endpoint: "http://localhost:4318"
Browser (@elto/telemetry/web)
createWebOtelLayer(config: WebSdkConfig)
Creates an Effect Layer with OpenTelemetry configured for browsers.
Config:
resource.serviceName- Service name for telemetryresource.serviceVersion- Service versionendpoints- Fallback endpoint URLsgetEndpoints- Optional function to dynamically get endpoints (for mode switching)
Returns: Effect Layer for OpenTelemetry
createDirectEndpoints(baseUrl: string)
Creates endpoints for direct connection to OpenTelemetry collector.
createProxyEndpoints(config: ProxyConfig)
Creates endpoints for proxy mode (telemetry sent through server).
Config:
serverUrl- Your server URLtracesPath- Optional, defaults to "/api/events"logsPath- Optional, defaults to "/api/signals"
createModeBasedEndpoints(directUrl: string, proxyConfig: ProxyConfig)
Creates a function that returns endpoints based on the current mode.
Example:
const getEndpoints = createModeBasedEndpoints(
"http://localhost:4318",
{ serverUrl: "https://api.example.com" }
);getOtelMode(): OtelMode
Returns the current telemetry mode from localStorage ("direct" or "proxy").
setOtelMode(mode: OtelMode): void
Sets the telemetry mode in localStorage.
withOtel(otelLayer, layer)
Helper to merge OpenTelemetry layer with another layer.
createBrowserLogger(config: BrowserLoggerConfig)
Creates a standalone logger for non-Effect browser contexts.
Config:
resource.serviceName- Service nameresource.serviceVersion- Service versionlogsEndpoint- Endpoint URL or function that returns URL
Returns: Logger instance with methods: debug, info, warning, error, flush
Proxy Mode
Browser apps can send telemetry through your server instead of directly to the collector. This:
- Hides OpenTelemetry infrastructure from the browser
- Works around CORS restrictions
- Provides obscured endpoint names (
/api/events,/api/signals)
Implementation
- Browser: Use
createModeBasedEndpoints()to support both modes - Server: Add proxy endpoints that forward to your collector
- User Control: Use
setOtelMode("proxy")to switch modes
Example server proxy (Express):
app.post("/api/events", async (req, res) => {
// Forward to OTLP collector traces endpoint
await fetch("http://localhost:4318/v1/traces", {
method: "POST",
headers: req.headers,
body: req.body,
});
res.sendStatus(200);
});
app.post("/api/signals", async (req, res) => {
// Forward to OTLP collector logs endpoint
await fetch("http://localhost:4318/v1/logs", {
method: "POST",
headers: req.headers,
body: req.body,
});
res.sendStatus(200);
});TypeScript
All types are exported from the main package and subpath exports:
import type { ResourceConfig, EndpointConfig } from "@elto/telemetry";
import type { NodeSdkConfig } from "@elto/telemetry/node";
import type { WebSdkConfig, OtelMode, BrowserLogger } from "@elto/telemetry/web";Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
Apache-2.0
