Skip to content
Logixlysia
Esc
navigateopen⌘Jpreview
On this page

ClickHouse

Insert logs into a ClickHouse table over HTTP

Send logs straight into a ClickHouse table via the HTTP interface with JSONEachRow — your own log warehouse with SQL, no agent or pipeline in between.

Setup

  1. Create the target table:
CREATE TABLE default.logs (
  timestamp DateTime64(3),
  level LowCardinality(String),
  message String,
  attributes Map(String, String)
)
ENGINE = MergeTree
ORDER BY timestamp
  1. Set the environment variables (all optional for a default local instance):
CLICKHOUSE_URL=http://localhost:8123
CLICKHOUSE_DATABASE=default
CLICKHOUSE_TABLE=logs
CLICKHOUSE_USERNAME=default
CLICKHOUSE_PASSWORD=secret
  1. Wire the transport:
import { Elysia } from 'elysia'
import logixlysia from 'logixlysia'
import { createClickHouseTransport } from 'logixlysia/clickhouse'

const app = new Elysia()
  .use(
    logixlysia({
      config: {
        transports: [createClickHouseTransport()]
      }
    })
  )
  .get('/', () => 'ok')
  .listen(3000)
  1. Query: SELECT * FROM logs WHERE level = 'ERROR' ORDER BY timestamp DESC.

Environment Variables

Variable Required Description
CLICKHOUSE_URL No HTTP interface base URL (default http://localhost:8123)
CLICKHOUSE_DATABASE No Target database (default default)
CLICKHOUSE_TABLE No Target table (default logs)
CLICKHOUSE_USERNAME No Sent as X-ClickHouse-User
CLICKHOUSE_PASSWORD No Sent as X-ClickHouse-Key

Options

const clickhouse = createClickHouseTransport({
  database: 'observability',
  table: 'app_logs'
})
Option Type Default Description
url string http://localhost:8123 HTTP interface base URL
database string default Target database
table string logs Target table
username string CLICKHOUSE_USERNAME Username
password string CLICKHOUSE_PASSWORD Password

Plus the shared batching options: maxBatchSize, flushIntervalMs, timeout, retries — see the overview.

Database and table names must be plain identifiers (letters, digits, underscores) — anything else throws at startup.

Payload

Each log becomes one JSONEachRow row. Meta flattens into the attributes map with values rendered as strings:

{
  "timestamp": "2026-08-22T12:00:00.000Z",
  "level": "INFO",
  "message": "GET /users",
  "attributes": {
    "request.method": "GET",
    "request.url": "http://localhost:3000/users",
    "status": "200",
    "durationMs": "12.4",
    "context.requestId": "0d5e…"
  }
}

Query attributes with map access, e.g. WHERE attributes['status'] = '500'. The insert URL includes date_time_input_format=best_effort so ISO 8601 timestamps parse into DateTime64 directly.

ClickHouse loves large batches — for high-volume services, raise maxBatchSize (e.g. 500) and flushIntervalMs (e.g. 5000) to cut down on tiny inserts.

Troubleshooting

  • 404 / UNKNOWN_TABLE — create the table first (DDL above), or check CLICKHOUSE_DATABASE / CLICKHOUSE_TABLE.
  • 516 — authentication failed; ClickHouse returns this for bad credentials.
  • Cannot parse errors — the table schema doesn’t match the row shape; align it with the DDL above or adjust column types.

Last updated on August 22, 2026

Was this page helpful?