feat(cli): route ingest adapter logs through operational logger

This commit is contained in:
Andrey Avtomonov 2026-05-12 01:35:19 +02:00
parent 20ac0329b8
commit 5cdae74825
12 changed files with 203 additions and 15 deletions

View file

@ -1,4 +1,5 @@
import type { KtxLocalProject, KtxProjectConnectionConfig } from '../../../project/index.js';
import type { LookerClientLogger } from './client.js';
import {
DefaultLookerClientFactory,
DefaultLookerConnectionClientFactory,
@ -59,8 +60,11 @@ export function createLocalLookerCredentialResolver(
export function createLocalLookerSourceAdapter(
project: KtxLocalProject,
env: NodeJS.ProcessEnv = process.env,
logger?: LookerClientLogger,
): LookerSourceAdapter {
const connectionFactory = new DefaultLookerConnectionClientFactory(createLocalLookerCredentialResolver(project, env));
const connectionFactory = new DefaultLookerConnectionClientFactory(createLocalLookerCredentialResolver(project, env), {
...(logger ? { logger } : {}),
});
return new LookerSourceAdapter({
clientFactory: new DefaultLookerClientFactory(connectionFactory),
});

View file

@ -21,7 +21,7 @@ class IngestInputError extends Error {
}
}
interface MetabaseFetchLogger {
export interface MetabaseFetchLogger {
log(message: string): void;
warn(message: string): void;
}

View file

@ -1,12 +1,13 @@
import type { KtxLocalProject, KtxProjectConnectionConfig } from '../../../project/index.js';
import { ktxLocalStateDbPath } from '../../../project/index.js';
import { resolveKtxConfigReference } from '../../../core/config-reference.js';
import { DEFAULT_METABASE_CLIENT_CONFIG, DefaultMetabaseConnectionClientFactory } from './client.js';
import { DEFAULT_METABASE_CLIENT_CONFIG, DefaultMetabaseConnectionClientFactory, type MetabaseClientLogger } from './client.js';
import {
IngestMetabaseClientFactory,
type MetabaseClientConfig,
type MetabaseClientRuntimeConfig,
} from './client-port.js';
import type { MetabaseFetchLogger } from './fetch.js';
import { LocalMetabaseSourceStateReader } from './local-source-state-store.js';
import { MetabaseSourceAdapter } from './metabase.adapter.js';
@ -50,6 +51,7 @@ export function metabaseRuntimeConfigFromLocalConnection(
interface CreateLocalMetabaseSourceAdapterOptions {
env?: NodeJS.ProcessEnv;
defaultClientConfig?: MetabaseClientConfig;
logger?: MetabaseClientLogger & MetabaseFetchLogger;
}
export function createLocalMetabaseSourceAdapter(
@ -63,11 +65,13 @@ export function createLocalMetabaseSourceAdapter(
metabaseConnectionId,
project.config.connections[metabaseConnectionId],
options.env,
),
),
options.defaultClientConfig ?? DEFAULT_METABASE_CLIENT_CONFIG,
options.logger,
);
return new MetabaseSourceAdapter({
clientFactory: new IngestMetabaseClientFactory(connectionFactory),
sourceStateReader,
...(options.logger ? { logger: options.logger } : {}),
});
}

View file

@ -4,7 +4,7 @@ import type { ChunkResult, DiffSet, FetchContext, ScopeDescriptor, SourceAdapter
import { chunkMetabaseStagedDir } from './chunk.js';
import type { MetabaseClientFactory } from './client-port.js';
import { detectMetabaseStagedDir } from './detect.js';
import { fetchMetabaseBundle } from './fetch.js';
import { fetchMetabaseBundle, type MetabaseFetchLogger } from './fetch.js';
import { computeFetchScope, hashScope, isPathInMetabaseScope } from './fetch-scope.js';
import type { MetabaseSourceStateReader } from './source-state-port.js';
import { STAGED_FILES, stagedSyncConfigSchema } from './types.js';
@ -12,6 +12,7 @@ import { STAGED_FILES, stagedSyncConfigSchema } from './types.js';
export interface MetabaseSourceAdapterDeps {
clientFactory: MetabaseClientFactory;
sourceStateReader: MetabaseSourceStateReader;
logger?: MetabaseFetchLogger;
}
export class MetabaseSourceAdapter implements SourceAdapter {
@ -31,6 +32,7 @@ export class MetabaseSourceAdapter implements SourceAdapter {
ctx,
clientFactory: this.deps.clientFactory,
sourceStateReader: this.deps.sourceStateReader,
...(this.deps.logger ? { logger: this.deps.logger } : {}),
});
}

View file

@ -12,7 +12,7 @@ import {
type NotionPullConfig,
} from './types.js';
interface NotionFetchLogger {
export interface NotionFetchLogger {
warn(message: string): void;
}

View file

@ -14,7 +14,7 @@ import type {
import { chunkNotionStagedDir, describeNotionScope } from './chunk.js';
import { clusterNotionWorkUnits } from './cluster.js';
import { detectNotionStagedDir } from './detect.js';
import { fetchNotionSnapshot } from './fetch.js';
import { fetchNotionSnapshot, type NotionFetchLogger } from './fetch.js';
import { NotionClient } from './notion-client.js';
import { parseNotionPullConfig } from './pull-config.js';
import { type NotionMetadata, notionManifestSchema, notionMetadataSchema } from './types.js';
@ -31,6 +31,7 @@ interface NotionPullSucceededContext {
export interface NotionSourceAdapterDeps {
onPullSucceeded?: (ctx: NotionPullSucceededContext) => Promise<void>;
logger?: NotionFetchLogger;
}
export class NotionSourceAdapter implements SourceAdapter {
@ -48,7 +49,12 @@ export class NotionSourceAdapter implements SourceAdapter {
async fetch(pullConfig: unknown, stagedDir: string, _ctx: FetchContext): Promise<void> {
const config = parseNotionPullConfig(pullConfig);
await fetchNotionSnapshot({ client: new NotionClient(config.authToken), config, stagedDir });
await fetchNotionSnapshot({
client: new NotionClient(config.authToken),
config,
stagedDir,
...(this.deps.logger ? { logger: this.deps.logger } : {}),
});
}
chunk(stagedDir: string, diffSet?: DiffSet): Promise<ChunkResult> {

View file

@ -19,6 +19,7 @@ import {
} from './adapters/live-database/daemon-introspection.js';
import { LiveDatabaseSourceAdapter } from './adapters/live-database/live-database.adapter.js';
import { createDaemonLookerTableIdentifierParser } from './adapters/looker/daemon-table-identifier-parser.js';
import type { LookerClientLogger } from './adapters/looker/client.js';
import { DefaultLookerConnectionClientFactory } from './adapters/looker/factory.js';
import { createLocalLookerCredentialResolver } from './adapters/looker/local-looker.adapter.js';
import { LocalLookerRuntimeStore } from './adapters/looker/local-runtime-store.js';
@ -32,12 +33,20 @@ import type { LookerRuntimeClient } from './adapters/looker/fetch.js';
import { LookmlSourceAdapter } from './adapters/lookml/lookml.adapter.js';
import { pullConfigFromIntegrationConfig } from './adapters/lookml/pull-config.js';
import { createLocalMetabaseSourceAdapter } from './adapters/metabase/local-metabase.adapter.js';
import type { MetabaseClientLogger } from './adapters/metabase/client.js';
import type { MetabaseFetchLogger } from './adapters/metabase/fetch.js';
import { MetricflowSourceAdapter } from './adapters/metricflow/metricflow.adapter.js';
import { pullConfigFromMetricflowIntegration } from './adapters/metricflow/pull-config.js';
import type { NotionFetchLogger } from './adapters/notion/fetch.js';
import { NotionSourceAdapter } from './adapters/notion/notion.adapter.js';
import { seedLocalMappingStateFromKtxYaml } from './local-mapping-reconcile.js';
import type { SourceAdapter } from './types.js';
type LocalIngestOperationalLogger = MetabaseClientLogger &
MetabaseFetchLogger &
LookerClientLogger &
NotionFetchLogger;
export interface DefaultLocalIngestAdaptersOptions {
databaseIntrospectionUrl?: string;
databaseIntrospection?: Omit<DaemonLiveDatabaseIntrospectionOptions, 'connections' | 'baseUrl'>;
@ -56,6 +65,7 @@ export interface DefaultLocalIngestAdaptersOptions {
parser?: LookerTableIdentifierParser;
env?: NodeJS.ProcessEnv;
};
logger?: LocalIngestOperationalLogger;
}
export function createDefaultLocalIngestAdapters(
@ -64,6 +74,9 @@ export function createDefaultLocalIngestAdapters(
): SourceAdapter[] {
const lookerConnectionFactory = new DefaultLookerConnectionClientFactory(
createLocalLookerCredentialResolver(project, options.looker?.env),
{
...(options.logger ? { logger: options.logger } : {}),
},
);
const adapters: SourceAdapter[] = [
@ -77,7 +90,9 @@ export function createDefaultLocalIngestAdapters(
}),
new LookmlSourceAdapter({ homeDir: join(project.projectDir, '.ktx/cache') }),
new DbtSourceAdapter({ homeDir: join(project.projectDir, '.ktx/cache') }),
createLocalMetabaseSourceAdapter(project),
createLocalMetabaseSourceAdapter(project, {
...(options.logger ? { logger: options.logger } : {}),
}),
new LookerSourceAdapter({
clientFactory: {
async createClient(config, ctx) {
@ -89,7 +104,9 @@ export function createDefaultLocalIngestAdapters(
},
}),
new MetricflowSourceAdapter({ homeDir: join(project.projectDir, '.ktx/cache') }),
new NotionSourceAdapter(),
new NotionSourceAdapter({
...(options.logger ? { logger: options.logger } : {}),
}),
];
if (options.historicSql) {
@ -205,6 +222,9 @@ export async function localPullConfigForAdapter(
} else {
const runtimeClient = await new DefaultLookerConnectionClientFactory(
createLocalLookerCredentialResolver(project, options.looker?.env),
{
...(options.logger ? { logger: options.logger } : {}),
},
).createClient(connectionId);
cleanupClient = runtimeClient;
client = runtimeClient;