Quickstart

LogDB OTLP ingest and query quickstart.

Run a local LogDB process, send OTLP JSON compatibility logs and traces, capture record IDs, query retained records, and see how the local tenant policy checks access.

Current availability

Private Beta. Public beta is scheduled for .

LiveConfirm First

Result

You will have a local LogDB workflow that accepts one log and two trace records, returns logdb_... record IDs, queries a retained log by exact record ID, queries retained trace spans by trace ID with pagination, and confirms that requests without x-logdb-tenant are rejected.

Readiness Notes

Ingest And Query Workflow

Live

Local OTLP JSON compatibility ingest and POST /v1/query search are implemented for this first-success path.

Product Boundary

Confirm First

This tutorial proves local behavior only. S3 publication, BYOC, public replay APIs, external sinks, performance, support, security, availability, scale, and GA claims still need evidence review.

Prerequisites

Required tools

Required

  • cargo --version for the Rust toolchain
  • curl --version for HTTP requests
  • sed for extracting the first returned record ID
  • Bash and mktemp for the local service wrapper.
  • A local LogDB source checkout

Product setup

Required

  • LOGDB_SOURCE_DIR points at the LogDB source checkout.
  • LOGDB_BIND_ADDRESS uses an unprivileged loopback port, such as 127.0.0.1:4318.
  • The startup script creates a fresh disposable LOGDB_DATA_DIR for every run.

Tenant policy

Required

  • Use x-logdb-tenant: local on OTLP JSON compatibility ingest and query requests.
  • Use Authorization: Bearer local-dev-token for the local development tenant.
  • The tenant claimed inside OTLP resource attributes must match the authenticated tenant.

Optional helpers

Optional

  • jq can make JSON inspection easier, but the copy/paste workflow uses only shell, curl, and sed.
  • An OpenTelemetry Collector binary is only needed for the later collector smoke path, not this quickstart.

See local prerequisites for the shared tool baseline.

Start The Service

Use one terminal for the LogDB service and a second terminal for the workflow commands. The startup script allocates a fresh temporary data directory for each run and deletes only that directory after stopping the owned process.

Set the source checkout

Workflow terminal

Point this shell at the LogDB source checkout. The service script uses the same value in its own terminal.

export LOGDB_SOURCE_DIR=/path/to/logdb

Expected output

LOGDB_SOURCE_DIR points at a checkout that contains LogDB/Cargo.toml.

Start isolated local LogDB

Service terminal

Save this complete file as start-logdb.sh, run bash start-logdb.sh, and keep it running while you use the workflow terminal. The script prints the temporary data directory it owns.

#!/usr/bin/env bash
set -euo pipefail

# Service terminal: keep this script running while using the workflow terminal.
cd "${LOGDB_SOURCE_DIR:?Set LOGDB_SOURCE_DIR to your LogDB source directory.}/LogDB"
cargo build --locked --bin logdb --target-dir "$LOGDB_SOURCE_DIR/LogDB/target"
export LOGDB_BIND_ADDRESS="${LOGDB_BIND_ADDRESS:-127.0.0.1:4318}"
export LOGDB_DATA_DIR
LOGDB_DATA_DIR="$(mktemp -d "${TMPDIR:-/tmp}/logdb-quickstart.XXXXXX")"
readonly LOGDB_DATA_DIR
service_pid=

cleanup() {
  trap '' INT TERM
  if [ -n "$service_pid" ]; then
    kill -TERM "$service_pid" 2>/dev/null || true
    for attempt in 1 2 3 4 5; do
      kill -0 "$service_pid" 2>/dev/null || break
      sleep 1
    done
    if kill -0 "$service_pid" 2>/dev/null; then
      kill -KILL "$service_pid" 2>/dev/null || true
    fi
    wait "$service_pid" 2>/dev/null || true
  fi
  rm -rf -- "$LOGDB_DATA_DIR"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

printf 'Temporary LogDB data: %s\n' "$LOGDB_DATA_DIR"
"$LOGDB_SOURCE_DIR/LogDB/target/debug/logdb" &
service_pid=$!
status=0
wait "$service_pid" || status=$?
service_pid=
exit "$status"

Expected output

Temporary LogDB data: /tmp/logdb-quickstart.<unique>
The service starts on 127.0.0.1:4318. The script stops the owned process and removes only its printed temporary directory when it exits.

Check readiness

Workflow terminal

Open a second terminal and confirm that the local HTTP service is ready before sending telemetry.

export LOGDB_ENDPOINT=http://127.0.0.1:4318
curl -fsS "$LOGDB_ENDPOINT/health"

Expected output

"Healthy"

Complete The Workflow

Configure tutorial values

Copy/paste

Set one endpoint, the local development tenant credentials, and the trace ID used by the trace query.

export LOGDB_ENDPOINT=http://127.0.0.1:4318
export LOGDB_TENANT=local
export LOGDB_TOKEN=local-dev-token
export LOGDB_DATASET=checkout
export LOGDB_TRACE_ID=4bf92f3577b34da6a3ce929d0e0e4736

Expected output

These values point at the local development policy. The dataset comes from the OTLP resource attribute service.name=checkout.

Send an OTLP JSON log batch

Copy/paste

Post one OpenTelemetry-shaped JSON compatibility log batch and keep the response for record ID extraction.

LOG_RESPONSE="$(curl -s -X POST "$LOGDB_ENDPOINT/v1/otlp/logs" \
  -H 'content-type: application/json' \
  -H "x-logdb-tenant: $LOGDB_TENANT" \
  -H "authorization: Bearer $LOGDB_TOKEN" \
  --data '{
    "resourceLogs": [{
      "resource": {
        "attributes": [
          {"key": "tenant_id", "value": {"stringValue": "local"}},
          {"key": "service.name", "value": {"stringValue": "checkout"}}
        ]
      },
      "scopeLogs": [{
        "logRecords": [{
          "timeUnixNano": "1783947600000000000",
          "body": {"stringValue": "payment accepted"}
        }]
      }]
    }]
  }')"

printf '%s\n' "$LOG_RESPONSE"

Expected output

{"status":"accepted","signal":"log","accepted_records":1,"record_ids":["logdb_..."]}

Capture the log record ID

Copy/paste

Extract the first deterministic logdb_... record ID from the accepted response.

LOG_RECORD_ID="$(printf '%s' "$LOG_RESPONSE" | sed -nE 's/.*"record_ids":\["([^"]+)".*/\1/p')"
printf '%s\n' "$LOG_RECORD_ID"

Expected output

logdb_...

Query by exact record ID

Copy/paste

Search the retained local segment bundles by exact record ID using the same tenant header and bearer token.

curl -s -X POST "$LOGDB_ENDPOINT/v1/query" \
  -H 'content-type: application/json' \
  -H "x-logdb-tenant: $LOGDB_TENANT" \
  -H "authorization: Bearer $LOGDB_TOKEN" \
  --data @- <<JSON
{
  "dataset": "$LOGDB_DATASET",
  "filter": {
    "record_id": "$LOG_RECORD_ID"
  },
  "page": {
    "limit": 10
  }
}
JSON

Expected output

{
  "status": "ok",
  "api_version": "v1",
  "tenant_id": "local",
  "dataset": "checkout",
  "matched_records": 1,
  "results": [{
    "record": {
      "record_id": "logdb_...",
      "source": "v1.otlp_http",
      "signal": "log",
      "raw_payload": {"record": {"body": {"stringValue": "payment accepted"}}}
    }
  }],
  "used_indexes": ["record_id"]
}

Send an OTLP JSON trace batch

Copy/paste

Post two trace spans with the same trace ID so the query route can prove a retained trace selection.

TRACE_RESPONSE="$(curl -s -X POST "$LOGDB_ENDPOINT/v1/otlp/traces" \
  -H 'content-type: application/json' \
  -H "x-logdb-tenant: $LOGDB_TENANT" \
  -H "authorization: Bearer $LOGDB_TOKEN" \
  --data '{
    "resourceSpans": [{
      "resource": {
        "attributes": [
          {"key": "tenant_id", "value": {"stringValue": "local"}},
          {"key": "service.name", "value": {"stringValue": "checkout"}}
        ]
      },
      "scopeSpans": [{
        "spans": [
          {
            "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
            "spanId": "00f067aa0ba902b7",
            "name": "POST /checkout",
            "startTimeUnixNano": "1783947600000000000"
          },
          {
            "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
            "spanId": "00f067aa0ba902b8",
            "name": "SELECT inventory",
            "startTimeUnixNano": "1783947600000000100"
          }
        ]
      }]
    }]
  }')"

printf '%s\n' "$TRACE_RESPONSE"

Expected output

{"status":"accepted","signal":"trace","accepted_records":2,"record_ids":["logdb_...","logdb_..."]}

Query by trace ID

Copy/paste

Search the same dataset by exact trace ID and request one result so pagination is visible.

TRACE_QUERY_RESPONSE="$(curl -s -X POST "$LOGDB_ENDPOINT/v1/query" \
  -H 'content-type: application/json' \
  -H "x-logdb-tenant: $LOGDB_TENANT" \
  -H "authorization: Bearer $LOGDB_TOKEN" \
  --data @- <<JSON
{
  "dataset": "$LOGDB_DATASET",
  "filter": {
    "trace_id": "$LOGDB_TRACE_ID"
  },
  "page": {
    "limit": 1
  }
}
JSON
)"

printf '%s\n' "$TRACE_QUERY_RESPONSE"

Expected output

{
  "status": "ok",
  "tenant_id": "local",
  "dataset": "checkout",
  "matched_records": 2,
  "results": [{"record": {"signal": "trace"}}],
  "page": {"limit": 1, "cursor": null, "next_cursor": "1"},
  "used_indexes": ["identity.trace_id"]
}

Fetch the next trace page

Copy/paste

Use the returned cursor to fetch the second retained trace record.

curl -s -X POST "$LOGDB_ENDPOINT/v1/query" \
  -H 'content-type: application/json' \
  -H "x-logdb-tenant: $LOGDB_TENANT" \
  -H "authorization: Bearer $LOGDB_TOKEN" \
  --data @- <<JSON
{
  "dataset": "$LOGDB_DATASET",
  "filter": {
    "trace_id": "$LOGDB_TRACE_ID"
  },
  "page": {
    "limit": 1,
    "cursor": "1"
  }
}
JSON

Expected output

{
  "status": "ok",
  "matched_records": 2,
  "results": [{"record": {"signal": "trace"}}],
  "page": {"limit": 1, "cursor": "1", "next_cursor": null}
}

Check the tenant policy boundary

Copy/paste

Send a query without the local tenant headers to see the current local policy reject the request.

curl -s -X POST "$LOGDB_ENDPOINT/v1/query" \
  -H 'content-type: application/json' \
  --data '{"dataset":"checkout"}'

Expected output

{"status":"rejected","retryable":false,"error":{"kind":"missing_tenant","message":"x-logdb-tenant header is required",...}}

Verify Success

The quickstart is successful when log ingest returns a logdb_... record ID, the exact-record query returns matched_records:1 with used_indexes:["record_id"], the trace query returns matched_records:2 with next_cursor:"1", the second trace page clears next_cursor, and the missing-header request returns missing_tenant.

Clean Up

Stop the service and remove owned data

Copy/paste

Return to the service terminal and stop the startup script. It terminates the owned LogDB process before deleting the temporary directory it printed.

# Press Ctrl-C in the service terminal running bash start-logdb.sh.

Expected output

The script exits, the local LogDB process stops, and only the printed /tmp/logdb-quickstart.<unique> directory is removed.

Troubleshooting

Common first-run failures
SymptomHow to recognize itLikely causeFix
Service does not startbash start-logdb.sh reports that the build failed, the address is already in use, or the service cannot bind.LOGDB_SOURCE_DIR does not point at a buildable checkout, or another local process is using 127.0.0.1:4318.Confirm LOGDB_SOURCE_DIR, or choose a different loopback port in LOGDB_BIND_ADDRESS and update LOGDB_ENDPOINT to match.
Health check cannot connectcurl reports connection refused or times out against 127.0.0.1:4318.The service terminal stopped, is still compiling, or is bound to a different address.Wait for Cargo to finish, confirm the service terminal is still open, and re-check LOGDB_ENDPOINT.
Ingest returns missing_tenantThe response status is 401 Unauthorized and the JSON error kind is missing_tenant.x-logdb-tenant was omitted or empty.Send x-logdb-tenant: local on OTLP JSON compatibility and query requests.
Ingest returns missing_credentialsThe response status is 401 Unauthorized and the JSON error kind is missing_credentials.The bearer token header was omitted or malformed.Send authorization: Bearer local-dev-token with the local tenant header.
Ingest returns tenant_mismatchThe response status is 403 Forbidden and the error mentions tenant claims.The OTLP resource attribute tenant_id or service.logdb.tenant_id does not match x-logdb-tenant.Use local in both the header and OTLP resource attributes for this quickstart.
Query returns zero matchesThe response status is ok, but matched_records is 0 and results is empty.The record ID, trace ID, dataset, or startup-script data directory does not match the ingest request.Re-run ingest, re-extract LOG_RECORD_ID, and confirm LOGDB_DATASET=checkout.
Trace page does not return next_cursorThe first trace query does not show page.next_cursor set to 1.Only one trace span was accepted, or the query used a different trace ID.Re-run the documented trace ingest command and query with LOGDB_TRACE_ID=4bf92f3577b34da6a3ce929d0e0e4736.

Source scope

Source-backed quickstart checks
Workflow stepSource scopeStatus
Service startup and healthLogDB source repository: README.md, LogDB/src/main.rs, LogDB/src/config.rs, and AutomatedTests/Health/HealthEndpointTests.csSource-backed; local run still needs product confirmation
OTLP JSON compatibility ingestLogDB source repository: README.md, LogDB/src/handlers/otlp_http_handler.rs, LogDB/src/ingestion/otlp_http.rs, and AutomatedTests/Otlp/OtlpHttpEndpointTests.csSource-backed
Tenant admission policyLogDB source repository: LogDB/src/ingestion/tenant_admission.rs and query/ingest endpoint testsSource-backed
Query by record ID and trace IDLogDB source repository: docs/query/http-query-api.md, LogDB/src/handlers/query_handler.rs, LogDB/src/query/api.rs, and AutomatedTests/Query/QueryApiEndpointTests.csSource-backed
Segment publication and local limitsLogDB source repository: docs/ingestion/live-wal-gateway.md, docs/storage/segment-bundle-v1.md, and docs/fact-sheets/logdb.mdSource-backed; claims remain product-bound

Limits

Local Ingest And Query

This quickstart uses OTLP HTTP JSON compatibility routes and the local segment query store under the startup script's temporary LOGDB_DATA_DIR.

Tenant Headers Required

The local development policy accepts tenant local with bearer token local-dev-token; the OTLP tenant claim must match the header.

Narrow Signal Scope

The workflow covers logs, traces, retained segment search, record IDs, trace IDs, and pagination only.

product-bound claims

S3 publication, BYOC deployment, public replay APIs, external sinks, scale, availability, support, and performance remain outside this quickstart.

Next Steps

  • Available now

    Review LogDB limits

    Use the limits and compatibility guidance before strengthening ingest, query, S3, replay, BYOC, or GA claims.

  • Available now

    Read tutorial prerequisites

    Check the shared local tool baseline and product-specific setup expectations for first-success tutorials.

  • Available

    Read OTLP and query reference

    OTLP HTTP/protobuf, optional gRPC, JSON compatibility, custom endpoint, tenant header, limit, error, and query filter details.

  • Available

    Compare OTLP and S3 query scope

    Distinguish live ingest, optional gRPC, local query, S3 query reads, metrics/profile exclusions, and future publication work.

  • Available

    Use OpenTelemetry Collector examples

    Extend the quickstart with collector configuration, curl payloads, retained-record query checks, tenant headers, and local policy warnings.