diff --git a/packages/libraries/core/src/client/agent.ts b/packages/libraries/core/src/client/agent.ts index d310a0cb94..c0a65bc9a7 100644 --- a/packages/libraries/core/src/client/agent.ts +++ b/packages/libraries/core/src/client/agent.ts @@ -67,7 +67,11 @@ export interface AgentOptions { */ maxSize?: number; /** - * Custom logger (defaults to console) + * Custom logger. + * + * Default: console based logger + * + * @deprecated Instead, provide a logger for the root Hive SDK. If a logger is provided on the root Hive SDK, this one is ignored. */ logger?: Logger; /** @@ -119,7 +123,7 @@ export function createAgent( ? null : pluginOptions.circuitBreaker, }; - const logger = createHiveLogger(pluginOptions.logger ?? console, '[agent]', pluginOptions.debug); + const logger = createHiveLogger(pluginOptions.logger ?? console, '[agent]'); let circuitBreaker: CircuitBreakerInterface< Parameters, diff --git a/packages/libraries/core/src/client/client.ts b/packages/libraries/core/src/client/client.ts index 4c37eadd22..b4888d869f 100644 --- a/packages/libraries/core/src/client/client.ts +++ b/packages/libraries/core/src/client/client.ts @@ -10,14 +10,61 @@ import { createPersistedDocuments } from './persisted-documents.js'; import { createReporting } from './reporting.js'; import type { HiveClient, HiveInternalPluginOptions, HivePluginOptions } from './types.js'; import { createUsage } from './usage.js'; -import { createHiveLogger, isLegacyAccessToken } from './utils.js'; +import { createHiveLogger, HiveLogger, isLegacyAccessToken } from './utils.js'; -export function createHive(options: HivePluginOptions): HiveClient { - const logger = createHiveLogger( - options?.agent?.logger ?? console, +function chooseDefaultLogger(options: HivePluginOptions): HiveLogger { + if (options.logger === 'debug') { + return createHiveLogger( + { + debug(...args) { + console.debug(...args); + }, + info(...args) { + console.info(...args); + }, + error(...args) { + console.error(...args); + }, + }, + '[hive]', + ); + } + if (options.logger === 'info') { + return createHiveLogger( + { + debug() {}, + info(...args) { + console.info(...args); + }, + error(...args) { + console.error(...args); + }, + }, + '[hive]', + ); + } + if (options.logger === 'error') { + return createHiveLogger( + { + debug() {}, + info() {}, + error(...args) { + console.error(...args); + }, + }, + '[hive]', + ); + } + + return createHiveLogger( + options?.logger ?? options?.agent?.logger ?? console, '[hive]', - options.debug ?? false, + options.debug, ); +} + +export function createHive(options: HivePluginOptions): HiveClient { + const logger = chooseDefaultLogger(options); let enabled = options.enabled ?? true; if (enabled === false && !options.experimental__persistedDocuments) { diff --git a/packages/libraries/core/src/client/types.ts b/packages/libraries/core/src/client/types.ts index 85374d3cfb..28181c4a7d 100644 --- a/packages/libraries/core/src/client/types.ts +++ b/packages/libraries/core/src/client/types.ts @@ -210,8 +210,16 @@ export type HivePluginOptions = OptionalWhenFalse< * Debugging mode * * Default: false + * + * @deprecated Use the {logger} property instead. */ debug?: boolean; + /** + * Custom logger. + * + * Default: 'info' + */ + logger?: Logger | 'error' | 'info' | 'debug'; /** * Access Token for usage reporting */ diff --git a/packages/libraries/core/src/client/utils.ts b/packages/libraries/core/src/client/utils.ts index 3ee19bbcf1..55c4c3867c 100644 --- a/packages/libraries/core/src/client/utils.ts +++ b/packages/libraries/core/src/client/utils.ts @@ -210,9 +210,6 @@ export function createHiveLogger(baseLogger: Logger, prefix: string, debug = tru return { [hiveSymbol]: context, - info: (message: string) => { - logger.info(printPath(path) + message); - }, error: (error: any, ...data: any[]) => { if (error.stack) { const pth = printPath(path); @@ -223,6 +220,9 @@ export function createHiveLogger(baseLogger: Logger, prefix: string, debug = tru logger.error(printPath(path) + String(error), ...data); } }, + info: (message: string) => { + logger.info(printPath(path) + message); + }, debug: (message: string) => { if (!context.debug) { return; diff --git a/packages/libraries/core/tests/usage.spec.ts b/packages/libraries/core/tests/usage.spec.ts index 74f844a5dc..dc49dee59c 100644 --- a/packages/libraries/core/tests/usage.spec.ts +++ b/packages/libraries/core/tests/usage.spec.ts @@ -885,3 +885,149 @@ test('constructs URL with usage.target (hvu1/)', async ({ expect }) => { expect(url).toEqual('http://localhost/the-guild/graphql-hive/staging'); await hive.dispose(); }); + +test('no debug property -> logger.debug is invoked', async ({ expect }) => { + const logger = createHiveTestingLogger(); + const token = 'hvu1/brrrrt'; + + const hive = createHive({ + enabled: true, + agent: { + timeout: 500, + maxRetries: 0, + sendInterval: 1, + maxSize: 1, + async fetch() { + return new Response('', { + status: 200, + }); + }, + logger, + }, + token, + selfHosting: { + graphqlEndpoint: 'http://localhost:2/graphql', + applicationUrl: 'http://localhost:1', + usageEndpoint: 'http://localhost', + }, + usage: { + target: 'the-guild/graphql-hive/staging', + }, + }); + + await hive.collectUsage()( + { + schema, + document: op, + operationName: 'asd', + }, + {}, + ); + + await hive.dispose(); + expect(logger.getLogs()).toMatchInlineSnapshot(` + [DBG] [hive][usage][agent] Disposing + [DBG] [hive][usage][agent] Sending immediately + [DBG] [hive][usage][agent] Sending report (queue 1) + [DBG] [hive][usage][agent] POST http://localhost/the-guild/graphql-hive/staging (x-request-id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) + [DBG] [hive][usage][agent] POST http://localhost/the-guild/graphql-hive/staging (x-request-id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) succeeded with status 200 (666ms). + [DBG] [hive][usage][agent] Report sent! + `); +}); + +test('debug: false -> logger.debug is not invoked', async ({ expect }) => { + const logger = createHiveTestingLogger(); + const token = 'hvu1/brrrrt'; + + const hive = createHive({ + enabled: true, + debug: false, + agent: { + timeout: 500, + maxRetries: 0, + sendInterval: 1, + maxSize: 1, + async fetch() { + return new Response('', { + status: 200, + }); + }, + logger, + }, + token, + selfHosting: { + graphqlEndpoint: 'http://localhost:2/graphql', + applicationUrl: 'http://localhost:1', + usageEndpoint: 'http://localhost', + }, + usage: { + target: 'the-guild/graphql-hive/staging', + }, + }); + + await hive.collectUsage()( + { + schema, + document: op, + operationName: 'asd', + }, + {}, + ); + + await hive.dispose(); + expect(logger.getLogs()).toMatchInlineSnapshot(``); +}); + +test('debug: true and missing logger.debug method -> logger.info is invoked (to cover legacy logger implementation)', async ({ + expect, +}) => { + const logger = createHiveTestingLogger(); + // @ts-expect-error we remove this property to emulate logger without it + logger.debug = undefined; + const token = 'hvu1/brrrrt'; + + const hive = createHive({ + enabled: true, + debug: true, + agent: { + timeout: 500, + maxRetries: 0, + sendInterval: 1, + maxSize: 1, + async fetch() { + return new Response('', { + status: 200, + }); + }, + logger, + }, + token, + selfHosting: { + graphqlEndpoint: 'http://localhost:2/graphql', + applicationUrl: 'http://localhost:1', + usageEndpoint: 'http://localhost', + }, + usage: { + target: 'the-guild/graphql-hive/staging', + }, + }); + + await hive.collectUsage()( + { + schema, + document: op, + operationName: 'asd', + }, + {}, + ); + + await hive.dispose(); + expect(logger.getLogs()).toMatchInlineSnapshot(` + [INF] [hive][usage][agent] Disposing + [INF] [hive][usage][agent] Sending immediately + [INF] [hive][usage][agent] Sending report (queue 1) + [INF] [hive][usage][agent] POST http://localhost/the-guild/graphql-hive/staging (x-request-id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) + [INF] [hive][usage][agent] POST http://localhost/the-guild/graphql-hive/staging (x-request-id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) succeeded with status 200 (666ms). + [INF] [hive][usage][agent] Report sent! + `); +});