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 119 120 121 122 123 124 125 126 127 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 1x 1x 1x 2x 2x 1x 1x 1x 1x 1x 4483x 4483x 4483x 4483x 1x 1x 1x 4468x 4468x 4468x 1x 1x 1x 15x 15x 15x 15x 15x 15x | /* ** MQTT+ -- MQTT Communication Patterns ** Copyright (c) 2018-2026 Dr. Ralf S. Engelschall <rse@engelschall.com> ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* built-in requirements */ import { EventEmitter } from "node:events" /* internal requirements */ import type { APISchema } from "./mqtt-plus-api" import { MsgTrait } from "./mqtt-plus-msg" import { JSONX } from "./mqtt-plus-codec" /* type of log events */ class LogEvent { constructor ( public timestamp: number, public level: string, public msg: string | Promise<string>, public data?: Record<string, Promise<any> | any> ) {} /* resolve all pending promises in the log event */ async resolve () { if (this.msg instanceof Promise) this.msg = await this.msg.catch(() => "<resolve-failed>") if (this.data) for (const k of Object.keys(this.data)) if (this.data[k] instanceof Promise) this.data[k] = await this.data[k].catch(() => "<resolve-failed>") } /* render log event as string */ toString () { /* render time */ const timestamp = new Date(this.timestamp) const year = timestamp.getFullYear() const month = (timestamp.getMonth() + 1).toString().padStart(2, "0") const day = timestamp.getDate().toString().padStart(2, "0") const hours = timestamp.getHours().toString().padStart(2, "0") const minutes = timestamp.getMinutes().toString().padStart(2, "0") const seconds = timestamp.getSeconds().toString().padStart(2, "0") const ms = timestamp.getMilliseconds().toString().padStart(3, "0") const time = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}` /* render message */ const msg = (this.msg instanceof Promise ? "<unresolved>" : this.msg) /* render optional data */ let extra = "" if (this.data !== undefined) { const data = this.data const kv = Object.keys(data).map((key) => { const value = data[key] instanceof Promise ? "<unresolved>" : data[key] return `${key}: ${JSONX.stringify(value)}` }).join(", ") extra = ` (${kv})` } /* render log entry */ return `[${time}] ${this.level}: ${msg}${extra}` } } /* Trace trait with event emitter and logging functionality */ export class TraceTrait<T extends APISchema = APISchema> extends MsgTrait<T> { /* internal state */ private _events = new EventEmitter() /* inline base event EventEmitter functionality (NOTICE: we cannot inherit from EventEmitter as its "emit" method is in conflict with our one) */ on (event: "error", callback: (error: Error) => void): void on (event: "log", callback: (log: LogEvent) => void): void on (...args: Parameters<typeof this._events.on>): void { this._events.on(...args) } off (event: "error", callback: (error: Error) => void): void off (event: "log", callback: (log: LogEvent) => void): void off (...args: Parameters<typeof this._events.off>): void { this._events.off(...args) } /* emit an event */ protected emitEvent (event: "error", error: Error): void protected emitEvent (event: "log", log: LogEvent): void protected emitEvent (...args: Parameters<typeof this._events.emit>): ReturnType<typeof this._events.emit> { if (this._events.listenerCount(args[0]) === 0) return false return this._events.emit(...args) } /* log an event */ protected log (level: string, msg: string | Promise<string>, data?: Record<string, Promise<any> | any>): void { const event = new LogEvent(Date.now(), level, msg, data) this.emitEvent("log", event) } /* raise an error event */ protected error (error: Error, msg?: string): void { let err = error if (msg !== undefined) err = new Error(`${msg}: ${error.message}`, { cause: error }) this.emitEvent("error", err) this.log("error", err.message) } } |