Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 13x 13x 13x 39x 39x 39x 8x 31x 31x 103x 9x 1x 8x 10x 10x 8x | import { resolve } from '@/core/resolve'
import type { LoggingSettings } from '@/types/LoggingSettings'
/**
* The declaration that admits every context field.
*
* @remarks
* The kernel's own default, and the reason it is this rather than a curated
* list: a package that decided for its consumers which fields matter would be
* deciding on behalf of runtimes it knows nothing about. Narrowing is an
* adapter's job — it is the layer that knows what the platform already records
* and therefore what a line need not repeat.
*/
const EVERY = '*'
/**
* @class
*
* Which context fields reach the log line.
*
* @remarks
* A log line carries two kinds of field, and only one of them is negotiable.
* *Context* is ambient — the request id, the method, the path, the address —
* repeated on every line for the span of a request, and stamped by middleware
* rather than chosen at the call site. *Payload* is what a caller passed to one
* log call because that call needed it. This governs the first and never the
* second: a field someone typed out deliberately is not noise to be filtered.
*
* The declaration is a comma-separated list, read from `LOG_FIELDS` in the
* environment first and the configuration surface second — the same order as
* `LOG_LEVEL`, and for the same reason. A deployment can narrow what it ingests
* without a rebuild.
*
* `*` admits everything and is the kernel default. An empty declaration admits
* nothing, which is not the same as leaving it unset: `LOG_FIELDS=""` is a
* deployment saying the level, the event, and the message are the whole line.
*
* @example
* ```ts
* const fields = LogFields.fromEnv({ LOG_FIELDS: 'requestId,method,path' })
* fields.permits('requestId') // true
* fields.permits('colo') // false
* ```
*
* @author Bayu Dwiyan Satria
* @version 1.2.1
* @since 1.2.1
*/
export class LogFields {
/**
* The admitted names, or `null` when every field is admitted.
*/
private readonly allowed: Set<string> | null
/**
* Constructs a LogFields.
*
* @param allowed The admitted names, or `null` to admit every field.
*/
private constructor(allowed: Set<string> | null) {
this.allowed = allowed
}
/**
* Reads the declaration for the current environment.
*
* @remarks
* `LOG_FIELDS` is checked for being *declared*, not for being truthy, because
* an empty string is a meaningful declaration — it means "no context fields"
* — and would otherwise fall through to the surface and quietly emit more
* than the deployment asked for.
*
* @param env Anything carrying an optional `LOG_FIELDS`. Tolerates `null`.
* @returns The resolved allow-list.
*/
public static fromEnv(env: { LOG_FIELDS?: string } | null | undefined): LogFields {
const declared = env ? env.LOG_FIELDS : undefined
if (declared !== undefined) {
return LogFields.parse(declared)
}
const settings = resolve<LoggingSettings>('logging')
return settings.fields ? LogFields.parse(settings.fields.join(',')) : new LogFields(null)
}
/**
* Whether a context field reaches the line.
*
* @param name The field name.
* @returns True when the field is admitted.
*/
public permits(name: string): boolean {
return this.allowed === null || this.allowed.has(name)
}
/**
* Reads a comma-separated declaration.
*
* @param value The declaration, such as `requestId,method,path`.
* @returns The allow-list it describes.
*/
private static parse(value: string): LogFields {
if (value.trim() === EVERY) {
return new LogFields(null)
}
const names = value
.split(',')
.map(name => name.trim())
.filter(name => name.length > 0)
return new LogFields(new Set(names))
}
}
|