Skip to content

Commit b277871

Browse files
authored
Improve script logging (#115)
* Initial copy over * Make it work in core & add impl method * Cache format tag * `debug` command: update to modern switch * logging contexts POC * Fix max arg count * Initial review fixes * fix `name` arg nullability * Move deprecation to core * The vision™ * Separate formatted debug * Remove `Key`, consistentify naming * Move `ElementTag.format` to core * It's not necessarily logging specific * Get context directly from entry
1 parent b2619ad commit b277871

File tree

11 files changed

+325
-48
lines changed

11 files changed

+325
-48
lines changed

src/main/java/com/denizenscript/denizencore/DenizenImplementation.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.denizenscript.denizencore.scripts.containers.ScriptContainer;
1010
import com.denizenscript.denizencore.scripts.queues.ScriptQueue;
1111
import com.denizenscript.denizencore.tags.TagContext;
12+
import com.denizenscript.denizencore.utilities.DefinitionProvider;
1213

1314
import java.io.File;
1415

@@ -91,6 +92,8 @@ public interface DenizenImplementation {
9192

9293
void doFinalDebugOutput(String rawText);
9394

95+
void addFormatScriptDefinitions(DefinitionProvider provider, TagContext context);
96+
9497
String stripColor(String message);
9598

9699
void reloadConfig();

src/main/java/com/denizenscript/denizencore/objects/core/ElementTag.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.denizenscript.denizencore.events.ScriptEvent;
44
import com.denizenscript.denizencore.objects.*;
5+
import com.denizenscript.denizencore.scripts.containers.core.FormatScriptContainer;
56
import com.denizenscript.denizencore.tags.*;
67
import com.denizenscript.denizencore.tags.core.EscapeTagUtil;
78
import com.denizenscript.denizencore.utilities.*;
@@ -2286,7 +2287,7 @@ else if (elem.equals("false")) {
22862287
// Returns a BinaryTag holding 8 bytes of this integer number converted to binary format using big-endian 64-bit integer twos-complement encoding.
22872288
// @example
22882289
// # Narrates '00000000000000ff'
2289-
// - narrate <element[255].to_binary>
2290+
// - narrate <element[255].integer_to_binary>
22902291
// -->
22912292
tagProcessor.registerStaticTag(BinaryTag.class, "integer_to_binary", (attribute, object) -> {
22922293
ByteBuffer buffer = ByteBuffer.allocate(8);
@@ -2536,6 +2537,21 @@ else if (elem.equals("false")) {
25362537
tagProcessor.registerStaticTag(ElementTag.class, "unaccented", (attribute, object) -> {
25372538
return new ElementTag(UNACCENTED_PATTERN.matcher(Normalizer.normalize(object.asString(), Normalizer.Form.NFKD)).replaceAll(""), true);
25382539
});
2540+
2541+
// <--[tag]
2542+
// @attribute <ElementTag.format[<format_script>]>
2543+
// @returns ElementTag
2544+
// @group text manipulation
2545+
// @description
2546+
// Returns the text re-formatted according to a <@link language Format Script Containers>.
2547+
// -->
2548+
ElementTag.tagProcessor.registerTag(ElementTag.class, ScriptTag.class, "format", (attribute, object, format) -> {
2549+
if (!(format.getContainer() instanceof FormatScriptContainer formatScript) || formatScript.getFormatTag() == null) {
2550+
attribute.echoError("Script '" + format + "' is not a format script.");
2551+
return null;
2552+
}
2553+
return new ElementTag(formatScript.getFormattedText(object.asString(), attribute.context));
2554+
});
25392555
}
25402556

25412557
public static ObjectTagProcessor<ElementTag> tagProcessor = new ObjectTagProcessor<>();

src/main/java/com/denizenscript/denizencore/objects/core/MapTag.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ public MapTag(Map<StringHolder, ObjectTag> map) {
142142
this.map = new LinkedHashMap<>(map);
143143
}
144144

145+
public MapTag(MapTag mapTag) {
146+
this(mapTag.map);
147+
}
148+
145149
@Override
146150
public MapTag duplicate() {
147151
MapTag newMap = new MapTag();

src/main/java/com/denizenscript/denizencore/scripts/ScriptEntry.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,10 @@ public ScriptTag getScript() {
766766
return internal.script;
767767
}
768768

769+
public ScriptContainer getScriptContainer() {
770+
return getScript() != null ? getScript().getContainer() : null;
771+
}
772+
769773
public ScriptEntry setScript(String scriptName) {
770774
internal.script = ScriptTag.valueOf(scriptName, CoreUtilities.basicContext);
771775
return this;
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.denizenscript.denizencore.scripts;
2+
3+
import com.denizenscript.denizencore.DenizenCore;
4+
import com.denizenscript.denizencore.scripts.containers.ScriptContainer;
5+
import com.denizenscript.denizencore.scripts.containers.core.FormatScriptContainer;
6+
import com.denizenscript.denizencore.tags.ParseableTag;
7+
import com.denizenscript.denizencore.tags.TagContext;
8+
import com.denizenscript.denizencore.tags.TagManager;
9+
import com.denizenscript.denizencore.utilities.CoreUtilities;
10+
import com.denizenscript.denizencore.utilities.YamlConfiguration;
11+
import com.denizenscript.denizencore.utilities.debugging.Debug;
12+
13+
import java.util.HashMap;
14+
import java.util.HashSet;
15+
import java.util.Map;
16+
import java.util.Set;
17+
18+
public record ScriptFormattingContext(Map<String, ParseableTag> formats, ParseableTag singleFormat) {
19+
20+
// <--[language]
21+
// @name Script Logging Format
22+
// @group Script Container System
23+
// @description
24+
// Script logging contexts provide the format certain commands will use for their texts. Most notably, this includes <@link command debug>.
25+
// See specific command's documentation for information on which formats they use (for example, the 'debug' command supports a 'debug' format and an 'error' format).
26+
// The formats are specified under a 'formats' key, and can be either a <@link language Format Script Containers> or a direct format with the same syntax as format scripts.
27+
// When specifying a direct format, use the format name as the key; When specifying a format script, use '<format>_script' as the key (see example below).
28+
// <code>
29+
// my_project_task:
30+
// type: task
31+
// formats:
32+
// # A direct format
33+
// debug: [MyProject] <[text]>
34+
// # A separate format script
35+
// error_script: my_project_error
36+
// script:
37+
// - if <util.real_time_since_start.in_hours> > 20:
38+
// # Will be formatted by the 'my_project_error' format script.
39+
// - debug error "The system has been running for over 20 hours! Please restart!"
40+
// - else:
41+
// # Will print "[MyProject] The system does not need a restart yet."
42+
// - debug "The system does not need a restart yet."
43+
// </code>
44+
// -->
45+
46+
public static final Set<String> FORMAT_TYPES = new HashSet<>();
47+
48+
public static String registerFormatType(String name) {
49+
String nameLower = CoreUtilities.toLowerCase(name);
50+
if (!FORMAT_TYPES.add(nameLower)) {
51+
throw new IllegalArgumentException("Tried registering duplicate format type! format '" + name + "' already exists.");
52+
}
53+
return nameLower;
54+
}
55+
56+
public static ScriptFormattingContext parseFromConfiguration(ScriptContainer script) {
57+
YamlConfiguration formatsConfig = script.getConfigurationSection("formats");
58+
if (formatsConfig == null) {
59+
return null;
60+
}
61+
Map<String, ParseableTag> formats = new HashMap<>();
62+
TagContext context = null;
63+
for (String formatType : FORMAT_TYPES) {
64+
String rawFormat = formatsConfig.getString(formatType);
65+
if (rawFormat != null) {
66+
if (context == null) {
67+
context = DenizenCore.implementation.getTagContext(script);
68+
}
69+
formats.put(formatType, TagManager.parseTextToTag(rawFormat, context));
70+
continue;
71+
}
72+
String formatScriptInput = formatsConfig.getString(formatType + "_script");
73+
if (formatScriptInput == null) {
74+
continue;
75+
}
76+
FormatScriptContainer formatScript = ScriptRegistry.getScriptContainerAs(formatScriptInput, FormatScriptContainer.class);
77+
if (formatScript == null || formatScript.getFormatTag() == null) {
78+
Debug.echoError(script, "Invalid format script '" + formatScriptInput + "' specified for format '" + formatType + "'.");
79+
continue;
80+
}
81+
formats.put(formatType, formatScript.getFormatTag());
82+
}
83+
if (formats.isEmpty()) {
84+
Debug.echoError(script, "Invalid formats config, must specify at least one valid format.");
85+
return null;
86+
}
87+
return new ScriptFormattingContext(formats, null);
88+
}
89+
90+
public boolean hasFormat(String formatType) {
91+
return singleFormat != null || formats.containsKey(formatType);
92+
}
93+
94+
public String formatOrNull(String formatType, String rawText, ScriptEntry entry) {
95+
ParseableTag formatTag = singleFormat == null ? formats.get(formatType) : singleFormat;
96+
return formatTag != null ? FormatScriptContainer.formatText(formatTag, rawText, entry.getContext(), entry.getScript()) : null;
97+
}
98+
99+
public String format(String formatType, String rawText, ScriptEntry entry) {
100+
String formatted = formatOrNull(formatType, rawText, entry);
101+
return formatted != null ? formatted : rawText;
102+
}
103+
}

src/main/java/com/denizenscript/denizencore/scripts/ScriptRegistry.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public static void _registerCoreTypes() {
2929
_registerType("world", WorldScriptContainer.class);
3030
_registerType("data", DataScriptContainer.class);
3131
_registerType("yaml data", DataScriptContainer.class);
32+
_registerType("format", FormatScriptContainer.class);
3233
}
3334

3435
public static boolean containsScript(String id, Class scriptContainerType) {

src/main/java/com/denizenscript/denizencore/scripts/commands/core/DebugCommand.java

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,36 @@
11
package com.denizenscript.denizencore.scripts.commands.core;
22

3+
import com.denizenscript.denizencore.objects.core.ElementTag;
4+
import com.denizenscript.denizencore.objects.core.ScriptTag;
5+
import com.denizenscript.denizencore.scripts.ScriptEntry;
6+
import com.denizenscript.denizencore.scripts.ScriptFormattingContext;
37
import com.denizenscript.denizencore.scripts.commands.AbstractCommand;
48
import com.denizenscript.denizencore.scripts.commands.Holdable;
59
import com.denizenscript.denizencore.scripts.commands.generator.*;
10+
import com.denizenscript.denizencore.scripts.containers.ScriptContainer;
11+
import com.denizenscript.denizencore.scripts.containers.core.FormatScriptContainer;
612
import com.denizenscript.denizencore.utilities.CoreUtilities;
713
import com.denizenscript.denizencore.utilities.debugging.Debug;
8-
import com.denizenscript.denizencore.objects.core.ElementTag;
9-
import com.denizenscript.denizencore.scripts.ScriptEntry;
1014
import com.denizenscript.denizencore.utilities.debugging.DebugSubmitter;
1115

1216
public class DebugCommand extends AbstractCommand implements Holdable {
1317

18+
public static final String DEBUG_FORMAT = ScriptFormattingContext.registerFormatType("debug");
19+
public static final String ERROR_FORMAT = ScriptFormattingContext.registerFormatType("error");
20+
1421
public DebugCommand() {
1522
setName("debug");
16-
setSyntax("debug [<type>] [<message>] (name:<name>)");
17-
setRequiredArguments(2, 3);
23+
setSyntax("debug (<type>) [<message>] (name:<name>) (format:<format>)");
24+
setRequiredArguments(1, 3);
1825
isProcedural = true;
1926
generateDebug = false;
2027
autoCompile();
2128
}
2229

2330
// <--[command]
2431
// @Name Debug
25-
// @Syntax debug [<type>] [<message>] (name:<name>)
26-
// @Required 2
32+
// @Syntax debug (<type>) [<message>] (name:<name>) (format:<format>)
33+
// @Required 1
2734
// @Maximum 3
2835
// @Short Shows a debug message.
2936
// @Group core
@@ -32,20 +39,20 @@ public DebugCommand() {
3239
// @Description
3340
// Use to quickly output debug information to console.
3441
//
35-
// Valid types include:
42+
// Outputs plain text debug to the console by default, supporting the 'debug' format type (see <@link language Script Logging Formats>).
43+
//
44+
// Alternatively, specify one of the following debug types:
3645
// DEBUG: standard hideable debug.
3746
// HEADER: standard hideable debug inside a header line.
3847
// FOOTER: a footer line.
3948
// SPACER: a spacer line.
4049
// LOG: global output, non-hideable.
4150
// APPROVAL: "Okay!" output, non-hideable.
42-
// ERROR: "Error!" output, non-hideable.
51+
// ERROR: "Error!" output, non-hideable. Supports the 'error' format type, see <@link language Script Logging Formats>.
4352
// REPORT: normally used to describe the arguments of a command, requires a name, hideable.
4453
// EXCEPTION: outputs a full java stacktrace.
4554
// RECORD: Use message 'start' to start recording, 'submit' to submit a recording, or 'cancel' to cancel a recording.
4655
//
47-
// TODO: Should [<type>] be required? Perhaps default to 'debug' mode?
48-
//
4956
// @Tags
5057
// <entry[saveName].submitted> returns the submit link (if any).
5158
//
@@ -55,7 +62,7 @@ public DebugCommand() {
5562
//
5663
// @Usage
5764
// Use to add some information to help your own ability to read debug output from you script.
58-
// - debug debug "Time is currently <[milliseconds].div[1000].round> seconds!"
65+
// - debug "Time is currently <[milliseconds].div[1000].round> seconds!"
5966
//
6067
// @Usage
6168
// Use to record a debug log of a certain script.
@@ -68,6 +75,7 @@ public DebugCommand() {
6875
// -->
6976

7077
public enum DebugType {
78+
OUTPUT,
7179
DEBUG,
7280
HEADER,
7381
FOOTER,
@@ -88,42 +96,51 @@ public void addCustomTabCompletions(TabCompletionsBuilder tab) {
8896

8997
public static void autoExecute(ScriptEntry scriptEntry,
9098
@ArgRaw @ArgLinear @ArgName("debug") String debug,
91-
@ArgName("type") DebugType dbType,
92-
@ArgPrefixed @ArgName("name") @ArgDefaultText("name") String name) {
99+
@ArgName("type") @ArgDefaultText("output") DebugType dbType,
100+
@ArgPrefixed @ArgName("name") @ArgDefaultNull String name,
101+
@ArgName("format") @ArgPrefixed @ArgDefaultNull ScriptTag formatScript) {
102+
ScriptFormattingContext formattingContext = null;
103+
ScriptContainer scriptContainer = scriptEntry.getScriptContainer();
104+
if (formatScript != null) {
105+
if (!(formatScript.getContainer() instanceof FormatScriptContainer formatScriptContainer) || formatScriptContainer.getFormatTag() == null) {
106+
Debug.echoError("Invalid 'format:' script specified: must be a format script container.");
107+
return;
108+
}
109+
formattingContext = formatScriptContainer.getAsFormattingContext();
110+
}
111+
else if (scriptContainer != null) {
112+
formattingContext = scriptContainer.getFormattingContext();
113+
}
114+
if (name == null) {
115+
name = scriptContainer != null ? scriptContainer.getOriginalName() : "DebugCommand";
116+
}
93117
if (dbType != DebugType.RECORD) {
94118
scriptEntry.setFinished(true);
95119
}
96120
switch (dbType) {
97-
case DEBUG:
98-
Debug.echoDebug(scriptEntry, debug);
99-
break;
100-
case HEADER:
101-
Debug.echoDebug(scriptEntry, Debug.DebugElement.Header, debug);
102-
break;
103-
case FOOTER:
104-
Debug.echoDebug(scriptEntry, Debug.DebugElement.Footer, debug);
105-
break;
106-
case SPACER:
107-
Debug.echoDebug(scriptEntry, Debug.DebugElement.Spacer, debug);
108-
break;
109-
case LOG:
110-
Debug.log(debug);
111-
break;
112-
case APPROVAL:
113-
Debug.echoApproval(debug);
114-
break;
115-
case ERROR:
116-
Debug.echoError(scriptEntry, debug);
117-
break;
118-
case REPORT:
121+
case OUTPUT -> Debug.echoDebug(null, formattingContext == null ? debug : formattingContext.format(DEBUG_FORMAT, debug, scriptEntry));
122+
case DEBUG -> Debug.echoDebug(scriptEntry, debug);
123+
case HEADER -> Debug.echoDebug(scriptEntry, Debug.DebugElement.Header, debug);
124+
case FOOTER -> Debug.echoDebug(scriptEntry, Debug.DebugElement.Footer, debug);
125+
case SPACER -> Debug.echoDebug(scriptEntry, Debug.DebugElement.Spacer, debug);
126+
case LOG -> Debug.log(name, debug);
127+
case APPROVAL -> Debug.echoApproval(debug);
128+
case ERROR -> {
129+
String formatted = formattingContext != null ? formattingContext.formatOrNull(ERROR_FORMAT, debug, scriptEntry) : null;
130+
if (formatted != null) {
131+
Debug.echoDebug(null, formatted);
132+
}
133+
else {
134+
Debug.echoError(scriptEntry, debug);
135+
}
136+
}
137+
case REPORT -> {
119138
if (scriptEntry.dbCallShouldDebug()) {
120139
Debug.report(scriptEntry, name, debug);
121140
}
122-
break;
123-
case EXCEPTION:
124-
Debug.echoError(scriptEntry, new RuntimeException(debug));
125-
break;
126-
case RECORD:
141+
}
142+
case EXCEPTION -> Debug.echoError(scriptEntry, new RuntimeException(debug));
143+
case RECORD -> {
127144
String form = CoreUtilities.toLowerCase(debug);
128145
switch (form) {
129146
case "start":
@@ -156,7 +173,7 @@ else if (s.equals("disabled")) {
156173
scriptEntry.setFinished(true);
157174
break;
158175
}
159-
break;
176+
}
160177
}
161178
}
162179
}

0 commit comments

Comments
 (0)