Skip to content

Commit

Permalink
feat: PrettyLogger enableColors option
Browse files Browse the repository at this point in the history
  • Loading branch information
sukovanej committed Jan 28, 2024
1 parent 6d59e3a commit ed79cc0
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/silver-spoons-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect-log": minor
---

Add `enableColors` option for PrettyLogger.
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const logger = PrettyLog.layer({
showFiberId: true,
showTime: true,
showSpans: true,
enableColors: true,
});

pipe(exampleEffect, Effect.provide(logger), Effect.runSync);
Expand Down
114 changes: 82 additions & 32 deletions src/PrettyLogger.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { List, LogSpan } from "effect";
import * as Cause from "effect/Cause";
import * as FiberId from "effect/FiberId";
import { pipe } from "effect/Function";
import { identity, pipe } from "effect/Function";
import * as HashMap from "effect/HashMap";
import * as Layer from "effect/Layer";
import * as LogLevel from "effect/LogLevel";
Expand All @@ -10,6 +10,33 @@ import * as ReadonlyArray from "effect/ReadonlyArray";

import { serializeUnknown } from "effect-log/internal";

const SEVERITY_TO_COLOR: Record<LogLevel.LogLevel["_tag"], (c: ColorService) => (s: string) => string> = {
All: (c: ColorService) => c.white,
None: (c: ColorService) => c.white,
Info: (c: ColorService) => c.green,
Debug: (c: ColorService) => c.blue,
Error: (c: ColorService) => c.red,
Fatal: (c: ColorService) => c.boldRed,
Trace: (c: ColorService) => c.dimWhite,
Warning: (c: ColorService) => c.yellow,
};

interface ColorService {
bold(text: string): string;
dim(text: string): string;
italic(text: string): string;

red(text: string): string;
green(text: string): string;
yellow(text: string): string;
blue(text: string): string;
white(text: string): string;

dimItalic(text: string): string;
dimWhite(text: string): string;
boldRed(text: string): string;
}

const RESET = "\x1b[0m";
const BOLD = "\x1b[1m";
const DIM = "\x1b[2m";
Expand All @@ -21,34 +48,57 @@ const YELLOW = "\x1b[33m";
const BLUE = "\x1b[34m";
const WHITE = "\x1b[37m";

const SEVERITY_TO_COLOR: Record<LogLevel.LogLevel["_tag"], string> = {
All: WHITE,
None: WHITE,
Info: GREEN,
Debug: BLUE,
Error: RED,
Fatal: BOLD + RED,
Trace: DIM + WHITE,
Warning: YELLOW,
};
const enabledColorService: ColorService = {
bold: (text: string) => `${BOLD}${text}${RESET}`,
dim: (text: string) => `${DIM}${text}${RESET}`,
italic: (text: string) => `${ITALIC}${text}${RESET}`,

red: (text: string) => `${RED}${text}${RESET}`,
green: (text: string) => `${GREEN}${text}${RESET}`,
yellow: (text: string) => `${YELLOW}${text}${RESET}`,
blue: (text: string) => `${BLUE}${text}${RESET}`,
white: (text: string) => `${WHITE}${text}${RESET}`,

dimItalic: (text: string) => `${DIM}${ITALIC}${text}${RESET}`,
dimWhite: (text: string) => `${DIM}${WHITE}${text}${RESET}`,
boldRed: (text: string) => `${BOLD}${RED}${text}${RESET}`,
}

const disabledColorService: ColorService = {
bold: identity,
dim: identity,
italic: identity,

red: identity,
green: identity,
yellow: identity,
blue: identity,
white: identity,

dimItalic: identity,
dimWhite: identity,
boldRed: identity,
}

export interface PrettyLoggerOptions {
showFiberId: boolean;
showTime: boolean;
showSpans: boolean;
enableColors: boolean;
}

const defaultOptions: PrettyLoggerOptions = {
showFiberId: true,
showTime: true,
showSpans: true,
enableColors: true
};

const createTimeString = (date: Date) => {
const createTimeString = (colorService: ColorService, date: Date) => {
const hoursText = date.getHours().toString().padStart(2, "0");
const minutesText = date.getMinutes().toString().padStart(2, "0");
const secondsText = date.getSeconds().toString().padStart(2, "0");
return `${YELLOW}${hoursText}:${minutesText}:${secondsText}${RESET}`;
return colorService.yellow(`${hoursText}:${minutesText}:${secondsText}`);
};

const createCauseMessage = (cause: Cause.Cause<unknown>) => {
Expand All @@ -58,31 +108,30 @@ const createCauseMessage = (cause: Cause.Cause<unknown>) => {
return Cause.pretty(cause);
};

const createLogLevelString = (logLevel: LogLevel.LogLevel) => {
const logLevelColor = SEVERITY_TO_COLOR[logLevel._tag];
const logLevelText = logLevel.label.padEnd(5, " ");
return `${logLevelColor}${logLevelText}${RESET}`;
const createLogLevelString = (colorService: ColorService, logLevel: LogLevel.LogLevel) => {
const logLevelColor = SEVERITY_TO_COLOR[logLevel._tag](colorService);
return logLevelColor(logLevel.label.padEnd(5, " "));
};

const messageText = (message: unknown) => {
const messageText = (colorService: ColorService, message: unknown) => {
if (message === undefined) {
return `${DIM}undefined${RESET}`;
return colorService.dim('undefined');
} else if (message === null) {
return `${DIM}null${RESET}`;
return colorService.dim('null');
} else if (message === "") {
return `${DIM}<empty message>${RESET}`;
return colorService.dim('<empty message>');
}
return serializeUnknown(message);
};

const createText = (message: unknown, cause: Cause.Cause<unknown>) =>
const createText = (colorService: ColorService, message: unknown, cause: Cause.Cause<unknown>) =>
pipe(
[createCauseMessage(cause), messageText(message)],
[createCauseMessage(cause), messageText(colorService, message)],
ReadonlyArray.filter((i) => i !== ""),
ReadonlyArray.join(" "),
);

const createSpanText = (spans: List.List<LogSpan.LogSpan>) => {
const createSpanText = (colorService: ColorService, spans: List.List<LogSpan.LogSpan>) => {
if (List.isNil(spans)) {
return "";
}
Expand All @@ -93,23 +142,24 @@ const createSpanText = (spans: List.List<LogSpan.LogSpan>) => {
(acc, span) => `${acc} -> ${span.label}`,
);

return ` ${DIM}${ITALIC}${text}${RESET}`;
return ` ${colorService.dimItalic(text)}`;
};

export const make = (options?: Partial<PrettyLoggerOptions>) =>
Logger.make(
({ fiberId, logLevel, message, annotations, cause, date, spans }) => {
const _options = { ...defaultOptions, ...options };
const colorService = _options.enableColors ? enabledColorService : disabledColorService;

const logLevelStr = createLogLevelString(logLevel);
const timeText = _options.showTime ? `${createTimeString(date)} ` : "";
const logLevelStr = createLogLevelString(colorService, logLevel);
const timeText = _options.showTime ? `${createTimeString(colorService, date)} ` : "";
const fiberText = _options.showFiberId
? `${DIM}(Fiber ${FiberId.threadName(fiberId)})${RESET} `
? colorService.dim(`(Fiber ${FiberId.threadName(fiberId)}) `)
: "";

const text = createText(message, cause);
const text = createText(colorService, message, cause);

const spansText = _options.showSpans ? createSpanText(spans) : "";
const spansText = _options.showSpans ? createSpanText(colorService, spans) : "";

console.log(`${timeText}${fiberText}${logLevelStr}${spansText} ${text}`);

Expand All @@ -119,10 +169,10 @@ export const make = (options?: Partial<PrettyLoggerOptions>) =>
[] as string[],
(acc, v, k) => [
...acc,
`${WHITE}"${k}"${RESET}: ${serializeUnknown(v)}`,
colorService.white(`"${k}"`) + `: ${serializeUnknown(v)}`,
],
);
console.log(`ᐉ ${DIM}{${RESET} ${text.join(", ")} ${DIM}}${RESET}`);
console.log(`ᐉ ${colorService.dim('{')} ${text.join(", ")} ${colorService.dim('}')}`);
}
},
);
Expand Down

0 comments on commit ed79cc0

Please sign in to comment.