@visiolab/effect-firebase-functions 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +228 -2
- package/dist/Functions.d.mts +339 -0
- package/dist/Functions.mjs +213 -0
- package/dist/Functions.mjs.map +1 -0
- package/dist/_virtual/_rolldown/runtime.mjs +12 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +1 -0
- package/dist/internal/callable.mjs +104 -0
- package/dist/internal/callable.mjs.map +1 -0
- package/dist/internal/documentTrigger.mjs +110 -0
- package/dist/internal/documentTrigger.mjs.map +1 -0
- package/dist/internal/kinds.mjs +19 -0
- package/dist/internal/kinds.mjs.map +1 -0
- package/dist/internal/member.mjs +12 -0
- package/dist/internal/member.mjs.map +1 -0
- package/dist/internal/runtime.mjs +79 -0
- package/dist/internal/runtime.mjs.map +1 -0
- package/dist/internal/serverSpan.mjs +187 -0
- package/dist/internal/serverSpan.mjs.map +1 -0
- package/dist/logger/CloudLogging.d.mts +92 -0
- package/dist/logger/CloudLogging.mjs +146 -0
- package/dist/logger/CloudLogging.mjs.map +1 -0
- package/dist/logger/index.d.mts +2 -0
- package/dist/logger/index.mjs +1 -0
- package/package.json +64 -3
- package/src/Functions.ts +482 -0
- package/src/index.ts +8 -0
- package/src/internal/callable.ts +156 -0
- package/src/internal/documentTrigger.ts +188 -0
- package/src/internal/kinds.ts +36 -0
- package/src/internal/member.ts +21 -0
- package/src/internal/runtime.ts +110 -0
- package/src/internal/serverSpan.ts +203 -0
- package/src/logger/CloudLogging.ts +152 -0
- package/src/logger/index.ts +8 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Logger that writes Effect logs as Cloud Logging structured entries, one
|
|
3
|
+
* JSON line each, tied to the trace of the fiber's current span.
|
|
4
|
+
*
|
|
5
|
+
* `Functions` installs it for every call or event, beneath the Layer you
|
|
6
|
+
* serve, so you need this module only to log the same way elsewhere, or to
|
|
7
|
+
* change how a deployed Function logs.
|
|
8
|
+
*
|
|
9
|
+
* @stability unstable
|
|
10
|
+
* @since 0.3.0
|
|
11
|
+
*/
|
|
12
|
+
import { Cause, Console, Formatter, Inspectable, type Layer, Logger, References } from "effect";
|
|
13
|
+
import type * as LogLevel from "effect/LogLevel";
|
|
14
|
+
|
|
15
|
+
const severities: Record<LogLevel.LogLevel, string> = {
|
|
16
|
+
All: "DEFAULT",
|
|
17
|
+
Trace: "DEBUG",
|
|
18
|
+
Debug: "DEBUG",
|
|
19
|
+
Info: "INFO",
|
|
20
|
+
Warn: "WARNING",
|
|
21
|
+
Error: "ERROR",
|
|
22
|
+
Fatal: "CRITICAL",
|
|
23
|
+
None: "DEFAULT",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The Cloud Logging logger: writes each entry as one JSON line, the format
|
|
28
|
+
* Firebase's own `logger` writes, to `console.error` for Error and Fatal and
|
|
29
|
+
* to `console.log` for everything else.
|
|
30
|
+
*
|
|
31
|
+
* **When to use**
|
|
32
|
+
*
|
|
33
|
+
* Use with `Logger.layer` to combine it with other loggers. To replace
|
|
34
|
+
* Effect's default logger with it, use {@link layer}.
|
|
35
|
+
*
|
|
36
|
+
* **Details**
|
|
37
|
+
*
|
|
38
|
+
* Each entry is a JSON object with these fields:
|
|
39
|
+
*
|
|
40
|
+
* - `severity`: Trace and Debug are `DEBUG`, Info is `INFO`, Warn is
|
|
41
|
+
* `WARNING`, Error is `ERROR` and Fatal is `CRITICAL`.
|
|
42
|
+
* - `message`: the first value logged. A non-empty Cause is appended as
|
|
43
|
+
* `Cause.pretty` output, with its stacks, so Error Reporting picks up
|
|
44
|
+
* defects.
|
|
45
|
+
* - `args`: the other values logged, if any.
|
|
46
|
+
* - `spans`: the log spans, as `{ label: milliseconds }`, if any.
|
|
47
|
+
* - `logging.googleapis.com/trace`, `logging.googleapis.com/spanId` and
|
|
48
|
+
* `logging.googleapis.com/trace_sampled`: the trace of the fiber's current
|
|
49
|
+
* span, with the project from `GCLOUD_PROJECT`, else `GOOGLE_CLOUD_PROJECT`.
|
|
50
|
+
* Left out when there is no span, and when neither variable is set, since a
|
|
51
|
+
* span id means nothing to Cloud Logging without its trace. Cloud Functions
|
|
52
|
+
* sets `GCLOUD_PROJECT`.
|
|
53
|
+
* - Every log annotation, as a field of its own. `severity`, `message` and the
|
|
54
|
+
* `logging.googleapis.com/` fields win over an annotation of the same name.
|
|
55
|
+
*
|
|
56
|
+
* The trace comes from the fiber, never from Firebase's `traceContext`, so
|
|
57
|
+
* concurrent requests on one instance each log under their own trace. Lines go
|
|
58
|
+
* to the `Console` service, which is the global `console` unless you provide
|
|
59
|
+
* another. Entries carry no uid: add one with `Effect.annotateLogs`.
|
|
60
|
+
*
|
|
61
|
+
* @example Logging under a uid
|
|
62
|
+
* ```ts
|
|
63
|
+
* import { Effect } from "effect";
|
|
64
|
+
* import { CloudLogging } from "@visiolab/effect-firebase-functions/logger";
|
|
65
|
+
*
|
|
66
|
+
* const program = Effect.log("Order placed").pipe(
|
|
67
|
+
* Effect.annotateLogs({ uid: "user-1" }),
|
|
68
|
+
* Effect.provide(CloudLogging.layer),
|
|
69
|
+
* );
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @see {@link layer} for replacing Effect's default logger with it
|
|
73
|
+
*
|
|
74
|
+
* @stability unstable
|
|
75
|
+
* @category loggers
|
|
76
|
+
* @since 0.3.0
|
|
77
|
+
*/
|
|
78
|
+
export const logger: Logger.Logger<unknown, void> = Logger.make(
|
|
79
|
+
({ cause, date, fiber, logLevel, message }) => {
|
|
80
|
+
const values = Array.isArray(message) ? message : [message];
|
|
81
|
+
const [first, ...args] = values;
|
|
82
|
+
let text =
|
|
83
|
+
values.length === 0
|
|
84
|
+
? ""
|
|
85
|
+
: typeof first === "string"
|
|
86
|
+
? first
|
|
87
|
+
: Inspectable.toStringUnknown(first);
|
|
88
|
+
if (cause.reasons.length > 0) {
|
|
89
|
+
const pretty = Cause.pretty(cause);
|
|
90
|
+
text = text === "" ? pretty : `${text}\n${pretty}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const entry: Record<string, unknown> = { ...fiber.getRef(References.CurrentLogAnnotations) };
|
|
94
|
+
if (args.length > 0) entry["args"] = args;
|
|
95
|
+
const logSpans = fiber.getRef(References.CurrentLogSpans);
|
|
96
|
+
if (logSpans.length > 0) {
|
|
97
|
+
const now = date.getTime();
|
|
98
|
+
entry["spans"] = Object.fromEntries(logSpans.map(([label, start]) => [label, now - start]));
|
|
99
|
+
}
|
|
100
|
+
for (const key of Object.keys(entry)) {
|
|
101
|
+
if (key.startsWith("logging.googleapis.com/")) delete entry[key];
|
|
102
|
+
}
|
|
103
|
+
entry["severity"] = severities[logLevel];
|
|
104
|
+
entry["message"] = text;
|
|
105
|
+
const span = fiber.cache.span;
|
|
106
|
+
const project = process.env["GCLOUD_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"];
|
|
107
|
+
if (span !== undefined && project !== undefined && project !== "") {
|
|
108
|
+
entry["logging.googleapis.com/trace"] = `projects/${project}/traces/${span.traceId}`;
|
|
109
|
+
entry["logging.googleapis.com/spanId"] = span.spanId;
|
|
110
|
+
entry["logging.googleapis.com/trace_sampled"] = span.sampled;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const console = fiber.getRef(Console.Console);
|
|
114
|
+
const line = Formatter.formatJson(entry);
|
|
115
|
+
if (logLevel === "Error" || logLevel === "Fatal") console.error(line);
|
|
116
|
+
else console.log(line);
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Replaces the current loggers with the Cloud Logging {@link logger} and
|
|
122
|
+
* Effect's `Logger.tracerLogger`, which records logs as span events.
|
|
123
|
+
*
|
|
124
|
+
* **When to use**
|
|
125
|
+
*
|
|
126
|
+
* Use to log as Cloud Logging entries outside a Function deployed with
|
|
127
|
+
* `Functions`, in any Effect program that runs on Google Cloud. Deployed
|
|
128
|
+
* Functions already log this way.
|
|
129
|
+
*
|
|
130
|
+
* **Details**
|
|
131
|
+
*
|
|
132
|
+
* Provided in the Layer of a deployed Function, it changes nothing. To log
|
|
133
|
+
* differently there, provide your own `Logger.layer`, which wins over this one.
|
|
134
|
+
*
|
|
135
|
+
* @example Logging from a scheduled job
|
|
136
|
+
* ```ts
|
|
137
|
+
* import { Effect } from "effect";
|
|
138
|
+
* import { CloudLogging } from "@visiolab/effect-firebase-functions/logger";
|
|
139
|
+
*
|
|
140
|
+
* const job = Effect.log("Cleaning up").pipe(
|
|
141
|
+
* Effect.withSpan("cleanup"),
|
|
142
|
+
* Effect.provide(CloudLogging.layer),
|
|
143
|
+
* );
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* @see {@link logger} for the logger on its own
|
|
147
|
+
*
|
|
148
|
+
* @stability unstable
|
|
149
|
+
* @category layers
|
|
150
|
+
* @since 0.3.0
|
|
151
|
+
*/
|
|
152
|
+
export const layer: Layer.Layer<never> = Logger.layer([logger, Logger.tracerLogger]);
|