Browse Source

Update logger

Make logger pino-compliant.
master
TheoryOfNekomata 1 year ago
parent
commit
6bf8fa5fa7
9 changed files with 154 additions and 53 deletions
  1. +6
    -0
      TODO.md
  2. +1
    -0
      packages/cli/package.json
  3. +2
    -2
      packages/cli/src/modules/adder/adder.controller.ts
  4. +74
    -48
      packages/cli/src/packages/cli-wrapper.ts
  5. +19
    -0
      packages/cli/src/packages/write-stream.ts
  6. +3
    -1
      packages/cli/test/index.test.ts
  7. +22
    -1
      packages/web-api/src/modules/adder/adder.controller.ts
  8. +24
    -1
      packages/web-api/src/routes.ts
  9. +3
    -0
      pnpm-lock.yaml

+ 6
- 0
TODO.md View File

@@ -0,0 +1,6 @@
- We could make a common code generation strategy between
CLI and Web API since we have unified the code structure.
- What this means is we just need to declare the connection
from the core SDK to the CLI or Web API functions, then we
can run some sort of code generation, so we don't have to
bother writing the Web API/CLI code manually.

+ 1
- 0
packages/cli/package.json View File

@@ -67,6 +67,7 @@
"dependencies": { "dependencies": {
"@clack/prompts": "^0.6.3", "@clack/prompts": "^0.6.3",
"@modal-sh/core": "workspace:*", "@modal-sh/core": "workspace:*",
"pino": "^8.14.1",
"yargs": "^17.7.2" "yargs": "^17.7.2"
} }
} }

+ 2
- 2
packages/cli/src/modules/adder/adder.controller.ts View File

@@ -34,7 +34,7 @@ export class AdderControllerImpl implements AdderController {
const { a, b } = params.args; const { a, b } = params.args;
try { try {
const response = this.adderService.addNumbers({ a: Number(a), b: Number(b) }); const response = this.adderService.addNumbers({ a: Number(a), b: Number(b) });
params.logger.log(response);
params.logger.info(response);
} catch (errorRaw) { } catch (errorRaw) {
const error = errorRaw as Error; const error = errorRaw as Error;
params.logger.error(error.message); params.logger.error(error.message);
@@ -65,7 +65,7 @@ export class AdderControllerImpl implements AdderController {
const { a, b } = params.args; const { a, b } = params.args;
try { try {
const response = this.adderService.addNumbers({ a: Number(a), b: -(Number(b)) }); const response = this.adderService.addNumbers({ a: Number(a), b: -(Number(b)) });
params.logger.log(response);
params.logger.info(response);
} catch (errorRaw) { } catch (errorRaw) {
const error = errorRaw as Error; const error = errorRaw as Error;
params.logger.error(error.message); params.logger.error(error.message);


+ 74
- 48
packages/cli/src/packages/cli-wrapper.ts View File

@@ -1,13 +1,10 @@
import tty from 'tty';
import yargs, {Argv} from 'yargs';
import yargs, { Argv } from 'yargs';
import * as clack from '@clack/prompts'; import * as clack from '@clack/prompts';
import { DummyWriteStream } from './write-stream';
import pino, {LogFn} from 'pino';
import * as util from 'util';


export interface Logger {
log: (message: unknown) => void;
error: (message: unknown) => void;
warn: (message: unknown) => void;
debug: (message: unknown) => void;
}
export interface Logger extends pino.BaseLogger {}


export interface CommandHandlerArgs { export interface CommandHandlerArgs {
self: any; self: any;
@@ -33,24 +30,6 @@ type PromptValueArgs = [Record<string, string>, ...never[]]
| [[string, string], ...never[]] | [[string, string], ...never[]]
| [[string, string][], ...never[]]; | [[string, string][], ...never[]];


class DummyWriteStream extends tty.WriteStream {
private bufferInternal = Buffer.from('');

constructor() {
super(0);
}

write(input: Uint8Array | string, _encoding?: BufferEncoding | ((err?: Error) => void), _cb?: (err?: Error) => void): boolean {
this.bufferInternal = Buffer.concat([this.bufferInternal, Buffer.from(input)]);
// noop
return true;
}

get buffer(): Buffer {
return this.bufferInternal;
}
}

export interface TestModeResult { export interface TestModeResult {
exitCode?: number; exitCode?: number;
stdout: DummyWriteStream; stdout: DummyWriteStream;
@@ -69,6 +48,7 @@ interface InteractiveModeOptions {
export interface CliOptions { export interface CliOptions {
name: string; name: string;
interactiveMode?: Partial<InteractiveModeOptions>; interactiveMode?: Partial<InteractiveModeOptions>;
logger?: Logger | boolean;
} }


export class Cli { export class Cli {
@@ -171,27 +151,6 @@ export class Cli {
}; };
} }


private generateLogger() {
return {
log: (message: unknown) => {
const stdout = this.testMode ? this.testModeResult.stdout : process.stdout;
stdout.write(`${message?.toString()}\n`);
},
warn: (message: unknown) => {
const stdout = this.testMode ? this.testModeResult.stdout : process.stdout;
stdout.write(`WARN: ${message?.toString()}\n`);
},
debug: (message: unknown) => {
const stdout = this.testMode ? this.testModeResult.stdout : process.stdout;
stdout.write(`DEBUG: ${message?.toString()}\n`);
},
error: (message: unknown) => {
const stderr = this.testMode ? this.testModeResult.stderr : process.stderr;
stderr.write(`${message?.toString()}\n`);
},
};
}

private buildHandler(handlerFn: Function, interactiveModeOptions: InteractiveModeOptions) { private buildHandler(handlerFn: Function, interactiveModeOptions: InteractiveModeOptions) {
const thisCli = this; const thisCli = this;
return async function handler(this: any, commandArgs: Record<string, unknown>) { return async function handler(this: any, commandArgs: Record<string, unknown>) {
@@ -207,11 +166,78 @@ export class Cli {
interactiveModeOptions, interactiveModeOptions,
); );


const stdoutLogFn: LogFn = (...args: unknown[]) => {
const stream = thisCli.testMode ? thisCli.testModeResult.stdout : process.stdout;
const [arg0, arg1, ...etcArgs] = args;
if (typeof arg0 === 'string' || typeof arg0 === 'number') {
if (arg1) {
if (etcArgs.length > 0) {
stream.write(util.format(`${arg0}\n`, arg1, ...etcArgs));
} else {
stream.write(util.format(`${arg0}\n`, arg1));
}
} else {
stream.write(util.format(`${arg0}\n`));
}
} else if (typeof arg0 === 'object' && (typeof arg1 === 'string' || typeof arg1 === 'number')) {
if (etcArgs.length > 0) {
stream.write(util.format(`${arg1}\n`, ...etcArgs));
} else {
stream.write(util.format(`${arg1}\n`));
}
}
};

const stderrLogFn: LogFn = (...args: unknown[]) => {
const stream = thisCli.testMode ? thisCli.testModeResult.stderr : process.stderr;
const [arg0, arg1, ...etcArgs] = args;
if (typeof arg0 === 'string' || typeof arg0 === 'number') {
if (arg1) {
if (etcArgs.length > 0) {
stream.write(util.format(`${arg0}\n`, arg1, ...etcArgs));
} else {
stream.write(util.format(`${arg0}\n`, arg1));
}
} else {
stream.write(util.format(`${arg0}\n`));
}
} else if (typeof arg0 === 'object' && (typeof arg1 === 'string' || typeof arg1 === 'number')) {
if (etcArgs.length > 0) {
stream.write(util.format(`${arg1}\n`, ...etcArgs));
} else {
stream.write(util.format(`${arg1}\n`));
}
}
};

const loggerFn = thisCli.options.logger;
const defaultLogger = {
level: 'info',
debug: stdoutLogFn,
info: stdoutLogFn,
warn: stdoutLogFn,
error: stderrLogFn,
fatal: stderrLogFn,
trace: stdoutLogFn,
silent: () => {},
} as Logger;
const loggerBooleanFn = typeof loggerFn === 'boolean' && loggerFn ? defaultLogger : {
level: 'silent',
debug: () => {},
info: () => {},
warn: () => {},
error: () => {},
fatal: () => {},
trace: () => {},
silent: () => {},
} as Logger;
const logger = typeof loggerFn === 'undefined' ? defaultLogger : (typeof loggerFn === 'function' ? loggerFn : loggerBooleanFn);

let exited = false; let exited = false;
const returnCode = await handlerFn({ const returnCode = await handlerFn({
self, self,
interactive, interactive,
logger: thisCli.generateLogger(),
logger,
send: (code: number) => { send: (code: number) => {
exited = true; exited = true;
thisCli.exit(code); thisCli.exit(code);


+ 19
- 0
packages/cli/src/packages/write-stream.ts View File

@@ -0,0 +1,19 @@
import {WriteStream} from 'tty';

export class DummyWriteStream extends WriteStream {
private bufferInternal = Buffer.from('');

constructor() {
super(0);
}

write(input: Uint8Array | string, _encoding?: BufferEncoding | ((err?: Error) => void), _cb?: (err?: Error) => void): boolean {
this.bufferInternal = Buffer.concat([this.bufferInternal, Buffer.from(input)]);
// noop
return true;
}

get buffer(): Buffer {
return this.bufferInternal;
}
}

+ 3
- 1
packages/cli/test/index.test.ts View File

@@ -1,7 +1,8 @@
import { describe, it, expect, vi, beforeAll } from 'vitest'; import { describe, it, expect, vi, beforeAll } from 'vitest';
import { Cli, createCli } from '../src/cli';
import { createCli } from '../src/cli';
import { addCommands } from '../src/commands'; import { addCommands } from '../src/commands';
import { AdderServiceImpl, InvalidArgumentTypeError, ArgumentOutOfRangeError } from '../src/modules/adder'; import { AdderServiceImpl, InvalidArgumentTypeError, ArgumentOutOfRangeError } from '../src/modules/adder';
import { Cli } from '../src/packages/cli-wrapper';


vi.mock('process'); vi.mock('process');


@@ -10,6 +11,7 @@ describe('blah', () => {
beforeAll(() => { beforeAll(() => {
cli = createCli({ cli = createCli({
name: 'cli-test', name: 'cli-test',
logger: false,
}); });
addCommands(cli); addCommands(cli);
}); });


+ 22
- 1
packages/web-api/src/modules/adder/adder.controller.ts View File

@@ -9,6 +9,7 @@ import { constants } from 'http2';


export interface AdderController { export interface AdderController {
addNumbers: RouteHandlerMethod; addNumbers: RouteHandlerMethod;
subtractNumbers: RouteHandlerMethod;
} }


export class AdderControllerImpl implements AdderController { export class AdderControllerImpl implements AdderController {
@@ -21,7 +22,27 @@ export class AdderControllerImpl implements AdderController {
readonly addNumbers: RouteHandlerMethod = async (request, reply) => { readonly addNumbers: RouteHandlerMethod = async (request, reply) => {
const { a, b } = request.body as { a: number; b: number }; const { a, b } = request.body as { a: number; b: number };
try { try {
const response = this.adderService.addNumbers({a, b});
const response = this.adderService.addNumbers({ a, b });
reply.send(response);
} catch (errorRaw) {
if (errorRaw instanceof InvalidArgumentTypeError) {
request.log.info(errorRaw);
reply.status(constants.HTTP_STATUS_BAD_REQUEST).send(errorRaw.message);
return;
}
if (errorRaw instanceof ArgumentOutOfRangeError) {
reply.status(constants.HTTP_STATUS_BAD_REQUEST).send(errorRaw.message);
return;
}
const error = errorRaw as Error;
reply.status(constants.HTTP_STATUS_INTERNAL_SERVER_ERROR).send(error.message);
}
}

readonly subtractNumbers: RouteHandlerMethod = async (request, reply) => {
const { a, b } = request.body as { a: number; b: number };
try {
const response = this.adderService.addNumbers({ a, b: -b });
reply.send(response); reply.send(response);
} catch (errorRaw) { } catch (errorRaw) {
if (errorRaw instanceof InvalidArgumentTypeError) { if (errorRaw instanceof InvalidArgumentTypeError) {


+ 24
- 1
packages/web-api/src/routes.ts View File

@@ -7,7 +7,30 @@ export const addRoutes = (server: FastifyInstance) => {
return server return server
.route({ .route({
method: 'POST', method: 'POST',
url: '/',
url: '/add',
schema: {
body: {
type: 'object',
properties: {
a: { type: 'number' },
b: { type: 'number' },
}
}
},
handler: adderController.addNumbers, handler: adderController.addNumbers,
})
.route({
method: 'POST',
url: '/subtract',
schema: {
body: {
type: 'object',
properties: {
a: { type: 'number' },
b: { type: 'number' },
}
}
},
handler: adderController.subtractNumbers,
}); });
}; };

+ 3
- 0
pnpm-lock.yaml View File

@@ -14,6 +14,9 @@ importers:
'@modal-sh/core': '@modal-sh/core':
specifier: workspace:* specifier: workspace:*
version: link:../core version: link:../core
pino:
specifier: ^8.14.1
version: 8.14.1
yargs: yargs:
specifier: ^17.7.2 specifier: ^17.7.2
version: 17.7.2 version: 17.7.2


Loading…
Cancel
Save