@larablox/monolog 0.1.1 → 0.2.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/README.md +23 -11
- package/out/Monolog/Handler/AbstractProcessingHandler.d.ts +3 -1
- package/out/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.d.ts +19 -0
- package/out/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.luau +30 -0
- package/out/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.d.ts +29 -0
- package/out/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.luau +61 -0
- package/out/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.d.ts +14 -0
- package/out/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.luau +32 -0
- package/out/Monolog/Handler/FingersCrossedHandler.d.ts +107 -0
- package/out/Monolog/Handler/FingersCrossedHandler.luau +208 -0
- package/out/Monolog/Handler/FormattableHandlerInterface.d.ts +23 -0
- package/out/Monolog/Handler/FormattableHandlerInterface.luau +36 -0
- package/out/Monolog/Handler/GroupHandler.d.ts +49 -0
- package/out/Monolog/Handler/GroupHandler.luau +139 -0
- package/out/Monolog/Handler/HandlerInterface.d.ts +10 -0
- package/out/Monolog/Handler/HandlerInterface.luau +36 -1
- package/out/Monolog/Handler/ProcessableHandlerInterface.d.ts +26 -0
- package/out/Monolog/Handler/ProcessableHandlerInterface.luau +15 -0
- package/out/Monolog/Handler/RobloxConsoleHandler.d.ts +19 -46
- package/out/Monolog/Handler/RobloxConsoleHandler.luau +28 -99
- package/out/Monolog/Handler/WhatFailureGroupHandler.d.ts +26 -0
- package/out/Monolog/Handler/WhatFailureGroupHandler.luau +85 -0
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ import { Level } from "@larablox/monolog/out/Monolog/Level";
|
|
|
27
27
|
|
|
28
28
|
// create a log channel
|
|
29
29
|
const log = new Logger("name");
|
|
30
|
-
log.pushHandler(new RobloxConsoleHandler(
|
|
30
|
+
log.pushHandler(new RobloxConsoleHandler(Level.Warning));
|
|
31
31
|
|
|
32
32
|
// add records to the log
|
|
33
33
|
log.warning("Foo");
|
|
@@ -39,36 +39,48 @@ so consumers deep-import each class the way the example above does.
|
|
|
39
39
|
|
|
40
40
|
## What's included
|
|
41
41
|
|
|
42
|
-
- `Logger`, `
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
- Core: `Logger`, `LoggerInterface` (the PSR-3 shape), `Level`, `LogRecord`,
|
|
43
|
+
`Registry`, `Utils`, `ResettableInterface`
|
|
44
|
+
- Handlers: `NullHandler`, `RobloxConsoleHandler` (writes formatted records to
|
|
45
|
+
the Roblox output console), and `TestHandler` for your own tests
|
|
46
|
+
- Wrapping handlers: `GroupHandler`, `WhatFailureGroupHandler`,
|
|
47
|
+
`FingersCrossedHandler` (with `ErrorLevelActivationStrategy` and
|
|
48
|
+
`ChannelLevelActivationStrategy`)
|
|
49
|
+
- Handler base classes and interfaces, for writing your own: `Handler`,
|
|
50
|
+
`AbstractHandler`, `AbstractProcessingHandler`, `HandlerInterface`,
|
|
51
|
+
`FormattableHandlerInterface`, `ProcessableHandlerInterface`
|
|
45
52
|
- Formatters: `NormalizerFormatter`, `LineFormatter`, `JsonFormatter`,
|
|
46
|
-
`ScalarFormatter`
|
|
53
|
+
`ScalarFormatter`, `HtmlFormatter`, and `FormatterInterface`
|
|
47
54
|
- Processors: `PsrLogMessageProcessor`, `ClosureContextProcessor`,
|
|
48
55
|
`IntrospectionProcessor`, `MemoryUsageProcessor`, `MemoryPeakUsageProcessor`,
|
|
49
|
-
`ProcessIdProcessor`, `TagProcessor`, `UidProcessor
|
|
56
|
+
`ProcessIdProcessor`, `TagProcessor`, `UidProcessor`, and
|
|
57
|
+
`ProcessorInterface`
|
|
50
58
|
- Attributes: `WithMonologChannel`, `AsMonologProcessor`
|
|
59
|
+
- Test helpers: `Test/MonologTestCase` (plus `Test/TestCase`, upstream's
|
|
60
|
+
deprecated alias for it), shipped in the published package the same way
|
|
61
|
+
upstream ships them, because consumers use them in their own suites
|
|
51
62
|
|
|
52
63
|
## Submitting bugs and feature requests
|
|
53
64
|
|
|
54
65
|
Bugs and feature requests are tracked on
|
|
55
66
|
[GitHub](https://github.com/larablox/monolog/issues).
|
|
56
67
|
|
|
57
|
-
|
|
68
|
+
## Requirements
|
|
58
69
|
|
|
59
70
|
- TypeScript 5.x compiled with [roblox-ts](https://roblox-ts.com/) `^3.0`
|
|
60
71
|
- A Rojo-synced Roblox place to run the compiled output in
|
|
61
72
|
|
|
62
|
-
|
|
73
|
+
## Framework Integration
|
|
63
74
|
|
|
64
75
|
[`larablox/framework`](https://github.com/larablox/framework)'s
|
|
65
76
|
`Illuminate\Log` is built on this package.
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
## License
|
|
68
79
|
|
|
69
80
|
MIT, matching upstream Monolog.
|
|
70
81
|
|
|
71
|
-
|
|
82
|
+
## Acknowledgements
|
|
72
83
|
|
|
73
84
|
This is a TypeScript/roblox-ts port of [Monolog](https://github.com/Seldaek/monolog)
|
|
74
|
-
by Jordi Boggiano, adapted to run on the Roblox platform as faithfully as it
|
|
85
|
+
by Jordi Boggiano, adapted to run on the Roblox platform as faithfully as it
|
|
86
|
+
allows.
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { AbstractHandler } from "./AbstractHandler";
|
|
2
|
+
import type { FormattableHandlerInterface } from "./FormattableHandlerInterface";
|
|
2
3
|
import type { FormatterInterface } from "../Formatter/FormatterInterface";
|
|
3
4
|
import type { LogRecord } from "../LogRecord";
|
|
5
|
+
import type { ProcessableHandlerInterface } from "./ProcessableHandlerInterface";
|
|
4
6
|
import type { Processor } from "../Processor/ProcessorInterface";
|
|
5
7
|
/**
|
|
6
8
|
* PHP: `Monolog\Handler\AbstractProcessingHandler`, which folds in
|
|
7
9
|
* `ProcessableHandlerTrait` and `FormattableHandlerTrait`.
|
|
8
10
|
*/
|
|
9
|
-
export declare abstract class AbstractProcessingHandler extends AbstractHandler {
|
|
11
|
+
export declare abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface {
|
|
10
12
|
protected processors: Processor[];
|
|
11
13
|
protected formatter?: FormatterInterface;
|
|
12
14
|
/** Handles a record. */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LogRecord } from "../../LogRecord";
|
|
2
|
+
/**
|
|
3
|
+
* PHP: `Monolog\Handler\FingersCrossed\ActivationStrategyInterface`.
|
|
4
|
+
*
|
|
5
|
+
* Interface for activation strategies for the `FingersCrossedHandler`.
|
|
6
|
+
*/
|
|
7
|
+
export interface ActivationStrategyInterface {
|
|
8
|
+
/** Returns whether the given record activates the handler. */
|
|
9
|
+
isHandlerActivated(record: LogRecord): boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* True when the given value implements `ActivationStrategyInterface`.
|
|
13
|
+
*
|
|
14
|
+
* `FingersCrossedHandler`'s constructor takes either a strategy or a bare
|
|
15
|
+
* level and branches on `instanceof`; there is no `instanceof` for an
|
|
16
|
+
* interface here, so this checks structurally for the one callable member, as
|
|
17
|
+
* `isResettable()` (`ResettableInterface.ts`) already does for its own.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isActivationStrategy(value: unknown): value is ActivationStrategyInterface;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
--[[
|
|
3
|
+
*
|
|
4
|
+
* PHP: `Monolog\Handler\FingersCrossed\ActivationStrategyInterface`.
|
|
5
|
+
*
|
|
6
|
+
* Interface for activation strategies for the `FingersCrossedHandler`.
|
|
7
|
+
|
|
8
|
+
]]
|
|
9
|
+
--[[
|
|
10
|
+
*
|
|
11
|
+
* True when the given value implements `ActivationStrategyInterface`.
|
|
12
|
+
*
|
|
13
|
+
* `FingersCrossedHandler`'s constructor takes either a strategy or a bare
|
|
14
|
+
* level and branches on `instanceof`; there is no `instanceof` for an
|
|
15
|
+
* interface here, so this checks structurally for the one callable member, as
|
|
16
|
+
* `isResettable()` (`ResettableInterface.ts`) already does for its own.
|
|
17
|
+
|
|
18
|
+
]]
|
|
19
|
+
local function isActivationStrategy(value)
|
|
20
|
+
local _value = value
|
|
21
|
+
if not (type(_value) == "table") then
|
|
22
|
+
return false
|
|
23
|
+
end
|
|
24
|
+
local candidate = value
|
|
25
|
+
local _isHandlerActivated = candidate.isHandlerActivated
|
|
26
|
+
return type(_isHandlerActivated) == "function"
|
|
27
|
+
end
|
|
28
|
+
return {
|
|
29
|
+
isActivationStrategy = isActivationStrategy,
|
|
30
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ActivationStrategyInterface } from "./ActivationStrategyInterface";
|
|
2
|
+
import type { Level } from "../../Level";
|
|
3
|
+
import type { LogLevel } from "../../LoggerInterface";
|
|
4
|
+
import type { LogRecord } from "../../LogRecord";
|
|
5
|
+
/**
|
|
6
|
+
* PHP: `Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy`.
|
|
7
|
+
*
|
|
8
|
+
* Channel and error level based activation strategy. Allows triggering
|
|
9
|
+
* activation based on level per channel: e.g. trigger activation on level
|
|
10
|
+
* `Error` by default, except for records of the `sql` channel, which should
|
|
11
|
+
* trigger activation on level `Warning`.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const activationStrategy = new ChannelLevelActivationStrategy(
|
|
15
|
+
* Level.Critical,
|
|
16
|
+
* { request: Level.Alert, sensitive: Level.Error },
|
|
17
|
+
* );
|
|
18
|
+
* const handler = new FingersCrossedHandler(
|
|
19
|
+
* new RobloxConsoleHandler(),
|
|
20
|
+
* activationStrategy,
|
|
21
|
+
* );
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare class ChannelLevelActivationStrategy implements ActivationStrategyInterface {
|
|
25
|
+
private readonly defaultActionLevel;
|
|
26
|
+
private readonly channelToActionLevel;
|
|
27
|
+
constructor(defaultActionLevel: Level | LogLevel, channelToActionLevel?: Record<string, Level | LogLevel>);
|
|
28
|
+
isHandlerActivated(record: LogRecord): boolean;
|
|
29
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local Logger = TS.import(script, script.Parent.Parent.Parent, "Logger").Logger
|
|
4
|
+
--[[
|
|
5
|
+
*
|
|
6
|
+
* PHP: `Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy`.
|
|
7
|
+
*
|
|
8
|
+
* Channel and error level based activation strategy. Allows triggering
|
|
9
|
+
* activation based on level per channel: e.g. trigger activation on level
|
|
10
|
+
* `Error` by default, except for records of the `sql` channel, which should
|
|
11
|
+
* trigger activation on level `Warning`.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const activationStrategy = new ChannelLevelActivationStrategy(
|
|
15
|
+
* Level.Critical,
|
|
16
|
+
* { request: Level.Alert, sensitive: Level.Error },
|
|
17
|
+
* );
|
|
18
|
+
* const handler = new FingersCrossedHandler(
|
|
19
|
+
* new RobloxConsoleHandler(),
|
|
20
|
+
* activationStrategy,
|
|
21
|
+
* );
|
|
22
|
+
* ```
|
|
23
|
+
|
|
24
|
+
]]
|
|
25
|
+
local ChannelLevelActivationStrategy
|
|
26
|
+
do
|
|
27
|
+
ChannelLevelActivationStrategy = setmetatable({}, {
|
|
28
|
+
__tostring = function()
|
|
29
|
+
return "ChannelLevelActivationStrategy"
|
|
30
|
+
end,
|
|
31
|
+
})
|
|
32
|
+
ChannelLevelActivationStrategy.__index = ChannelLevelActivationStrategy
|
|
33
|
+
function ChannelLevelActivationStrategy.new(...)
|
|
34
|
+
local self = setmetatable({}, ChannelLevelActivationStrategy)
|
|
35
|
+
return self:constructor(...) or self
|
|
36
|
+
end
|
|
37
|
+
function ChannelLevelActivationStrategy:constructor(defaultActionLevel, channelToActionLevel)
|
|
38
|
+
if channelToActionLevel == nil then
|
|
39
|
+
channelToActionLevel = {}
|
|
40
|
+
end
|
|
41
|
+
self.channelToActionLevel = {}
|
|
42
|
+
self.defaultActionLevel = Logger:toMonologLevel(defaultActionLevel)
|
|
43
|
+
for channel, level in pairs(channelToActionLevel) do
|
|
44
|
+
local _channelToActionLevel = self.channelToActionLevel
|
|
45
|
+
local _arg1 = Logger:toMonologLevel(level)
|
|
46
|
+
_channelToActionLevel[channel] = _arg1
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
function ChannelLevelActivationStrategy:isHandlerActivated(record)
|
|
50
|
+
local _channelToActionLevel = self.channelToActionLevel
|
|
51
|
+
local _channel = record.channel
|
|
52
|
+
local channelLevel = _channelToActionLevel[_channel]
|
|
53
|
+
if channelLevel ~= nil then
|
|
54
|
+
return record.level >= channelLevel
|
|
55
|
+
end
|
|
56
|
+
return record.level >= self.defaultActionLevel
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
return {
|
|
60
|
+
ChannelLevelActivationStrategy = ChannelLevelActivationStrategy,
|
|
61
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ActivationStrategyInterface } from "./ActivationStrategyInterface";
|
|
2
|
+
import type { Level } from "../../Level";
|
|
3
|
+
import type { LogLevel } from "../../LoggerInterface";
|
|
4
|
+
import type { LogRecord } from "../../LogRecord";
|
|
5
|
+
/**
|
|
6
|
+
* PHP: `Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy`.
|
|
7
|
+
*
|
|
8
|
+
* Error level based activation strategy.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ErrorLevelActivationStrategy implements ActivationStrategyInterface {
|
|
11
|
+
private readonly actionLevel;
|
|
12
|
+
constructor(actionLevel: Level | LogLevel);
|
|
13
|
+
isHandlerActivated(record: LogRecord): boolean;
|
|
14
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local Logger = TS.import(script, script.Parent.Parent.Parent, "Logger").Logger
|
|
4
|
+
--[[
|
|
5
|
+
*
|
|
6
|
+
* PHP: `Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy`.
|
|
7
|
+
*
|
|
8
|
+
* Error level based activation strategy.
|
|
9
|
+
|
|
10
|
+
]]
|
|
11
|
+
local ErrorLevelActivationStrategy
|
|
12
|
+
do
|
|
13
|
+
ErrorLevelActivationStrategy = setmetatable({}, {
|
|
14
|
+
__tostring = function()
|
|
15
|
+
return "ErrorLevelActivationStrategy"
|
|
16
|
+
end,
|
|
17
|
+
})
|
|
18
|
+
ErrorLevelActivationStrategy.__index = ErrorLevelActivationStrategy
|
|
19
|
+
function ErrorLevelActivationStrategy.new(...)
|
|
20
|
+
local self = setmetatable({}, ErrorLevelActivationStrategy)
|
|
21
|
+
return self:constructor(...) or self
|
|
22
|
+
end
|
|
23
|
+
function ErrorLevelActivationStrategy:constructor(actionLevel)
|
|
24
|
+
self.actionLevel = Logger:toMonologLevel(actionLevel)
|
|
25
|
+
end
|
|
26
|
+
function ErrorLevelActivationStrategy:isHandlerActivated(record)
|
|
27
|
+
return record.level >= self.actionLevel
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
return {
|
|
31
|
+
ErrorLevelActivationStrategy = ErrorLevelActivationStrategy,
|
|
32
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Handler } from "./Handler";
|
|
2
|
+
import { Level } from "../Level";
|
|
3
|
+
import type { ActivationStrategyInterface } from "./FingersCrossed/ActivationStrategyInterface";
|
|
4
|
+
import type { FormattableHandlerInterface } from "./FormattableHandlerInterface";
|
|
5
|
+
import type { FormatterInterface } from "../Formatter/FormatterInterface";
|
|
6
|
+
import type { HandlerInterface } from "./HandlerInterface";
|
|
7
|
+
import type { LogLevel } from "../LoggerInterface";
|
|
8
|
+
import type { LogRecord } from "../LogRecord";
|
|
9
|
+
import type { Processor } from "../Processor/ProcessorInterface";
|
|
10
|
+
import type { ProcessableHandlerInterface } from "./ProcessableHandlerInterface";
|
|
11
|
+
import type { ResettableInterface } from "../ResettableInterface";
|
|
12
|
+
/**
|
|
13
|
+
* PHP: the `Closure(LogRecord|null, HandlerInterface): HandlerInterface`
|
|
14
|
+
* factory `FingersCrossedHandler` accepts in place of a handler.
|
|
15
|
+
*/
|
|
16
|
+
export type HandlerFactory = (record: LogRecord | undefined, handler: FingersCrossedHandler) => HandlerInterface;
|
|
17
|
+
/**
|
|
18
|
+
* PHP: `Monolog\Handler\FingersCrossedHandler`.
|
|
19
|
+
*
|
|
20
|
+
* Buffers all records until a certain level is reached.
|
|
21
|
+
*
|
|
22
|
+
* The advantage of this approach is that you get no clutter in your log. Only
|
|
23
|
+
* sessions which actually trigger an error (or whatever the
|
|
24
|
+
* `activationStrategy` is) end up in the log, but they contain all records,
|
|
25
|
+
* not only those above the level threshold.
|
|
26
|
+
*
|
|
27
|
+
* There is a `passthruLevel` as well, which means that at the end, even if the
|
|
28
|
+
* handler never got activated, it will still send through log records of at
|
|
29
|
+
* least that level.
|
|
30
|
+
*
|
|
31
|
+
* Upstream gets its processor stack from `ProcessableHandlerTrait`; Luau has
|
|
32
|
+
* no traits, so the trait's members are spelled out here -- see the same note
|
|
33
|
+
* on `GroupHandler` and `AbstractProcessingHandler`.
|
|
34
|
+
*/
|
|
35
|
+
export declare class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface {
|
|
36
|
+
protected processors: Processor[];
|
|
37
|
+
/** The nested handler, or the factory that will produce it on first use. */
|
|
38
|
+
protected handler: HandlerInterface | HandlerFactory;
|
|
39
|
+
protected activationStrategy: ActivationStrategyInterface;
|
|
40
|
+
protected buffering: boolean;
|
|
41
|
+
protected bufferSize: number;
|
|
42
|
+
protected buffer: LogRecord[];
|
|
43
|
+
protected stopBuffering: boolean;
|
|
44
|
+
protected passthruLevel?: Level;
|
|
45
|
+
protected bubble: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* @param handler Handler or factory `(record | undefined, fingersCrossedHandler)`.
|
|
48
|
+
* @param activationStrategy Strategy determining when this handler takes
|
|
49
|
+
* action, or a level (name or `Level`) at which it is activated.
|
|
50
|
+
* @param bufferSize How many entries to buffer at most; beyond that the
|
|
51
|
+
* oldest items are removed from the buffer. `0` means unbounded.
|
|
52
|
+
* @param bubble Whether the handled messages can bubble up the stack.
|
|
53
|
+
* @param stopBuffering Whether to stop buffering after being triggered.
|
|
54
|
+
* @param passthruLevel Minimum level to always flush to the nested handler
|
|
55
|
+
* on close, even if the strategy never triggered.
|
|
56
|
+
*/
|
|
57
|
+
constructor(handler: HandlerInterface | HandlerFactory, activationStrategy?: Level | LogLevel | ActivationStrategyInterface, bufferSize?: number, bubble?: boolean, stopBuffering?: boolean, passthruLevel?: Level | LogLevel);
|
|
58
|
+
/** Always true -- this handler decides what to do with a record later. */
|
|
59
|
+
isHandling(_record: LogRecord): boolean;
|
|
60
|
+
/** Manually activates this handler regardless of the activation strategy. */
|
|
61
|
+
activate(): void;
|
|
62
|
+
/** Handles a record, buffering it until the strategy activates. */
|
|
63
|
+
handle(record: LogRecord): boolean;
|
|
64
|
+
/** Flushes anything left at the passthru level, then closes the nested handler. */
|
|
65
|
+
close(): void;
|
|
66
|
+
/** Resets this handler, its processors, and the nested handler. */
|
|
67
|
+
reset(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Clears the buffer without flushing any messages down to the wrapped
|
|
70
|
+
* handler. Also resets the handler to its initial buffering state.
|
|
71
|
+
*/
|
|
72
|
+
clear(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Resets the state of the handler. Stops forwarding records to the wrapped
|
|
75
|
+
* handler.
|
|
76
|
+
*/
|
|
77
|
+
private flushBuffer;
|
|
78
|
+
/**
|
|
79
|
+
* Returns the nested handler.
|
|
80
|
+
*
|
|
81
|
+
* If the handler was provided as a factory, this triggers the handler's
|
|
82
|
+
* instantiation.
|
|
83
|
+
*
|
|
84
|
+
* Upstream's `\RuntimeException` for a factory that returns a non-handler
|
|
85
|
+
* is kept -- there is no exception class hierarchy on this platform, so it
|
|
86
|
+
* is a plain `error()` carrying upstream's message.
|
|
87
|
+
*/
|
|
88
|
+
getHandler(record?: LogRecord): HandlerInterface;
|
|
89
|
+
/** Adds a processor in the stack. */
|
|
90
|
+
pushProcessor(processor: Processor): this;
|
|
91
|
+
/** Removes the processor on top of the stack and returns it. */
|
|
92
|
+
popProcessor(): Processor | undefined;
|
|
93
|
+
/** Processes a record. */
|
|
94
|
+
protected processRecord(record: LogRecord): LogRecord;
|
|
95
|
+
/** Resets any processors on this handler that support resetting. */
|
|
96
|
+
protected resetProcessors(): void;
|
|
97
|
+
/**
|
|
98
|
+
* Sets the formatter on the nested handler.
|
|
99
|
+
*
|
|
100
|
+
* Upstream's `\UnexpectedValueException` names the offending class with
|
|
101
|
+
* `get_class()`; `Utils.getClass()` is not ported (see `Utils.ts`), so the
|
|
102
|
+
* name comes from the roblox-ts class metatable's `__tostring` instead.
|
|
103
|
+
*/
|
|
104
|
+
setFormatter(formatter: FormatterInterface): this;
|
|
105
|
+
/** Gets the formatter of the nested handler. */
|
|
106
|
+
getFormatter(): FormatterInterface;
|
|
107
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local ErrorLevelActivationStrategy = TS.import(script, script.Parent, "FingersCrossed", "ErrorLevelActivationStrategy").ErrorLevelActivationStrategy
|
|
4
|
+
local Handler = TS.import(script, script.Parent, "Handler").Handler
|
|
5
|
+
local isActivationStrategy = TS.import(script, script.Parent, "FingersCrossed", "ActivationStrategyInterface").isActivationStrategy
|
|
6
|
+
local isFormattable = TS.import(script, script.Parent, "FormattableHandlerInterface").isFormattable
|
|
7
|
+
local isHandler = TS.import(script, script.Parent, "HandlerInterface").isHandler
|
|
8
|
+
local isResettable = TS.import(script, script.Parent.Parent, "ResettableInterface").isResettable
|
|
9
|
+
local _Level = TS.import(script, script.Parent.Parent, "Level")
|
|
10
|
+
local Level = _Level.Level
|
|
11
|
+
local Levels = _Level.Levels
|
|
12
|
+
local Logger = TS.import(script, script.Parent.Parent, "Logger").Logger
|
|
13
|
+
local runProcessor = TS.import(script, script.Parent.Parent, "Processor", "ProcessorInterface").runProcessor
|
|
14
|
+
--[[
|
|
15
|
+
*
|
|
16
|
+
* PHP: the `Closure(LogRecord|null, HandlerInterface): HandlerInterface`
|
|
17
|
+
* factory `FingersCrossedHandler` accepts in place of a handler.
|
|
18
|
+
|
|
19
|
+
]]
|
|
20
|
+
--[[
|
|
21
|
+
*
|
|
22
|
+
* PHP: `Monolog\Handler\FingersCrossedHandler`.
|
|
23
|
+
*
|
|
24
|
+
* Buffers all records until a certain level is reached.
|
|
25
|
+
*
|
|
26
|
+
* The advantage of this approach is that you get no clutter in your log. Only
|
|
27
|
+
* sessions which actually trigger an error (or whatever the
|
|
28
|
+
* `activationStrategy` is) end up in the log, but they contain all records,
|
|
29
|
+
* not only those above the level threshold.
|
|
30
|
+
*
|
|
31
|
+
* There is a `passthruLevel` as well, which means that at the end, even if the
|
|
32
|
+
* handler never got activated, it will still send through log records of at
|
|
33
|
+
* least that level.
|
|
34
|
+
*
|
|
35
|
+
* Upstream gets its processor stack from `ProcessableHandlerTrait`; Luau has
|
|
36
|
+
* no traits, so the trait's members are spelled out here -- see the same note
|
|
37
|
+
* on `GroupHandler` and `AbstractProcessingHandler`.
|
|
38
|
+
|
|
39
|
+
]]
|
|
40
|
+
local FingersCrossedHandler
|
|
41
|
+
do
|
|
42
|
+
local super = Handler
|
|
43
|
+
FingersCrossedHandler = setmetatable({}, {
|
|
44
|
+
__tostring = function()
|
|
45
|
+
return "FingersCrossedHandler"
|
|
46
|
+
end,
|
|
47
|
+
__index = super,
|
|
48
|
+
})
|
|
49
|
+
FingersCrossedHandler.__index = FingersCrossedHandler
|
|
50
|
+
function FingersCrossedHandler.new(...)
|
|
51
|
+
local self = setmetatable({}, FingersCrossedHandler)
|
|
52
|
+
return self:constructor(...) or self
|
|
53
|
+
end
|
|
54
|
+
function FingersCrossedHandler:constructor(handler, activationStrategy, bufferSize, bubble, stopBuffering, passthruLevel)
|
|
55
|
+
if bufferSize == nil then
|
|
56
|
+
bufferSize = 0
|
|
57
|
+
end
|
|
58
|
+
if bubble == nil then
|
|
59
|
+
bubble = true
|
|
60
|
+
end
|
|
61
|
+
if stopBuffering == nil then
|
|
62
|
+
stopBuffering = true
|
|
63
|
+
end
|
|
64
|
+
super.constructor(self)
|
|
65
|
+
self.processors = {}
|
|
66
|
+
self.buffering = true
|
|
67
|
+
self.buffer = {}
|
|
68
|
+
local strategy = activationStrategy
|
|
69
|
+
if strategy == nil then
|
|
70
|
+
strategy = ErrorLevelActivationStrategy.new(Level.Warning)
|
|
71
|
+
end
|
|
72
|
+
-- Convert a bare level activationStrategy to an object.
|
|
73
|
+
if not isActivationStrategy(strategy) then
|
|
74
|
+
strategy = ErrorLevelActivationStrategy.new(strategy)
|
|
75
|
+
end
|
|
76
|
+
self.handler = handler
|
|
77
|
+
self.activationStrategy = strategy
|
|
78
|
+
self.bufferSize = bufferSize
|
|
79
|
+
self.bubble = bubble
|
|
80
|
+
self.stopBuffering = stopBuffering
|
|
81
|
+
if passthruLevel ~= nil then
|
|
82
|
+
self.passthruLevel = Logger:toMonologLevel(passthruLevel)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
function FingersCrossedHandler:isHandling(_record)
|
|
86
|
+
return true
|
|
87
|
+
end
|
|
88
|
+
function FingersCrossedHandler:activate()
|
|
89
|
+
if self.stopBuffering then
|
|
90
|
+
self.buffering = false
|
|
91
|
+
end
|
|
92
|
+
self:getHandler(self.buffer[#self.buffer]):handleBatch(self.buffer)
|
|
93
|
+
self.buffer = {}
|
|
94
|
+
end
|
|
95
|
+
function FingersCrossedHandler:handle(record)
|
|
96
|
+
local processed = record
|
|
97
|
+
if not (#self.processors == 0) then
|
|
98
|
+
processed = self:processRecord(processed)
|
|
99
|
+
end
|
|
100
|
+
if self.buffering then
|
|
101
|
+
local _buffer = self.buffer
|
|
102
|
+
local _processed = processed
|
|
103
|
+
table.insert(_buffer, _processed)
|
|
104
|
+
if self.bufferSize > 0 and #self.buffer > self.bufferSize then
|
|
105
|
+
table.remove(self.buffer, 1)
|
|
106
|
+
end
|
|
107
|
+
if self.activationStrategy:isHandlerActivated(processed) then
|
|
108
|
+
self:activate()
|
|
109
|
+
end
|
|
110
|
+
else
|
|
111
|
+
self:getHandler(processed):handle(processed)
|
|
112
|
+
end
|
|
113
|
+
return self.bubble == false
|
|
114
|
+
end
|
|
115
|
+
function FingersCrossedHandler:close()
|
|
116
|
+
self:flushBuffer()
|
|
117
|
+
self:getHandler():close()
|
|
118
|
+
end
|
|
119
|
+
function FingersCrossedHandler:reset()
|
|
120
|
+
self:flushBuffer()
|
|
121
|
+
self:resetProcessors()
|
|
122
|
+
local handler = self:getHandler()
|
|
123
|
+
if isResettable(handler) then
|
|
124
|
+
handler:reset()
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
function FingersCrossedHandler:clear()
|
|
128
|
+
self.buffer = {}
|
|
129
|
+
self:reset()
|
|
130
|
+
end
|
|
131
|
+
function FingersCrossedHandler:flushBuffer()
|
|
132
|
+
local passthruLevel = self.passthruLevel
|
|
133
|
+
if passthruLevel ~= nil then
|
|
134
|
+
local _exp = self.buffer
|
|
135
|
+
-- ▼ ReadonlyArray.filter ▼
|
|
136
|
+
local _newValue = {}
|
|
137
|
+
local _callback = function(record)
|
|
138
|
+
return Levels:includes(passthruLevel, record.level)
|
|
139
|
+
end
|
|
140
|
+
local _length = 0
|
|
141
|
+
for _k, _v in _exp do
|
|
142
|
+
if _callback(_v, _k - 1, _exp) == true then
|
|
143
|
+
_length += 1
|
|
144
|
+
_newValue[_length] = _v
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
-- ▲ ReadonlyArray.filter ▲
|
|
148
|
+
self.buffer = _newValue
|
|
149
|
+
if not (#self.buffer == 0) then
|
|
150
|
+
self:getHandler(self.buffer[#self.buffer]):handleBatch(self.buffer)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
self.buffer = {}
|
|
154
|
+
self.buffering = true
|
|
155
|
+
end
|
|
156
|
+
function FingersCrossedHandler:getHandler(record)
|
|
157
|
+
local _handler = self.handler
|
|
158
|
+
if type(_handler) == "function" then
|
|
159
|
+
local produced = self.handler(record, self)
|
|
160
|
+
if not isHandler(produced) then
|
|
161
|
+
error("The factory Closure should return a HandlerInterface")
|
|
162
|
+
end
|
|
163
|
+
self.handler = produced
|
|
164
|
+
end
|
|
165
|
+
return self.handler
|
|
166
|
+
end
|
|
167
|
+
function FingersCrossedHandler:pushProcessor(processor)
|
|
168
|
+
local _processors = self.processors
|
|
169
|
+
local _processor = processor
|
|
170
|
+
table.insert(_processors, 1, _processor)
|
|
171
|
+
return self
|
|
172
|
+
end
|
|
173
|
+
function FingersCrossedHandler:popProcessor()
|
|
174
|
+
return table.remove(self.processors, 1)
|
|
175
|
+
end
|
|
176
|
+
function FingersCrossedHandler:processRecord(record)
|
|
177
|
+
local processed = record
|
|
178
|
+
for _, processor in self.processors do
|
|
179
|
+
processed = runProcessor(processor, processed)
|
|
180
|
+
end
|
|
181
|
+
return processed
|
|
182
|
+
end
|
|
183
|
+
function FingersCrossedHandler:resetProcessors()
|
|
184
|
+
for _, processor in self.processors do
|
|
185
|
+
if isResettable(processor) then
|
|
186
|
+
processor:reset()
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
function FingersCrossedHandler:setFormatter(formatter)
|
|
191
|
+
local handler = self:getHandler()
|
|
192
|
+
if isFormattable(handler) then
|
|
193
|
+
handler:setFormatter(formatter)
|
|
194
|
+
return self
|
|
195
|
+
end
|
|
196
|
+
return error(`The nested handler of type {tostring((getmetatable(handler)))} does not support formatters.`)
|
|
197
|
+
end
|
|
198
|
+
function FingersCrossedHandler:getFormatter()
|
|
199
|
+
local handler = self:getHandler()
|
|
200
|
+
if isFormattable(handler) then
|
|
201
|
+
return handler:getFormatter()
|
|
202
|
+
end
|
|
203
|
+
return error(`The nested handler of type {tostring((getmetatable(handler)))} does not support formatters.`)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
return {
|
|
207
|
+
FingersCrossedHandler = FingersCrossedHandler,
|
|
208
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { FormatterInterface } from "../Formatter/FormatterInterface";
|
|
2
|
+
import type { HandlerInterface } from "./HandlerInterface";
|
|
3
|
+
/**
|
|
4
|
+
* PHP: `Monolog\Handler\FormattableHandlerInterface`.
|
|
5
|
+
*
|
|
6
|
+
* Upstream pairs this with `FormattableHandlerTrait`; Luau has no traits, so
|
|
7
|
+
* implementers carry the `$formatter` field and `getDefaultFormatter()`
|
|
8
|
+
* themselves -- see `AbstractProcessingHandler`.
|
|
9
|
+
*/
|
|
10
|
+
export interface FormattableHandlerInterface extends HandlerInterface {
|
|
11
|
+
/** Sets the formatter. */
|
|
12
|
+
setFormatter(formatter: FormatterInterface): this;
|
|
13
|
+
/** Gets the formatter. */
|
|
14
|
+
getFormatter(): FormatterInterface;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* True when the given value implements `FormattableHandlerInterface`.
|
|
18
|
+
*
|
|
19
|
+
* PHP checks this with `instanceof`; there is no `instanceof` for an
|
|
20
|
+
* interface here, so this checks structurally for the two callable members,
|
|
21
|
+
* exactly as `isResettable()` (`ResettableInterface.ts`) already does.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isFormattable(value: unknown): value is FormattableHandlerInterface;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
--[[
|
|
3
|
+
*
|
|
4
|
+
* PHP: `Monolog\Handler\FormattableHandlerInterface`.
|
|
5
|
+
*
|
|
6
|
+
* Upstream pairs this with `FormattableHandlerTrait`; Luau has no traits, so
|
|
7
|
+
* implementers carry the `$formatter` field and `getDefaultFormatter()`
|
|
8
|
+
* themselves -- see `AbstractProcessingHandler`.
|
|
9
|
+
|
|
10
|
+
]]
|
|
11
|
+
--[[
|
|
12
|
+
*
|
|
13
|
+
* True when the given value implements `FormattableHandlerInterface`.
|
|
14
|
+
*
|
|
15
|
+
* PHP checks this with `instanceof`; there is no `instanceof` for an
|
|
16
|
+
* interface here, so this checks structurally for the two callable members,
|
|
17
|
+
* exactly as `isResettable()` (`ResettableInterface.ts`) already does.
|
|
18
|
+
|
|
19
|
+
]]
|
|
20
|
+
local function isFormattable(value)
|
|
21
|
+
local _value = value
|
|
22
|
+
if not (type(_value) == "table") then
|
|
23
|
+
return false
|
|
24
|
+
end
|
|
25
|
+
local candidate = value
|
|
26
|
+
local _setFormatter = candidate.setFormatter
|
|
27
|
+
local _condition = type(_setFormatter) == "function"
|
|
28
|
+
if _condition then
|
|
29
|
+
local _getFormatter = candidate.getFormatter
|
|
30
|
+
_condition = type(_getFormatter) == "function"
|
|
31
|
+
end
|
|
32
|
+
return _condition
|
|
33
|
+
end
|
|
34
|
+
return {
|
|
35
|
+
isFormattable = isFormattable,
|
|
36
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Handler } from "./Handler";
|
|
2
|
+
import type { FormatterInterface } from "../Formatter/FormatterInterface";
|
|
3
|
+
import type { HandlerInterface } from "./HandlerInterface";
|
|
4
|
+
import type { LogRecord } from "../LogRecord";
|
|
5
|
+
import type { Processor } from "../Processor/ProcessorInterface";
|
|
6
|
+
import type { ProcessableHandlerInterface } from "./ProcessableHandlerInterface";
|
|
7
|
+
import type { ResettableInterface } from "../ResettableInterface";
|
|
8
|
+
/**
|
|
9
|
+
* PHP: `Monolog\Handler\GroupHandler`. Forwards records to multiple handlers.
|
|
10
|
+
*
|
|
11
|
+
* Upstream gets its processor stack from `ProcessableHandlerTrait`; Luau has
|
|
12
|
+
* no traits, so the trait's members (`processors`, `processRecord()`,
|
|
13
|
+
* `resetProcessors()`, `pushProcessor()`, `popProcessor()`) are spelled out
|
|
14
|
+
* here, the same way `AbstractProcessingHandler` already folds it in.
|
|
15
|
+
*/
|
|
16
|
+
export declare class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface {
|
|
17
|
+
protected processors: Processor[];
|
|
18
|
+
protected handlers: Array<HandlerInterface>;
|
|
19
|
+
protected bubble: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Upstream's `\InvalidArgumentException` for a non-handler in the array is
|
|
22
|
+
* kept rather than dropped: it is `GroupHandler`'s own explicit check, not
|
|
23
|
+
* PHP's parameter type-check, and this port is consumed from plain Luau
|
|
24
|
+
* too, where TypeScript's `Array<HandlerInterface>` proves nothing. There
|
|
25
|
+
* is no exception class hierarchy on this platform, so it is a plain
|
|
26
|
+
* `error()` carrying upstream's message.
|
|
27
|
+
*/
|
|
28
|
+
constructor(handlers: Array<HandlerInterface>, bubble?: boolean);
|
|
29
|
+
/** Checks whether any of the grouped handlers will handle the record. */
|
|
30
|
+
isHandling(record: LogRecord): boolean;
|
|
31
|
+
/** Handles a record, forwarding a clone of it to every grouped handler. */
|
|
32
|
+
handle(record: LogRecord): boolean;
|
|
33
|
+
/** Handles a set of records at once. */
|
|
34
|
+
handleBatch(records: Array<LogRecord>): void;
|
|
35
|
+
/** Adds a processor in the stack. */
|
|
36
|
+
pushProcessor(processor: Processor): this;
|
|
37
|
+
/** Removes the processor on top of the stack and returns it. */
|
|
38
|
+
popProcessor(): Processor | undefined;
|
|
39
|
+
/** Processes a record. */
|
|
40
|
+
protected processRecord(record: LogRecord): LogRecord;
|
|
41
|
+
/** Resets any processors on this handler that support resetting. */
|
|
42
|
+
protected resetProcessors(): void;
|
|
43
|
+
/** Resets this handler's processors and every grouped handler. */
|
|
44
|
+
reset(): void;
|
|
45
|
+
/** Closes every grouped handler. */
|
|
46
|
+
close(): void;
|
|
47
|
+
/** Sets the formatter on every grouped handler that supports one. */
|
|
48
|
+
setFormatter(formatter: FormatterInterface): this;
|
|
49
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local Handler = TS.import(script, script.Parent, "Handler").Handler
|
|
4
|
+
local isFormattable = TS.import(script, script.Parent, "FormattableHandlerInterface").isFormattable
|
|
5
|
+
local isHandler = TS.import(script, script.Parent, "HandlerInterface").isHandler
|
|
6
|
+
local isResettable = TS.import(script, script.Parent.Parent, "ResettableInterface").isResettable
|
|
7
|
+
local runProcessor = TS.import(script, script.Parent.Parent, "Processor", "ProcessorInterface").runProcessor
|
|
8
|
+
--[[
|
|
9
|
+
*
|
|
10
|
+
* PHP: `Monolog\Handler\GroupHandler`. Forwards records to multiple handlers.
|
|
11
|
+
*
|
|
12
|
+
* Upstream gets its processor stack from `ProcessableHandlerTrait`; Luau has
|
|
13
|
+
* no traits, so the trait's members (`processors`, `processRecord()`,
|
|
14
|
+
* `resetProcessors()`, `pushProcessor()`, `popProcessor()`) are spelled out
|
|
15
|
+
* here, the same way `AbstractProcessingHandler` already folds it in.
|
|
16
|
+
|
|
17
|
+
]]
|
|
18
|
+
local GroupHandler
|
|
19
|
+
do
|
|
20
|
+
local super = Handler
|
|
21
|
+
GroupHandler = setmetatable({}, {
|
|
22
|
+
__tostring = function()
|
|
23
|
+
return "GroupHandler"
|
|
24
|
+
end,
|
|
25
|
+
__index = super,
|
|
26
|
+
})
|
|
27
|
+
GroupHandler.__index = GroupHandler
|
|
28
|
+
function GroupHandler.new(...)
|
|
29
|
+
local self = setmetatable({}, GroupHandler)
|
|
30
|
+
return self:constructor(...) or self
|
|
31
|
+
end
|
|
32
|
+
function GroupHandler:constructor(handlers, bubble)
|
|
33
|
+
if bubble == nil then
|
|
34
|
+
bubble = true
|
|
35
|
+
end
|
|
36
|
+
super.constructor(self)
|
|
37
|
+
self.processors = {}
|
|
38
|
+
for _, handler in handlers do
|
|
39
|
+
if not isHandler(handler) then
|
|
40
|
+
error("The first argument of the GroupHandler must be an array of HandlerInterface instances.")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
self.handlers = handlers
|
|
44
|
+
self.bubble = bubble
|
|
45
|
+
end
|
|
46
|
+
function GroupHandler:isHandling(record)
|
|
47
|
+
for _, handler in self.handlers do
|
|
48
|
+
if handler:isHandling(record) then
|
|
49
|
+
return true
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
return false
|
|
53
|
+
end
|
|
54
|
+
function GroupHandler:handle(record)
|
|
55
|
+
local processed = record
|
|
56
|
+
if not (#self.processors == 0) then
|
|
57
|
+
processed = self:processRecord(processed)
|
|
58
|
+
end
|
|
59
|
+
for _, handler in self.handlers do
|
|
60
|
+
handler:handle(processed:clone())
|
|
61
|
+
end
|
|
62
|
+
return self.bubble == false
|
|
63
|
+
end
|
|
64
|
+
function GroupHandler:handleBatch(records)
|
|
65
|
+
local batch = records
|
|
66
|
+
if not (#self.processors == 0) then
|
|
67
|
+
-- ▼ ReadonlyArray.map ▼
|
|
68
|
+
local _newValue = table.create(#records)
|
|
69
|
+
local _callback = function(record)
|
|
70
|
+
return self:processRecord(record)
|
|
71
|
+
end
|
|
72
|
+
for _k, _v in records do
|
|
73
|
+
_newValue[_k] = _callback(_v, _k - 1, records)
|
|
74
|
+
end
|
|
75
|
+
-- ▲ ReadonlyArray.map ▲
|
|
76
|
+
batch = _newValue
|
|
77
|
+
end
|
|
78
|
+
for _, handler in self.handlers do
|
|
79
|
+
-- ▼ ReadonlyArray.map ▼
|
|
80
|
+
local _newValue = table.create(#batch)
|
|
81
|
+
local _callback = function(record)
|
|
82
|
+
return record:clone()
|
|
83
|
+
end
|
|
84
|
+
for _k, _v in batch do
|
|
85
|
+
_newValue[_k] = _callback(_v, _k - 1, batch)
|
|
86
|
+
end
|
|
87
|
+
-- ▲ ReadonlyArray.map ▲
|
|
88
|
+
handler:handleBatch(_newValue)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
function GroupHandler:pushProcessor(processor)
|
|
92
|
+
local _processors = self.processors
|
|
93
|
+
local _processor = processor
|
|
94
|
+
table.insert(_processors, 1, _processor)
|
|
95
|
+
return self
|
|
96
|
+
end
|
|
97
|
+
function GroupHandler:popProcessor()
|
|
98
|
+
return table.remove(self.processors, 1)
|
|
99
|
+
end
|
|
100
|
+
function GroupHandler:processRecord(record)
|
|
101
|
+
local processed = record
|
|
102
|
+
for _, processor in self.processors do
|
|
103
|
+
processed = runProcessor(processor, processed)
|
|
104
|
+
end
|
|
105
|
+
return processed
|
|
106
|
+
end
|
|
107
|
+
function GroupHandler:resetProcessors()
|
|
108
|
+
for _, processor in self.processors do
|
|
109
|
+
if isResettable(processor) then
|
|
110
|
+
processor:reset()
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
function GroupHandler:reset()
|
|
115
|
+
self:resetProcessors()
|
|
116
|
+
for _, handler in self.handlers do
|
|
117
|
+
if isResettable(handler) then
|
|
118
|
+
handler:reset()
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
function GroupHandler:close()
|
|
123
|
+
super.close(self)
|
|
124
|
+
for _, handler in self.handlers do
|
|
125
|
+
handler:close()
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
function GroupHandler:setFormatter(formatter)
|
|
129
|
+
for _, handler in self.handlers do
|
|
130
|
+
if isFormattable(handler) then
|
|
131
|
+
handler:setFormatter(formatter)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
return self
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
return {
|
|
138
|
+
GroupHandler = GroupHandler,
|
|
139
|
+
}
|
|
@@ -10,3 +10,13 @@ export interface HandlerInterface {
|
|
|
10
10
|
/** Closes the handler. */
|
|
11
11
|
close(): void;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* True when the given value implements `HandlerInterface`.
|
|
15
|
+
*
|
|
16
|
+
* PHP checks this with `instanceof` -- `GroupHandler`'s constructor and
|
|
17
|
+
* `FingersCrossedHandler::getHandler()` both reject a non-handler at runtime.
|
|
18
|
+
* There is no `instanceof` for an interface here, so this checks structurally
|
|
19
|
+
* for the four callable members, as `isResettable()`
|
|
20
|
+
* (`ResettableInterface.ts`) already does for its own interface.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isHandler(value: unknown): value is HandlerInterface;
|
|
@@ -1,3 +1,38 @@
|
|
|
1
1
|
-- Compiled with roblox-ts v3.0.0
|
|
2
2
|
--* PHP: `Monolog\Handler\HandlerInterface`.
|
|
3
|
-
|
|
3
|
+
--[[
|
|
4
|
+
*
|
|
5
|
+
* True when the given value implements `HandlerInterface`.
|
|
6
|
+
*
|
|
7
|
+
* PHP checks this with `instanceof` -- `GroupHandler`'s constructor and
|
|
8
|
+
* `FingersCrossedHandler::getHandler()` both reject a non-handler at runtime.
|
|
9
|
+
* There is no `instanceof` for an interface here, so this checks structurally
|
|
10
|
+
* for the four callable members, as `isResettable()`
|
|
11
|
+
* (`ResettableInterface.ts`) already does for its own interface.
|
|
12
|
+
|
|
13
|
+
]]
|
|
14
|
+
local function isHandler(value)
|
|
15
|
+
local _value = value
|
|
16
|
+
if not (type(_value) == "table") then
|
|
17
|
+
return false
|
|
18
|
+
end
|
|
19
|
+
local candidate = value
|
|
20
|
+
local _isHandling = candidate.isHandling
|
|
21
|
+
local _condition = type(_isHandling) == "function"
|
|
22
|
+
if _condition then
|
|
23
|
+
local _handle = candidate.handle
|
|
24
|
+
_condition = type(_handle) == "function"
|
|
25
|
+
if _condition then
|
|
26
|
+
local _handleBatch = candidate.handleBatch
|
|
27
|
+
_condition = type(_handleBatch) == "function"
|
|
28
|
+
if _condition then
|
|
29
|
+
local _close = candidate.close
|
|
30
|
+
_condition = type(_close) == "function"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
return _condition
|
|
35
|
+
end
|
|
36
|
+
return {
|
|
37
|
+
isHandler = isHandler,
|
|
38
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { HandlerInterface } from "./HandlerInterface";
|
|
2
|
+
import type { Processor } from "../Processor/ProcessorInterface";
|
|
3
|
+
/**
|
|
4
|
+
* PHP: `Monolog\Handler\ProcessableHandlerInterface`.
|
|
5
|
+
*
|
|
6
|
+
* Upstream pairs this with `ProcessableHandlerTrait`, which carries the
|
|
7
|
+
* `$processors` stack and the `processRecord()`/`resetProcessors()` helpers.
|
|
8
|
+
* Luau has no traits, so every implementer in this port spells those members
|
|
9
|
+
* out itself -- `AbstractProcessingHandler`, `GroupHandler` and
|
|
10
|
+
* `FingersCrossedHandler` each carry their own copy, the same way
|
|
11
|
+
* `AbstractProcessingHandler` already folded the trait in before this
|
|
12
|
+
* interface existed.
|
|
13
|
+
*/
|
|
14
|
+
export interface ProcessableHandlerInterface extends HandlerInterface {
|
|
15
|
+
/** Adds a processor in the stack. */
|
|
16
|
+
pushProcessor(processor: Processor): this;
|
|
17
|
+
/**
|
|
18
|
+
* Removes the processor on top of the stack and returns it.
|
|
19
|
+
*
|
|
20
|
+
* Upstream throws `\LogicException` when the stack is empty; every
|
|
21
|
+
* pop-style method in this port (`Logger.popHandler()`,
|
|
22
|
+
* `AbstractProcessingHandler.popProcessor()`) returns `undefined` instead,
|
|
23
|
+
* and this signature follows them rather than upstream.
|
|
24
|
+
*/
|
|
25
|
+
popProcessor(): Processor | undefined;
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
--[[
|
|
3
|
+
*
|
|
4
|
+
* PHP: `Monolog\Handler\ProcessableHandlerInterface`.
|
|
5
|
+
*
|
|
6
|
+
* Upstream pairs this with `ProcessableHandlerTrait`, which carries the
|
|
7
|
+
* `$processors` stack and the `processRecord()`/`resetProcessors()` helpers.
|
|
8
|
+
* Luau has no traits, so every implementer in this port spells those members
|
|
9
|
+
* out itself -- `AbstractProcessingHandler`, `GroupHandler` and
|
|
10
|
+
* `FingersCrossedHandler` each carry their own copy, the same way
|
|
11
|
+
* `AbstractProcessingHandler` already folded the trait in before this
|
|
12
|
+
* interface existed.
|
|
13
|
+
|
|
14
|
+
]]
|
|
15
|
+
return nil
|
|
@@ -1,55 +1,28 @@
|
|
|
1
1
|
import { AbstractProcessingHandler } from "./AbstractProcessingHandler";
|
|
2
2
|
import { Level } from "../Level";
|
|
3
|
-
import type { FormatterInterface } from "../Formatter/FormatterInterface";
|
|
4
3
|
import type { LogRecord } from "../LogRecord";
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
5
|
+
* Writes formatted records to the Roblox output console.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
* (`
|
|
18
|
-
* `
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
7
|
+
* No upstream ancestor: this is an original class for this platform.
|
|
8
|
+
*
|
|
9
|
+
* By shape it is this platform's `Monolog\Handler\StreamHandler`: hand
|
|
10
|
+
* `record.formatted` to the sink and nothing more. Roblox's console is that
|
|
11
|
+
* sink, and none of `StreamHandler`'s own API survives the swap -- there is
|
|
12
|
+
* no stream to open, no url/resource to accept, no file permissions, no
|
|
13
|
+
* `close()`, no buffering to flush. What is left is the level/bubble pair
|
|
14
|
+
* every handler has, plus the one choice the console does offer: records
|
|
15
|
+
* below `Level.Warning` go out through `print` (`MessageOutput`), the rest
|
|
16
|
+
* through `warn` (`MessageWarning`), so Studio colors them accordingly and
|
|
17
|
+
* `LogService` reports the right `Enum.MessageType`. `error` is deliberately
|
|
18
|
+
* not used for higher levels -- on Luau it throws and unwinds the caller
|
|
19
|
+
* instead of merely writing a line.
|
|
20
|
+
*
|
|
21
|
+
* The formatter is respected, unlike the PHP Console port that preceded this
|
|
22
|
+
* one: it is what turns the record into the printed text. The default is
|
|
23
|
+
* `AbstractProcessingHandler`'s own `LineFormatter`.
|
|
24
24
|
*/
|
|
25
|
-
export interface Options {
|
|
26
|
-
/** Whether this handler is active at all. */
|
|
27
|
-
enabled: boolean;
|
|
28
|
-
/** Context keys checked, in order, for a debug "tag" to prefix the message with. */
|
|
29
|
-
debugTagsKeysInContext: Array<string>;
|
|
30
|
-
}
|
|
31
25
|
export declare class RobloxConsoleHandler extends AbstractProcessingHandler {
|
|
32
|
-
|
|
33
|
-
constructor(options?: Partial<Options>, level?: Level, bubble?: boolean);
|
|
34
|
-
/** PHP: `PHPConsoleHandler::getOptions()`. */
|
|
35
|
-
getOptions(): Options;
|
|
36
|
-
/** PHP: `PHPConsoleHandler::handle()`. */
|
|
37
|
-
handle(record: LogRecord): boolean;
|
|
38
|
-
/** PHP: `PHPConsoleHandler::write()`. */
|
|
26
|
+
constructor(level?: Level, bubble?: boolean);
|
|
39
27
|
protected write(record: LogRecord): void;
|
|
40
|
-
/** PHP: `PHPConsoleHandler::handleDebugRecord()`. */
|
|
41
|
-
private writeDebugRecord;
|
|
42
|
-
/** PHP: `PHPConsoleHandler::handleExceptionRecord()`. */
|
|
43
|
-
private writeExceptionRecord;
|
|
44
|
-
/** PHP: `PHPConsoleHandler::handleErrorRecord()`. */
|
|
45
|
-
private writeErrorRecord;
|
|
46
|
-
/**
|
|
47
|
-
* PHP: `PHPConsoleHandler::getRecordTags()`. Upstream also checks
|
|
48
|
-
* `$filteredContext[0]`, PHP's implicit first-array-index -- Luau/TS have
|
|
49
|
-
* no positional index on a `RecordBag`, so only the named
|
|
50
|
-
* `debugTagsKeysInContext` keys are checked.
|
|
51
|
-
*/
|
|
52
|
-
private getRecordTags;
|
|
53
|
-
/** PHP: `PHPConsoleHandler::getDefaultFormatter()`. */
|
|
54
|
-
protected getDefaultFormatter(): FormatterInterface;
|
|
55
28
|
}
|
|
@@ -4,36 +4,29 @@ local AbstractProcessingHandler = TS.import(script, script.Parent, "AbstractProc
|
|
|
4
4
|
local _Level = TS.import(script, script.Parent.Parent, "Level")
|
|
5
5
|
local Level = _Level.Level
|
|
6
6
|
local Levels = _Level.Levels
|
|
7
|
-
local LineFormatter = TS.import(script, script.Parent.Parent, "Formatter", "LineFormatter").LineFormatter
|
|
8
|
-
local Utils = TS.import(script, script.Parent.Parent, "Utils").Utils
|
|
9
7
|
--[[
|
|
10
8
|
*
|
|
11
|
-
*
|
|
9
|
+
* Writes formatted records to the Roblox output console.
|
|
12
10
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* configure with them.
|
|
11
|
+
* No upstream ancestor: this is an original class for this platform.
|
|
12
|
+
*
|
|
13
|
+
* By shape it is this platform's `Monolog\Handler\StreamHandler`: hand
|
|
14
|
+
* `record.formatted` to the sink and nothing more. Roblox's console is that
|
|
15
|
+
* sink, and none of `StreamHandler`'s own API survives the swap -- there is
|
|
16
|
+
* no stream to open, no url/resource to accept, no file permissions, no
|
|
17
|
+
* `close()`, no buffering to flush. What is left is the level/bubble pair
|
|
18
|
+
* every handler has, plus the one choice the console does offer: records
|
|
19
|
+
* below `Level.Warning` go out through `print` (`MessageOutput`), the rest
|
|
20
|
+
* through `warn` (`MessageWarning`), so Studio colors them accordingly and
|
|
21
|
+
* `LogService` reports the right `Enum.MessageType`. `error` is deliberately
|
|
22
|
+
* not used for higher levels -- on Luau it throws and unwinds the caller
|
|
23
|
+
* instead of merely writing a line.
|
|
24
|
+
*
|
|
25
|
+
* The formatter is respected, unlike the PHP Console port that preceded this
|
|
26
|
+
* one: it is what turns the record into the printed text. The default is
|
|
27
|
+
* `AbstractProcessingHandler`'s own `LineFormatter`.
|
|
31
28
|
|
|
32
29
|
]]
|
|
33
|
-
local DEFAULT_OPTIONS = {
|
|
34
|
-
enabled = true,
|
|
35
|
-
debugTagsKeysInContext = { "tag" },
|
|
36
|
-
}
|
|
37
30
|
local RobloxConsoleHandler
|
|
38
31
|
do
|
|
39
32
|
local super = AbstractProcessingHandler
|
|
@@ -48,10 +41,7 @@ do
|
|
|
48
41
|
local self = setmetatable({}, RobloxConsoleHandler)
|
|
49
42
|
return self:constructor(...) or self
|
|
50
43
|
end
|
|
51
|
-
function RobloxConsoleHandler:constructor(
|
|
52
|
-
if options == nil then
|
|
53
|
-
options = {}
|
|
54
|
-
end
|
|
44
|
+
function RobloxConsoleHandler:constructor(level, bubble)
|
|
55
45
|
if level == nil then
|
|
56
46
|
level = Level.Debug
|
|
57
47
|
end
|
|
@@ -59,81 +49,20 @@ do
|
|
|
59
49
|
bubble = true
|
|
60
50
|
end
|
|
61
51
|
super.constructor(self, level, bubble)
|
|
62
|
-
local _object = {}
|
|
63
|
-
local _left = "enabled"
|
|
64
|
-
local _condition = options.enabled
|
|
65
|
-
if _condition == nil then
|
|
66
|
-
_condition = DEFAULT_OPTIONS.enabled
|
|
67
|
-
end
|
|
68
|
-
_object[_left] = _condition
|
|
69
|
-
_object.debugTagsKeysInContext = options.debugTagsKeysInContext or DEFAULT_OPTIONS.debugTagsKeysInContext
|
|
70
|
-
self.options = _object
|
|
71
|
-
end
|
|
72
|
-
function RobloxConsoleHandler:getOptions()
|
|
73
|
-
return self.options
|
|
74
|
-
end
|
|
75
|
-
function RobloxConsoleHandler:handle(record)
|
|
76
|
-
if self.options.enabled then
|
|
77
|
-
return super.handle(self, record)
|
|
78
|
-
end
|
|
79
|
-
-- Upstream also requires `connector.isActiveClient()` here; there is no
|
|
80
|
-
-- connector to ask, so `enabled` is the only gate left.
|
|
81
|
-
return not self.bubble
|
|
82
52
|
end
|
|
83
53
|
function RobloxConsoleHandler:write(record)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
self:writeErrorRecord(record)
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
function RobloxConsoleHandler:writeDebugRecord(record)
|
|
93
|
-
local _binding = self:getRecordTags(record)
|
|
94
|
-
local tags = _binding[1]
|
|
95
|
-
local filteredContext = _binding[2]
|
|
96
|
-
local message = record.message
|
|
97
|
-
if (next(filteredContext)) ~= nil then
|
|
98
|
-
message = `{message} {Utils:jsonEncode(filteredContext)}`
|
|
54
|
+
-- `handle()` always fills `formatted` in before calling `write()`;
|
|
55
|
+
-- the fallback is for a direct call that did not go through it.
|
|
56
|
+
local _condition = record.formatted
|
|
57
|
+
if _condition == nil then
|
|
58
|
+
_condition = record.message
|
|
99
59
|
end
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
warn(tostring(record.context.exception))
|
|
104
|
-
end
|
|
105
|
-
function RobloxConsoleHandler:writeErrorRecord(record)
|
|
106
|
-
local context = record.context
|
|
107
|
-
local message = if context.message ~= nil then tostring(context.message) else record.message
|
|
108
|
-
if context.file == nil and context.line == nil then
|
|
109
|
-
warn(message)
|
|
60
|
+
local message = _condition
|
|
61
|
+
if Levels:isLowerThan(record.level, Level.Warning) then
|
|
62
|
+
print(message)
|
|
110
63
|
return nil
|
|
111
64
|
end
|
|
112
|
-
|
|
113
|
-
local line = if context.line ~= nil then tostring(context.line) else "?"
|
|
114
|
-
warn(`{message} ({file}:{line})`)
|
|
115
|
-
end
|
|
116
|
-
function RobloxConsoleHandler:getRecordTags(record)
|
|
117
|
-
local filteredContext = {}
|
|
118
|
-
for key, value in pairs(record.context) do
|
|
119
|
-
filteredContext[key] = value
|
|
120
|
-
end
|
|
121
|
-
local tag
|
|
122
|
-
for _, key in self.options.debugTagsKeysInContext do
|
|
123
|
-
if filteredContext[key] ~= nil then
|
|
124
|
-
tag = tostring(filteredContext[key])
|
|
125
|
-
filteredContext[key] = nil
|
|
126
|
-
break
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
local _condition = tag
|
|
130
|
-
if _condition == nil then
|
|
131
|
-
_condition = Levels:toPsrLogLevel(record.level)
|
|
132
|
-
end
|
|
133
|
-
return { _condition, filteredContext }
|
|
134
|
-
end
|
|
135
|
-
function RobloxConsoleHandler:getDefaultFormatter()
|
|
136
|
-
return LineFormatter.new("%message%")
|
|
65
|
+
warn(message)
|
|
137
66
|
end
|
|
138
67
|
end
|
|
139
68
|
return {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { GroupHandler } from "./GroupHandler";
|
|
2
|
+
import type { LogRecord } from "../LogRecord";
|
|
3
|
+
/**
|
|
4
|
+
* PHP: `Monolog\Handler\WhatFailureGroupHandler`.
|
|
5
|
+
*
|
|
6
|
+
* Forwards records to multiple handlers, suppressing failures of each handler
|
|
7
|
+
* and continuing through to give every handler a chance to succeed.
|
|
8
|
+
*
|
|
9
|
+
* Upstream wraps each call in `try { ... } catch (Throwable) {}`. Luau's
|
|
10
|
+
* error mechanism is `error()`/`pcall()`, not exceptions, so each call goes
|
|
11
|
+
* through `pcall()` and its failure result is discarded -- same "what
|
|
12
|
+
* failure?" semantics, same swallow-everything breadth.
|
|
13
|
+
*/
|
|
14
|
+
export declare class WhatFailureGroupHandler extends GroupHandler {
|
|
15
|
+
/** Handles a record, letting every grouped handler fail independently. */
|
|
16
|
+
handle(record: LogRecord): boolean;
|
|
17
|
+
/** Handles a set of records at once, ignoring any handler that fails. */
|
|
18
|
+
handleBatch(records: Array<LogRecord>): void;
|
|
19
|
+
/**
|
|
20
|
+
* Closes every grouped handler, ignoring any that fails.
|
|
21
|
+
*
|
|
22
|
+
* Upstream deliberately does not call `parent::close()` here (unlike
|
|
23
|
+
* `GroupHandler::close()`), so neither does this.
|
|
24
|
+
*/
|
|
25
|
+
close(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local GroupHandler = TS.import(script, script.Parent, "GroupHandler").GroupHandler
|
|
4
|
+
--[[
|
|
5
|
+
*
|
|
6
|
+
* PHP: `Monolog\Handler\WhatFailureGroupHandler`.
|
|
7
|
+
*
|
|
8
|
+
* Forwards records to multiple handlers, suppressing failures of each handler
|
|
9
|
+
* and continuing through to give every handler a chance to succeed.
|
|
10
|
+
*
|
|
11
|
+
* Upstream wraps each call in `try { ... } catch (Throwable) {}`. Luau's
|
|
12
|
+
* error mechanism is `error()`/`pcall()`, not exceptions, so each call goes
|
|
13
|
+
* through `pcall()` and its failure result is discarded -- same "what
|
|
14
|
+
* failure?" semantics, same swallow-everything breadth.
|
|
15
|
+
|
|
16
|
+
]]
|
|
17
|
+
local WhatFailureGroupHandler
|
|
18
|
+
do
|
|
19
|
+
local super = GroupHandler
|
|
20
|
+
WhatFailureGroupHandler = setmetatable({}, {
|
|
21
|
+
__tostring = function()
|
|
22
|
+
return "WhatFailureGroupHandler"
|
|
23
|
+
end,
|
|
24
|
+
__index = super,
|
|
25
|
+
})
|
|
26
|
+
WhatFailureGroupHandler.__index = WhatFailureGroupHandler
|
|
27
|
+
function WhatFailureGroupHandler.new(...)
|
|
28
|
+
local self = setmetatable({}, WhatFailureGroupHandler)
|
|
29
|
+
return self:constructor(...) or self
|
|
30
|
+
end
|
|
31
|
+
function WhatFailureGroupHandler:constructor(...)
|
|
32
|
+
super.constructor(self, ...)
|
|
33
|
+
end
|
|
34
|
+
function WhatFailureGroupHandler:handle(record)
|
|
35
|
+
local processed = record
|
|
36
|
+
if not (#self.processors == 0) then
|
|
37
|
+
processed = self:processRecord(processed)
|
|
38
|
+
end
|
|
39
|
+
for _, handler in self.handlers do
|
|
40
|
+
pcall(function()
|
|
41
|
+
return handler:handle(processed:clone())
|
|
42
|
+
end)
|
|
43
|
+
end
|
|
44
|
+
return self.bubble == false
|
|
45
|
+
end
|
|
46
|
+
function WhatFailureGroupHandler:handleBatch(records)
|
|
47
|
+
local batch = records
|
|
48
|
+
if not (#self.processors == 0) then
|
|
49
|
+
-- ▼ ReadonlyArray.map ▼
|
|
50
|
+
local _newValue = table.create(#records)
|
|
51
|
+
local _callback = function(record)
|
|
52
|
+
return self:processRecord(record)
|
|
53
|
+
end
|
|
54
|
+
for _k, _v in records do
|
|
55
|
+
_newValue[_k] = _callback(_v, _k - 1, records)
|
|
56
|
+
end
|
|
57
|
+
-- ▲ ReadonlyArray.map ▲
|
|
58
|
+
batch = _newValue
|
|
59
|
+
end
|
|
60
|
+
for _, handler in self.handlers do
|
|
61
|
+
pcall(function()
|
|
62
|
+
-- ▼ ReadonlyArray.map ▼
|
|
63
|
+
local _newValue = table.create(#batch)
|
|
64
|
+
local _callback = function(record)
|
|
65
|
+
return record:clone()
|
|
66
|
+
end
|
|
67
|
+
for _k, _v in batch do
|
|
68
|
+
_newValue[_k] = _callback(_v, _k - 1, batch)
|
|
69
|
+
end
|
|
70
|
+
-- ▲ ReadonlyArray.map ▲
|
|
71
|
+
return handler:handleBatch(_newValue)
|
|
72
|
+
end)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
function WhatFailureGroupHandler:close()
|
|
76
|
+
for _, handler in self.handlers do
|
|
77
|
+
pcall(function()
|
|
78
|
+
return handler:close()
|
|
79
|
+
end)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
return {
|
|
84
|
+
WhatFailureGroupHandler = WhatFailureGroupHandler,
|
|
85
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://www.schemastore.org/package.json",
|
|
3
3
|
"name": "@larablox/monolog",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "The Larablox port of Monolog.",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"keywords": [
|
|
7
|
+
"keywords": [
|
|
8
|
+
"monolog",
|
|
9
|
+
"logging",
|
|
10
|
+
"larablox",
|
|
11
|
+
"roblox-ts"
|
|
12
|
+
],
|
|
8
13
|
"homepage": "https://github.com/larablox/monolog",
|
|
9
14
|
"repository": {
|
|
10
15
|
"type": "git",
|
|
@@ -16,7 +21,10 @@
|
|
|
16
21
|
"publishConfig": {
|
|
17
22
|
"access": "public"
|
|
18
23
|
},
|
|
19
|
-
"files": [
|
|
24
|
+
"files": [
|
|
25
|
+
"out/Monolog",
|
|
26
|
+
"out/index.d.ts"
|
|
27
|
+
],
|
|
20
28
|
"types": "out/index.d.ts",
|
|
21
29
|
"type": "module",
|
|
22
30
|
"scripts": {
|