> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.hyjal.cloud/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.hyjal.cloud/_mcp/server.

# Instrument a Resource with OpenTelemetry

> Emit structured OTLP logs to the telemetry Door from Rust with nanocar or from JavaScript with an OTLP exporter, then read severity badges in the Logs tab.

This tutorial sends structured OpenTelemetry (OTLP) logs from a Resource to the
reserved `telemetry` [Door](/get-started/what-is-hyjal). You will emit
severity-tagged log records (from Rust with the `nanocar` guest library or
from JavaScript with an OTLP/HTTP exporter), deploy, and read the structured
severity badges in the Logs tab. It takes about fifteen minutes.

Every Resource can push OTLP logs to the `telemetry` Door with no network
configuration; the Door is host-provided. Raw stdout and stderr capture always
works alongside it. See [Telemetry](/observability/telemetry) for the
reference.

## Prerequisites

* A deployed Resource. This tutorial uses `acme` with an `api` Resource.
* The [`hyjal` CLI](/reference/cli) installed and logged in.
* The toolchain for your lane: Rust with `cargo component`, or Node.js for the
  JS lane.

## 1. Emit structured logs

Pick the lane that matches your Resource.

### Rust with nanocar

Add the `nanocar` guest library and initialize it once at the top of your
handler. It routes OTLP log records to the `telemetry` Door. Emit records with
a severity and message.

```rust
use nanocar::{log, Severity};

fn handle_request() {
    nanocar::init();

    log(Severity::Info, "request accepted", &[("route", "/orders")]);

    match process() {
        Ok(_) => log(Severity::Info, "order processed", &[]),
        Err(e) => log(Severity::Error, "order failed", &[("error", &e.to_string())]),
    }
}
```

Each record carries a severity (`Info`, `Warn`, `Error`, and so on) and
optional key-value attributes. The severity is what renders as a badge in the
Logs table.

### JavaScript with an OTLP exporter

On the JS lane, point any OTLP/HTTP logs exporter at the `telemetry` Door. The
Door endpoint is available to the instance as `HYJAL_TELEMETRY_ENDPOINT`.

```js
import { LoggerProvider, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { SeverityNumber } from "@opentelemetry/api-logs";

const provider = new LoggerProvider();
provider.addLogRecordProcessor(
  new SimpleLogRecordProcessor(
    new OTLPLogExporter({ url: process.env.HYJAL_TELEMETRY_ENDPOINT }),
  ),
);
const logger = provider.getLogger("api");

export default {
  async fetch(request) {
    logger.emit({
      severityNumber: SeverityNumber.INFO,
      body: "request accepted",
      attributes: { route: new URL(request.url).pathname },
    });
    return new Response("ok");
  },
};
```

The exporter posts OTLP over HTTP to the Door; the severity number maps to the
badge shown in the Logs table.

## 2. Deploy

Deploy the Resource. No egress allowlist entry is needed for the `telemetry`
Door: it is a host-provided capability, not an external host.

```bash
$ hyjal deploy
Building resource api...
  cargo component build ✓
Uploading artifact... digest sha256:b83f2c…4a10
Applying policy... ✓
Flipping route... ✓

Deployed acme/api
  https://api--acme.hyjal.cloud
```

## 3. Generate traffic

Send a few requests so the instance emits records.

```bash
$ curl -s https://api--acme.hyjal.cloud/orders
ok
```

## 4. Read severity badges in the Logs

Open the [Logs tab](/observability/logs) in the Console, or stream from the
CLI. Structured OTLP records render with a severity badge (`INFO`, `WARN`,
`ERROR`) while raw output keeps honest `STDOUT` and `STDERR` badges.

```bash
$ hyjal logs -f --resource api
[16:04:22] GET  /orders   200   18ms
[16:04:22] INFO   request accepted   route=/orders
[16:04:22] INFO   order processed
[16:07:01] GET  /orders   500   22ms
[16:07:01] ERROR  order failed   error=timeout contacting payment API
```

Each structured record is correlated with the request that produced it. Filter
the table by method in the Console to isolate the requests you care about; the
severity badges make error records scannable at a glance.

## What you built

* A Resource that emits structured OTLP logs to the `telemetry` Door, from
  Rust with `nanocar` or from JavaScript with an OTLP exporter.
* Severity-tagged records rendered as badges in the Logs table.
* Structured telemetry alongside honest stdout and stderr capture, with no
  egress configuration.

## Next steps

* [Telemetry](/observability/telemetry): the OTLP contract and the
  `telemetry` Door.
* [Logs](/observability/logs): the request timeline, search, and filters.
* [Stats and metering](/observability/stats-and-metering): request counts,
  compute time, and peak memory.