@effectstream/log 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +24 -0
- package/src/brands.ts +10 -0
- package/src/const.ts +108 -0
- package/src/mod.ts +35 -0
- package/src/otel/logger.ts +32 -0
- package/src/otel/setup.ts +104 -0
- package/src/tslog.ts +194 -0
- package/src/utils.ts +20 -0
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@effectstream/log",
|
|
3
|
+
"version": "0.100.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"exports": "./src/mod.ts",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@effectstream/utils": "0.100.0",
|
|
8
|
+
"@opentelemetry/api": "^1.9.0",
|
|
9
|
+
"@opentelemetry/api-logs": "0.56.0",
|
|
10
|
+
"@opentelemetry/exporter-logs-otlp-http": "0.56.0",
|
|
11
|
+
"@opentelemetry/exporter-metrics-otlp-http": "0.56.0",
|
|
12
|
+
"@opentelemetry/exporter-trace-otlp-http": "0.56.0",
|
|
13
|
+
"@opentelemetry/resources": "1.29.0",
|
|
14
|
+
"@opentelemetry/sdk-logs": "0.56.0",
|
|
15
|
+
"@opentelemetry/sdk-metrics": "1.29.0",
|
|
16
|
+
"@opentelemetry/sdk-trace-base": "1.29.0",
|
|
17
|
+
"@opentelemetry/semantic-conventions": "1.28.0",
|
|
18
|
+
"chalk": "5.4.1",
|
|
19
|
+
"effection": "3.5.0",
|
|
20
|
+
"material-chalk": "1.1.1",
|
|
21
|
+
"superjson": "^2.2.1",
|
|
22
|
+
"tslog": "^4.9.3"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/brands.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Format, matchColor, registerBrand } from "material-chalk";
|
|
2
|
+
|
|
3
|
+
const paimaBrandColor = matchColor("#1BB17B");
|
|
4
|
+
registerBrand("paima", paimaBrandColor.formatAs(Format.Hct));
|
|
5
|
+
|
|
6
|
+
const hardhatBrandColor = matchColor("#FFF100");
|
|
7
|
+
registerBrand("hardhat", hardhatBrandColor.formatAs(Format.Hct));
|
|
8
|
+
|
|
9
|
+
const dolosBrandColor = matchColor("#FF0065");
|
|
10
|
+
registerBrand("dolos", dolosBrandColor.formatAs(Format.Hct));
|
package/src/const.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
2
|
+
import { getEnv } from "@effectstream/utils/runtime";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Main components
|
|
6
|
+
* These components get a "paima" prefix in the log.
|
|
7
|
+
*/
|
|
8
|
+
export const PaimaComponents = {
|
|
9
|
+
EFFECTSTREAM_SYNC: "sync",
|
|
10
|
+
EFFECTSTREAM_RUNTIME: "runtime",
|
|
11
|
+
EFFECTSTREAM_PGLITE: "pglite",
|
|
12
|
+
APPLY_MIGRATIONS: "apply-migrations",
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* External components
|
|
16
|
+
*/
|
|
17
|
+
export const ExternalComponents = {
|
|
18
|
+
HARDHAT: "hardhat",
|
|
19
|
+
YACI_DEVKIT: "yaci-devkit",
|
|
20
|
+
DOLOS: "dolos",
|
|
21
|
+
MIDNIGHT_NODE: "midnight-node",
|
|
22
|
+
MIDNIGHT_INDEXER: "midnight-indexer",
|
|
23
|
+
MIDNIGHT_PROOF_SERVER: "midnight-proof-server",
|
|
24
|
+
AVAIL_NODE: "avail-node",
|
|
25
|
+
AVAIL_CLIENT: "avail-light-client",
|
|
26
|
+
BITCOIN_CORE: "bitcoin-core",
|
|
27
|
+
NEAR_SANDBOX: "near-sandbox",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Paima Tools, helper and services to support the main components.
|
|
32
|
+
* These components get a "paima" prefix in the log.
|
|
33
|
+
*/
|
|
34
|
+
export const PaimaToolsComponents = {
|
|
35
|
+
CHECKER: "checker",
|
|
36
|
+
TUI: "tui",
|
|
37
|
+
TMUX: "tmux",
|
|
38
|
+
COLLECTOR: "collector",
|
|
39
|
+
EXPLORER: "explorer",
|
|
40
|
+
DEPLOY_EVM_CONTRACTS: "deploy-evm-contracts",
|
|
41
|
+
MIDNIGHT_CONTRACT: "midnight-contract",
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Secondary components
|
|
45
|
+
* These components are launched automatically.
|
|
46
|
+
*/
|
|
47
|
+
export const SecondaryComponents = {
|
|
48
|
+
// This is the entry point, so it gets launched by default.
|
|
49
|
+
ORCHESTRATOR: "orchestrator",
|
|
50
|
+
// Thses processes are launched by their counterpart.
|
|
51
|
+
DOLOS_FILL_TEMPLATE: "dolos-fill-template",
|
|
52
|
+
DOLOS_WAIT: "dolos-wait",
|
|
53
|
+
YACI_DEVKIT_WAIT: "yaci-devkit-wait",
|
|
54
|
+
MIDNIGHT_NODE_WAIT: "midnight-node-wait",
|
|
55
|
+
MIDNIGHT_INDEXER_WAIT: "midnight-indexer-wait",
|
|
56
|
+
MIDNIGHT_PROOF_SERVER_WAIT: "midnight-proof-server-wait",
|
|
57
|
+
BITCOIN_CORE_WAIT: "bitcoin-core-wait",
|
|
58
|
+
BITCOIN_GENERATE_BLOCKS: "bitcoin-mine-blocks",
|
|
59
|
+
BITCOIN_WAIT_FOR_BLOCK: "bitcoin-wait-for-block",
|
|
60
|
+
AVAIL_NODE_WAIT: "avail-node-wait",
|
|
61
|
+
AVAIL_CLIENT_WAIT: "avail-light-client-wait",
|
|
62
|
+
NEAR_SANDBOX_WAIT: "near-sandbox-wait",
|
|
63
|
+
HARDHAT_WAIT: "hardhat-wait",
|
|
64
|
+
COLLECTOR_WAIT: "collector-wait",
|
|
65
|
+
EFFECTSTREAM_DB_WAIT: "db-wait",
|
|
66
|
+
LOKI: "loki",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/** All the components that can be launched by the orchestrator */
|
|
70
|
+
export const LaunchableComponents = {
|
|
71
|
+
...PaimaComponents,
|
|
72
|
+
...PaimaToolsComponents,
|
|
73
|
+
...ExternalComponents,
|
|
74
|
+
};
|
|
75
|
+
/** All the components */
|
|
76
|
+
export const ComponentNames = {
|
|
77
|
+
...LaunchableComponents,
|
|
78
|
+
...SecondaryComponents,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const parseSeverity = (): SeverityNumber => {
|
|
82
|
+
const level = getEnv("EFFECTSTREAM_LOG_LEVEL")?.toUpperCase();
|
|
83
|
+
if (level && level in SeverityNumber) {
|
|
84
|
+
return SeverityNumber[level as keyof typeof SeverityNumber];
|
|
85
|
+
}
|
|
86
|
+
return SeverityNumber.INFO;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const defaultSeverity = parseSeverity();
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* extra step in the logger to avoid performance degradation.
|
|
93
|
+
* otherwise, the following can slow down the program:
|
|
94
|
+
* `log.local(LogLevels.DEBUG, JSON.stringify(largeObj))`
|
|
95
|
+
* since it runs `JSON.stringify` even if the default `LogLevel` excludes it
|
|
96
|
+
*/
|
|
97
|
+
export type DeferredLog = (logger: typeof console.log) => void;
|
|
98
|
+
|
|
99
|
+
// TODO: should re-export a type built-into material-chalk instead of defining it here
|
|
100
|
+
// notably, https://github.com/SebastienGllmt/material-chalk/blob/master/src/core.ts#L175
|
|
101
|
+
export type Namespace = string | string[];
|
|
102
|
+
|
|
103
|
+
export type LogFunc = (
|
|
104
|
+
component: string,
|
|
105
|
+
namespace: Namespace,
|
|
106
|
+
level: SeverityNumber,
|
|
107
|
+
doLog: DeferredLog,
|
|
108
|
+
) => void;
|
package/src/mod.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
2
|
+
import { tsLogFormatted, tsLogGetFormattedMessage, tsLogOrchestrator, type TslogLogFunc } from "./tslog.ts";
|
|
3
|
+
import { otelLog, type OtelLogFunc } from "./otel/logger.ts";
|
|
4
|
+
import "./brands.ts"; // register material-chalk brands
|
|
5
|
+
import { getEnv } from "@effectstream/utils/runtime";
|
|
6
|
+
export * from "./otel/setup.ts";
|
|
7
|
+
export {
|
|
8
|
+
ComponentNames,
|
|
9
|
+
LaunchableComponents,
|
|
10
|
+
type Namespace,
|
|
11
|
+
} from "./const.ts";
|
|
12
|
+
export { attachTransport } from "./tslog.ts";
|
|
13
|
+
export { DefaultLogLevels } from "./tslog.ts";
|
|
14
|
+
|
|
15
|
+
// re-exporting this
|
|
16
|
+
// so that we don't need to re-import opentelemetry in every component
|
|
17
|
+
export { SeverityNumber };
|
|
18
|
+
|
|
19
|
+
const orchestratorEnv = getEnv("EFFECTSTREAM_ORCHESTRATOR");
|
|
20
|
+
const localLogger = orchestratorEnv ? tsLogOrchestrator : tsLogFormatted;
|
|
21
|
+
const remoteLogger = orchestratorEnv ? tsLogOrchestrator : otelLog;
|
|
22
|
+
|
|
23
|
+
export const log: {
|
|
24
|
+
local: TslogLogFunc;
|
|
25
|
+
localForce: TslogLogFunc;
|
|
26
|
+
remote: TslogLogFunc;
|
|
27
|
+
remoteForce: OtelLogFunc;
|
|
28
|
+
formatMessage: typeof tsLogGetFormattedMessage
|
|
29
|
+
} = {
|
|
30
|
+
local: localLogger,
|
|
31
|
+
remote: remoteLogger,
|
|
32
|
+
localForce: tsLogFormatted,
|
|
33
|
+
remoteForce: otelLog,
|
|
34
|
+
formatMessage: tsLogGetFormattedMessage,
|
|
35
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as otlApi from "@opentelemetry/api-logs";
|
|
2
|
+
import type { LogFunc } from "../const.ts";
|
|
3
|
+
import { defaultSeverity } from "../const.ts";
|
|
4
|
+
import { toString } from "../utils.ts";
|
|
5
|
+
|
|
6
|
+
export type OtelLogFunc = LogFunc;
|
|
7
|
+
|
|
8
|
+
export const otelLog: OtelLogFunc = (
|
|
9
|
+
component,
|
|
10
|
+
namespace,
|
|
11
|
+
level,
|
|
12
|
+
doLog,
|
|
13
|
+
): void => {
|
|
14
|
+
if (level < defaultSeverity) return;
|
|
15
|
+
// note: we can't just use otel span events, or debugging gets really hard
|
|
16
|
+
// since span events only get sent to the collector after the span completed.
|
|
17
|
+
// there are some open questions about logs/span/events too, so we just don't use span events for now
|
|
18
|
+
// See https://github.com/open-telemetry/opentelemetry-specification/discussions/3732
|
|
19
|
+
|
|
20
|
+
// TODO: modify these constants
|
|
21
|
+
const logger = otlApi.logs.getLogger(component, "0.3.0");
|
|
22
|
+
doLog((...data: unknown[]) => {
|
|
23
|
+
return logger.emit({
|
|
24
|
+
severityNumber: level,
|
|
25
|
+
body: data.map(toString).join(" "),
|
|
26
|
+
attributes: {
|
|
27
|
+
component,
|
|
28
|
+
namespace: Array.isArray(namespace) ? namespace.join(".") : namespace,
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// careful: only include opentelemetry libs that work in the browser too
|
|
2
|
+
import type opentelemetry from "@opentelemetry/api";
|
|
3
|
+
import { createContext, type Operation } from "effection";
|
|
4
|
+
import { type IResource, Resource } from "@opentelemetry/resources";
|
|
5
|
+
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
6
|
+
import {
|
|
7
|
+
BatchLogRecordProcessor,
|
|
8
|
+
type LogRecordProcessor,
|
|
9
|
+
SimpleLogRecordProcessor,
|
|
10
|
+
} from "@opentelemetry/sdk-logs";
|
|
11
|
+
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
|
12
|
+
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
13
|
+
import type { MetricReader } from "@opentelemetry/sdk-metrics";
|
|
14
|
+
import {
|
|
15
|
+
BatchSpanProcessor,
|
|
16
|
+
SimpleSpanProcessor,
|
|
17
|
+
type SpanProcessor,
|
|
18
|
+
} from "@opentelemetry/sdk-trace-base";
|
|
19
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
20
|
+
import {
|
|
21
|
+
ATTR_SERVICE_NAME,
|
|
22
|
+
ATTR_SERVICE_VERSION,
|
|
23
|
+
} from "@opentelemetry/semantic-conventions";
|
|
24
|
+
import { env } from "node:process";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Note: this is based on `NodeSDKConfiguration`.
|
|
28
|
+
* We just don't want to include a nodejs dependency here
|
|
29
|
+
*/
|
|
30
|
+
type DefaultOtelSetup = {
|
|
31
|
+
resource: IResource;
|
|
32
|
+
logRecordProcessors: LogRecordProcessor[];
|
|
33
|
+
metricReader: MetricReader;
|
|
34
|
+
spanProcessors?: SpanProcessor[];
|
|
35
|
+
};
|
|
36
|
+
export function defaultOtelSetup(
|
|
37
|
+
name: string,
|
|
38
|
+
version: string,
|
|
39
|
+
): DefaultOtelSetup {
|
|
40
|
+
const collectorOptions = {
|
|
41
|
+
// override default on dev builds so no logs disappear
|
|
42
|
+
concurrencyLimit: env.NODE_ENV === "development"
|
|
43
|
+
? Number.MAX_SAFE_INTEGER
|
|
44
|
+
: undefined,
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
47
|
+
const baseLog = new OTLPLogExporter(collectorOptions);
|
|
48
|
+
const logProcessor = env.NODE_ENV === "development"
|
|
49
|
+
? new SimpleLogRecordProcessor(baseLog)
|
|
50
|
+
: new BatchLogRecordProcessor(baseLog);
|
|
51
|
+
|
|
52
|
+
const baseTrace = new OTLPTraceExporter(collectorOptions);
|
|
53
|
+
const spanProcessor = env.NODE_ENV === "development"
|
|
54
|
+
? new SimpleSpanProcessor(baseTrace)
|
|
55
|
+
: new BatchSpanProcessor(baseTrace);
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
resource: Resource.default().merge(
|
|
59
|
+
new Resource({
|
|
60
|
+
[ATTR_SERVICE_NAME]: name,
|
|
61
|
+
[ATTR_SERVICE_VERSION]: version,
|
|
62
|
+
}),
|
|
63
|
+
),
|
|
64
|
+
spanProcessors: [spanProcessor],
|
|
65
|
+
logRecordProcessors: [logProcessor],
|
|
66
|
+
metricReader: new PeriodicExportingMetricReader({
|
|
67
|
+
exporter: new OTLPMetricExporter(collectorOptions),
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type TelemetryContext = {
|
|
73
|
+
tracer: opentelemetry.Tracer;
|
|
74
|
+
span: undefined | opentelemetry.Span;
|
|
75
|
+
};
|
|
76
|
+
export const PaimaTelemetryContext = createContext<TelemetryContext>(
|
|
77
|
+
"paima:telemetry",
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
export function* useTelemetryContext(): Operation<TelemetryContext> {
|
|
81
|
+
const otel = yield* PaimaTelemetryContext.get();
|
|
82
|
+
if (!otel) {
|
|
83
|
+
throw new Error("Paima telemetry is not initialized");
|
|
84
|
+
}
|
|
85
|
+
return otel;
|
|
86
|
+
}
|
|
87
|
+
export function* withSpan<T>(
|
|
88
|
+
name: string,
|
|
89
|
+
continuation: () => Operation<T>,
|
|
90
|
+
): Operation<T> {
|
|
91
|
+
const otel = yield* useTelemetryContext();
|
|
92
|
+
return yield* otel.tracer.startActiveSpan(
|
|
93
|
+
name,
|
|
94
|
+
function* (span: opentelemetry.Span) {
|
|
95
|
+
yield* PaimaTelemetryContext.set(
|
|
96
|
+
{
|
|
97
|
+
tracer: otel.tracer,
|
|
98
|
+
span,
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
return yield* continuation();
|
|
102
|
+
},
|
|
103
|
+
);
|
|
104
|
+
}
|
package/src/tslog.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
2
|
+
import { type ILogObj, type IMeta, Logger } from "tslog";
|
|
3
|
+
import {
|
|
4
|
+
defaultSeverity,
|
|
5
|
+
type LogFunc,
|
|
6
|
+
type Namespace,
|
|
7
|
+
PaimaComponents,
|
|
8
|
+
PaimaToolsComponents,
|
|
9
|
+
} from "./const.ts";
|
|
10
|
+
import { chainedMessage, Format, matchColor } from "material-chalk";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import { toString } from "./utils.ts";
|
|
13
|
+
// TODO How to handle this in the frontend & backend?
|
|
14
|
+
const ENV = { NODE_ENV: 'development' } // TODO This should come from @effectstream/utils/node-env ENV
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* https://github.com/fullstack-build/tslog/pull/308
|
|
18
|
+
*/
|
|
19
|
+
export enum DefaultLogLevels {
|
|
20
|
+
SILLY = 0,
|
|
21
|
+
TRACE = 1,
|
|
22
|
+
DEBUG = 2,
|
|
23
|
+
INFO = 3,
|
|
24
|
+
WARN = 4,
|
|
25
|
+
ERROR = 5,
|
|
26
|
+
FATAL = 6,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function mapSeverity(level: SeverityNumber): DefaultLogLevels {
|
|
30
|
+
if (level === SeverityNumber.UNSPECIFIED) {
|
|
31
|
+
return DefaultLogLevels.INFO;
|
|
32
|
+
}
|
|
33
|
+
if (level < SeverityNumber.DEBUG) {
|
|
34
|
+
return DefaultLogLevels.TRACE;
|
|
35
|
+
}
|
|
36
|
+
if (level < SeverityNumber.INFO) {
|
|
37
|
+
return DefaultLogLevels.DEBUG;
|
|
38
|
+
}
|
|
39
|
+
if (level < SeverityNumber.WARN) {
|
|
40
|
+
return DefaultLogLevels.INFO;
|
|
41
|
+
}
|
|
42
|
+
if (level < SeverityNumber.ERROR) {
|
|
43
|
+
return DefaultLogLevels.WARN;
|
|
44
|
+
}
|
|
45
|
+
if (level < SeverityNumber.FATAL) {
|
|
46
|
+
return DefaultLogLevels.ERROR;
|
|
47
|
+
}
|
|
48
|
+
return DefaultLogLevels.FATAL;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const chalkFormat = Format.Chalk(chalk);
|
|
52
|
+
const logLevels = {
|
|
53
|
+
SILLY: matchColor("#FFFFFF").formatAs(chalkFormat).dim,
|
|
54
|
+
TRACE: matchColor("#FFFFFF").formatAs(chalkFormat).dim,
|
|
55
|
+
DEBUG: matchColor("#00FF00").formatAs(chalkFormat).dim,
|
|
56
|
+
INFO: matchColor("#0000FF").formatAs(chalkFormat).dim,
|
|
57
|
+
WARN: matchColor("#FFFF00").formatAs(chalkFormat),
|
|
58
|
+
ERROR: matchColor("#FF0000").formatAs(chalkFormat),
|
|
59
|
+
FATAL: matchColor("#FF0000").formatAs(chalkFormat),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function padLeft(
|
|
63
|
+
input: number | string,
|
|
64
|
+
targetLength: number,
|
|
65
|
+
padChar: string = "0",
|
|
66
|
+
): string {
|
|
67
|
+
const str = input.toString();
|
|
68
|
+
return str.padStart(targetLength, padChar);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const log: Logger<ILogObj> = new Logger({
|
|
72
|
+
minLevel: mapSeverity(defaultSeverity),
|
|
73
|
+
overwrite: {
|
|
74
|
+
formatMeta: (meta?: IMeta) => {
|
|
75
|
+
if (meta == null) return "";
|
|
76
|
+
const timestamp = chalk.white.dim((() => {
|
|
77
|
+
if (ENV.NODE_ENV !== "development") {
|
|
78
|
+
return meta.date.toISOString();
|
|
79
|
+
}
|
|
80
|
+
// use a shorter timestamp locally so it takes less space on the console
|
|
81
|
+
return `${padLeft(meta.date.getHours(), 2)}:${
|
|
82
|
+
padLeft(meta.date.getMinutes(), 2)
|
|
83
|
+
}:${padLeft(meta.date.getSeconds(), 2)}`;
|
|
84
|
+
})());
|
|
85
|
+
const logLevelName = logLevels
|
|
86
|
+
[meta.logLevelName as keyof typeof logLevels](meta.logLevelName);
|
|
87
|
+
return [timestamp, logLevelName].join(" ") + "\t";
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export function attachTransport(callback: (logObj: ILogObj) => void) {
|
|
93
|
+
log.attachTransport((logObj) => {
|
|
94
|
+
callback(logObj);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getLogMethod(level: SeverityNumber) {
|
|
99
|
+
if (level === SeverityNumber.UNSPECIFIED) {
|
|
100
|
+
const level = mapSeverity(defaultSeverity);
|
|
101
|
+
return log.log.bind(
|
|
102
|
+
log,
|
|
103
|
+
level,
|
|
104
|
+
DefaultLogLevels[level],
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
if (level < SeverityNumber.DEBUG) {
|
|
108
|
+
return log.trace.bind(log);
|
|
109
|
+
}
|
|
110
|
+
if (level < SeverityNumber.INFO) {
|
|
111
|
+
return log.debug.bind(log);
|
|
112
|
+
}
|
|
113
|
+
if (level < SeverityNumber.WARN) {
|
|
114
|
+
return log.info.bind(log);
|
|
115
|
+
}
|
|
116
|
+
if (level < SeverityNumber.ERROR) {
|
|
117
|
+
return log.warn.bind(log);
|
|
118
|
+
}
|
|
119
|
+
if (level < SeverityNumber.FATAL) {
|
|
120
|
+
return log.error.bind(log);
|
|
121
|
+
}
|
|
122
|
+
return log.fatal.bind(log);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export type TslogLogFunc = LogFunc;
|
|
126
|
+
|
|
127
|
+
const formatMessage = (
|
|
128
|
+
namespaces: Namespace,
|
|
129
|
+
...data: unknown[]
|
|
130
|
+
): string => {
|
|
131
|
+
return chainedMessage(
|
|
132
|
+
chalk,
|
|
133
|
+
namespaces,
|
|
134
|
+
...data.map(toString),
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
const namespaces = (component: string, namespace: Namespace) => {
|
|
140
|
+
const subNamespaces = Array.isArray(namespace) ? namespace : [namespace];
|
|
141
|
+
if (
|
|
142
|
+
Object.values(PaimaComponents).includes(component as any) ||
|
|
143
|
+
Object.values(PaimaToolsComponents).includes(component as any)
|
|
144
|
+
) {
|
|
145
|
+
return ["effectstream", component, ...subNamespaces];
|
|
146
|
+
}
|
|
147
|
+
return [component, ...subNamespaces];
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const tsLogFormatted: TslogLogFunc = (
|
|
151
|
+
component,
|
|
152
|
+
namespace,
|
|
153
|
+
level,
|
|
154
|
+
doLog,
|
|
155
|
+
): void => {
|
|
156
|
+
doLog((...data: unknown[]) =>
|
|
157
|
+
getLogMethod(level)(
|
|
158
|
+
formatMessage(namespaces(component, namespace), ...data),
|
|
159
|
+
)
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const tsLogOrchestrator: TslogLogFunc = (
|
|
164
|
+
component,
|
|
165
|
+
namespace,
|
|
166
|
+
level,
|
|
167
|
+
doLog,
|
|
168
|
+
): void => {
|
|
169
|
+
if (level < defaultSeverity) return;
|
|
170
|
+
doLog((...data: unknown[]) =>
|
|
171
|
+
console.log(JSON.stringify({
|
|
172
|
+
"__ORCHESTRATOR__": true,
|
|
173
|
+
data,
|
|
174
|
+
namespaces: namespaces(component, namespace),
|
|
175
|
+
level,
|
|
176
|
+
date: new Date().getTime(),
|
|
177
|
+
},
|
|
178
|
+
(_, v) => typeof v === "bigint" ? v.toString() : v
|
|
179
|
+
))
|
|
180
|
+
)
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export const tsLogGetFormattedMessage = (
|
|
184
|
+
component: string,
|
|
185
|
+
namespace: Namespace,
|
|
186
|
+
level: SeverityNumber,
|
|
187
|
+
doLog: any,
|
|
188
|
+
): string => {
|
|
189
|
+
let val = "";
|
|
190
|
+
doLog((...data: unknown[]) => {
|
|
191
|
+
val = formatMessage(namespaces(component, namespace), ...data);
|
|
192
|
+
});
|
|
193
|
+
return val;
|
|
194
|
+
};
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import superjson from "superjson";
|
|
2
|
+
|
|
3
|
+
export function toString(str: unknown): string {
|
|
4
|
+
if (typeof str !== "object") {
|
|
5
|
+
if (typeof str === "function") {
|
|
6
|
+
return str.name === "" ? "[Function]" : `[Function: ${str.name}]`;
|
|
7
|
+
}
|
|
8
|
+
return (String(str));
|
|
9
|
+
}
|
|
10
|
+
if (str instanceof Error) {
|
|
11
|
+
return (`${str.name}: ${str.message}\nStack: ${str.stack}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
return superjson.stringify(str);
|
|
16
|
+
} catch (e) {
|
|
17
|
+
return toString(e);
|
|
18
|
+
// should not happen, but maybe there is some type that fails for some reason
|
|
19
|
+
}
|
|
20
|
+
}
|