DEV Community

Cover image for let Jev score OpenTelemetry logs before a bigger LLM sees them
jalil laaraichi
jalil laaraichi

Posted on Originally published at github.com

let Jev score OpenTelemetry logs before a bigger LLM sees them

Health checks. Cache hits. A payment failure hiding in the middle. If every OpenTelemetry log goes into a reasoning model, you pay for noise before the investigation starts.

Jev Logs is a small open-source layer that puts TypeSafe’s Jev in front of those logs. Jev makes the first decision: how useful is this record, how urgent is it, and does it deserve a more expensive model?

I wrote it. MIT licensed. Independent not a TypeSafe, Vercel, or OpenTelemetry product.

GitHub logo reachjalil / jevlogs

Open-source Jev log triage for OpenTelemetry. Score the signal before expensive LLM analysis.

Jev Logs — Keep your logs. Spend on the signal. A relaxed robot in blue headphones sorts log records into background and signal.

Jev Logs

A little intelligence between your logs and your LLM bill.
Score, prioritize, and route OpenTelemetry logs with Jev. Keep the signal. Keep your stack

npm version CI status MIT license Node.js 22 or later Status: preview

Website · Guide · llms.txt · npm · Feedback


Meet your log filter’s smarter friend

Health checks. Cache hits. A payment failure hiding in the middle. Sending every event to a reasoning model adds cost before the investigation even starts.

Jev Logs makes the first decision: how useful is this log, how urgent is it, and does it deserve deeper analysis? It uses TypeSafe’s Jev through the Vercel AI SDK, with a small TypeScript API and an OpenTelemetry exporter wrapper.

























A small layer What you get
Score the signal A 0–100 diagnostic-value score, priority, and actionable probability.
Keep your pipeline Wrap your existing exporter; preserve resource, scope, timestamps, and trace context.
Start with visibility Annotation mode keeps every record and attaches jev.* attributes.
Spend





What Jev actually returns

Jev is built for structured choices, not paragraphs. For each log, Jev Logs asks it for:

  • a diagnostic value (0–100)
  • a priority (critical, high, normal, low)
  • an actionable probability
  • a route: analyze or retain

Your archive still gets every record. The analysis branch only needs the ones Jev (or a rule, or a conservative fallback) says are worth it.

A log may skip deeper analysis only when all three are true: priority is low, value is 25 or below, and actionable probability is under 0.1. Errors, jev.protected records, timeouts, and provider failures stay eligible. Nothing in the SDK deletes your logs.

Try Jev Logs in one command

Offline demo. No key. No network.

npx jevlogs
Enter fullscreen mode Exit fullscreen mode
   0 / 100  low      RETAIN   GET /health returned 200 in 2ms
  25 / 100  low      RETAIN   Cache hit for product:482
 100 / 100  critical ANALYZE  Payment capture failed after three retries
  75 / 100  high     ANALYZE  Database connection pool at 94% capacity
Enter fullscreen mode Exit fullscreen mode

That walkthrough uses fixed answers so you can see the shape. It does not call Jev.

Real Jev, still on your machine:

export AI_GATEWAY_API_KEY=your-vercel-ai-gateway-key

npx jevlogs --live --sample
npx jevlogs --live --file ./app.log --limit 20
Enter fullscreen mode Exit fullscreen mode

--live alone starts a local OTLP HTTP/JSON receiver on http://127.0.0.1:4318/v1/logs. Point your app at it; Jev Logs prints one JSON decision per record and can forward annotated batches to the collector you already run. Node.js 22+.

Score a log from TypeScript

npm install jevlogs
Enter fullscreen mode Exit fullscreen mode
import { createJevLogs } from "jevlogs";

const jev = createJevLogs();
const decision = await jev.triage({
  body: "Database connection pool at 94% capacity",
  severityText: "WARN",
});

console.log(decision);
// value · priority · route · actionableProbability · reason
Enter fullscreen mode Exit fullscreen mode

Skip health checks without spending a Jev call:

const jev = createJevLogs({
  rules: [{ name: "health", match: "^GET /health", route: "retain" }],
});
Enter fullscreen mode Exit fullscreen mode

Already on OpenTelemetry?

Wrap the exporter you have. Annotation mode keeps every log and attaches jev.* attributes.

import {
  LoggerProvider,
  BatchLogRecordProcessor,
  ConsoleLogRecordExporter,
} from "@opentelemetry/sdk-logs";
import { JevLogExporter } from "jevlogs";

const provider = new LoggerProvider({
  processors: [
    new BatchLogRecordProcessor({
      exporter: new JevLogExporter({
        exporter: new ConsoleLogRecordExporter(),
        mode: "annotate",
      }),
      maxExportBatchSize: 16,
    }),
  ],
});
Enter fullscreen mode Exit fullscreen mode

Keep that archive processor. Add a second exporter with mode: "analysis-only" when you actually want to drop low-value records from the LLM path. Annotation alone does not cut the bill — the downstream pipeline has to honor route.

Why Jev, not “another LLM pass”

A second giant completion per log is the thing this is trying to avoid. Jev’s published rate is cheap structured evaluation (TypeSafe lists $0.042/M input, free output). You pay Jev for a small decision, then pay GPT-class analysis only for the selected slice.

The README has an illustrative table: 1M logs/month, if 10% still need analysis, a $1,000 GPT-4.1-style bill models down to about $129 including Jev triage. That is not a measured production result. Measure incident recall on your logs before you filter.

What this is not

No hosted dashboard. No log storage. No Collector plugin. No root-cause write-up. Preview software: jevlogs on npm, TypeScript first, CI on the repo. Jev itself is a hosted model via Vercel AI Gateway; this repo is the integration.

Links

If you try it, I want feedback on the Jev scoring/routing shape and whether wrapping an exporter is the right split vs the local receiver. Issues with sanitized examples are welcome.

Top comments (0)