@orieljs/framework 0.1.0-alpha.5 → 0.1.0-alpha.7
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/NOTICE +7 -0
- package/README.md +10 -1
- package/TRADEMARKS.md +33 -0
- package/dist/{chunk-WNJP46B7.js → chunk-4MWG3B7G.js} +1785 -626
- package/dist/chunk-4MWG3B7G.js.map +1 -0
- package/dist/chunk-7ZQZYDOC.js +3253 -0
- package/dist/chunk-7ZQZYDOC.js.map +1 -0
- package/dist/{chunk-BSQKOLSK.js → chunk-GVNLZZTN.js} +1 -1
- package/dist/chunk-GVNLZZTN.js.map +1 -0
- package/dist/{chunk-A5XRLEWM.js → chunk-VOYW3R5J.js} +4 -4
- package/dist/chunk-VOYW3R5J.js.map +1 -0
- package/dist/contracts.d.ts +535 -17
- package/dist/contracts.js +1 -1
- package/dist/index.d-WQpfdIbG.d.ts +630 -0
- package/dist/index.d.ts +2202 -5952
- package/dist/index.js +4900 -944
- package/dist/index.js.map +1 -1
- package/dist/{jsx-runtime-WKTl3EEw.d.ts → jsx-runtime-6um4Mwfb.d.ts} +97 -51
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/jsx-runtime.js +3 -3
- package/dist/{testing-BErengYY.d.ts → testing-CKwfWJGn.d.ts} +313 -994
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +19 -5
- package/docs/authentication.md +487 -0
- package/docs/authorization.md +435 -0
- package/docs/cache.md +647 -0
- package/docs/collections.md +2270 -0
- package/docs/configuration.md +334 -0
- package/docs/console.md +651 -0
- package/docs/container.md +430 -0
- package/docs/controllers.md +406 -0
- package/docs/csrf.md +126 -0
- package/docs/database-testing.md +144 -0
- package/docs/database.md +236 -0
- package/docs/deployment.md +276 -0
- package/docs/eloquent-collections.md +218 -0
- package/docs/eloquent-factories.md +255 -0
- package/docs/eloquent-mutators.md +254 -0
- package/docs/eloquent-observers.md +145 -0
- package/docs/eloquent-relationships.md +557 -0
- package/docs/eloquent.md +494 -0
- package/docs/encryption.md +104 -0
- package/docs/errors.md +318 -0
- package/docs/events.md +336 -0
- package/docs/facades.md +278 -0
- package/docs/hashing.md +118 -0
- package/docs/helpers.md +1281 -0
- package/docs/http-tests.md +386 -0
- package/docs/inertia.md +485 -0
- package/docs/installation.md +209 -0
- package/docs/lifecycle.md +234 -0
- package/docs/live-tests.md +226 -0
- package/docs/live.md +910 -0
- package/docs/logging.md +255 -0
- package/docs/mail.md +619 -0
- package/docs/middleware.md +384 -0
- package/docs/migrations.md +416 -0
- package/docs/mocking.md +264 -0
- package/docs/navigation.json +273 -0
- package/docs/notifications.md +225 -0
- package/docs/pagination.md +247 -0
- package/docs/passwords.md +207 -0
- package/docs/providers.md +187 -0
- package/docs/queries.md +600 -0
- package/docs/queues.md +330 -0
- package/docs/requests.md +533 -0
- package/docs/responses.md +444 -0
- package/docs/routing.md +738 -0
- package/docs/scheduling.md +277 -0
- package/docs/seeding.md +122 -0
- package/docs/session.md +409 -0
- package/docs/starter-kits.md +396 -0
- package/docs/storage.md +192 -0
- package/docs/structure.md +264 -0
- package/docs/testing.md +165 -0
- package/docs/time-travel.md +138 -0
- package/docs/validation.md +1792 -0
- package/docs/verification.md +152 -0
- package/docs/views.md +566 -0
- package/docs/vite.md +322 -0
- package/package.json +34 -32
- package/dist/chunk-A5XRLEWM.js.map +0 -1
- package/dist/chunk-BSQKOLSK.js.map +0 -1
- package/dist/chunk-PM5YTG57.js +0 -880
- package/dist/chunk-PM5YTG57.js.map +0 -1
- package/dist/chunk-WNJP46B7.js.map +0 -1
- package/dist/index.d-DOlTeG-8.d.ts +0 -99
package/docs/logging.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Logging
|
|
2
|
+
|
|
3
|
+
Oriel writes your app's log through channels: a file, a file per day, or stderr. Each entry is one line of JSON,
|
|
4
|
+
written by pino.
|
|
5
|
+
|
|
6
|
+
## Introduction
|
|
7
|
+
|
|
8
|
+
Logging is organized in channels, as in Laravel. Each channel writes entries one way: the `single` channel appends
|
|
9
|
+
them to one file, the `daily` channel to a file per day, and the `stderr` channel to the process's standard error.
|
|
10
|
+
A `stack` channel sends each entry to several channels at once, and each channel keeps only the entries at or above
|
|
11
|
+
its level.
|
|
12
|
+
|
|
13
|
+
The channels are written with pino, a Node logger, which writes each entry as a line of JSON.
|
|
14
|
+
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
The channels are configured in `config/logging.ts`. A new app's file:
|
|
18
|
+
|
|
19
|
+
```ts title="config/logging.ts"
|
|
20
|
+
import { dirname, join } from 'node:path'
|
|
21
|
+
import { env } from '@orieljs/framework'
|
|
22
|
+
|
|
23
|
+
const basePath = dirname(import.meta.dirname)
|
|
24
|
+
const path = join(basePath, 'storage/logs/oriel.log')
|
|
25
|
+
const level = env('LOG_LEVEL', 'debug')
|
|
26
|
+
|
|
27
|
+
export default {
|
|
28
|
+
default: env('LOG_CHANNEL', 'stack'),
|
|
29
|
+
channels: {
|
|
30
|
+
stack: { driver: 'stack', channels: String(env('LOG_STACK', 'single')).split(',') },
|
|
31
|
+
single: { driver: 'single', path, level },
|
|
32
|
+
daily: { driver: 'daily', path, level, days: Number(env('LOG_DAILY_DAYS', 14)) },
|
|
33
|
+
stderr: { driver: 'stderr', level },
|
|
34
|
+
mail: { driver: 'single', path: join(basePath, 'storage/logs/mail.log'), level },
|
|
35
|
+
null: { driver: 'null' },
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`default` is the channel your entries go to: `LOG_CHANNEL`, `stack` unless you set it. The `stack` channel holds the
|
|
41
|
+
channels `LOG_STACK` lists, comma-separated: `single` in a new app, which writes `storage/logs/oriel.log`. `LOG_LEVEL`
|
|
42
|
+
sets the level of every channel, `debug` by default, so every entry is kept. The `mail` channel is there for the [`log`
|
|
43
|
+
mailer](mail.md#configuration): with `MAIL_LOG_CHANNEL=mail` in your `.env`, the mail it would have sent goes to
|
|
44
|
+
`storage/logs/mail.log` rather than to the default channel.
|
|
45
|
+
|
|
46
|
+
A channel name that `channels` doesn't define is an error when it is used: "Log channel [name] is not defined in
|
|
47
|
+
config('logging.channels').".
|
|
48
|
+
|
|
49
|
+
### Available channel drivers
|
|
50
|
+
|
|
51
|
+
| Name | What it does |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `single` | Appends every entry to the file `path` names, and creates its folder if needed |
|
|
54
|
+
| `daily` | Writes to one file per day, `oriel-2026-10-19.log` for `oriel.log`, and keeps the files of the last `days` days (14 by default): each time it starts a day's file, it deletes older ones |
|
|
55
|
+
| `stderr` | Writes to the process's standard error, for a container or a process manager to collect |
|
|
56
|
+
| `stack` | Sends every entry to each of the channels it lists, in `channels` |
|
|
57
|
+
| `null` | Discards every entry |
|
|
58
|
+
|
|
59
|
+
Each channel but `stack` and `null` takes a `level`. A driver that isn't one of these is an error when its channel
|
|
60
|
+
is used, "Log driver [name] is not registered.".
|
|
61
|
+
|
|
62
|
+
> [!NOTE]
|
|
63
|
+
> Laravel's channels are Monolog handlers, with drivers for Slack, Papertrail, syslog and PHP's error log, and
|
|
64
|
+
> `custom` and `monolog` drivers for your own. Oriel writes with pino and has the five drivers above:
|
|
65
|
+
> on a server, `stderr` hands the log to whatever collects your process's output, which can forward it anywhere.
|
|
66
|
+
|
|
67
|
+
## Building log stacks
|
|
68
|
+
|
|
69
|
+
The `stack` driver combines channels, so one entry goes to all of them. Here the stack writes every entry to the
|
|
70
|
+
daily files, and errors and worse to stderr too:
|
|
71
|
+
|
|
72
|
+
```ts title="config/logging.ts"
|
|
73
|
+
import { dirname, join } from 'node:path'
|
|
74
|
+
import { env } from '@orieljs/framework'
|
|
75
|
+
|
|
76
|
+
const basePath = dirname(import.meta.dirname)
|
|
77
|
+
const path = join(basePath, 'storage/logs/oriel.log')
|
|
78
|
+
|
|
79
|
+
export default {
|
|
80
|
+
default: env('LOG_CHANNEL', 'stack'),
|
|
81
|
+
channels: {
|
|
82
|
+
stack: { driver: 'stack', channels: ['daily', 'stderr'] },
|
|
83
|
+
daily: { driver: 'daily', path, level: 'debug', days: 14 },
|
|
84
|
+
stderr: { driver: 'stderr', level: 'error' },
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A stack has no level of its own: each of its channels keeps the entries that meet its own level. A stack that lists
|
|
90
|
+
itself, directly or through another stack, is an error, "Log channel [stack] includes itself.".
|
|
91
|
+
|
|
92
|
+
#### Log levels
|
|
93
|
+
|
|
94
|
+
A channel's `level` is the least severe level it keeps. Oriel has the eight levels of RFC 5424, from the most severe:
|
|
95
|
+
`emergency`, `alert`, `critical`, `error`, `warning`, `notice`, `info` and `debug`. A channel at `warning` keeps
|
|
96
|
+
`warning`, `error`, `critical`, `alert` and `emergency` entries, and drops the others:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
Log.debug('An informational message.')
|
|
100
|
+
|
|
101
|
+
Log.emergency('The system is down!')
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
With the stack above, the first entry is written to the daily file only, and the second to both channels. A level
|
|
105
|
+
that isn't one of the eight, such as pino's `warn`, is an error when the channel is made: "Invalid log level
|
|
106
|
+
[warn].".
|
|
107
|
+
|
|
108
|
+
## Writing log messages
|
|
109
|
+
|
|
110
|
+
The `Log` facade writes to the default channel, with a method for each level:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
declare const message: string
|
|
114
|
+
// ---cut---
|
|
115
|
+
Log.emergency(message)
|
|
116
|
+
Log.alert(message)
|
|
117
|
+
Log.critical(message)
|
|
118
|
+
Log.error(message)
|
|
119
|
+
Log.warning(message)
|
|
120
|
+
Log.notice(message)
|
|
121
|
+
Log.info(message)
|
|
122
|
+
Log.debug(message)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`Log.log('warning', message)` takes the level as its first argument. The `logger()` helper writes a `debug`
|
|
126
|
+
entry, `logger('Debug message')`, and with no arguments returns the same log manager as the facade.
|
|
127
|
+
|
|
128
|
+
A controller logs as any other code does:
|
|
129
|
+
|
|
130
|
+
```ts title="app/Http/Controllers/UserController.ts"
|
|
131
|
+
import { Log, view } from '@orieljs/framework'
|
|
132
|
+
import { User } from '../../Models/User'
|
|
133
|
+
import { Controller } from './Controller'
|
|
134
|
+
|
|
135
|
+
export class UserController extends Controller {
|
|
136
|
+
/** Show the profile for the given user. */
|
|
137
|
+
async show(id: string) {
|
|
138
|
+
Log.info('Showing the user profile for user', { id })
|
|
139
|
+
|
|
140
|
+
return view('user.profile', { user: await User.findOrFail(id) })
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Contextual information
|
|
146
|
+
|
|
147
|
+
A method's second argument is the entry's context, an object written beside the message:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
declare const user: User
|
|
151
|
+
// ---cut---
|
|
152
|
+
Log.info('User failed to login.', { id: user.id })
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The message is written as it is. Laravel replaces placeholders such as `{id}` in the message with the context's
|
|
156
|
+
values; Oriel keeps the context in its own fields, where log tools can search them. An `Error` in the context is
|
|
157
|
+
written as its `name`, `message` and `stack`.
|
|
158
|
+
|
|
159
|
+
`Log.withContext()` adds context to every entry written after it in the same request, whatever the channel. A
|
|
160
|
+
middleware can give each request an id:
|
|
161
|
+
|
|
162
|
+
```ts title="app/Http/Middleware/AssignRequestId.ts"
|
|
163
|
+
import { randomUUID } from 'node:crypto'
|
|
164
|
+
import { Log, withHeaders } from '@orieljs/framework'
|
|
165
|
+
import type { HttpRequest, Middleware, Next } from '@orieljs/framework/contracts'
|
|
166
|
+
|
|
167
|
+
export class AssignRequestId implements Middleware {
|
|
168
|
+
async handle(request: HttpRequest, next: Next): Promise<Response> {
|
|
169
|
+
const requestId = randomUUID()
|
|
170
|
+
|
|
171
|
+
Log.withContext({ 'request-id': requestId })
|
|
172
|
+
|
|
173
|
+
const response = await next(request)
|
|
174
|
+
|
|
175
|
+
return withHeaders(response, (headers) => headers.set('Request-Id', requestId))
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The context belongs to the request: two requests running at once keep their own, and it is gone when the request ends. A
|
|
181
|
+
console command and a queued job have a context of their own in the same way. Context added outside all of them, as in a
|
|
182
|
+
[service provider](providers.md), reaches none of them.
|
|
183
|
+
|
|
184
|
+
> [!NOTE]
|
|
185
|
+
> Laravel's `withContext()` adds context to the default channel, and `shareContext()` to every channel. Oriel keeps
|
|
186
|
+
> the context with the request, command or job rather than with a channel, so `withContext()` already reaches every
|
|
187
|
+
> channel, and there is no `shareContext()`.
|
|
188
|
+
|
|
189
|
+
### Writing to specific channels
|
|
190
|
+
|
|
191
|
+
`Log.channel()` writes to a channel other than the default one:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
Log.channel('stderr').info('Something happened!')
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`Log.stack()` makes a stack of channels for one entry:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
Log.stack(['single', 'stderr']).info('Something happened!')
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Laravel's `Log::build()`, which makes a channel from a configuration given at run time, isn't in Oriel: add the
|
|
204
|
+
channel to `config/logging.ts`.
|
|
205
|
+
|
|
206
|
+
## The log format
|
|
207
|
+
|
|
208
|
+
Each entry is one line of JSON, as pino writes it. This is a line of `storage/logs/oriel.log`, which your app writes
|
|
209
|
+
and you don't type, for the controller above:
|
|
210
|
+
|
|
211
|
+
```json nocheck
|
|
212
|
+
{"level":30,"time":1792387488361,"pid":39221,"hostname":"web-1","id":"7","rfc5424Level":"info","msg":"Showing the user profile for user"}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`msg` is the message and `rfc5424Level` its level. `time` is in milliseconds since 1970, and `pid` and `hostname`
|
|
216
|
+
tell the processes apart. The context's keys sit beside them, `id` here. `level` is pino's number for the level:
|
|
217
|
+
20 for `debug`, 30 for `info` and `notice`, 40 for `warning`, 50 for `error`, and 60 for `critical`, `alert` and
|
|
218
|
+
`emergency`.
|
|
219
|
+
|
|
220
|
+
Reading the lines is up to the tools you use: `tail -f storage/logs/oriel.log` follows the file, and log services
|
|
221
|
+
read JSON lines as they are. Laravel Pail, which tails the log in the terminal, isn't in Oriel.
|
|
222
|
+
|
|
223
|
+
## Flushing the log
|
|
224
|
+
|
|
225
|
+
pino writes in the background, so an entry may not be in the file yet when the line that logged it returns. Your app
|
|
226
|
+
writes out every waiting entry, and closes the files, when it shuts down: `node oriel` after its command finishes, and
|
|
227
|
+
the server when it receives `SIGINT` or `SIGTERM` (see [Deployment](deployment.md)). The log is closed after the
|
|
228
|
+
database, queue and cache connections, so an entry logged while they close is kept.
|
|
229
|
+
|
|
230
|
+
A script that makes the app itself must shut it down before it exits, or the entries still waiting are lost:
|
|
231
|
+
|
|
232
|
+
```ts title="scripts/report.ts"
|
|
233
|
+
// @filename: bootstrap/app.ts
|
|
234
|
+
export declare function createApplication(): Promise<import('@orieljs/framework').Application>
|
|
235
|
+
// ---cut---
|
|
236
|
+
import { Log } from '@orieljs/framework'
|
|
237
|
+
import { createApplication } from '../bootstrap/app'
|
|
238
|
+
|
|
239
|
+
const app = await createApplication()
|
|
240
|
+
|
|
241
|
+
Log.info('Report started')
|
|
242
|
+
|
|
243
|
+
// ...
|
|
244
|
+
|
|
245
|
+
await app.shutdown()
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Calling `process.exit()` before `shutdown()` has finished loses them too. To write the waiting entries out without
|
|
249
|
+
shutting the app down, `await Log.flush()`. A [queued job](queues.md) or a command run by `node oriel` needs nothing:
|
|
250
|
+
the command shuts the app down when it ends.
|
|
251
|
+
|
|
252
|
+
## Monolog channel customization
|
|
253
|
+
|
|
254
|
+
Oriel doesn't use Monolog, so Laravel's `tap`, Monolog handlers, formatters and processors, and channels made by
|
|
255
|
+
your own factories, aren't in Oriel.
|