@witchcraft/nuxt-logger 0.0.1
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/README.md +145 -0
- package/package.json +82 -0
- package/src/module.ts +173 -0
- package/src/runtime/composables/setupElectronMainToRendererLogging.ts +12 -0
- package/src/runtime/composables/useLogger.ts +5 -0
- package/src/runtime/electron/createElectronLoggerApi.ts +23 -0
- package/src/runtime/electron/index.ts +6 -0
- package/src/runtime/electron/receiveFromRendererOnMain.ts +12 -0
- package/src/runtime/electron/types.ts +9 -0
- package/src/runtime/electron/useElectronLogger.ts +98 -0
- package/src/runtime/electron/wrapLoggerForElectron.ts +16 -0
- package/src/runtime/helpers/getBaseOptions.ts +156 -0
- package/src/runtime/plugins/init.ts +25 -0
- package/src/runtime/server/middleware/log.ts +13 -0
- package/src/runtime/server/plugins/init.ts +23 -0
- package/src/runtime/server/tsconfig.json +3 -0
- package/src/runtime/server/utils/useServerLogger.ts +5 -0
- package/src/runtime/shared/createUseLogger.ts +74 -0
- package/src/runtime/types.ts +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# @witchcraft/nuxt-logger
|
|
2
|
+
|
|
3
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
|
+
[![License][license-src]][license-href]
|
|
6
|
+
[![Nuxt][nuxt-src]][nuxt-href]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Nuxt isomorphic-ish logging module (it can be used on the client/server/electron) and automatically adds a transport on the server and electron main to save logs to a file.
|
|
10
|
+
|
|
11
|
+
It's simplified wrapper around pino that exposes only the default logger methods.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
- [🏀 Online playground](https://stackblitz.com/github/witchcraftjs/nuxt-logger?file=playground%2Fapp.vue)
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- Creates a nuxt plugin client side to log environment info (`info`).
|
|
19
|
+
- Creates a server middleware for logging requests (`trace`) with customizable redaction.
|
|
20
|
+
- `useLogger` composable for creating a client logger.
|
|
21
|
+
- `useServerLogger` composable for creating a server logger.
|
|
22
|
+
- `useElectronLogger` for electron + utilities for setting things up.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
```bash
|
|
26
|
+
pnpx nuxi module add @witchcraft/nuxt-logger
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
Just start using it with the `useLogger` or `useServerLogger` composable, they should be auto imported if you have auto imports on.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const logger = useLogger()
|
|
35
|
+
logger.info({ns:"namespace", msg:"hello world"})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Redaction
|
|
39
|
+
|
|
40
|
+
By default redacts some known sensitive keys for the middleware logging requests.
|
|
41
|
+
|
|
42
|
+
Also redacts the `redact` key, but only in production, so you can put anything semi-sensitive in there that you need to inspect in development.
|
|
43
|
+
|
|
44
|
+
### Electron
|
|
45
|
+
|
|
46
|
+
The electron logger requires a bit of setup since electron's main cannot be passed the runtime config normally.
|
|
47
|
+
|
|
48
|
+
This assumes the usage of
|
|
49
|
+
[@witchcraft/nuxt-electron](todo) since it can handle this among other issues. Note this plugin needs to be before it in the plugins list.
|
|
50
|
+
|
|
51
|
+
There is a full example in the [@witchcraft/nuxt-electron playground](TODO).
|
|
52
|
+
|
|
53
|
+
```ts [main.ts]
|
|
54
|
+
import { STATIC, useDevDataDir } from "@witchcraft/nuxt-electron/electron"
|
|
55
|
+
import { ELECTRON } from "@witchcraft/nuxt-logger/electron"
|
|
56
|
+
|
|
57
|
+
// assuming you stick your active window instances here
|
|
58
|
+
const windows = []
|
|
59
|
+
const userDataDir = useDevDataDir() ?? parsed(args).userDataDir ?? app.getPath("appData")
|
|
60
|
+
|
|
61
|
+
// this only needs to be called like this the first time
|
|
62
|
+
const logger = useElectronLogger(
|
|
63
|
+
{
|
|
64
|
+
...STATIC.ELECTRON_RUNTIME_CONFIG.logger,
|
|
65
|
+
// override the log path since with electron we only know it at runtime
|
|
66
|
+
// this module sets it to undefined for electron anyways
|
|
67
|
+
logPath: path.join(userDataDir, "log.txt"),
|
|
68
|
+
},
|
|
69
|
+
// give it a way to access windows to be able to communicate with them
|
|
70
|
+
() => windows,
|
|
71
|
+
{
|
|
72
|
+
// true by default, allows recieving logs from the renderer
|
|
73
|
+
recieveFromRenderer: true
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
// later/elsewhere
|
|
77
|
+
// NOTE that the namespaces MUST begin with `main:`
|
|
78
|
+
useElectronLogger().debug({ns:"main:hello"}) // will work
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
We can setup the apis for two way communication in the preload script.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// preload.ts
|
|
85
|
+
import { contextBridge, ipcRenderer } from "electron"
|
|
86
|
+
import { createElectronLoggerApi, type ElectronLoggerApi } from "@witchcraft/nuxt-logger/electron"
|
|
87
|
+
|
|
88
|
+
declare global {
|
|
89
|
+
interface Window {
|
|
90
|
+
electron: {
|
|
91
|
+
api: ElectronLoggerApi {
|
|
92
|
+
//...
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// creates window.electron.api.log/_logs
|
|
98
|
+
// note the naming/structure must be like this, it's currently hardcoded
|
|
99
|
+
contextBridge.exposeInMainWorld("electron", {
|
|
100
|
+
api: {
|
|
101
|
+
...createElectronLoggerApi(ipcRenderer)
|
|
102
|
+
},
|
|
103
|
+
meta: {
|
|
104
|
+
env {
|
|
105
|
+
// the regular useLogger in the renderer will look for this variable automatically
|
|
106
|
+
LOG_LEVEL: process.env.LOG_LEVEL,
|
|
107
|
+
WRITE_LEVEL: process.env.LOG_WRITE_LEVEL,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
And receive `main`'s logs on the client. This must be called before other calls to `useLogger`.
|
|
114
|
+
|
|
115
|
+
```vue
|
|
116
|
+
// app.vue
|
|
117
|
+
|
|
118
|
+
<script lang="ts" setup>
|
|
119
|
+
// setup the regular client side logger
|
|
120
|
+
useLogger({
|
|
121
|
+
// this is the default
|
|
122
|
+
redirectTo: "electron",
|
|
123
|
+
// prefixes all messages with `renderer:` so that they will be forwarded, can be a function
|
|
124
|
+
redirectAll: true,
|
|
125
|
+
})
|
|
126
|
+
setupElectronMainToRendererLogging()
|
|
127
|
+
|
|
128
|
+
useLogger().debug({ns: "renderer:hello"}) // will get forwarder
|
|
129
|
+
useLogger().debug({ns: "hello"}) // will also get forwarded if redirectAll is true
|
|
130
|
+
</script>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
<!-- Badges -->
|
|
135
|
+
[npm-version-src]: https://img.shields.io/npm/v/@witchcraft/nuxt-logger/latest.svg?style=flat&colorA=020420&colorB=00DC82
|
|
136
|
+
[npm-version-href]: https://npmjs.com/package/@witchcraft/nuxt-logger
|
|
137
|
+
|
|
138
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/@witchcraft/nuxt-logger.svg?style=flat&colorA=020420&colorB=00DC82
|
|
139
|
+
[npm-downloads-href]: https://npmjs.com/package/@witchcraft/nuxt-logger
|
|
140
|
+
|
|
141
|
+
[license-src]: https://img.shields.io/npm/l/@witchcraft/nuxt-logger.svg?style=flat&colorA=020420&colorB=00DC82
|
|
142
|
+
[license-href]: https://npmjs.com/package/@witchcraft/nuxt-logger
|
|
143
|
+
|
|
144
|
+
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
|
|
145
|
+
[nuxt-href]: https://nuxt.com
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@witchcraft/nuxt-logger",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Nuxt module for logging.",
|
|
5
|
+
"repository": "witchcraftjs/nuxt-logger",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types.d.mts",
|
|
12
|
+
"import": "./dist/module.mjs"
|
|
13
|
+
},
|
|
14
|
+
"./*": {
|
|
15
|
+
"types": "./dist/runtime/*.d.ts",
|
|
16
|
+
"import": "./dist/runtime/*.js"
|
|
17
|
+
},
|
|
18
|
+
"./electron": {
|
|
19
|
+
"types": "./dist/runtime/electron/index.d.ts",
|
|
20
|
+
"import": "./dist/runtime/electron/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"prepare": "husky && cd playground && pnpm i",
|
|
29
|
+
"build": "nuxt-module-build prepare && nuxt-module-build build && nuxi build playground",
|
|
30
|
+
"build:only": "nuxt-module-build build",
|
|
31
|
+
"dev": "nuxi dev playground",
|
|
32
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
33
|
+
"lint": "eslint \"{src,test,playground/app}/**/*.{ts,vue}\" \"*.{js,cjs,mjs,ts}\"",
|
|
34
|
+
"lint:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest watch"
|
|
37
|
+
},
|
|
38
|
+
"//peerDependencies": "Because we need the real paths to the workers, they must both be in the app's node_modules",
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"pino": "^9.8.0",
|
|
41
|
+
"pino-pretty": "^11.2.2"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@alanscodelog/tsconfigs": "^6.1.0",
|
|
45
|
+
"@alanscodelog/utils": "^6.0.1",
|
|
46
|
+
"@nuxt/kit": "^4.0.3",
|
|
47
|
+
"defu": "latest",
|
|
48
|
+
"pino": "^9.9.0",
|
|
49
|
+
"pino-pretty": "^11.3.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@alanscodelog/eslint-config": "^6.3.0",
|
|
53
|
+
"@alanscodelog/semantic-release-config": "^5.0.4",
|
|
54
|
+
"@nuxt/eslint-config": "^1.9.0",
|
|
55
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
56
|
+
"@nuxt/schema": "^4.0.3",
|
|
57
|
+
"@nuxt/test-utils": "^3.19.2",
|
|
58
|
+
"@types/node": "latest",
|
|
59
|
+
"changelogen": "^0.6.2",
|
|
60
|
+
"electron": "^34.5.8",
|
|
61
|
+
"eslint": "^9.34.0",
|
|
62
|
+
"husky": "^9.1.7",
|
|
63
|
+
"nuxt": "^4.0.3",
|
|
64
|
+
"semantic-release": "^24.2.8",
|
|
65
|
+
"typescript": "^5.9.2",
|
|
66
|
+
"vitest": "^3.2.4",
|
|
67
|
+
"vue-tsc": "^2.2.12"
|
|
68
|
+
},
|
|
69
|
+
"release": {
|
|
70
|
+
"extends": [
|
|
71
|
+
"@alanscodelog/semantic-release-config"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"commitlint": {
|
|
75
|
+
"extends": [
|
|
76
|
+
"@alanscodelog"
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
"publishConfig": {
|
|
80
|
+
"access": "public"
|
|
81
|
+
}
|
|
82
|
+
}
|
package/src/module.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addImportsDir,
|
|
3
|
+
addPlugin,
|
|
4
|
+
addServerHandler,
|
|
5
|
+
addServerImportsDir,
|
|
6
|
+
createResolver,
|
|
7
|
+
defineNuxtModule,
|
|
8
|
+
resolvePath
|
|
9
|
+
} from "@nuxt/kit"
|
|
10
|
+
import { defu } from "defu"
|
|
11
|
+
import fs from "node:fs/promises"
|
|
12
|
+
import path from "node:path"
|
|
13
|
+
import type { TransportMultiOptions } from "pino"
|
|
14
|
+
|
|
15
|
+
declare module "@nuxt/schema" {
|
|
16
|
+
interface PublicRuntimeConfig {
|
|
17
|
+
logger: Pick<ModuleOptions, "redact" | "additionalServerTransportTargets" | "disabledServerTransportTargets"> & {
|
|
18
|
+
logPath: string
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ModuleOptions {
|
|
24
|
+
/**
|
|
25
|
+
* See {@link https://getpino.io/#/docs/redaction?id=redaction Pino redaction options}.
|
|
26
|
+
*/
|
|
27
|
+
redact: string[]
|
|
28
|
+
/** This is "/var/log/testing/${appName}.log". */
|
|
29
|
+
serverLogPath?: string
|
|
30
|
+
/** This is "~~/logs/server.log" by default. */
|
|
31
|
+
devServerLogPath?: string
|
|
32
|
+
/** The module tries to get the app name from `runtimeConfig.appInfo.name`, otherwise it uses the name of the `~~` directory by default. */
|
|
33
|
+
appName?: string
|
|
34
|
+
/** @default false */
|
|
35
|
+
enableServerRequestLogging?: boolean
|
|
36
|
+
/**
|
|
37
|
+
* Additional transport targets to add to the server transport.
|
|
38
|
+
* File and console + pino-pretty are added by default. See disabledServerTransportTargets for disabling a transport.
|
|
39
|
+
*
|
|
40
|
+
* Since some transports will require options, but passing them via the nuxt config will hard code them, there is an additional `_loadFromEnv` property that can list a list of dot seperated properties to load from `process.env` and `_contexts` to set what context it will run in.
|
|
41
|
+
*
|
|
42
|
+
* For example, to add `pino-loki`:
|
|
43
|
+
* ```ts
|
|
44
|
+
* additionalServerTransportTargets: [
|
|
45
|
+
* {
|
|
46
|
+
* target: "pino-loki",
|
|
47
|
+
* _contexts: ["server"], // can be any of ["server", "client", "electron-client", "electron-main"] (all by default)
|
|
48
|
+
* _loadFromEnv: ["options.host", "options.basicAuth.username", "options.basicAuth.password"],
|
|
49
|
+
* options: {
|
|
50
|
+
* //...
|
|
51
|
+
* labels: {
|
|
52
|
+
* // set additional labels
|
|
53
|
+
* application: "...",
|
|
54
|
+
* },
|
|
55
|
+
* propsToLabels: ["level", ...don't forget to set your labels ],
|
|
56
|
+
* replaceTimestamp: true, // loki requires timestamps in a specific format
|
|
57
|
+
* host: "PINO_LOKI_HOST",
|
|
58
|
+
* basicAuth: {
|
|
59
|
+
* // don't forget the quotes!
|
|
60
|
+
* username: "PINO_LOKI_BASIC_AUTH_USERNAME",
|
|
61
|
+
* password: "PINO_LOKI_BASIC_AUTH_PASSWORD",
|
|
62
|
+
* },
|
|
63
|
+
* },
|
|
64
|
+
* },
|
|
65
|
+
* ]
|
|
66
|
+
* ```
|
|
67
|
+
* The location for the auth part on grafana cloud is confusing as fuck, leaving this here for posterity.
|
|
68
|
+
*
|
|
69
|
+
* Go to https://grafana.com/orgs/YOURORGHERE/stacks => Details => Loki => Send Logs
|
|
70
|
+
*
|
|
71
|
+
* There you can get the user and create a token with write access and it sets the right stuff. It will then appear in https://YOURORGHERE.grafana.net/a/grafana-auth-app
|
|
72
|
+
*
|
|
73
|
+
* If nothing is erroring, you should be able to see grafana receiving logs in Drilldown => Logs => Logs (https://YOURORGHERE.grafana.net/drilldown).
|
|
74
|
+
*
|
|
75
|
+
* You can then create a dashboard based on your labels.
|
|
76
|
+
*/
|
|
77
|
+
additionalServerTransportTargets?: (TransportMultiOptions["targets"][number] & {
|
|
78
|
+
_contexts?: ("server" | "client" | "electron-client" | "electron-main")[]
|
|
79
|
+
_loadFromEnv: string[]
|
|
80
|
+
})[]
|
|
81
|
+
/** Disable a transport target by it's target name. */
|
|
82
|
+
disabledServerTransportTargets?: string[]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default defineNuxtModule<ModuleOptions>({
|
|
86
|
+
meta: {
|
|
87
|
+
name: "logger",
|
|
88
|
+
configKey: "logger"
|
|
89
|
+
},
|
|
90
|
+
defaults: {
|
|
91
|
+
redact: [
|
|
92
|
+
...(process.env.NODE_ENV === "development" ? [] : ["redact"]),
|
|
93
|
+
`req.headers["sec-ch-ua"]`,
|
|
94
|
+
`req.headers.cookie`,
|
|
95
|
+
`req.headers["user-agent"]`,
|
|
96
|
+
`req.headers.authorization`,
|
|
97
|
+
`req.headers.referer`,
|
|
98
|
+
`req.headers["x-forwarded-for"]`,
|
|
99
|
+
`req.headers["set-cookie"]`,
|
|
100
|
+
// i don't think this is actually sensitive, but just in case
|
|
101
|
+
`req["sec-websocket-key"]`
|
|
102
|
+
] as string[],
|
|
103
|
+
enableServerRequestLogging: false,
|
|
104
|
+
additionalServerTransportTargets: [],
|
|
105
|
+
disabledServerTransportTargets: []
|
|
106
|
+
},
|
|
107
|
+
async setup(options, nuxt) {
|
|
108
|
+
const { resolve } = createResolver(import.meta.url)
|
|
109
|
+
options.devServerLogPath ??= await resolvePath("~~/logs/server.log", { alias: nuxt.options.alias })
|
|
110
|
+
const maybeAppName = (nuxt.options.runtimeConfig.public as any)?.appInfo?.name
|
|
111
|
+
const appName = options.appName ?? maybeAppName ?? path.basename(await resolvePath("~~", { alias: nuxt.options.alias }))
|
|
112
|
+
options.serverLogPath ??= `/var/log/testing/${appName}.log`
|
|
113
|
+
if (process.env.NODE_ENV === "development") {
|
|
114
|
+
if (!await fs.stat(options.devServerLogPath).then(() => true).catch(() => false)) {
|
|
115
|
+
await fs.mkdir(path.dirname(options.devServerLogPath), {
|
|
116
|
+
recursive: true
|
|
117
|
+
})
|
|
118
|
+
await fs.writeFile(options.devServerLogPath, "")
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
if (!await fs.stat(options.serverLogPath).then(() => true).catch(() => false)) {
|
|
122
|
+
await fs.mkdir(path.dirname(options.serverLogPath), { recursive: true })
|
|
123
|
+
await fs.writeFile(options.serverLogPath, "")
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
nuxt.options.runtimeConfig.public.logger = defu(
|
|
128
|
+
nuxt.options.runtimeConfig.public.logger as any,
|
|
129
|
+
{
|
|
130
|
+
redact: options.redact,
|
|
131
|
+
logPath: nuxt.options.dev ? options.devServerLogPath : options.serverLogPath,
|
|
132
|
+
additionalServerTransportTargets: options.additionalServerTransportTargets,
|
|
133
|
+
disabledServerTransportTargets: options.disabledServerTransportTargets
|
|
134
|
+
}
|
|
135
|
+
)
|
|
136
|
+
if (nuxt.options.modules.includes("@witchcraft/nuxt-electron")) {
|
|
137
|
+
const opts = nuxt.options as any
|
|
138
|
+
opts.electron ??= {}
|
|
139
|
+
opts.electron.electronOnlyRuntimeConfig ??= {}
|
|
140
|
+
opts.electron.electronOnlyRuntimeConfig.logger ??= {}
|
|
141
|
+
opts.electron.electronOnlyRuntimeConfig.logger.logPath = undefined
|
|
142
|
+
}
|
|
143
|
+
// the server files require transpilation to use #imports and #logger
|
|
144
|
+
// they are needed, otherwise I can't seem to get the imports to resolve correctly
|
|
145
|
+
// see also runtime/shared/createUseLogger.ts
|
|
146
|
+
for (const file of [
|
|
147
|
+
"./runtime",
|
|
148
|
+
"./runtime/server/middleware/log",
|
|
149
|
+
"./runtime/server/utils/useServerLogger"
|
|
150
|
+
]) {
|
|
151
|
+
nuxt.options.build.transpile.push(resolve(file))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
addImportsDir(resolve("./runtime/composables"))
|
|
155
|
+
addServerImportsDir(resolve("./runtime/server/utils"))
|
|
156
|
+
|
|
157
|
+
if (options.enableServerRequestLogging) {
|
|
158
|
+
addServerHandler({
|
|
159
|
+
middleware: true,
|
|
160
|
+
handler: resolve("./runtime/server/middleware/log")
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
addPlugin(resolve("./runtime/plugins/init"))
|
|
164
|
+
|
|
165
|
+
nuxt.options.alias["#logger"] = resolve("./runtime")
|
|
166
|
+
nuxt.hook("vite:extendConfig", config => {
|
|
167
|
+
// fixes https://github.com/nuxt/nuxt/issues/13247 partially (also transpiling it breaks it)
|
|
168
|
+
config.optimizeDeps ??= {}
|
|
169
|
+
config.optimizeDeps.include ??= []
|
|
170
|
+
config.optimizeDeps.include.push("pino")
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useLogger } from "./useLogger.js"
|
|
2
|
+
|
|
3
|
+
// todo see about importing the composable from nuxt-electron
|
|
4
|
+
const isElectronClient = (typeof window !== "undefined" && "electron" in window && window.electron)
|
|
5
|
+
|
|
6
|
+
export function setupElectronMainToRendererLogging(): void {
|
|
7
|
+
if (!isElectronClient) return
|
|
8
|
+
const logger = useLogger()
|
|
9
|
+
;(window as any).electron?.api._logs.on((level: string, data: any) => {
|
|
10
|
+
(logger as any)[level](data)
|
|
11
|
+
})
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ipcRenderer as IpcRenderer } from "electron"
|
|
2
|
+
|
|
3
|
+
import { ELECTRON_LOG_TYPE } from "./types.js"
|
|
4
|
+
|
|
5
|
+
import type { Levels } from "../types.js"
|
|
6
|
+
|
|
7
|
+
export type ElectronLoggerApi = {
|
|
8
|
+
log: (level: Levels, data: any) => void
|
|
9
|
+
_logs: { on(cb: (level: Levels, data: any) => void): void }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createElectronLoggerApi(ipcRenderer: typeof IpcRenderer): ElectronLoggerApi {
|
|
13
|
+
return {
|
|
14
|
+
log: (level: Levels, data: any) => ipcRenderer.send(ELECTRON_LOG_TYPE.TO_MAIN, { level, data }),
|
|
15
|
+
_logs: {
|
|
16
|
+
on(cb: (level: Levels, data: any) => void) {
|
|
17
|
+
ipcRenderer.on(ELECTRON_LOG_TYPE.FROM_MAIN_ON_RENDERER, (_event, level, data) => {
|
|
18
|
+
cb(level, data)
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// note adding file endings breaks build types
|
|
2
|
+
export { createElectronLoggerApi, type ElectronLoggerApi } from "./createElectronLoggerApi"
|
|
3
|
+
|
|
4
|
+
export { useElectronLogger } from "./useElectronLogger"
|
|
5
|
+
export { wrapLoggerForElectron } from "./wrapLoggerForElectron"
|
|
6
|
+
export * from "./types"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ipcMain } from "electron"
|
|
2
|
+
|
|
3
|
+
import { ELECTRON_LOG_TYPE } from "./types.js"
|
|
4
|
+
import type { useElectronLogger } from "./useElectronLogger.js"
|
|
5
|
+
|
|
6
|
+
export function receiveFromRendererOnMain(logger: ReturnType<typeof useElectronLogger>): void {
|
|
7
|
+
ipcMain.on(ELECTRON_LOG_TYPE.TO_MAIN, (_event, message) => {
|
|
8
|
+
if (typeof message.data === "object" && typeof message.level === "string") {
|
|
9
|
+
logger[message.level as keyof typeof logger](message.data)
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EnumLike } from "@alanscodelog/utils"
|
|
2
|
+
import { enumFromArray } from "@alanscodelog/utils/enumFromArray"
|
|
3
|
+
|
|
4
|
+
export const ELECTRON_LOG_TYPE = enumFromArray([
|
|
5
|
+
"FROM_MAIN_ON_RENDERER",
|
|
6
|
+
"TO_MAIN"
|
|
7
|
+
], "ELECTRON_LOG_TYPE.")
|
|
8
|
+
|
|
9
|
+
export type ElectronLogType = EnumLike<typeof ELECTRON_LOG_TYPE>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { keys } from "@alanscodelog/utils/keys"
|
|
2
|
+
import type { PublicRuntimeConfig } from "@nuxt/schema"
|
|
3
|
+
import { app, type BrowserWindow } from "electron"
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import { type Logger, pino } from "pino"
|
|
6
|
+
|
|
7
|
+
import { receiveFromRendererOnMain } from "./receiveFromRendererOnMain.js"
|
|
8
|
+
import { ELECTRON_LOG_TYPE } from "./types.js"
|
|
9
|
+
|
|
10
|
+
import { getBaseOptions } from "../helpers/getBaseOptions.js"
|
|
11
|
+
|
|
12
|
+
declare const globalThis: any
|
|
13
|
+
globalThis.__bundlerPathsOverrides = {
|
|
14
|
+
"pino-worker": `./node_modules/pino/lib/worker.js`,
|
|
15
|
+
"pino-worker-pipeline": `./node_modules/pino/lib/worker-pipeline.js`,
|
|
16
|
+
"thread-stream-worker": `./node_modules/thread-stream/lib/worker.js`,
|
|
17
|
+
"pino/file": `./node_modules/pino/file.js`,
|
|
18
|
+
"pino-pretty": `./node_modules/pino-pretty/index.js`
|
|
19
|
+
}
|
|
20
|
+
const bundlerOverrides = globalThis.__bundlerPathsOverrides
|
|
21
|
+
|
|
22
|
+
const appPath = app.getAppPath()
|
|
23
|
+
for (const key of keys(bundlerOverrides)) {
|
|
24
|
+
bundlerOverrides[key] = path.resolve(appPath, bundlerOverrides[key])
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let initiated = false
|
|
28
|
+
const levels: (keyof Logger)[] = ["error", "warn", "debug", "trace", "info", "fatal"] as const
|
|
29
|
+
const logger: Record<typeof levels[number], ((data: any) => void)> = {} as any
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A simplified wrapper around pino for use in electron main. Note that it does not support child logger and is actually a simple wrapper around just the basic levels.
|
|
33
|
+
*
|
|
34
|
+
* This is in part because pino might get swapped out (see @{link usePino}, but also to allow us to forward messages to the renderer easily because otherwise to do so we would have to use a custom pino transport that then communicates back again with the main process to send messages to the correct window which is a ridiculous round trip for no benefit.
|
|
35
|
+
*
|
|
36
|
+
* This is seperate since it requires special handling because of the following:
|
|
37
|
+
* - We can't use useRuntimeConfig so it's injected by using vite define instead ({@link STATIC}).
|
|
38
|
+
* - We need to pass the logPath which is defined at runtime.
|
|
39
|
+
* - We need to use some electron only imports which causes problems for the regular server.
|
|
40
|
+
* - Pino needs to be told were to find it's workers electron side in prod.
|
|
41
|
+
* {@link https://github.com/pinojs/pino/blob/master/docs/bundling.md}
|
|
42
|
+
* Using vite's ?worker&url imports almost works (the paths are correct) but something about the transpiling breaks things I think.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
export function useElectronLogger(
|
|
46
|
+
pinoConfig?: PublicRuntimeConfig["logger"],
|
|
47
|
+
windows?: () => BrowserWindow[], {
|
|
48
|
+
receiveFromRenderer = true
|
|
49
|
+
}: { receiveFromRenderer?: boolean } = {}
|
|
50
|
+
) {
|
|
51
|
+
if (initiated && pinoConfig) {
|
|
52
|
+
throw new Error("Config can only be passed the first time when initiating.")
|
|
53
|
+
}
|
|
54
|
+
// doing initiated check because we need to be able to pass config from electron
|
|
55
|
+
if (!initiated) {
|
|
56
|
+
if (!pinoConfig) {
|
|
57
|
+
throw new Error("Config must be passed the first time using useElectronLogger.")
|
|
58
|
+
}
|
|
59
|
+
if (!windows) {
|
|
60
|
+
throw new Error("windows getter must be passed the first time using useElectronLogger.")
|
|
61
|
+
}
|
|
62
|
+
initiated = true
|
|
63
|
+
|
|
64
|
+
const { opts, transports, debug } = getBaseOptions(pinoConfig)
|
|
65
|
+
|
|
66
|
+
const pinoLogger = pino(
|
|
67
|
+
opts,
|
|
68
|
+
pino.transport(transports)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
for (const level of levels) {
|
|
72
|
+
// the references are safe
|
|
73
|
+
|
|
74
|
+
logger[level] = (data: any) => {
|
|
75
|
+
if (typeof data === "object") {
|
|
76
|
+
if (data.ns.startsWith("renderer:")) {
|
|
77
|
+
(pinoLogger as any)[level](data)
|
|
78
|
+
return
|
|
79
|
+
} else if (!data.ns.startsWith("main:")) {
|
|
80
|
+
throw new Error(`Namespace on electron's main side must start with "main:", got ${data.ns} instead in log: ${JSON.stringify(data)}.`)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
(pinoLogger as any)[level](data)
|
|
84
|
+
const wins = windows()
|
|
85
|
+
for (const win of wins) {
|
|
86
|
+
win.webContents.send(ELECTRON_LOG_TYPE.FROM_MAIN_ON_RENDERER,
|
|
87
|
+
level, data)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (receiveFromRenderer) {
|
|
92
|
+
receiveFromRendererOnMain(logger)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
logger.debug({ ns: "main:log", ...debug })
|
|
96
|
+
}
|
|
97
|
+
return logger
|
|
98
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { useElectronLogger } from "./useElectronLogger.js"
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a logger (prefixed the first argument's namespace key with "main:") for use in electron.
|
|
4
|
+
*/
|
|
5
|
+
export function wrapLoggerForElectron<T extends ReturnType<typeof useElectronLogger>>(
|
|
6
|
+
logger: T,
|
|
7
|
+
prefix: string = "main:"
|
|
8
|
+
): T {
|
|
9
|
+
const res = {}
|
|
10
|
+
for (const key of Object.keys(logger)) {
|
|
11
|
+
res[key] = (...args: any[]) => {
|
|
12
|
+
logger[key]({ ...args[0], ns: prefix + args[0].ns }, ...args.slice(1))
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return res
|
|
16
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { get } from "@alanscodelog/utils/get"
|
|
2
|
+
import { keys } from "@alanscodelog/utils/keys"
|
|
3
|
+
import { readable } from "@alanscodelog/utils/readable"
|
|
4
|
+
import { set } from "@alanscodelog/utils/set"
|
|
5
|
+
import { walk } from "@alanscodelog/utils/walk"
|
|
6
|
+
import type { PublicRuntimeConfig } from "@nuxt/schema"
|
|
7
|
+
import type { LoggerOptions, TransportMultiOptions } from "pino"
|
|
8
|
+
import { pino } from "pino"
|
|
9
|
+
|
|
10
|
+
// normally we would access logger.levels, but because we create
|
|
11
|
+
// the shared config here we can't
|
|
12
|
+
const levels = {
|
|
13
|
+
values: {
|
|
14
|
+
fatal: 60,
|
|
15
|
+
error: 50,
|
|
16
|
+
warn: 40,
|
|
17
|
+
info: 30,
|
|
18
|
+
debug: 20,
|
|
19
|
+
trace: 10
|
|
20
|
+
},
|
|
21
|
+
labels: {
|
|
22
|
+
10: "trace",
|
|
23
|
+
20: "debug",
|
|
24
|
+
30: "info",
|
|
25
|
+
40: "warn",
|
|
26
|
+
50: "error",
|
|
27
|
+
60: "fatal"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const isElectronClient = (typeof window !== "undefined" && "electron" in window && window.electron)
|
|
32
|
+
|
|
33
|
+
export function getBaseOptions(
|
|
34
|
+
config: PublicRuntimeConfig["logger"]
|
|
35
|
+
): {
|
|
36
|
+
opts: LoggerOptions
|
|
37
|
+
browserOpts: LoggerOptions
|
|
38
|
+
transports: TransportMultiOptions
|
|
39
|
+
debug: {
|
|
40
|
+
logPath: string
|
|
41
|
+
writeLevel: string
|
|
42
|
+
logLevel: string
|
|
43
|
+
}
|
|
44
|
+
} {
|
|
45
|
+
// @ts-expect-error the path is wrong on electron's client side
|
|
46
|
+
// but it's not used anyways
|
|
47
|
+
if (isElectronClient) { delete config.logPath }
|
|
48
|
+
const {
|
|
49
|
+
redact,
|
|
50
|
+
logPath,
|
|
51
|
+
additionalServerTransportTargets,
|
|
52
|
+
disabledServerTransportTargets
|
|
53
|
+
} = config
|
|
54
|
+
const validLevels = keys(levels.values)
|
|
55
|
+
if (process.env.LOG_LEVEL && !validLevels.includes(process.env.LOG_LEVEL as any)) {
|
|
56
|
+
throw new Error(`logLevel is not a valid level: ${readable(validLevels)}`)
|
|
57
|
+
}
|
|
58
|
+
const maybeElectronLogLevel = import.meta.client
|
|
59
|
+
? (window as any).electron?.meta?.env?.LOG_LEVEL
|
|
60
|
+
: undefined
|
|
61
|
+
const maybeElectronLogWriteLevel = import.meta.client
|
|
62
|
+
? (window as any).electron?.meta?.env?.LOG_WRITE_LEVEL
|
|
63
|
+
: undefined
|
|
64
|
+
const writeLevel: (typeof validLevels)[number]
|
|
65
|
+
= maybeElectronLogWriteLevel
|
|
66
|
+
?? maybeElectronLogLevel
|
|
67
|
+
?? process.env.LOG_WRITE_LEVEL as any
|
|
68
|
+
?? process.env.LOG_LEVEL as any
|
|
69
|
+
?? (import.meta.dev ? "debug" : undefined)
|
|
70
|
+
?? "warn"
|
|
71
|
+
const logLevel: (typeof validLevels)[number]
|
|
72
|
+
= maybeElectronLogWriteLevel
|
|
73
|
+
?? process.env.LOG_LEVEL as any
|
|
74
|
+
?? (import.meta.dev ? "debug" : undefined)
|
|
75
|
+
?? "debug"
|
|
76
|
+
|
|
77
|
+
const processedAdditionalServerTransportTargets = []
|
|
78
|
+
const thisContexts = [
|
|
79
|
+
...(isElectronClient ? ["electron-client"] : []),
|
|
80
|
+
// might not be available, careful
|
|
81
|
+
...((import.meta as any)?.electron ? ["electron-main"] : []),
|
|
82
|
+
...(import.meta.client ? ["client"] : []),
|
|
83
|
+
...(import.meta.server ? ["server"] : [])
|
|
84
|
+
]
|
|
85
|
+
for (const target of additionalServerTransportTargets ?? []) {
|
|
86
|
+
const contexts = (target as any)._contexts as string[]
|
|
87
|
+
const contextMatches = contexts === undefined || contexts.find(c => thisContexts.includes(c))
|
|
88
|
+
if (!contextMatches) continue
|
|
89
|
+
|
|
90
|
+
const propPaths = (target as any)._loadFromEnv as string[]
|
|
91
|
+
// we have to clone because we can't write to runtimeConfig
|
|
92
|
+
const clone = walk(target, undefined, { save: true })
|
|
93
|
+
processedAdditionalServerTransportTargets.push(clone)
|
|
94
|
+
for (const propPath of propPaths) {
|
|
95
|
+
const splitPath = propPath.split(".")
|
|
96
|
+
if (propPath) {
|
|
97
|
+
set(clone, splitPath, process.env[get(clone, splitPath)])
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
delete (clone as any)._loadFromEnv
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
debug: {
|
|
105
|
+
logPath,
|
|
106
|
+
writeLevel,
|
|
107
|
+
logLevel
|
|
108
|
+
},
|
|
109
|
+
opts: {
|
|
110
|
+
redact,
|
|
111
|
+
// do not under any circumstances change how level looks, it breaks the logging ??? :/
|
|
112
|
+
level: logLevel,
|
|
113
|
+
formatters: {
|
|
114
|
+
bindings: () => ({})
|
|
115
|
+
},
|
|
116
|
+
timestamp: pino.stdTimeFunctions.isoTime
|
|
117
|
+
},
|
|
118
|
+
browserOpts: {
|
|
119
|
+
browser: {
|
|
120
|
+
asObject: false,
|
|
121
|
+
transmit: isElectronClient
|
|
122
|
+
? {
|
|
123
|
+
level: logLevel,
|
|
124
|
+
send: (level, logEvent) => {
|
|
125
|
+
if (!logEvent.messages[0]?.ns?.startsWith("main") && levels.values[level] < levels.values[writeLevel]) {
|
|
126
|
+
logEvent.messages[0].ns &&= `renderer:${logEvent.messages[0].ns}`
|
|
127
|
+
;(window as any).electron.api.log(logEvent)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
: undefined
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
transports: {
|
|
135
|
+
targets: [
|
|
136
|
+
{
|
|
137
|
+
level: writeLevel,
|
|
138
|
+
target: "pino/file",
|
|
139
|
+
options: {
|
|
140
|
+
destination: logPath,
|
|
141
|
+
mkdir: true
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
level: logLevel,
|
|
146
|
+
target: "pino-pretty",
|
|
147
|
+
options: {
|
|
148
|
+
colorize: true
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
...(processedAdditionalServerTransportTargets ?? [])
|
|
152
|
+
// not sure when target doesn't exist, #future investigate
|
|
153
|
+
].filter(t => !disabledServerTransportTargets?.includes((t as any).target))
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useRuntimeConfig } from "#imports"
|
|
2
|
+
|
|
3
|
+
import { useLogger } from "../composables/useLogger"
|
|
4
|
+
|
|
5
|
+
export default defineNuxtPlugin({
|
|
6
|
+
name: "client-init-log",
|
|
7
|
+
setup(nuxtApp) {
|
|
8
|
+
if (import.meta.client) {
|
|
9
|
+
nuxtApp.hook("app:created", () => {
|
|
10
|
+
const config = useRuntimeConfig().public
|
|
11
|
+
const initialLog: any = { appInfo: config.appInfo }
|
|
12
|
+
if (window.navigator !== undefined) {
|
|
13
|
+
initialLog.environmentInfo = {
|
|
14
|
+
userAgent: navigator.userAgent
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
useLogger().info({ ns: "client:init", ...initialLog })
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
env: {
|
|
22
|
+
// don't run when rendering server-only or island components.
|
|
23
|
+
islands: false
|
|
24
|
+
}
|
|
25
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineEventHandler, useServerLogger } from "#imports"
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler(event => {
|
|
4
|
+
const logger = useServerLogger()
|
|
5
|
+
logger.trace({
|
|
6
|
+
ns: "server:req:all",
|
|
7
|
+
req: {
|
|
8
|
+
headers: event.node.req.headers,
|
|
9
|
+
method: event.node.req.method,
|
|
10
|
+
url: event.node.req.url
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import os from "node:os"
|
|
2
|
+
|
|
3
|
+
import { defineNitroPlugin, useRuntimeConfig, useServerLogger } from "#imports"
|
|
4
|
+
|
|
5
|
+
export default defineNitroPlugin(_nitroApp => {
|
|
6
|
+
const cpus = os.cpus()
|
|
7
|
+
const environmentInfo = {
|
|
8
|
+
os: os.platform(),
|
|
9
|
+
osType: os.type(),
|
|
10
|
+
osVersion: os.release(),
|
|
11
|
+
arch: os.arch(),
|
|
12
|
+
nodeVersion: process.version,
|
|
13
|
+
cpu: cpus?.[0]?.model,
|
|
14
|
+
cpus: cpus.length,
|
|
15
|
+
totalMem: os.totalmem()
|
|
16
|
+
}
|
|
17
|
+
useServerLogger().info({
|
|
18
|
+
ns: "server:init",
|
|
19
|
+
appInfo: useRuntimeConfig().public,
|
|
20
|
+
environmentInfo,
|
|
21
|
+
isUsingTestingDb: process.env.LOCALSERVERDB
|
|
22
|
+
})
|
|
23
|
+
})
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { RuntimeConfig } from "nuxt/schema"
|
|
2
|
+
import { type Logger, pino } from "pino"
|
|
3
|
+
|
|
4
|
+
import { getBaseOptions } from "../helpers/getBaseOptions.js"
|
|
5
|
+
import type { Levels } from "../types.js"
|
|
6
|
+
|
|
7
|
+
export type BaseLogger = Record<Levels, ((data: any) => void)> & {
|
|
8
|
+
flush: Logger["flush"]
|
|
9
|
+
}
|
|
10
|
+
const logger: BaseLogger = {} as any
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This exists to allow creating useLogger both on the client and the server since for the module to work on the server we must import from `#imports` and have the module transpile the files, but this is not neccesary from the composable.
|
|
14
|
+
*
|
|
15
|
+
* So they both create useLogger by passing in useRuntimeConfig how they can.
|
|
16
|
+
*/
|
|
17
|
+
export function createUseLogger(
|
|
18
|
+
rc: () => RuntimeConfig
|
|
19
|
+
) {
|
|
20
|
+
let initiated = false
|
|
21
|
+
return function useLogger({
|
|
22
|
+
redirectTo,
|
|
23
|
+
redirectAll = true
|
|
24
|
+
}: {
|
|
25
|
+
redirectTo?: "electron"
|
|
26
|
+
/**
|
|
27
|
+
* Prefixes all messages not coming from main with `renderer:` so that they will be forwarded.
|
|
28
|
+
*
|
|
29
|
+
* Can also be a function to filter what gets forwarded.
|
|
30
|
+
*/
|
|
31
|
+
redirectAll?: boolean | ((level: keyof Logger, data: any) => boolean)
|
|
32
|
+
} = {}) {
|
|
33
|
+
const levels: (Levels)[] = ["error", "warn", "debug", "trace", "info", "fatal"] as const
|
|
34
|
+
// there's a bit of a problem calling useRuntimeConfig/useNuxtApp outside the function
|
|
35
|
+
// so doing initiated check instead
|
|
36
|
+
if (!initiated) {
|
|
37
|
+
initiated = true
|
|
38
|
+
|
|
39
|
+
const config = rc().public
|
|
40
|
+
const { opts, transports, browserOpts, debug } = getBaseOptions(config.logger)
|
|
41
|
+
let transport: any
|
|
42
|
+
if (import.meta.server) {
|
|
43
|
+
transport = pino.transport(transports)
|
|
44
|
+
}
|
|
45
|
+
const pinoLogger = pino(
|
|
46
|
+
{
|
|
47
|
+
...opts,
|
|
48
|
+
...(import.meta.client
|
|
49
|
+
? browserOpts
|
|
50
|
+
: {})
|
|
51
|
+
},
|
|
52
|
+
transport
|
|
53
|
+
)
|
|
54
|
+
for (const level of levels) {
|
|
55
|
+
logger[level] = (data: any) => {
|
|
56
|
+
if (redirectTo === "electron") {
|
|
57
|
+
if (typeof data === "object" && typeof data.ns === "string" && !data.ns.startsWith("main:")) {
|
|
58
|
+
if (data.ns.startsWith("renderer:")) {
|
|
59
|
+
(window as any).electron?.api.log(level, data)
|
|
60
|
+
} else if (typeof redirectAll === "function" ? redirectAll(level, data) : redirectAll) {
|
|
61
|
+
data.ns = `renderer:${data.ns}`
|
|
62
|
+
;(window as any).electron?.api.log(level, data)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
(pinoLogger as any)[level](data)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
logger.debug({ ns: "log", ...debug, enabledTransportTargets: transports.targets.map(t => (t as any)?.target) })
|
|
70
|
+
return logger
|
|
71
|
+
}
|
|
72
|
+
return logger
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Levels = "error" | "warn" | "debug" | "trace" | "info" | "fatal"
|