Skip to content

Commit

Permalink
Merge pull request #1071 from robertpanzer/extension-logging-docs
Browse files Browse the repository at this point in the history
Add documentation for logging from extensions
  • Loading branch information
robertpanzer committed Dec 31, 2021
2 parents 6dd9015 + fff4fc5 commit 0bfe9c6
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 15 deletions.
2 changes: 2 additions & 0 deletions asciidoctorj-documentation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ task copyExtensionsExamplesToDocs(type: Copy) {
include '**/extension/ImageInlineMacroProcessor.java'
include '**/extension/IssueInlineMacroProcessor.java'
include '**/extension/KeyboardInlineMacroProcessor.java'
include '**/extension/LoggingBlockMacroProcessor.java'
include '**/extension/LsIncludeProcessor.java'
include '**/extension/RobotsDocinfoProcessor.java'
include '**/extension/TerminalCommandExtension.java'
Expand All @@ -65,6 +66,7 @@ task copyExtensionsExamplesToDocs(type: Copy) {
include 'comment.adoc'
include 'comment-with-note.adoc'
include 'gist-macro.adoc'
include 'logging-macro.adoc'
include 'issue-inline-macro.adoc'
include 'treeprocessorcontent.adoc'
include 'treeprocessorresult.adoc'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,44 @@
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.converter.ConverterFor;
import org.asciidoctor.converter.StringConverter;
import org.asciidoctor.log.LogRecord;
import org.asciidoctor.log.Severity;

import java.util.Map;

@ConverterFor("text") // <1>
@ConverterFor("text") // <1>
public class TextConverter extends StringConverter {

private String LINE_SEPARATOR = "\n";

public TextConverter(String backend, Map<String, Object> opts) { // <2>
public TextConverter(String backend, Map<String, Object> opts) { // <2>
super(backend, opts);
}

@Override
public String convert(
ContentNode node, String transform, Map<Object, Object> o) { // <3>
ContentNode node, String transform, Map<Object, Object> o) { // <3>

if (transform == null) { // <4>
if (transform == null) { // <4>
transform = node.getNodeName();
}

if (node instanceof Document) {
Document document = (Document) node;
return document.getContent().toString(); // <5>
return document.getContent().toString(); // <5>
} else if (node instanceof Section) {
Section section = (Section) node;
return new StringBuilder()
.append("== ").append(section.getTitle()).append(" ==")
.append(LINE_SEPARATOR).append(LINE_SEPARATOR)
.append(section.getContent()).toString(); // <5>
.append(section.getContent()).toString(); // <5>
} else if (transform.equals("paragraph")) {
StructuralNode block = (StructuralNode) node;
String content = (String) block.getContent();
return new StringBuilder(content.replaceAll(LINE_SEPARATOR, " "))
.append(LINE_SEPARATOR).toString(); // <5>
.append(LINE_SEPARATOR).toString(); // <5>
} else {
log(new LogRecord(Severity.WARN, "Unexpected node")); // <6>
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.asciidoctor.integrationguide.extension;

import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.BlockMacroProcessor;
import org.asciidoctor.extension.Name;

import java.util.Map;

//tag::include[]
@Name("log")
public class LoggingBlockMacroProcessor extends BlockMacroProcessor {

@Override
public Object process(
StructuralNode parent, String target, Map<String, Object> attributes) {

String message = target;

log(new org.asciidoctor.log.LogRecord( // <1>
org.asciidoctor.log.Severity.INFO,
parent.getSourceLocation(), // <2>
message));

return createBlock(parent, "paragraph", "Hello from the logging macro");
}

}
//end::include[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.asciidoctor.integrationguide.extension;

import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Options;
import org.asciidoctor.log.LogRecord;
import org.asciidoctor.util.ClasspathResources;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

@RunWith(Arquillian.class)
public class LoggingBlockMacroProcessorTest {

@ArquillianResource
private Asciidoctor asciidoctor;

@ArquillianResource
private ClasspathResources classpathResources;

@Test
public void should_log_from_extension() {

//tag::include[]
File loggingmacro_adoc = // ...
//end::include[]
classpathResources.getResource("logging-macro.adoc");
//tag::include[]
List<LogRecord> logRecords = new ArrayList<>();
asciidoctor.registerLogHandler(logRecords::add);
asciidoctor.javaExtensionRegistry().blockMacro(LoggingBlockMacroProcessor.class); // <1>

asciidoctor.convertFile(loggingmacro_adoc,
Options.builder()
.sourcemap(true)
.toFile(false)
.build());

assertEquals(1, logRecords.size());
assertTrue(logRecords.get(0).getCursor().getFile().endsWith("logging-macro.adoc"));
assertEquals(3, logRecords.get(0).getCursor().getLineNumber());
assertEquals("HelloWorld", logRecords.get(0).getMessage());
//end::include[]
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
= Log Test

== A logging macro

log::HelloWorld[]
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,44 @@
import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.converter.ConverterFor;
import org.asciidoctor.converter.StringConverter;
import org.asciidoctor.log.LogRecord;
import org.asciidoctor.log.Severity;

import java.util.Map;

@ConverterFor("text") // <1>
@ConverterFor("text") // <1>
public class TextConverter extends StringConverter {

private String LINE_SEPARATOR = "\n";

public TextConverter(String backend, Map<String, Object> opts) { // <2>
public TextConverter(String backend, Map<String, Object> opts) { // <2>
super(backend, opts);
}

@Override
public String convert(
ContentNode node, String transform, Map<Object, Object> o) { // <3>
ContentNode node, String transform, Map<Object, Object> o) { // <3>

if (transform == null) { // <4>
if (transform == null) { // <4>
transform = node.getNodeName();
}

if (node instanceof Document) {
Document document = (Document) node;
return document.getContent().toString(); // <5>
return document.getContent().toString(); // <5>
} else if (node instanceof Section) {
Section section = (Section) node;
return new StringBuilder()
.append("== ").append(section.getTitle()).append(" ==")
.append(LINE_SEPARATOR).append(LINE_SEPARATOR)
.append(section.getContent()).toString(); // <5>
.append(section.getContent()).toString(); // <5>
} else if (transform.equals("paragraph")) {
StructuralNode block = (StructuralNode) node;
String content = (String) block.getContent();
return new StringBuilder(content.replaceAll(LINE_SEPARATOR, " "))
.append(LINE_SEPARATOR).toString(); // <5>
.append(LINE_SEPARATOR).toString(); // <5>
} else {
log(new LogRecord(Severity.WARN, "Unexpected node")); // <6>
}
return null;
}
Expand Down
14 changes: 14 additions & 0 deletions docs/modules/ROOT/pages/asciidoctor-api-options.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ OptionsBuilder optionsBuilder =
Options.builder().templateDirs(new File("templates_path"));
----

[[sourcemap]]
== sourcemap

Keeps track of the file and line number for each parsed block.
This is useful for tooling applications where the association between the converted output and the source file is important.
The default for this option is `false`.

[source,java]
.Setting the option sourcemap
----
OptionsBuilder optionsBuilder =
Options.builder().sourcemap(true);
----

== attributes

This option allows to define document attributes externally.
Expand Down
29 changes: 28 additions & 1 deletion docs/modules/ROOT/pages/logs-handling.adoc
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
= Logs Handling API
:uri-javadocs-logmanager: https://docs.oracle.com/javase/8/docs/api/java/util/logging/LogManager.html

NOTE: This API is inspired by Java Logging API (JUL).
[NOTE]
This API is inspired by Java Logging API (JUL).
If you are familiar with `java.util.logging.*` you will see familiar analogies with some of its components.

AsciidoctorJ (v1.5.7+) offers the possibility to capture messages generated during document rendering.
While the API described here explains how to forward these messages to any receiver, AsciidoctorJ already logs all messages to the java.util.logging Logger with the name `asciidoctor`.

[NOTE]
The default log level of java.util.logging is INFO.
That means you might see messages for provisional messages about possible invalid xrefs which could be valid xrefs that Asciidoctor just can't resolve.

[TIP]
--
The behavior of the java.util.logging logger can be configured with a properties file.
For other ways to configure this behavior please check the documentation of {uri-javadocs-logmanager}[java.util.logging.LogManager].
To restrict the logger to only log messages with severity WARNING or higher create this file:

.logging.properties
[source,properties]
----
handlers = java.util.logging.ConsoleHandler
.level = WARNING
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format = %3$
----

To use this configuration when running asciidoctorj via the CLI pass the location of the file like this:

----
# JAVA_OPTS="-Djava.util.logging.config.file=$PWD/logging.properties" asciidoctorj document.adoc
----
--

== Logs Handling API

Expand Down
3 changes: 3 additions & 0 deletions docs/modules/ROOT/pages/write-converter.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ This could be for example the value `embedded` to indicate that the resulting do
If it is `null` the transformation usually is defined by the node type and name.
<5> Calls to the method `getContent()` of a node will recursively call the method `convert()` with the child nodes again.
Thereby the converter can collect the rendered child nodes, merge them appropriately and return the rendering of the whole node.
<6> Converters can log messages in the same way as xref:extensions:logging.adoc[extensions].
These messages will also be forwarded to build tools like the Asciidoctor Maven plugin and allow xref:maven-tools:plugin:goals/process-asciidoc.adoc#configuration-logHandler[failing the build] on certain messages.


Finally, the converter can be registered and used for conversion of AsciiDoc documents:

Expand Down
5 changes: 5 additions & 0 deletions docs/modules/extensions/examples/logging-macro.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
= Log Test

== A logging macro

log::HelloWorld[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.asciidoctor.integrationguide.extension;

import org.asciidoctor.ast.StructuralNode;
import org.asciidoctor.extension.BlockMacroProcessor;
import org.asciidoctor.extension.Name;

import java.util.Map;

//tag::include[]
@Name("log")
public class LoggingBlockMacroProcessor extends BlockMacroProcessor {

@Override
public Object process(
StructuralNode parent, String target, Map<String, Object> attributes) {

String message = target;

log(new org.asciidoctor.log.LogRecord( // <1>
org.asciidoctor.log.Severity.INFO,
parent.getSourceLocation(), // <2>
message));

return createBlock(parent, "paragraph", "Hello from the logging macro");
}

}
//end::include[]
2 changes: 2 additions & 0 deletions docs/modules/extensions/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@
*** xref:register-extensions-manually.adoc[]
*** xref:register-extensions-bulk.adoc[]
*** xref:register-extensions-automatically.adoc[]
** xref:logging.adoc[]
17 changes: 17 additions & 0 deletions docs/modules/extensions/pages/logging.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
= Logging

Extensions are also able to log messages that are handled in the same way as messages logged by Asciidoctor itself as explained in xref:ROOT:logs-handling.adoc[].

Logging messages via this API allows build tools like the Asciidoctor Maven plugin to capture them and for example xref:maven-tools:plugin:goals/process-asciidoc.adoc#configuration-logHandler[fail the build] in case an error or warning is logged.
Therefore it might be preferable to use this instead of directly logging messages via slf4j or other APIs.

Every extension inherits the method `Processor.log()` that allows it to log messages.
The following example shows a Block Macro Processor that logs a message containing the target of the macro:

[source,java]
----
include::example$org/asciidoctor/integrationguide/extension/LoggingBlockMacroProcessor.java[tags=include]
----
<1> The method `log(LogRecord)` is inherited from the Processor class hierarchy.
<2> You can access the source location of the parent node to put the message in relation to the source document.
Note that the source location will be null unless you enable the xref:ROOT:asciidoctor-api-options.adoc#sourcemap[sourcemap] option

0 comments on commit 0bfe9c6

Please sign in to comment.