Grooy スクリプトをそのまま渡して実行する Spring Boot+Picocli ベースのコマンドラインアプリを作成する ( その6 )( Groovy スクリプトからログをコンソールやファイルに出力する )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
- Groovy スクリプトからログをコンソール、あるいはファイルに出力できるようにします。
参照したサイト・書籍
Runtime and compile-time metaprogramming
https://groovy-lang.org/metaprogramming.html- @groovy.util.logging.Slf4j の説明が https://groovy-lang.org/metaprogramming.html#xform-Slf4j にあります。
Unicode Support
https://conemu.github.io/en/UnicodeSupport.htmlChapter 3: Logback configuration - Conditional processing of configuration files
http://logback.qos.ch/manual/configuration.html#conditionalバッチファイルでファイルパスからファイル名や拡張子を自由に取り出す方法
https://orangeclover.hatenablog.com/entry/20101004/1286120668Get Log Output in JSON
https://www.baeldung.com/java-log-json-outputStructured logging with SLF4J and Logback
https://gquintana.github.io/2017/12/01/Structured-logging-with-SL-FJ-and-Logback.htmllogfellow / logstash-logback-encoder
https://github.com/logfellow/logstash-logback-encoderJava ログ収集
https://docs.datadoghq.com/ja/logs/log_collection/java/?tab=logback
目次
手順
Groovy スクリプトからログを出力する
CsvFileToBookTable.groovy にログ出力処理を追加する
groovy-script-executor/src/main/groovy/sample/CsvFileToBookTable.groovy にログ出力処理を追加します。
package sample import com.univocity.parsers.annotations.Parsed import com.univocity.parsers.common.processor.BeanListProcessor import com.univocity.parsers.csv.CsvParserSettings import com.univocity.parsers.csv.CsvRoutines import groovy.sql.Sql import groovy.util.logging.Slf4j @Slf4j class CsvFileToBookTable { static class CsvRecord { @Parsed(index = 0, field = "isbm") String isbm @Parsed(index = 1, field = "title_author") String title_author } static void main(args) { def sql = Sql.newInstance("jdbc:mysql://localhost:3306/testdb?sslMode=DISABLED&characterEncoding=utf8", "testdb_user", "xxxxxxxx", "org.mariadb.jdbc.Driver") sql.connection.autoCommit = false CsvParserSettings settings = new CsvParserSettings() settings.format.lineSeparator = "\r\n" settings.headerExtractionEnabled = true BeanListProcessor<CsvRecord> rowProcessor = new BeanListProcessor<>(CsvRecord) settings.processor = rowProcessor sql.execute("truncate table book") log.info("bookテーブルをtruncateしました。") new File("publications.csv").withReader { reader -> CsvRoutines csvRoutines = new CsvRoutines(settings) for (CsvRecord csvRecord : csvRoutines.iterate(CsvRecord, reader)) { String[] titleAndAuthor = csvRecord.title_author.split(" / ") def title = titleAndAuthor[0] def author = null if (titleAndAuthor.size() == 1) { log.warn("title_authorカラムにはauthorが記載されていません。") } else { author = titleAndAuthor[1] } sql.execute(""" insert into book (isbm, title, author) values (:isbm, :title, :author) """, isbm: csvRecord.isbm, title: title, author: author) log.info("bookテーブルに登録しました (isbm = {}, title = {}, author = {})", csvRecord.isbm, title, author) } } sql.commit() sql.close() } }
- クラスに
@Slf4j
アノテーションを付与します。これは Lombok のアノテーションではなく Groovy が提供している groovy.util.logging.Slf4j アノテーションです(Groovy では Lombok の@Slf4j
アノテーションは機能しません)。 - ログを出力する箇所に
log.info(...)
、log.warn(...)
を追加します。
コンソールにログを出力する
D:\tmp の下に application.properties を新規作成し、以下の内容を記述します。Groovy スクリプトと同じディレクトリにある application.properties の設定で groovy-script-executor.jar の groovy-script-executor/src/main/resources/application.properties の設定が上書きされます。
logging.level.root=INFO #sample パッケージのログだけ出力したい場合には、root → sample に変更する #logging.level.sample=INFO
gse CsvFileToBookTable.groovy
で Groovy スクリプトを実行するとログが出力されますが、gse.bat 内で -Dfile.encoding=UTF-8
を指定しているのでログの文字コードは UTF-8 になります。そのままでは Windows のコマンドプロンプトでは文字化けします。
chcp 65001 & cmd
コマンドを実行してから gse CsvFileToBookTable.groovy
を実行すれば文字化けしなくなります(Unicode Support 参照)。
ファイルにログを出力する
logback-spring.xml の記述で janino を使いたいので build.gradle に追加します。
dependencies { .......... // dependency-management-plugin によりバージョン番号が自動で設定されるもの // Dependency Versions ( https://docs.spring.io/spring-boot/docs/current/reference/html/dependency-versions.html#dependency-versions ) 参照 implementation("org.springframework.boot:spring-boot-starter") implementation("org.apache.commons:commons-lang3") implementation("org.codehaus.janino:janino") testImplementation("org.springframework.boot:spring-boot-starter-test") ..........
- dependencies block に
implementation("org.codehaus.janino:janino")
を追加します。
groovy-script-executor/src/main/resources の下に logback-spring.xml を新規作成し、以下の内容を記述します。logging.file.name が設定されていればログファイルへ、設定されていなければコンソールへログを出力します。
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <include resource="org/springframework/boot/logging/logback/console-appender.xml"/> <property name="LOGGING_APPENDER" value="${logging.appender:-FILE}"/> <if condition='isDefined("LOG_FILE")'> <then> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <file>${LOG_FILE}</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> </appender> </then> </if> <if condition='isDefined("LOG_FILE")'> <then> <root> <appender-ref ref="${LOGGING_APPENDER}"/> </root> </then> <else> <root> <appender-ref ref="CONSOLE"/> </root> </else> </if> </configuration>
build タスクを実行し、生成した groovy-script-executor.jar を D:\tmp にコピーします。
Groovy スクリプトと同じディレクトリにログを出力するよう gse.bat を以下のように変更します。
@echo off java -Dfile.encoding=UTF-8 ^ -XX:TieredStopAtLevel=1 ^ -Dspring.main.lazy-initialization=true ^ -Dlogging.file.name=%~n1.log ^ -jar groovy-script-executor-1.0.0-RELEASE.jar ^ %*
-Dlogging.file.name=%~n1.log ^
を追加します。
gse CsvFileToBookTable.groovy
で Groovy スクリプトを実行するとコンソールには何も出力されず、
CsvFileToBookTable.groovy と同じディレクトリに CsvFileToBookTable.log が作成されて、その中にログが出力されます。
gse.bat 内の -Dlogging.file.name=%~n1.log ^
を削除すれば、コンソールにログが出力されます。
ログを JSON フォーマットで出力する
logstash-logback-encoder を導入してログファイルを JSON フォーマットで出力してみます。
build.gradle を以下のように変更します。
dependencies { .......... // dependency-management-plugin によりバージョン番号が自動で設定されないもの、あるいは最新バージョンを指定したいもの runtimeOnly("${postgresqlJdbcDriver}") runtimeOnly("${mariadbJdbcDriver}") implementation("com.univocity:univocity-parsers:2.9.1") implementation("net.logstash.logback:logstash-logback-encoder:6.6") testImplementation("org.assertj:assertj-core:3.21.0")
- dependencies block に
implementation("net.logstash.logback:logstash-logback-encoder:6.6")
を追加します。
logback-spring.xml を以下のように変更します。
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <include resource="org/springframework/boot/logging/logback/console-appender.xml"/> <property name="LOGGING_APPENDER" value="${logging.appender:-FILE}"/> <appender name="JSON" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="net.logstash.logback.encoder.LogstashEncoder"> <jsonGeneratorDecorator class="net.logstash.logback.decorate.PrettyPrintingJsonGeneratorDecorator"/> <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter"> <maxDepthPerThrowable>30</maxDepthPerThrowable> <maxLength>2048</maxLength> <shortenedClassNameLength>20</shortenedClassNameLength> <rootCauseFirst>true</rootCauseFirst> <inlineHash>true</inlineHash> </throwableConverter> </encoder> </appender> <if condition='isDefined("LOG_FILE")'> <then> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <file>${LOG_FILE}</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> </appender> </then> </if> <if condition='isDefined("LOG_FILE")'> <then> <root> <appender-ref ref="${LOGGING_APPENDER}"/> </root> </then> <else> <root> <!--<appender-ref ref="CONSOLE"/>--> <appender-ref ref="JSON"/> </root> </else> </if> </configuration>
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">...</appender>
を追加します。<appender-ref ref="CONSOLE"/>
をコメントアウトして<appender-ref ref="JSON"/>
を追加します。
今のままでは stack trace が JSON フォーマットで出力されないので、groovy-script-executor/src/main/java/ksby/cmdapp/groovyscriptexecutor/command/GroovyScriptExecutorCommand.java を以下のように変更します。
public class GroovyScriptExecutorCommand implements Callable<Integer>, IExitCodeExceptionMapper, IVersionProvider { .......... @Override public Integer call() throws IOException { try { Binding binding = new Binding(); GroovyShell shell = new GroovyShell(binding); shell.run(groovyScript, args); } catch (Exception e) { log.error("Groovyスクリプトでエラーが発生しました。", e); } return ExitCode.OK; } .......... }
- call メソッド内の処理を
try { ... } catch (Exception e) { log.error("Groovyスクリプトでエラーが発生しました。", e); }
で囲みます。
build タスクを実行し、生成した groovy-script-executor.jar を D:\tmp にコピーします。
gse.bat から -Dlogging.file.name=%~n1.log
を削除してログがコンソールに出力されるようにしてから gse CsvFileToBookTable.groovy
を実行すると、コンソールにログが JSON フォーマットで出力されます。
また CsvFileToBookTable.groovy 内でエラーが発生するよう変更してから実行すると stack trace も JSON フォーマットで出力されます。
履歴
2021/11/16
初版発行。