Skip to content

Commit

Permalink
feat: full HTTP request logging (#5234)
Browse files Browse the repository at this point in the history
  • Loading branch information
n1ru4l authored Aug 1, 2024
1 parent 5953562 commit e6dc5c9
Show file tree
Hide file tree
Showing 37 changed files with 818 additions and 507 deletions.
27 changes: 27 additions & 0 deletions .changeset/chilly-balloons-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
'@graphql-hive/apollo': minor
---

Better HTTP info, error and debug logging.

For the supergraph manager, pass a `console` instance as the `logger` property.

```ts
import { createSupergraphManager } from '@graphql-hive/apollo';

const manager = createSupergraphManager({
...otherOptions,
logger: console,
})
```

For the supergraph SDL fetcher pass a `console` instance as the `logger` property.

```ts
import { createSupergraphSDLFetcher } from '@graphql-hive/apollo';

const manager = createSupergraphSDLFetcher({
...otherOptions,
logger: console,
})
```
7 changes: 7 additions & 0 deletions .changeset/fuzzy-readers-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@graphql-hive/core': minor
'@graphql-hive/yoga': minor
'@graphql-hive/apollo': minor
---

Improved logging output of HTTP requests and retires.
5 changes: 5 additions & 0 deletions .changeset/stupid-rabbits-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-hive/cli': minor
---

Provide debug logging for HTTP requests when providing the `--debug` flag.
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const OPERATIONS_PATHS = [
const rulesToExtends = Object.fromEntries(
Object.entries(guildConfig.rules).filter(([key]) =>
[
'no-implicit-coercion',
'import/first',
'no-restricted-globals',
'@typescript-eslint/no-unused-vars',
Expand Down Expand Up @@ -189,6 +188,7 @@ module.exports = {
'jsx-a11y/no-static-element-interactions': 'off',
'@next/next/no-html-link-for-pages': 'off',
'unicorn/no-negated-condition': 'off',
'no-implicit-coercion': 'off',
},
},
{
Expand Down
7 changes: 5 additions & 2 deletions packages/libraries/apollo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
http,
isHiveClient,
joinUrl,
Logger,
} from '@graphql-hive/core';
import { version } from './version.js';

Expand All @@ -17,6 +18,7 @@ export { atLeastOnceSampler, createSchemaFetcher, createServicesFetcher } from '
export interface SupergraphSDLFetcherOptions {
endpoint: string;
key: string;
logger?: Logger;
}

export function createSupergraphSDLFetcher(options: SupergraphSDLFetcherOptions) {
Expand Down Expand Up @@ -44,13 +46,13 @@ export function createSupergraphSDLFetcher(options: SupergraphSDLFetcherOptions)
return http
.get(endpoint, {
headers,
isRequestOk: response => response.status === 304 || response.ok,
retry: {
retryWhen: response => response.status >= 500,
okWhen: response => response.status === 304,
retries: 10,
maxTimeout: 200,
minTimeout: 1,
},
logger: options.logger,
})
.then(async response => {
if (response.ok) {
Expand Down Expand Up @@ -87,6 +89,7 @@ export function createSupergraphManager(
const fetchSupergraph = createSupergraphSDLFetcher({
endpoint: options.endpoint,
key: options.key,
logger: options.logger,
});
let timer: ReturnType<typeof setTimeout> | null = null;

Expand Down
8 changes: 4 additions & 4 deletions packages/libraries/apollo/tests/apollo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ test('should not interrupt the process', async () => {
logger,
},
reporting: {
endpoint: 'http://404.localhost/registry',
endpoint: 'http://404.localhost.noop/registry',
author: 'jest',
commit: 'js',
},
usage: {
endpoint: 'http://404.localhost/usage',
endpoint: 'http://404.localhost.noop/usage',
},
}),
],
Expand All @@ -100,7 +100,7 @@ test('should not interrupt the process', async () => {
}
`,
});
await waitFor(50);
await waitFor(200);
await apollo.stop();
clean();
expect(logger.error).toHaveBeenCalledWith(expect.stringContaining('[hive][info]'));
Expand Down Expand Up @@ -299,7 +299,7 @@ describe('supergraph SDL fetcher', async () => {
await fetcher();
} catch (err) {
expect(err).toMatchInlineSnapshot(
`[Error: Failed to fetch http://localhost/supergraph, received: 500 Internal Server Error]`,
`[Error: GET http://localhost/supergraph failed with status 500.]`,
);
}
});
Expand Down
55 changes: 49 additions & 6 deletions packages/libraries/cli/src/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,53 @@ import { print, type GraphQLError } from 'graphql';
import type { ExecutionResult } from 'graphql';
import { http } from '@graphql-hive/core';
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
import { Command, Errors, Config as OclifConfig } from '@oclif/core';
import { Command, Errors, Flags, Interfaces } from '@oclif/core';
import { Config, GetConfigurationValueType, ValidConfigurationKeys } from './helpers/config';

export type Flags<T extends typeof Command> = Interfaces.InferredFlags<
(typeof BaseCommand)['baseFlags'] & T['flags']
>;
export type Args<T extends typeof Command> = Interfaces.InferredArgs<T['args']>;

type OmitNever<T> = { [K in keyof T as T[K] extends never ? never : K]: T[K] };

export default abstract class extends Command {
protected _userConfig: Config;
export default abstract class BaseCommand<T extends typeof Command> extends Command {
protected _userConfig: Config | undefined;

static baseFlags = {
debug: Flags.boolean({
default: false,
summary: 'Whether debug output for HTTP calls and similar should be enabled.',
}),
};

protected flags!: Flags<T>;
protected args!: Args<T>;

protected get userConfig(): Config {
if (!this._userConfig) {
throw new Error('User config is not initialized');
}
return this._userConfig!;
}

protected constructor(argv: string[], config: OclifConfig) {
super(argv, config);
public async init(): Promise<void> {
await super.init();

this._userConfig = new Config({
// eslint-disable-next-line no-process-env
filepath: process.env.HIVE_CONFIG,
rootDir: process.cwd(),
});

const { args, flags } = await this.parse({
flags: this.ctor.flags,
baseFlags: (super.ctor as typeof BaseCommand).baseFlags,
args: this.ctor.args,
strict: this.ctor.strict,
});
this.flags = flags as Flags<T>;
this.args = args as Args<T>;
}

success(...args: any[]) {
Expand Down Expand Up @@ -122,7 +153,7 @@ export default abstract class extends Command {
return process.env[env] as TArgs[keyof TArgs] as NonNullable<GetConfigurationValueType<TKey>>;
}

const userConfigValue = this._userConfig.get(key);
const userConfigValue = this._userConfig!.get(key);

if (userConfigValue != null) {
return userConfigValue;
Expand Down Expand Up @@ -161,6 +192,8 @@ export default abstract class extends Command {
...additionalHeaders,
};

const isDebug = this.flags.debug;

return {
async request<TResult, TVariables>(
operation: TypedDocumentNode<TResult, TVariables>,
Expand All @@ -173,6 +206,16 @@ export default abstract class extends Command {
variables,
}),
{
logger: {
info: (...args) => {
if (isDebug) {
console.info(...args);
}
},
error: (...args) => {
console.error(...args);
},
},
headers: requestHeaders,
},
);
Expand Down
5 changes: 1 addition & 4 deletions packages/libraries/cli/src/commands/artifact/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { http, URL } from '@graphql-hive/core';
import { Flags } from '@oclif/core';
import Command from '../../base-command';

export default class ArtifactsFetch extends Command {
export default class ArtifactsFetch extends Command<typeof ArtifactsFetch> {
static description = 'fetch artifacts from the CDN';
static flags = {
'cdn.endpoint': Flags.string({
Expand Down Expand Up @@ -47,9 +47,6 @@ export default class ArtifactsFetch extends Command {
},
retry: {
retries: 3,
retryWhen(response) {
return response.status >= 500;
},
},
});

Expand Down
4 changes: 2 additions & 2 deletions packages/libraries/cli/src/commands/config/delete.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Args } from '@oclif/core';
import Command from '../../base-command';

export default class DeleteConfig extends Command {
export default class DeleteConfig extends Command<typeof DeleteConfig> {
static description = 'deletes specific cli configuration';
static args = {
key: Args.string({
Expand All @@ -13,7 +13,7 @@ export default class DeleteConfig extends Command {

async run() {
const { args } = await this.parse(DeleteConfig);
this._userConfig.delete(args.key);
this._userConfig!.delete(args.key);
this.success(this.bolderize(`Config flag "${args.key}" was deleted`));
}
}
4 changes: 2 additions & 2 deletions packages/libraries/cli/src/commands/config/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Args } from '@oclif/core';
import Command from '../../base-command';
import { allowedKeys, ValidConfigurationKeys } from '../../helpers/config';

export default class GetConfig extends Command {
export default class GetConfig extends Command<typeof GetConfig> {
static description = 'prints specific cli configuration';
static args = {
key: Args.string({
Expand All @@ -15,6 +15,6 @@ export default class GetConfig extends Command {

async run() {
const { args } = await this.parse(GetConfig);
console.dir(this._userConfig.get(args.key as ValidConfigurationKeys));
console.dir(this.userConfig.get(args.key as ValidConfigurationKeys));
}
}
4 changes: 2 additions & 2 deletions packages/libraries/cli/src/commands/config/reset.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Command from '../../base-command';

export default class ResetConfig extends Command {
export default class ResetConfig extends Command<typeof ResetConfig> {
static description = 'resets local cli configuration';

async run() {
this._userConfig.clear();
this.userConfig.clear();
this.success('Config cleared.');
}
}
4 changes: 2 additions & 2 deletions packages/libraries/cli/src/commands/config/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Args } from '@oclif/core';
import Command from '../../base-command';
import { allowedKeys, ValidConfigurationKeys } from '../../helpers/config';

export default class SetConfig extends Command {
export default class SetConfig extends Command<typeof SetConfig> {
static description = 'updates specific cli configuration';
static args = {
key: Args.string({
Expand All @@ -20,7 +20,7 @@ export default class SetConfig extends Command {

async run() {
const { args } = await this.parse(SetConfig);
this._userConfig.set(args.key as ValidConfigurationKeys, args.value);
this.userConfig.set(args.key as ValidConfigurationKeys, args.value);
this.success(this.bolderize(`Config flag "${args.key}" was set to "${args.value}"`));
}
}
2 changes: 1 addition & 1 deletion packages/libraries/cli/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ type ServiceWithSource = {
};
};

export default class Dev extends Command {
export default class Dev extends Command<typeof Dev> {
static description = [
'Develop and compose Supergraph with your local services.',
'Only available for Federation projects.',
Expand Down
2 changes: 1 addition & 1 deletion packages/libraries/cli/src/commands/introspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Args, Flags } from '@oclif/core';
import Command from '../base-command';
import { loadSchema } from '../helpers/schema';

export default class Introspect extends Command {
export default class Introspect extends Command<typeof Introspect> {
static description = 'introspects a GraphQL Schema';
static flags = {
write: Flags.string({
Expand Down
2 changes: 1 addition & 1 deletion packages/libraries/cli/src/commands/operations/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const fetchLatestVersionQuery = graphql(/* GraphQL */ `
}
`);

export default class OperationsCheck extends Command {
export default class OperationsCheck extends Command<typeof OperationsCheck> {
static description = 'checks operations against a published schema';
static flags = {
'registry.endpoint': Flags.string({
Expand Down
2 changes: 1 addition & 1 deletion packages/libraries/cli/src/commands/schema/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const schemaCheckMutation = graphql(/* GraphQL */ `
}
`);

export default class SchemaCheck extends Command {
export default class SchemaCheck extends Command<typeof SchemaCheck> {
static description = 'checks schema';
static flags = {
service: Flags.string({
Expand Down
2 changes: 1 addition & 1 deletion packages/libraries/cli/src/commands/schema/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const schemaDeleteMutation = graphql(/* GraphQL */ `
}
`);

export default class SchemaDelete extends Command {
export default class SchemaDelete extends Command<typeof SchemaDelete> {
static description = 'deletes a schema';
static flags = {
'registry.endpoint': Flags.string({
Expand Down
2 changes: 1 addition & 1 deletion packages/libraries/cli/src/commands/schema/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const SchemaVersionForActionIdQuery = graphql(/* GraphQL */ `
}
`);

export default class SchemaFetch extends Command {
export default class SchemaFetch extends Command<typeof SchemaFetch> {
static description = 'fetch schema or supergraph from the Hive API';
static flags = {
/** @deprecated */
Expand Down
2 changes: 1 addition & 1 deletion packages/libraries/cli/src/commands/schema/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const schemaPublishMutation = graphql(/* GraphQL */ `
}
`);

export default class SchemaPublish extends Command {
export default class SchemaPublish extends Command<typeof SchemaPublish> {
static description = 'publishes schema';
static flags = {
service: Flags.string({
Expand Down
2 changes: 1 addition & 1 deletion packages/libraries/cli/src/commands/whoami.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const myTokenInfoQuery = graphql(/* GraphQL */ `
}
`);

export default class WhoAmI extends Command {
export default class WhoAmI extends Command<typeof WhoAmI> {
static description = 'shows information about the current token';
static flags = {
'registry.endpoint': Flags.string({
Expand Down
Loading

0 comments on commit e6dc5c9

Please sign in to comment.