Conversation
d7d03f7 to
c567b7f
Compare
api/click-house-client.ts
Outdated
| query: ` | ||
| CREATE TABLE IF NOT EXISTS logs ( | ||
| deployment_id LowCardinality(String), | ||
| timestamp DateTime64(3, 'UTC') DEFAULT now64(3, 'UTC'), |
api/click-house-client.ts
Outdated
| INDEX idx_message message TYPE tokenbf_v1(8192, 3, 0) GRANULARITY 4 | ||
| ) | ||
| ENGINE = MergeTree | ||
| PARTITION BY toYYYYMMDD(timestamp) |
a9e74ae to
4ea28b7
Compare
4ea28b7 to
8e914e1
Compare
8e914e1 to
66fb184
Compare
66fb184 to
014ad5e
Compare
There was a problem hiding this comment.
Pull Request Overview
This pull request implements ClickHouse integration for log storage and management, adding database client configuration and deployment token-based authentication for log ingestion.
- Adds ClickHouse client with table creation for structured log storage
- Implements deployment management API endpoints with encrypted token authentication
- Integrates ClickHouse setup into the development and production workflows
Reviewed Changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tasks/clickhouse.ts | Creates ClickHouse logs table with proper schema and partitioning |
| deno.json | Adds ClickHouse dependency and integrates setup tasks into dev/prod workflows |
| api/user.ts | Exports encryption functions for deployment token generation |
| api/server.ts | Adds resource field to request context for deployment tracking |
| api/routes.ts | Implements complete deployment CRUD API and log ingestion endpoint |
| api/lib/validator.ts | Adds UNION validator for handling multiple input types |
| api/lib/env.ts | Adds required ClickHouse environment variable validation |
| api/lib/context.ts | Extends request context with resource tracking |
| api/click-house-client.ts | Implements ClickHouse client and log insertion functionality |
| .env.test | Adds ClickHouse configuration for test environment |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| return deployments.map(({ tokenSalt: _, ...d }) => { | ||
| return { | ||
| ...d, | ||
| createdAt: d.createdAt, | ||
| updatedAt: d.updatedAt, | ||
| token: undefined, | ||
| sqlToken: undefined, | ||
| sqlEndpoint: undefined, | ||
| } | ||
| }) |
There was a problem hiding this comment.
The explicit assignment of createdAt and updatedAt fields is redundant since they're already included in the spread operator ...d. These lines can be removed.
api/routes.ts
Outdated
| createdAt: deployment.createdAt, | ||
| updatedAt: deployment.updatedAt, |
There was a problem hiding this comment.
The explicit assignment of createdAt and updatedAt fields is redundant since they're already included in the spread operator ...deployment. These lines can be removed.
api/routes.ts
Outdated
| createdAt: deployment.createdAt, | ||
| updatedAt: deployment.updatedAt, |
There was a problem hiding this comment.
The explicit assignment of createdAt and updatedAt fields is redundant since they're already included in the spread operator ...deployment. These lines can be removed.
api/routes.ts
Outdated
| createdAt: deployment.createdAt, | ||
| updatedAt: deployment.updatedAt, |
There was a problem hiding this comment.
The explicit assignment of createdAt and updatedAt fields is redundant since they're already included in the spread operator ...deployment. These lines can be removed.
api/routes.ts
Outdated
| createdAt: deployment.createdAt, | ||
| updatedAt: deployment.updatedAt, |
There was a problem hiding this comment.
The explicit assignment of createdAt and updatedAt fields is redundant since they're already included in the spread operator ...deployment. These lines can be removed.
api/click-house-client.ts
Outdated
| severity_text: STR(), | ||
| severity_number: NUM(), | ||
| attributes: OBJ({}), | ||
| event_name: STR(), |
There was a problem hiding this comment.
describe each fields, isn't severity_text & severity_number redundant ? do we ever use severity_number ?
…and update log insertion functionality
api/routes.ts
Outdated
| return { | ||
| ...d, | ||
| createdAt: d.createdAt, | ||
| updatedAt: d.updatedAt, |
There was a problem hiding this comment.
should be fixed with a type assertion, not JS (IMO)
tasks/clickhouse.ts
Outdated
| timestamp DateTime64(3, 'UTC') DEFAULT now64(3, 'UTC'), | ||
| observed_timestamp DateTime64(3, 'UTC') DEFAULT now64(3, 'UTC'), | ||
| trace_id UInt64, | ||
| span_id UInt64, |
There was a problem hiding this comment.
trace_id & span_id should be FixedString(16) as per Otel spec:
https://opentelemetry.io/docs/specs/otel/logs/data-model/#trace-context-fields
To convert from our internal float64 id to their 128bit id we can do this:
function float64ToId128(id) {
const id128 = new Uint8Array(16)
crypto.getRandomValues(id128) // prefill with random values
const view = new DataView(id128.buffer)
view.setFloat64(8, id, false)
return id128
}| observed_timestamp DateTime64(3, 'UTC') DEFAULT now64(3, 'UTC'), | ||
| trace_id UInt64, | ||
| span_id UInt64, | ||
| severity_number UInt8, |
There was a problem hiding this comment.
we could store also the severity_string in the db and derive it from the severity_number just not take it as a parameter to the route, or do the oposite
tasks/clickhouse.ts
Outdated
| severity_number UInt8, | ||
| attributes JSON, | ||
| event_name String, | ||
| context JSON |
There was a problem hiding this comment.
remove context and resource
and instead use "flattened" attributes for service
-- Flattened resource fields
service_name LowCardinality(String),
service_version LowCardinality(String),
service_instance_id String,| attributes JSON, | ||
| event_name String, | ||
| context JSON | ||
| ) |
There was a problem hiding this comment.
to be compliant, looks like we need a body, even if we don't use it we can make it nullable:
-- Often empty, but kept for OTEL spec compliance
body Nullable(String),We could try to add support to set it from the API as a special attribute, could be use ingesting log that only have text and no attributes ?
698a7ca to
913ff62
Compare
913ff62 to
0e431e1
Compare
…ata types for better logging structure
0e431e1 to
54ee31c
Compare
api/click-house-client.ts
Outdated
|
|
||
| export function bytesToHex(bytes: Uint8Array) { | ||
| return Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('') | ||
| } |
There was a problem hiding this comment.
const numberToHex128 = (() => {
const alphabet = new TextEncoder().encode("0123456789abcdef")
const output = new Uint8Array(16)
const view = new DataView(new Uint8Array(8).buffer)
const dec = new TextDecoder()
return (id: number) => {
view.setFloat64(0, id, false)
let i = -1
while (++i < 8) {
const x = view.getUint8(i)
output[i * 2] = alphabet[x >> 4]
output[i * 2 + 1] = alphabet[x & 0xF]
}
return dec.decode(output)
}
})()
api/click-house-client.ts
Outdated
| } | ||
|
|
||
| function toClickhouseDateTime(iso: string) { | ||
| // "2025-09-11T17:35:00.000Z" → "2025-09-11 17:35:00" |
There was a problem hiding this comment.
"2025-09-11T17:35:00.000Z".replace('T', ' ').slice(0, -5)There was a problem hiding this comment.
tasks/clickhouse.ts
Outdated
| span_id FixedString(16), | ||
| severity_number UInt8, | ||
| -- derived column, computed by DB from severity_number | ||
| severity_text String MATERIALIZED CASE |
There was a problem hiding this comment.
should be LowCardinality(String)
e9d522e to
11636bb
Compare
| data: LogsInput, | ||
| ) { | ||
| const logsToInsert = Array.isArray(data) ? data : [data] | ||
| if (logsToInsert.length === 0) throw respond.NoContent() |
| return respond.OK() | ||
| } catch (error) { | ||
| log.error('Error inserting logs into ClickHouse:', { error }) | ||
| throw respond.InternalServerError() |
There was a problem hiding this comment.
same here, shouldn't we just return in that case ?
3fb67ff to
e1dac52
Compare
e1dac52 to
a0e50df
Compare
| { project, team }: { project: Project; team: Team }, | ||
| ) => { | ||
| console.log(project) | ||
| console.log(team) |
No description provided.