// Path: asciidoctorj-api/src/main/java/org/asciidoctor/log/LogHandler.java
// public interface LogHandler {
// 
//     /**
//      * Process a log record.
//      * This method is called during conversion and will abort completely
//      * if an exception is thrown from within.
//      *
//      * @param logRecord
//      */
//     void log(LogRecord logRecord);
// 
// }
// 
// Path: asciidoctorj-api/src/main/java/org/asciidoctor/log/LogRecord.java
// public class LogRecord {
// 
//     private final Severity severity;
// 
//     private Cursor cursor;
// 
//     private final String message;
// 
//     private String sourceFileName;
// 
//     private String sourceMethodName;
// 
//     public LogRecord(Severity severity, String message) {
//         this.severity = severity;
//         this.message = message;
//     }
// 
//     public LogRecord(Severity severity, Cursor cursor, String message) {
//         this.severity = severity;
//         this.cursor = cursor;
//         this.message = message;
//     }
// 
//     public LogRecord(Severity severity, Cursor cursor, String message, String sourceFileName, String sourceMethodName) {
//         this.severity = severity;
//         this.cursor = cursor;
//         this.message = message;
//         this.sourceFileName = sourceFileName;
//         this.sourceMethodName = sourceMethodName;
//     }
// 
//     /**
//      * @return Severity level of the current record.
//      */
//     public Severity getSeverity() {
//         return severity;
//     }
// 
//     /**
//      * @return Information about the location of the event.
//      */
//     public Cursor getCursor() {
//         return cursor;
//     }
// 
//     /**
//      * @return Descriptive message about the event.
//      */
//     public String getMessage() {
//         return message;
//     }
// 
//     /**
//      * @return The value <code>&lt;script&gt;</code>. For the source filename use {@link Cursor#getFile()} obtained with the {@link #getCursor()} method.
//      */
//     public String getSourceFileName() {
//         return sourceFileName;
//     }
// 
//     /**
//      * @return The Asciidoctor Ruby engine method used to convert the file; <code>convertFile</code> or <code>convert</code> whether you are converting a File or a String.
//      */
//     public String getSourceMethodName() {
//         return sourceMethodName;
//     }
// }


import org.asciidoctor.log.LogHandler;
import org.asciidoctor.ast.Cursor;
import org.asciidoctor.jruby.ast.impl.CursorImpl;
import org.asciidoctor.log.LogRecord;
import org.asciidoctor.log.Severity;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyHash;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.backtrace.BacktraceElement;
import org.jruby.runtime.builtin.IRubyObject;
import java.util.Objects;
import java.util.Optional;

  @JRubyMethod(name = "initialize", required = 1, optional = 2)
  public IRubyObject initialize(final ThreadContext threadContext, final IRubyObject[] args) {
    return Helpers.invokeSuper(
        threadContext,
        this,
        getMetaClass(),
        "initialize",
        args,
        Block.NULL_BLOCK);
  }

  /**
   * @param threadContext
   * @param args
   */
  @JRubyMethod(name = "add", required = 1, optional = 2)
  public IRubyObject add(final ThreadContext threadContext, final IRubyObject[] args, Block block) {
    final IRubyObject rubyMessage;
    if (args.length >= 2 && !args[1].isNil()) {
      rubyMessage = args[1];
    } else if (block.isGiven()) {
      rubyMessage = block.yield(threadContext, getRuntime().getNil());
    } else {
      rubyMessage = args[2];
    }
    final Cursor cursor = getSourceLocation(rubyMessage);
    final String message = formatMessage(rubyMessage);
    final Severity severity = mapRubyLogLevel(args[0]);

// Path: asciidoctorj-api/src/main/java/org/asciidoctor/log/LogHandler.java
// public interface LogHandler {
// 
//     /**
//      * Process a log record.
//      * This method is called during conversion and will abort completely
//      * if an exception is thrown from within.
//      *
//      * @param logRecord
//      */
//     void log(LogRecord logRecord);
// 
// }
// 
// Path: asciidoctorj-api/src/main/java/org/asciidoctor/log/LogRecord.java
// public class LogRecord {
// 
//     private final Severity severity;
// 
//     private Cursor cursor;
// 
//     private final String message;
// 
//     private String sourceFileName;
// 
//     private String sourceMethodName;
// 
//     public LogRecord(Severity severity, String message) {
//         this.severity = severity;
//         this.message = message;
//     }
// 
//     public LogRecord(Severity severity, Cursor cursor, String message) {
//         this.severity = severity;
//         this.cursor = cursor;
//         this.message = message;
//     }
// 
//     public LogRecord(Severity severity, Cursor cursor, String message, String sourceFileName, String sourceMethodName) {
//         this.severity = severity;
//         this.cursor = cursor;
//         this.message = message;
//         this.sourceFileName = sourceFileName;
//         this.sourceMethodName = sourceMethodName;
//     }
// 
//     /**
//      * @return Severity level of the current record.
//      */
//     public Severity getSeverity() {
//         return severity;
//     }
// 
//     /**
//      * @return Information about the location of the event.
//      */
//     public Cursor getCursor() {
//         return cursor;
//     }
// 
//     /**
//      * @return Descriptive message about the event.
//      */
//     public String getMessage() {
//         return message;
//     }
// 
//     /**
//      * @return The value <code>&lt;script&gt;</code>. For the source filename use {@link Cursor#getFile()} obtained with the {@link #getCursor()} method.
//      */
//     public String getSourceFileName() {
//         return sourceFileName;
//     }
// 
//     /**
//      * @return The Asciidoctor Ruby engine method used to convert the file; <code>convertFile</code> or <code>convert</code> whether you are converting a File or a String.
//      */
//     public String getSourceMethodName() {
//         return sourceMethodName;
//     }
// }


// Path: asciidoctorj-core/src/main/java/org/asciidoctor/jruby/log/internal/JavaLogger.java
import org.asciidoctor.log.LogHandler;
import org.asciidoctor.ast.Cursor;
import org.asciidoctor.jruby.ast.impl.CursorImpl;
import org.asciidoctor.log.LogRecord;
import org.asciidoctor.log.Severity;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyHash;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.backtrace.BacktraceElement;
import org.jruby.runtime.builtin.IRubyObject;
import java.util.Objects;
import java.util.Optional;


  @JRubyMethod(name = "initialize", required = 1, optional = 2)
  public IRubyObject initialize(final ThreadContext threadContext, final IRubyObject[] args) {
    return Helpers.invokeSuper(
        threadContext,
        this,
        getMetaClass(),
        "initialize",
        args,
        Block.NULL_BLOCK);
  }

  /**
   * @param threadContext
   * @param args
   */
  @JRubyMethod(name = "add", required = 1, optional = 2)
  public IRubyObject add(final ThreadContext threadContext, final IRubyObject[] args, Block block) {
    final IRubyObject rubyMessage;
    if (args.length >= 2 && !args[1].isNil()) {
      rubyMessage = args[1];
    } else if (block.isGiven()) {
      rubyMessage = block.yield(threadContext, getRuntime().getNil());
    } else {
      rubyMessage = args[2];
    }
    final Cursor cursor = getSourceLocation(rubyMessage);
    final String message = formatMessage(rubyMessage);
    final Severity severity = mapRubyLogLevel(args[0]);

    final LogRecord record = createLogRecord(threadContext, severity, cursor, message);