Spring BootでログやActuatorにバージョン情報を含める
ログやActuatorにバージョン情報を含めておくと、本番環境でどのバージョンのアプリケーションが実行されているか簡単に確認できるので便利です。
ビルド時にapplication.ymlにバージョン情報を含める
Gradleでは、以下のようなビルドスクリプトを書くとapplication.ymlの文字列を置換できます。
version = System.getenv('TAG_NAME') ?: 'SNAPSHOT' processResources { filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [ 'APP_NAME': project.name, 'APP_VERSION': project.version ]) }
ここでは、ビルド時に TAG_NAME
という環境変数にバージョン番号が設定されている前提で、application.ymlを置換しています。バージョン番号でなくてもコミットハッシュや日時、ビルド番号など何でも構いません。
例えば、application.ymlに以下を書くとアプリケーション名やバージョン番号に置換されます。
app: name: "@APP_NAME@" version: "@APP_VERSION@"
なお、Spring Bootの公式ドキュメントではSimpleTemplateEngineによる置換が紹介されていますが、Placeholderと競合するので使いづらいです。ReplaceTokensを利用する方がおすすめです。
72. Properties & configuration
ログにバージョン情報を含める
Spring Cloud Sleuthを使用している場合は、以下を設定するとログの全行にバージョン情報が付加されます。
spring: application: name: "@APP_NAME@-@APP_VERSION@"
2016-02-26 11:15:47.561 INFO [example-1.0.0,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] com.example.Application : Hello
(追記) spring.application.nameはService Discoveryのキーに使われるので、上記は避けた方がよいです。
@making 確かに。バージョンも入れたい場合は自分でMDCでカスタマイズする方がよさそうですね。
— いわてぃ (@int128) 2017年2月16日
起動時に出るだけでよいという場合は、適当なプロパティにバージョン情報を入れてログで出力するとよいでしょう。
@Slf4j @SpringBootApplication class App { static void main(String[] args) { def context = SpringApplication.run(App, args) log.info('Started {}', context.environment.getProperty('info.app.name')) } }
Actuatorでバージョン情報を返す
Actuatorを使用している場合は、以下のように設定するとREST APIでバージョン情報を取得できるようになります。
info: app: name: "@APP_NAME@" version: "@APP_VERSION@"
GET /management/info
にアクセスすると以下のようなJSONが得られます。
{ "app": { "name": "example", "version": "1.0.0" } }
Actuatorでバージョン情報が取れると便利なのでぜひ使ってみてください。
ターミナルのウィンドウタイトルにホスト名などを入れる
久しぶりにzshネタです。
複数のタブを開いていろんなサーバにSSHしていると区別が付かなくなってきたので、ウィンドウタイトルに実行コマンド、ホスト名、カレントディレクトリを入れてみました。zshの組み込みコマンドだけで実現してみました。
function _window_title_cmd () { local pwd="${PWD/~HOME/~}" print -n "\e]0;" print -n "${pwd##*/} (${HOST%%.*})" print -n "\a" } function _window_title_exec () { local pwd="${PWD/~HOME/~}" print -n "\e]0;" print -n "${1%% *}:${pwd##*/} (${HOST%%.*})" print -n "\a" } [[ "$TERM" =~ "^xterm" ]] && { add-zsh-hook precmd _window_title_cmd add-zsh-hook preexec _window_title_exec }
コマンドプロンプトのカスタマイズは下記のエントリで紹介しています。ご参考まで。
Spring Security OAuth2でリクエストログを出力する
Spring Security OAuth2でアクセストークンのリクエストとレスポンスのログを出力するには、Apache HttpClientを使うと簡単です。
概要
- Spring Security OAuth2のアクセストークン取得はRestTemplateを利用しています。
- RestTemplateはデフォルトではHttpURLConnectionを利用しますが、HttpClientを利用することもできます。
- HttpClientはログレベルを設定するだけでヘッダやボディのログを出力してくれます。
OAuth2RestTemplateの場合
OAuth2RestTemplateを生成する際に以下を行うことで、アクセストークンのリクエストやレスポンスのログ出力が有効になります。アクセストークン取得後のリクエストやレスポンスは別物なので注意してください。
@Configuration class AppConfiguration { @Bean OAuth2RestTemplate oAuth2RestTemplate(OAuth2ClientContext oAuth2ClientContext) { def restTemplate = new OAuth2RestTemplate(resource(), oAuth2ClientContext) // HttpClientを利用するように設定する def requestFactory = new HttpComponentsClientHttpRequestFactory() def accessTokenProviders = Arrays.asList( new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider() ) accessTokenProviders*.requestFactory = requestFactory restTemplate.accessTokenProvider = new AccessTokenProviderChain(accessTokenProviders) restTemplate } private resource() { def details = new ClientCredentialsResourceDetails() // TODO: client_id, client_secretなどを設定する details } }
@RestController class HelloController { @Inject OAuth2RestTemplate restTemplate @GetMapping('/hello') Hello helloWorld() { // OAuth2RestTemplateを利用してAPIを実行する def response = restTemplate.getForEntity("https://.../hello", Hello) } }
OAuth2FeignRequestInterceptorの場合
OAuth2FeignRequestInterceptorはAccessTokenProviderを設定するメソッドを提供していないため、クラスを継承して拡張する必要があります。
class HelloOAuth2FeignRequestInterceptor extends OAuth2FeignRequestInterceptor { private final OAuth2ClientContext oAuth2ClientContext private final AccessTokenProvider accessTokenProvider = { // HttpClientを利用するように設定する def requestFactory = new HttpComponentsClientHttpRequestFactory() def accessTokenProviders = Arrays.asList( new AuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(), new ClientCredentialsAccessTokenProvider() ) accessTokenProviders*.requestFactory = requestFactory new AccessTokenProviderChain(accessTokenProviders) }() private final OAuth2ProtectedResourceDetails resource = { def details = new ClientCredentialsResourceDetails() // TODO: client_id, client_secretなどを設定する details }() def HelloOAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext) { super(oAuth2ClientContext, null) this.oAuth2ClientContext = oAuth2ClientContext } /** * @see OAuth2FeignRequestInterceptor#acquireAccessToken() */ @Override protected OAuth2AccessToken acquireAccessToken() { // (中略) OAuth2FeignRequestInterceptor#acquireAccessToken() の実装をコピー } }
OAuth2FeignRequestInterceptorに setAccessTokenProvider()
メソッドを追加するPull Requestがあるので、将来のバージョンでは改善されるかもしれません。
ログレベルの設定
HttpClientがログを出力するようにapplication.ymlを設定します。
logging.level: # ヘッダのみ出力する場合 org.apache.http.headers: DEBUG # ヘッダとボディを出力する場合 org.apache.http.wire: DEBUG
アクセストークンリクエストのカスタマイズ(おまけ)
上記と同様に setTokenRequestEnhancer()
を利用することで、アクセストークンのリクエストをカスタマイズできます。例えば、client_id
や client_secret
をボディに入れるといった独自仕様に対応できます。
@Bean OAuth2RestTemplate oAuth2RestTemplate(OAuth2ClientContext oAuth2ClientContext) { def restTemplate = new OAuth2RestTemplate(resource(), oAuth2ClientContext) def accessTokenProvider = new ClientCredentialsAccessTokenProvider() // HttpClientを利用するように設定する accessTokenProvider.requestFactory = new HttpComponentsClientHttpRequestFactory() // アクセストークンリクエストをカスタマイズする accessTokenProviders*.tokenRequestEnhancer = new RequestEnhancer() { @Override void enhance(AccessTokenRequest request, OAuth2ProtectedResourceDetails resource, MultiValueMap<String, String> form, HttpHeaders headers) { } } restTemplate.accessTokenProvider = accessTokenProvider restTemplate }
ご参考まで。