Apache FTPClientを使って、JavaアプリケーションからFTP操作が可能となります。
この際、FTPクライアントとFTPサーバ間との通信内容をログに出力する方法を説明します。
概要
- Apache FTPClientは、Apache Commons Netライブラリの一部です。
commons-net(3.6版)のjavadocはこちらをご覧ください。 - 動作確認や単体試験時では、開発環境のIDE(デバッグ機能)を使って、アプリとFTPサーバとの通信内容を検証できます。結合試験や本番環境等では、IDE等のツールは一般的に導入されていることは少なく、環境固有の問題が発生した場合は、問題の検証が難しくなります。
このような事態に備えて、アプリとFTPサーバとの通信をトレースできるログを出力しておきます。こうすることで、動作確認や単体試験時のエビデンスとしても使用できます。 - 私のこれまでの経験上、このレベルの設計や実装方針は細かすぎて開発者任せになることが多い。アプリ開発者からすると、製造や単体試験をやりきることに主眼が置かれ、今回のような後工程での事象に対して考慮するような考えは乏しいです。設計、開発ガイド、教育等で「後工程でのトラブル解決が容易になるような考慮」を盛り込むことをおすすめします。
- 通常、このようなログは細かすぎて環境が変わると大量のログが出力される可能性があります。そのため、必要な場合だけログ出力するようTRACEレベルでログ出力することをお薦めします。(感覚的には、DEBUGは点で動作を把握、TRACEは面で動作を把握するイメージで使い分けています。)
サンプルプログラム
サンプルの説明
主にFTPClientクラスを使って、FTP操作を行います。
このFTPClientに対して、addProtocolCommandListenerメソッドでトレースするためのクラス(ProtocolCommandListener)を指定します。
ProtocolCommandListenerでは、FTPClientでコマンドを実行する際に呼び出されるprotocolCommandSentメソッド、FTPサーバからの応答があった場合に呼び出されるprotocolReplyReceivedメソッドが定義されています。目的に応じて、それぞれのメソッドでログを出力します。
なお、FTP操作部分のコードですが、サンプルのため簡易的な実装になっています。業務で使用する場合、FTPReply#isPositiveCompletion()等を使って、コマンド個別の正常性確認をお薦めします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package test; import java.io.IOException; import org.apache.commons.net.ProtocolCommandEvent; import org.apache.commons.net.ProtocolCommandListener; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FTPCommandTraceTest { private static final String FTP_SERVER = "192.168.220.3"; private static final String FTP_USER_NAME = "valuser"; private static final String FTP_USER_PASS = "valuser"; private static Logger logger = LoggerFactory.getLogger(FTPCommandTraceTest.class); public static void main(String[] args) throws IOException { FTPClient client = new FTPClient(); client.addProtocolCommandListener(new ExampleCommandListener()); // FTPサーバ操作例 client.connect(FTP_SERVER); client.login(FTP_USER_NAME, FTP_USER_PASS); for (FTPFile file : client.listFiles(".")) { logger.debug("name={}", file.getName()); } client.quit(); client.disconnect(); } } class ExampleCommandListener implements ProtocolCommandListener { private static Logger logger = LoggerFactory.getLogger(ExampleCommandListener.class); public void protocolCommandSent(ProtocolCommandEvent event) { logger.trace("> " + event.getCommand()); } public void protocolReplyReceived(ProtocolCommandEvent event) { StringBuilder sb = new StringBuilder(); logger.trace("< " + event.getMessage()); } } |
実行に必要なライブラリはcommons-netとなります。
ログ出力のために、slf4j、slf4j-simpleを追加しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>test</groupId> <artifactId>ftp-example</artifactId> <version>1.0.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- FTP --> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.6</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> </dependency> </dependencies> </project> |
参考ですが、今回の例ではログ出力ライブラリとしてslf4j-simpleを使用しています。このライブラリで、trace以上のログを出力するための設定を行っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | # SLF4J's SimpleLogger configuration file # Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. # Default logging detail level for all instances of SimpleLogger. # Must be one of ("trace", "debug", "info", "warn", or "error"). # If not specified, defaults to "info". #org.slf4j.simpleLogger.defaultLogLevel=info org.slf4j.simpleLogger.defaultLogLevel=trace # Logging detail level for a SimpleLogger instance named "xxxxx". # Must be one of ("trace", "debug", "info", "warn", or "error"). # If not specified, the default logging detail level is used. #org.slf4j.simpleLogger.log.xxxxx= #org.slf4j.simpleLogger.log.test.FTPCommandTraceTest=trace # Set to true if you want the current date and time to be included in output messages. # Default is false, and will output the number of milliseconds elapsed since startup. #org.slf4j.simpleLogger.showDateTime=false org.slf4j.simpleLogger.showDateTime=true # The date and time format to be used in the output messages. # The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. # If the format is not specified or is invalid, the default format is used. # The default format is yyyy-MM-dd HH:mm:ss:SSS Z. #org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss:SSS Z # Set to true if you want to output the current thread name. # Defaults to true. #org.slf4j.simpleLogger.showThreadName=true # Set to true if you want the Logger instance name to be included in output messages. # Defaults to true. #org.slf4j.simpleLogger.showLogName=true # Set to true if you want the last component of the name to be included in output messages. # Defaults to false. #org.slf4j.simpleLogger.showShortLogName=false |
実行結果例
FTPClientからFTPサーバへの要求は”>”、FTPサーバからFTPClientへの応答は”<“として、TRACEレベルでログを出力しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ... [main] TRACE test.ExampleCommandListener - < 220 (vsFTPd 3.0.2) ... ... [main] TRACE test.ExampleCommandListener - > USER ... [main] TRACE test.ExampleCommandListener - < 331 Please specify the password. ... ... [main] TRACE test.ExampleCommandListener - > PASS ... [main] TRACE test.ExampleCommandListener - < 230 Login successful. ... ... [main] TRACE test.ExampleCommandListener - > SYST ... [main] TRACE test.ExampleCommandListener - < 215 UNIX Type: L8 ... ... [main] TRACE test.ExampleCommandListener - > PORT ... [main] TRACE test.ExampleCommandListener - < 200 PORT command successful. Consider using PASV. ... ... [main] TRACE test.ExampleCommandListener - > LIST ... [main] TRACE test.ExampleCommandListener - < 150 Here comes the directory listing. ... ... [main] TRACE test.ExampleCommandListener - < 226 Directory send OK. ... ... [main] DEBUG test.FTPCommandTraceTest - name=test.txt ... [main] DEBUG test.FTPCommandTraceTest - name=work ... [main] TRACE test.ExampleCommandListener - > QUIT ... [main] TRACE test.ExampleCommandListener - < 221 Goodbye. |