Skip to content

Commit 9fd51e4

Browse files
committed
feat: JSON logger options + log spans
1 parent fbd472d commit 9fd51e4

File tree

8 files changed

+132
-60
lines changed

8 files changed

+132
-60
lines changed

.changeset/giant-dots-try.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect-log": minor
3+
---
4+
5+
Add options for JSON logger. Add support for log spans.

assets/json.png

-18.8 KB
Loading

assets/pretty.png

-80.4 KB
Loading

examples/example-logging-effect.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const exampleEffect = pipe(
1313
Effect.annotateLogs("json", { value: 1, another: { hello: [3, 2, 1] } }),
1414
),
1515
),
16+
Effect.withLogSpan("mySpan"),
1617
Effect.annotateLogs("rootCause", "milan"),
1718
Effect.tap(() =>
1819
pipe(Effect.logFatal("Don Quijote"), Effect.annotateLogs("likes", "fp-ts")),
@@ -21,6 +22,7 @@ export const exampleEffect = pipe(
2122
Effect.tap(() =>
2223
Effect.logWarning("Lesnek is a beautiful surname, is it not?"),
2324
),
25+
Effect.withLogSpan("stuff"),
2426
Effect.annotateLogs("myName", "Earl"),
2527
Effect.tap(() => Effect.logDebug("Sooo sad, not annotations for me")),
2628
Effect.tap(() => Effect.logTrace("Never Gonna Give You Up")),
@@ -29,9 +31,11 @@ export const exampleEffect = pipe(
2931
Effect.flatMap(() =>
3032
pipe(Effect.log(""), Effect.annotateLogs("likes", "fp-ts")),
3133
),
34+
Effect.withSpan("span-label-3"),
3235
Effect.flatMap(() =>
3336
pipe(Effect.log(undefined), Effect.annotateLogs("likes", "fp-ts")),
3437
),
38+
Effect.withLogSpan("helloWorld"),
3539
Effect.flatMap(() =>
3640
pipe(Effect.log(null), Effect.annotateLogs("likes", "fp-ts")),
3741
),

examples/pretty-logger.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,4 @@ import { PrettyLogger } from "effect-log";
33

44
import { exampleEffect } from "./example-logging-effect";
55

6-
pipe(
7-
exampleEffect,
8-
Effect.provide(PrettyLogger.layer({ showFiberId: false, showTime: false })),
9-
Effect.runSync,
10-
);
6+
pipe(exampleEffect, Effect.provide(PrettyLogger.layer()), Effect.runSync);

readme.md

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,22 @@ Logging batteris for effect-ts.
55
## [Pretty logger](examples/pretty-logger.ts)
66

77
Use `PrettyLogger.make` to create the pretty logger or `PrettyLogger.layer` to
8-
obtain a layer replacing the default logger. Optionally, use the argument
9-
to configure what information gets propagated to the output.
8+
obtain a layer replacing the default logger. Optionally, these functions
9+
accept an options object configuring what information gets
10+
to the output.
1011

1112
```typescript
1213
import { Effect, pipe } from "effect";
1314
import { PrettyLog } from "effect-log";
1415

1516
import { exampleEffect } from "./example-logging-effect";
1617

18+
// These are the defaults, you can ommit the argument
19+
// completely if you're okay with the defaults.
1720
const logger = PrettyLog.layer({
18-
showFiberId: false,
19-
showTime: false,
21+
showFiberId: true,
22+
showTime: true,
23+
showSpans: true,
2024
});
2125

2226
pipe(exampleEffect, Effect.provide(logger), Effect.runSync);
@@ -26,17 +30,25 @@ pipe(exampleEffect, Effect.provide(logger), Effect.runSync);
2630

2731
## [JSON logger](examples/json-logger.ts)
2832

29-
Use `JsonLogger.make` to create the pretty logger or `JsonLogger.layer` to
30-
obtain a layer replacing the default loggger. Optionally, specify a name
31-
of the message field by the input argument.
33+
Use `JsonLogger.make` to create the JSON logger or `JsonLogger.layer` to
34+
obtain a layer replacing the default loggger. Optionally, these functions
35+
accept an options object configuring what information gets
36+
to the output.
3237

3338
```typescript
3439
import { Effect, pipe } from "effect";
3540
import { JsonLogger } from "effect-log";
3641

3742
import { exampleEffect } from "./example-logging-effect";
3843

39-
const logger = JsonLogger.layer();
44+
// These are the defaults, you can ommit the argument
45+
// completely if you're okay with the defaults.
46+
const logger = JsonLogger.layer({
47+
showFiberId: true,
48+
showTime: true,
49+
showSpans: true,
50+
messageField: "message",
51+
});
4052

4153
pipe(exampleEffect, Effect.provide(logger), Effect.runSync);
4254
```

src/JsonLogger.ts

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,62 @@
1+
import { List } from "effect";
12
import * as FiberId from "effect/FiberId";
23
import * as HashMap from "effect/HashMap";
34
import * as Layer from "effect/Layer";
45
import * as Logger from "effect/Logger";
56

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

8-
export const make = (messageField?: string) =>
9-
Logger.make(({ fiberId, logLevel, message, annotations, cause, date }) => {
10-
const tags: Record<string, unknown> = HashMap.reduce(
11-
annotations,
12-
{},
13-
(acc, v, k) => ({
14-
...acc,
15-
[k]: v,
16-
}),
17-
);
18-
19-
tags["date"] = date;
20-
tags["logLevel"] = logLevel.label;
21-
tags[messageField ?? "message"] = serializeUnknown(message);
22-
tags["fiberId"] = FiberId.threadName(fiberId);
23-
24-
if (cause._tag !== "Empty") {
25-
tags["cause"] = cause;
26-
}
27-
28-
console.log(JSON.stringify(tags));
29-
});
9+
export interface Options {
10+
showFiberId: boolean;
11+
showTime: boolean;
12+
showSpans: boolean;
13+
messageField: string;
14+
}
15+
16+
const defaultOptions: Options = {
17+
showFiberId: true,
18+
showTime: true,
19+
showSpans: true,
20+
messageField: "message",
21+
};
22+
23+
export const make = (options?: Partial<Options>) =>
24+
Logger.make(
25+
({ fiberId, logLevel, message, annotations, cause, date, spans }) => {
26+
const _options = { ...defaultOptions, ...options };
27+
28+
const tags: Record<string, unknown> = HashMap.reduce(
29+
annotations,
30+
{},
31+
(acc, v, k) => ({
32+
...acc,
33+
[k]: v,
34+
}),
35+
);
36+
37+
if (_options.showTime) {
38+
tags["date"] = date;
39+
}
40+
tags["logLevel"] = logLevel.label;
41+
tags[_options.messageField] = serializeUnknown(message);
42+
43+
if (_options.showFiberId) {
44+
tags["fiberId"] = FiberId.threadName(fiberId);
45+
}
46+
47+
if (_options.showSpans && List.isCons(spans)) {
48+
tags["spans"] = List.toArray(spans).map((span) => span.label);
49+
}
50+
51+
if (cause._tag !== "Empty") {
52+
tags["cause"] = cause;
53+
}
54+
55+
console.log(JSON.stringify(tags));
56+
},
57+
);
3058

3159
export const layer: (
32-
messageFields?: string,
33-
) => Layer.Layer<never, never, never> = (messageField) =>
34-
Logger.replace(Logger.defaultLogger, make(messageField));
60+
options?: Partial<Options>,
61+
) => Layer.Layer<never, never, never> = (options) =>
62+
Logger.replace(Logger.defaultLogger, make(options ?? {}));

src/PrettyLogger.ts

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { List, LogSpan } from "effect";
12
import * as Cause from "effect/Cause";
23
import * as FiberId from "effect/FiberId";
34
import { pipe } from "effect/Function";
@@ -10,8 +11,10 @@ import * as ReadonlyArray from "effect/ReadonlyArray";
1011
import { serializeUnknown } from "effect-log/internal";
1112

1213
const RESET = "\x1b[0m";
13-
const DIM = "\x1b[2m";
1414
const BOLD = "\x1b[1m";
15+
const DIM = "\x1b[2m";
16+
const ITALIC = "\x1b[3m";
17+
1518
const RED = "\x1b[31m";
1619
const GREEN = "\x1b[32m";
1720
const YELLOW = "\x1b[33m";
@@ -32,11 +35,13 @@ const SEVERITY_TO_COLOR: Record<LogLevel.LogLevel["_tag"], string> = {
3235
export interface PrettyLoggerOptions {
3336
showFiberId: boolean;
3437
showTime: boolean;
38+
showSpans: boolean;
3539
}
3640

3741
const defaultOptions: PrettyLoggerOptions = {
3842
showFiberId: true,
3943
showTime: true,
44+
showSpans: true,
4045
};
4146

4247
const createTimeString = (date: Date) => {
@@ -77,28 +82,50 @@ const createText = (message: unknown, cause: Cause.Cause<unknown>) =>
7782
ReadonlyArray.join(" "),
7883
);
7984

85+
const createSpanText = (spans: List.List<LogSpan.LogSpan>) => {
86+
if (List.isNil(spans)) {
87+
return "";
88+
}
89+
90+
const text = List.reduce(
91+
List.unsafeTail(spans),
92+
List.unsafeHead(spans).label,
93+
(acc, span) => `${acc} -> ${span.label}`,
94+
);
95+
96+
return ` ${DIM}${ITALIC}${text}${RESET}`;
97+
};
98+
8099
export const make = (options?: Partial<PrettyLoggerOptions>) =>
81-
Logger.make(({ fiberId, logLevel, message, annotations, cause, date }) => {
82-
const _options = { ...defaultOptions, ...options };
83-
84-
const logLevelStr = createLogLevelString(logLevel);
85-
const timeText = _options.showTime ? `${createTimeString(date)} ` : "";
86-
const fiberText = _options?.showFiberId
87-
? `${DIM}(Fiber ${FiberId.threadName(fiberId)})${RESET} `
88-
: "";
89-
90-
const text = createText(message, cause);
91-
92-
console.log(`${timeText}${fiberText}${logLevelStr} ${text}`);
93-
94-
if (!HashMap.isEmpty(annotations)) {
95-
const text = HashMap.reduce(annotations, [] as string[], (acc, v, k) => [
96-
...acc,
97-
`${WHITE}"${k}"${RESET}: ${serializeUnknown(v)}`,
98-
]);
99-
console.log(`ᐉ ${DIM}{${RESET} ${text.join(", ")} ${DIM}}${RESET}`);
100-
}
101-
});
100+
Logger.make(
101+
({ fiberId, logLevel, message, annotations, cause, date, spans }) => {
102+
const _options = { ...defaultOptions, ...options };
103+
104+
const logLevelStr = createLogLevelString(logLevel);
105+
const timeText = _options.showTime ? `${createTimeString(date)} ` : "";
106+
const fiberText = _options.showFiberId
107+
? `${DIM}(Fiber ${FiberId.threadName(fiberId)})${RESET} `
108+
: "";
109+
110+
const text = createText(message, cause);
111+
112+
const spansText = _options.showSpans ? createSpanText(spans) : "";
113+
114+
console.log(`${timeText}${fiberText}${logLevelStr}${spansText} ${text}`);
115+
116+
if (!HashMap.isEmpty(annotations)) {
117+
const text = HashMap.reduce(
118+
annotations,
119+
[] as string[],
120+
(acc, v, k) => [
121+
...acc,
122+
`${WHITE}"${k}"${RESET}: ${serializeUnknown(v)}`,
123+
],
124+
);
125+
console.log(`ᐉ ${DIM}{${RESET} ${text.join(", ")} ${DIM}}${RESET}`);
126+
}
127+
},
128+
);
102129

103130
export const layer: (
104131
options?: Partial<PrettyLoggerOptions>,

0 commit comments

Comments
 (0)