Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2f4c60a
fix(common-utils): make ClickHouse query debug logging opt-in
truehazker Jul 18, 2026
b31d323
fix(docker): default HYPERDX_LOG_QUERIES to empty in compose passthrough
truehazker Jul 18, 2026
5e0766d
test(common-utils): split query-log silence test into it.each cases
truehazker Jul 18, 2026
28da011
fix(common-utils): replace HYPERDX_LOG_QUERIES env gate with injectab…
truehazker Jul 20, 2026
de59dda
Merge branch 'claude/gate-clickhouse-query-debug-log' of https://gith…
truehazker Jul 20, 2026
a550f21
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
truehazker Jul 25, 2026
4d032d8
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
truehazker Jul 31, 2026
968b3c6
fix(common-utils): :loud_sound: log SQL queries at debug level instea…
truehazker Aug 4, 2026
b9b523f
fix(api): :loud_sound: inject pino-backed logger into ClickHouse clients
truehazker Aug 4, 2026
41f6cc0
fix(api): :mute: default HYPERDX_LOG_LEVEL to info
truehazker Aug 4, 2026
c2804b6
fix(common-utils): :loud_sound: log browser SQL queries to devtools b…
truehazker Aug 4, 2026
a02db7f
Merge branch 'claude/gate-clickhouse-query-debug-log' of https://gith…
truehazker Aug 4, 2026
fe81fdd
Merge remote-tracking branch 'origin/main' into claude/gate-clickhous…
truehazker Aug 4, 2026
ae22145
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
truehazker Aug 5, 2026
b9cf2ed
fix(common-utils): :loud_sound: print browser SQL logs as readable text
truehazker Aug 7, 2026
a566534
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
truehazker Aug 11, 2026
f0dab80
Merge remote-tracking branch 'origin/main' into claude/gate-clickhous…
truehazker Aug 14, 2026
1393bca
Merge remote-tracking branch 'fork/claude/gate-clickhouse-query-debug…
truehazker Aug 14, 2026
f805a51
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
truehazker Aug 18, 2026
38bd4e5
fix: :loud_sound: pretty-print ClickHouse query logs in devtools
truehazker Aug 20, 2026
358ae8c
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
kodiakhq[bot] Aug 20, 2026
7f52235
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
kodiakhq[bot] Aug 21, 2026
cf92818
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
kodiakhq[bot] Aug 21, 2026
69fd250
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
kodiakhq[bot] Aug 21, 2026
13f0aed
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
kodiakhq[bot] Aug 21, 2026
8333738
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
kodiakhq[bot] Aug 21, 2026
31de35d
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
kodiakhq[bot] Aug 21, 2026
e4a44a2
Merge branch 'main' into claude/gate-clickhouse-query-debug-log
kodiakhq[bot] Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .changeset/gate-clickhouse-query-debug-log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
'@hyperdx/common-utils': patch
'@hyperdx/api': patch
'@hyperdx/app': patch
---

fix: route per-query SQL debug logging through an injectable logger (#2416)

`BaseClickhouseClient` dumped raw SQL to the console on every ClickHouse query,
unconditionally and outside the pino logger, flooding API logs with query spam.

Query logging now goes through an optional per-client `customLogger` on
`ClickhouseClientOptions`, logged at `debug`, and is silent when no logger is
passed. The API injects a pino-backed logger, so query logging follows the
existing `HYPERDX_LOG_LEVEL` setting instead of writing to `console.debug`. The
browser client defaults to a console logger that pretty-prints the SQL as a
single multi-line block, so query SQL stays visible and readable in devtools in
all builds instead of wrapping into one long line.

The API's log level now defaults to `info` (was `debug`), so SQL logging is
silent in production unless `HYPERDX_LOG_LEVEL=debug` is set. Dev and CI env
files already pin their levels explicitly and are unaffected. The default also
now applies when `HYPERDX_LOG_LEVEL` is set but empty — which is what Compose
passes when the variable is unset in the environment, and which previously made
pino throw at startup.
26 changes: 26 additions & 0 deletions packages/api/src/clickhouse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type {
ClickhouseClientOptions,
Logger as ClickhouseLogger,
} from '@hyperdx/common-utils/dist/clickhouse';
import { ClickhouseClient as BaseClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';

import logger from '@/utils/logger';

const customLogger: ClickhouseLogger = {
trace: ({ module, message, args }) =>
logger.trace({ ...args, module }, message),
debug: ({ module, message, args }) =>
logger.debug({ ...args, module }, message),
info: ({ module, message, args }) =>
logger.info({ ...args, module }, message),
warn: ({ module, message, args, err }) =>
logger.warn({ ...args, module, err }, message),
error: ({ module, message, args, err }) =>
logger.error({ ...args, module, err }, message),
};

export class ClickhouseClient extends BaseClickhouseClient {
constructor(options: ClickhouseClientOptions) {
super({ ...options, customLogger: options.customLogger ?? customLogger });
}
}
2 changes: 1 addition & 1 deletion packages/api/src/controllers/ai.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { createAnthropic } from '@ai-sdk/anthropic';
import { createOpenAI } from '@ai-sdk/openai';
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import {
getMetadata,
TableMetadata,
Expand All @@ -16,6 +15,7 @@ import * as chrono from 'chrono-node';
import ms from 'ms';
import z from 'zod';

import { ClickhouseClient } from '@/clickhouse';
import * as config from '@/config';
import { ISource } from '@/models/source';
import { parseJSON } from '@/utils/common';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/mcp/tools/query/denoise.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
import { getFirstTimestampValueExpression } from '@hyperdx/common-utils/dist/core/utils';
import {
Expand All @@ -12,6 +11,7 @@ import {
import type { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
import { DisplayType } from '@hyperdx/common-utils/dist/types';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';

Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/mcp/tools/query/eventDeltas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import {
getStableSampleExpression,
rankProperties,
Expand All @@ -12,6 +11,7 @@ import {
} from '@hyperdx/common-utils/dist/types';
import { z } from 'zod';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import type { ToolRegistrar } from '@/mcp/tools/types';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/mcp/tools/query/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ClickHouseError } from '@clickhouse/client-common';
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
import {
convertToCategoricalChartConfig,
Expand All @@ -19,6 +18,7 @@ import {
import { ObjectId } from 'mongodb';
import ms from 'ms';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import type { McpErrorResult } from '@/mcp/utils/errors';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/mcp/tools/query/runEventPatterns.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
import {
getFirstTimestampValueExpression,
Expand All @@ -8,6 +7,7 @@ import { minePatterns } from '@hyperdx/common-utils/dist/drain';
import type { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
import { DisplayType } from '@hyperdx/common-utils/dist/types';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import {
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/mcp/tools/sources/describeMetric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import {
JSDataType,
tableExpr,
} from '@hyperdx/common-utils/dist/clickhouse';
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
import { SourceKind } from '@hyperdx/common-utils/dist/types';
import { z } from 'zod';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import { clickHouseErrorResult } from '@/mcp/tools/query/helpers';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/mcp/tools/sources/describeSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import {
JSDataType,
tableExpr,
} from '@hyperdx/common-utils/dist/clickhouse';
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
import { type MetricTable, SourceKind } from '@hyperdx/common-utils/dist/types';
import SqlString from 'sqlstring';
import { z } from 'zod';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import type { ToolRegistrar } from '@/mcp/tools/types';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/mcp/tools/sources/listMetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {
concatChSql,
tableExpr,
} from '@hyperdx/common-utils/dist/clickhouse';
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
import { SourceKind } from '@hyperdx/common-utils/dist/types';
import { z } from 'zod';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import type { ToolRegistrar } from '@/mcp/tools/types';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/mcp/tools/trace/breakdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
* endpoint" without dropping into raw SQL with self-JOINs. The builder
* tools (table / timeseries / search) can't express the TraceId subselect.
*/
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { SourceKind } from '@hyperdx/common-utils/dist/types';
import { z } from 'zod';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import {
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/mcp/tools/trace/waterfall.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
import {
type BuilderChartConfigWithDateRange,
Expand All @@ -8,6 +7,7 @@ import {
} from '@hyperdx/common-utils/dist/types';
import { z } from 'zod';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import {
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/routers/api/prometheus.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import express from 'express';
import { performance } from 'perf_hooks';
import { Readable } from 'stream';
import { pipeline } from 'stream/promises';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getNonNullUserWithTeam } from '@/middleware/auth';
import { getCounter, getHistogram } from '@/utils/instrumentation';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/routers/external-api/v2/charts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
import { Granularity } from '@hyperdx/common-utils/dist/core/utils';
import {
Expand All @@ -11,6 +10,7 @@ import express from 'express';
import _ from 'lodash';
import { z } from 'zod';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import { getTeam } from '@/controllers/team';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/routers/external-api/v2/utils/search.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { getMetadata } from '@hyperdx/common-utils/dist/core/metadata';
import { buildSearchChartConfig } from '@hyperdx/common-utils/dist/core/searchChartConfig';
import {
Expand All @@ -15,6 +14,7 @@ import {
isTraceSource,
} from '@hyperdx/common-utils/dist/types';

import { ClickhouseClient } from '@/clickhouse';
import { getConnectionById } from '@/controllers/connection';
import { getSource } from '@/controllers/sources';
import type { ExternalDashboardSearchRequestConfig } from '@/utils/zod';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/tasks/checkAlerts/providers/default.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import PQueue from '@esm2cjs/p-queue';
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { displayTypeSupportsRawSqlAlerts } from '@hyperdx/common-utils/dist/core/utils';
import { isRawSqlSavedChartConfig } from '@hyperdx/common-utils/dist/guards';
import { Tile } from '@hyperdx/common-utils/dist/types';
import mongoose from 'mongoose';
import ms from 'ms';
import { URLSearchParams } from 'url';

import { ClickhouseClient } from '@/clickhouse';
import * as config from '@/config';
import { ALERT_HISTORY_QUERY_CONCURRENCY } from '@/controllers/alertHistory';
import { LOCAL_APP_TEAM } from '@/controllers/team';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/tasks/usageStats.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ResponseJSON } from '@hyperdx/common-utils/dist/clickhouse';
import { ClickhouseClient } from '@hyperdx/common-utils/dist/clickhouse/node';
import { MetricsDataType, SourceKind } from '@hyperdx/common-utils/dist/types';
import * as HyperDX from '@hyperdx/node-opentelemetry';
import ms from 'ms';
import os from 'os';
import pino from 'pino';

import { ClickhouseClient } from '@/clickhouse';
import * as config from '@/config';
import Connection from '@/models/connection';
import { Source, SourceDocument } from '@/models/source';
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import pinoHttp from 'pino-http';

import * as config from '@/config';

const MAX_LEVEL = config.HYPERDX_LOG_LEVEL ?? 'debug';
const MAX_LEVEL = config.HYPERDX_LOG_LEVEL || 'info';

const hyperdxTransport = config.HYPERDX_API_KEY
? getPinoTransport(MAX_LEVEL, {
Expand Down
3 changes: 0 additions & 3 deletions packages/cli/src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,6 @@ export class ProxyClickhouseClient extends BaseClickhouseClient {
});
}

// Silence the "Sending Query: ..." debug output from BaseClickhouseClient
protected override logDebugQuery(): void {}

// This subclass always builds a node client, so narrow the base class's
// platform-agnostic client type to the node-specific one.
protected getClient(): NodeClickHouseClient {
Expand Down
65 changes: 65 additions & 0 deletions packages/common-utils/src/clickhouse/__tests__/browser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { consoleLogger } from '@/clickhouse/browser';

describe('consoleLogger', () => {
afterEach(() => {
jest.restoreAllMocks();
});

it('pretty-prints SQL into a single multi-line message', () => {
const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {});
const sql = 'SELECT a, b FROM default.otel_logs WHERE a = 1';

consoleLogger.debug({
module: 'clickhouse',
message: 'Sending query',
args: { sql },
});

// Exact layout is sqlFormatter's business; here we only pin the contract
// devtools needs: one call, one string, and SQL broken across lines.
expect(debugSpy).toHaveBeenCalledTimes(1);
expect(debugSpy.mock.calls[0]).toHaveLength(1);

const logged = String(debugSpy.mock.calls[0][0]);
expect(logged.startsWith('Sending query:\n')).toBe(true);
expect(logged.split('\n').length).toBeGreaterThan(2);
});

it('falls back to the raw SQL when it cannot be formatted', () => {
const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {});
const sql = 'SELECT ((( FROM';

consoleLogger.debug({
module: 'clickhouse',
message: 'Sending query',
args: { sql },
});

expect(debugSpy).toHaveBeenCalledWith(`Sending query:\n${sql}`);
});

it('keeps the prefixed form for logs that carry no SQL', () => {
const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {});

consoleLogger.debug({
module: 'clickhouse',
message: 'Response received',
args: { rows: 5 },
});

expect(debugSpy).toHaveBeenCalledWith('[clickhouse] Response received', 5);
});

it('appends the error to error logs', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const err = new Error('boom');

consoleLogger.error({
module: 'clickhouse',
message: 'Query failed',
err,
});

expect(errorSpy).toHaveBeenCalledWith('[clickhouse] Query failed', err);
});
});
Loading
Loading