スキップしてメイン コンテンツに移動

Spring Frameworkでlog4jやAOPロギング

いろいろ試行錯誤したので、できた所までの覚え書き。

環境

  • SpringSource Toole Suite :2.8.1.RELEASE
  • JDK:1.6.0_30

実装のベース

Spring Frameworkへのロギングについてはググればいろいろ出てくると思いますが、今回試行錯誤したのはSpring Source公式サンプルの

・Samples | SpringSource.org
http://www.springsource.org/samples

「Spring Framework Samples」のmvc-ajaxプロジェクトへの実装。導入と動かし方等は過去の記事で。

log4j.xmlを操作してみる

まず、サーバを実行すると

WS000340

このようなログがコンソールへ出力されます。日付が入っていないので、試しに日付が出るようにlog4j.xmlを修正してみたいと思います。
ディレクトリで言うと、{保存先のディレクトリ}mvc-ajax\trunk\src\main\resources\log4j.xml

パッケージエクスプローラで言うと、src/main/resourcesに入っています。後は以下のように日付フォーマットを記述して

  <!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss}%-5p: %c - %m%n" />
</layout>
</appender>


サーバを起動させると、日付が挿入されました。


WS000341




次にこれをファイルに出力してみます。これもlog4j.xmlへ以下を追加します。



    <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="D:/logs/mvc-ajax.log" />
<param name="MaxFileSize" value="1024KB" />
<param name="MaxBackupIndex" value="10" />
<param name="Append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%d{yyyy/MM/dd HH:mm:ss} %p:%r:%c:%m%n" />
</layout>
</appender>


設定の詳細については、log4jのリファレンスにて。。D:/logs/mvc-ajax.logへログファイルが出力される設定です。このままではファイルは出力されないので、同ファイルのRoot Loggerへ1行追加します。



  <!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
<appender-ref ref="FILE" />
</root>


これでサーバを起動すると、コンソールとファイルへlog4jのログが出力されます。



オリジナルのログを出力する



上記では、デフォルトで出力されるログについてですが、任意の場所で出力できるようJavaコードを修正してみたいと思います。動作を確認しやすいようにAccountController.javaを対象にします。importへorg.apache.log4j.Loggerを記述。インスタンス生成時とGetリクエストが呼ばれたタイミングでログを出力するようにしてみます。



public class AccountController {
private static Logger logger = Logger.getLogger(AccountController.class);

private Map<Long, Account> accounts = new ConcurrentHashMap<Long, Account>();

private Validator validator;

@Autowired
public AccountController(Validator validator) {
logger.info("create instance");
this.validator = validator;
}

@RequestMapping(method=RequestMethod.GET)
public String getCreateForm(Model model) {
logger.info("GET Request");
model.addAttribute(new Account());
return "account/createForm";
}


このままでは、エラーは出ませんが埋め込んだロギングを認識しません。log4j.xmlへorg.springframework.samples.mvc.ajax.account配下のクラスでログ出力するように以下の記述を追加します。



  <logger name="org.springframework.samples.mvc.ajax.account">
<level value="info" />
</logger>


これでサーバを起動したタイミングで、



2012-01-07 04:05:05 INFO : org.springframework.samples.mvc.ajax.account.AccountController - create instance



という出力や、クリックしたタイミングで、



WS000342



2012-01-07 04:05:36 INFO : org.springframework.samples.mvc.ajax.account.AccountController - GET Request



のように表示されます。



アスペクト指向(AOP)でロギングを自動的に埋め込む



コードをいじらなくても、AOPでメソッドの最初と終りにログを埋め込める魔法のようなやり方だと思ってましたが、なかなか一筋縄にいかず、とりあえずできた方法を掲載します。このmvc-ajaxというサンプルでなく、単にAOPの実装を動かしてみたい場合は、ググればもっと簡単なやり方があるのでそちらを。。



まず、mvc-ajaxはデフォルトでアスペクト指向を使えないので、mavenへdependencyを追加してやります。



mvc-ajaxのプロジェクト右クリック → Maven → Add Dependencyを選択します。



WS000343



aspectで検索かけると2件ヒットするので、2件とも追加します。



そして以下の3ファイルをorg.springframework.samples.mvc.ajax.accountへ追加します。




  • IHoge.java・・・・・・Hogehoge.javaのインタフェース。ロギングしたいメソッドを記述します


  • Hogehoge.java・・・・ロギングされるクラス。


  • MethodLogger.java・・Hogehoge.javaへ埋め込むロギング処理



せっかくベースとなるAccoutController.javaがあるのでそちらへロギングを埋め込もうとしましたが、エラーが出て実装できないのでHogehoge.javaへロギングを埋め込み、AccountControllerから呼び出す形でログを出力したいと思います。



IHoge.java



package org.springframework.samples.mvc.ajax.account;

public interface IHoge {
public void testMethod();
}


Hogehoge.java



package org.springframework.samples.mvc.ajax.account;

public class Hogehoge implements IHoge{

public void testMethod() {
System.out.println("hello world!");
}
}


MethodLogger.java



package org.springframework.samples.mvc.ajax.account;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Component
@Aspect
public class MethodLogger {

@Around("execution(* org.springframework.samples.mvc.ajax.account.Hogehoge.*(..))")
public Object timeMethod(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();

Logger.getLogger(this.getClass()).info("Method Start");

stopWatch.start();

Object retVal = joinPoint.proceed();

stopWatch.stop();

StringBuffer logMessageStringBuffer = new StringBuffer();
logMessageStringBuffer.append(joinPoint.getTarget().getClass().getName());
logMessageStringBuffer.append(".");
logMessageStringBuffer.append(joinPoint.getSignature().getName());
logMessageStringBuffer.append("(");
logMessageStringBuffer.append(joinPoint.getArgs());
logMessageStringBuffer.append(")");
logMessageStringBuffer.append(" execution time: ");
logMessageStringBuffer.append(stopWatch.getTotalTimeMillis());
logMessageStringBuffer.append(" ms");
logMessageStringBuffer.append(" end");

Logger.getLogger(this.getClass()).info(logMessageStringBuffer.toString());

return retVal;
}
}


この処理の、@Around("execution(* org.springframework.samples.mvc.ajax.account.Hogehoge.*(..))")でロギングを埋め込みたいメソッドを指定しています。今回はワイルドカードを使ってHogehogeクラス全てのメソッドへ適用するようにしています。で、処理開始と処理終了のロギングをObject retVal = joinPoint.proceed();の前後で行っている所がポイントです。この前後にLogger.getLogger(this.getClass()).info("Method Start");のようなメソッドの開始(または終了)だと分かる処理を記述してやります。



これらの処理をbeen定義ファイルで有効にするにはservlet-context.xmlを修正してやります。ちゃんと実装する場合には、このファイルではなく別途been定義ファイルを作成した方が良いような記述を見受けました。以下beansのタグ内とaopタグbeanタグを追加します。



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

(中略)

<aop:aspectj-autoproxy />

<bean class="org.springframework.samples.mvc.ajax.account.Hogehoge" />

</beans>


で最後にAccountControllerからメソッド呼び出しの処理を追加します。今回はGetリクエスト処理内へ追加しました。



public class AccountController {
private static Logger logger = Logger.getLogger(AccountController.class);

private Map<Long, Account> accounts = new ConcurrentHashMap<Long, Account>();

private Validator validator;

@Autowired
private IHoge hoge;

@Autowired
public AccountController(Validator validator) {
logger.info("create instance AccountController");
this.validator = validator;
}

@RequestMapping(method=RequestMethod.GET)
public String getCreateForm(Model model) {
logger.info("GET Request AccountController");

this.hoge.testMethod();

model.addAttribute(new Account());
return "account/createForm";
}


private IHoge hogeを定義し、このインタフェースへロギング対象ということで@Autowiredを記述。Getリクエストを受けてthis.hoge.testMethod()のメソッドを呼び出しすよう修正しました。



これでサーバを起動し、



WS000342



クリックすると、メソッド呼び出しで以下のようにログが出力されます。



2012-01-07 04:48:40 INFO : org.springframework.samples.mvc.ajax.account.MethodLogger - Method Start

hello world!


2012-01-07 04:48:40 INFO : org.springframework.samples.mvc.ajax.account.MethodLogger - org.springframework.samples.mvc.ajax.account.Hogehoge.testMethod([Ljava.lang.Object;@4f13f501) execution time: 0 ms end




太字の部分が埋め込まれたロギング出力です。



AccountControllerクラスでHogehogeクラスをnewしてインスタンスを生成した場合だとロギングは埋め込まれません。必ずインタフェースからメソッドを呼び出す必要があります。これがSpring Frameworkの思想との事ですが、自由にロギングを追加/削除できるイメージでしたが、このやり方だとある程度最初からAOPを意識した実装にしておかないといけないですね。。(´ヘ`;)

コメント

このブログの人気の投稿

Javaでprivateなfieldやmethodにアクセスする

JUnitでテストしてると、privateなフィールドにアクセスして、値を参照したりセットしたりしたくなるわけですが、よく使うのでメモしておきます。 例えば、次のような対象のクラスがあるとします。 public class ParentClass { private String hoge = "ParentClass!!"; public void dispMsg() { System.out.println("dispMsg:" + hoge); } private void privateDispMsg(String msg) { System.out.println("dispMsg:" + msg); } }

GolangでWindows GUIアプリケーション

GUIアプリ作成の前提 社内ツールとしてexeで配布 開発環境はGoLandを使う 社内ツールとしてexeを配布ということであれば、Visual StudioでC#による開発だと思います。しかしながら、Go言語を習得したいのと、GoLandの補完機能が便利で、Android Studio使っていたこともあり、とっつきやすいという点からGo縛りでGUIアプリケーションを考えたいと思います。 lxn/walk Windows application library kit for Go. Windows向けしか考えていないので、まずこのライブラリなのですが、ボタンやコンボボックスが思ったように並ばなかったり、手軽にイメージボタンを配置したりなど、簡単にレイアウトを変更できない課題に直面しました。レイアウト作成だけで時間を取られてしまいます。そう言えば、Androidアプリ開発のときは、XMLでデザイン部分を切り離してたのを思い出して、今回の調査の運びとなりました。 fyne-io/fyne Cross platform native GUIs designed for Go based on Material Design. Supports: Linux, macOS, Windows, BSD, iOS and Android. walkと比べると、クロスプラットフォームで作成できるのですが、こちらもコードの中にデザインを書いていく形でした。 therecipe/qt therecipe/qt allows you to write Qt applications entirely in Go or JavaScript. Qt Creatorなるものがあるようで、デザイン部分を切り離せる印象はあるものの、最新のPCでもビルドに時間がかかるようで、学習コストもかかるという記事を見かけて断念しました。 どうしたものかとツイートしましたら、下記のようにご助言をいただいた。 同じ課題に対し色々試した結果、PWAのフルキャッシュドに落ち着いた。中身はGo+WASMで。 https://t.co/e60whDTV16 — のぼのぼ📡 (@nobonobo) July 21, 2020 PWA 早速調べてみました。Googleが進めているプロジェクトで、ネイティブアプリのよ

Eclipseの高速化メモ

Eclipseが重いと一言に言っても、いろいろな工程での話があると思いますが、過去記事のなども含めてこの記事にピックアップしておきたいと思います。以下はWindows環境での話です。