Last modified by Vincent Massol on 2026/11/12 20:27

Show last authors
1 This page builds a ##log## language for the [["code" Macro>>doc:documentation.xs.user.base.page.edit.formatting.code-macro.WebHome]]: a Java component that colours each line of a log according to its level. Building it shows how to highlight any language that the Pygments library bundled with XWiki doesn't know, which the Macro otherwise displays without highlighting. The [[Formatting>>doc:documentation.xs.dev.edit.formatting.WebHome]] page lists the other ways to extend the Macro.
2
3 1. Add the Code Macro as a dependency of your Maven module: (((
4 {{code language="xml"}}
5 <dependency>
6 <groupId>org.xwiki.platform</groupId>
7 <artifactId>xwiki-platform-rendering-macro-code</artifactId>
8 <version>${platform.version}</version>
9 <scope>provided</scope>
10 </dependency>
11 {{/code}}
12 )))
13 1. Write a component that extends ##AbstractHighlightParser##, with the name of the language as its hint. Its ##highlight## method returns the code as a list of blocks: the ##plain/1.0## parser turns each line into words and spaces without interpreting any wiki markup, and a ##FormatBlock## with a ##style## parameter colours the lines that contain a level: (((
14 {{code language="java"}}
15 package com.acme.internal;
16
17 import java.io.BufferedReader;
18 import java.io.IOException;
19 import java.io.Reader;
20 import java.io.StringReader;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
26
27 import jakarta.inject.Inject;
28 import jakarta.inject.Named;
29 import jakarta.inject.Singleton;
30
31 import org.xwiki.component.annotation.Component;
32 import org.xwiki.rendering.block.Block;
33 import org.xwiki.rendering.block.FormatBlock;
34 import org.xwiki.rendering.block.NewLineBlock;
35 import org.xwiki.rendering.listener.Format;
36 import org.xwiki.rendering.parser.AbstractHighlightParser;
37 import org.xwiki.rendering.parser.ParseException;
38 import org.xwiki.rendering.parser.Parser;
39 import org.xwiki.rendering.syntax.Syntax;
40 import org.xwiki.rendering.syntax.SyntaxType;
41
42 @Component
43 @Singleton
44 @Named("log")
45 public class LogHighlightParser extends AbstractHighlightParser
46 {
47 private static final Syntax SYNTAX = new Syntax(new SyntaxType("log-highlight", "Log"), "1.0");
48
49 private static final Pattern LEVEL = Pattern.compile("\\b(ERROR|WARN|INFO|DEBUG)\\b");
50
51 private static final Map<String, String> STYLES = Map.of(
52 "ERROR", "color: #C00000; font-weight: bold;",
53 "WARN", "color: #A05A00;",
54 "INFO", "color: #006000;",
55 "DEBUG", "color: #707070;");
56
57 @Inject
58 @Named("plain/1.0")
59 private Parser plainTextParser;
60
61 @Override
62 public Syntax getSyntax()
63 {
64 return SYNTAX;
65 }
66
67 @Override
68 public List<Block> highlight(String syntaxId, Reader source) throws ParseException
69 {
70 List<Block> blocks = new ArrayList<>();
71 try (BufferedReader reader = new BufferedReader(source)) {
72 String line = reader.readLine();
73 while (line != null) {
74 blocks.addAll(highlightLine(line));
75 line = reader.readLine();
76 if (line != null) {
77 blocks.add(new NewLineBlock());
78 }
79 }
80 } catch (IOException e) {
81 throw new ParseException("Failed to read the log", e);
82 }
83 return blocks;
84 }
85
86 private List<Block> highlightLine(String line) throws ParseException
87 {
88 if (line.isEmpty()) {
89 return List.of();
90 }
91
92 List<Block> lineBlocks = this.plainTextParser.parse(new StringReader(line)).getChildren().get(0).getChildren();
93
94 Matcher matcher = LEVEL.matcher(line);
95 if (!matcher.find()) {
96 return lineBlocks;
97 }
98
99 FormatBlock coloredLine = new FormatBlock(lineBlocks, Format.NONE);
100 coloredLine.setParameter("style", STYLES.get(matcher.group(1)));
101 return List.of(coloredLine);
102 }
103 }
104 {{/code}}
105 )))
106 1. Register the component by adding its class name to ##src/main/resources/META-INF/components.txt##: (((
107 {{code language="none"}}
108 com.acme.internal.LogHighlightParser
109 {{/code}}
110 )))
111 1. Build the module and deploy its JAR like any other component, as the "Deploying the Component" section of [[Creating XWiki Components>>doc:xwiki:Documentation.DevGuide.Tutorials.WritingComponents.WebHome]] describes.
112 1. Edit a Page and use the new language in the ##language## parameter of the Macro: (((
113 {{code language="xwiki"}}
114 {{code language="log"}}
115 2026-11-12 10:15:02 INFO Starting the indexing of 1250 pages
116 2026-11-12 10:15:07 DEBUG Indexed page Sandbox.WebHome
117 2026-11-12 10:15:31 WARN Page Main.Archive is larger than 10 MB, its content is truncated
118 2026-11-12 10:16:44 ERROR Failed to index page Blog.Release-18: the page is locked
119 2026-11-12 10:16:45 INFO Indexing done in 103 seconds
120 {{/code}}
121 {{/code}}
122 )))
123 1. View the Page. Each line of the log is coloured according to its level:(((
124 {{image reference="code-macro-log-language.png" size="extra" alt="A log displayed by the code Macro, with the INFO lines in green, the DEBUG line in grey, the WARN line in orange and the ERROR line in bold red"/}}
125 )))

Get Connected