Kata benda buku catatan , /lɑɡ bʊk/: Sebuah buku yang mencatat pengukuran dari catatan kapal, bersama dengan rincian penting perjalanan lainnya.
Logbook adalah pustaka Java yang dapat diperluas untuk mengaktifkan pencatatan permintaan dan respons lengkap untuk berbagai teknologi sisi klien dan server. Ini memenuhi kebutuhan khusus dengan a) memungkinkan pengembang aplikasi web untuk mencatat lalu lintas HTTP apa pun yang diterima atau dikirim oleh aplikasi b) dengan cara yang memudahkan untuk bertahan dan menganalisisnya nanti. Hal ini dapat berguna untuk analisis log tradisional, memenuhi persyaratan audit, atau menyelidiki masalah lalu lintas historis tertentu.
Buku catatan siap digunakan untuk sebagian besar pengaturan umum. Bahkan untuk aplikasi dan teknologi yang tidak umum, implementasi antarmuka yang diperlukan untuk menghubungkan perpustakaan/kerangka kerja/dll haruslah mudah. untuk itu.
Tambahkan ketergantungan berikut ke proyek Anda:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-core</ artifactId >
< version >${logbook.version}</ version >
</ dependency >
Untuk kompatibilitas mundur Spring 5 / Spring Boot 2 harap tambahkan impor berikut:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-servlet</ artifactId >
< version >${logbook.version}</ version >
< classifier >javax</ classifier >
</ dependency >
Modul/artefak tambahan dari Logbook selalu memiliki nomor versi yang sama.
Alternatifnya, Anda dapat mengimpor bill of material kami ...
< dependencyManagement >
< dependencies >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-bom</ artifactId >
< version >${logbook.version}</ version >
< type >pom</ type >
< scope >import</ scope >
</ dependency >
</ dependencies >
</ dependencyManagement >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-core</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-httpclient</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-jaxrs</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-json</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-netty</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-okhttp</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-okhttp2</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-servlet</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-spring-boot-starter</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-ktor-common</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-ktor-client</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-ktor-server</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-ktor</ artifactId >
</ dependency >
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-logstash</ artifactId >
</ dependency >
Pencatat buku catatan harus dikonfigurasikan ke tingkat penelusuran untuk mencatat permintaan dan respons. Dengan Spring Boot 2 (menggunakan Logback) hal ini dapat dilakukan dengan menambahkan baris berikut ke application.properties
Anda
logging.level.org.zalando.logbook: TRACE
Semua integrasi memerlukan sebuah instance dari Logbook
yang menyatukan semua konfigurasi dan menyatukan semua bagian yang diperlukan. Anda dapat membuatnya menggunakan semua default:
Logbook logbook = Logbook . create ();
atau buat versi yang disesuaikan menggunakan LogbookBuilder
:
Logbook logbook = Logbook . builder ()
. condition ( new CustomCondition ())
. queryFilter ( new CustomQueryFilter ())
. pathFilter ( new CustomPathFilter ())
. headerFilter ( new CustomHeaderFilter ())
. bodyFilter ( new CustomBodyFilter ())
. requestFilter ( new CustomRequestFilter ())
. responseFilter ( new CustomResponseFilter ())
. sink ( new DefaultSink (
new CustomHttpLogFormatter (),
new CustomHttpLogWriter ()
))
. build ();
Buku catatan dulunya memiliki strategi yang sangat kaku bagaimana melakukan pencatatan permintaan/respons:
Beberapa pembatasan tersebut dapat dikurangi dengan penerapan HttpLogWriter
khusus, namun pembatasan tersebut tidak pernah ideal.
Dimulai dengan versi 2.0 Logbook kini hadir dengan pola Strategi sebagai intinya. Pastikan Anda membaca dokumentasi antarmuka Strategy
untuk memahami implikasinya.
Buku catatan hadir dengan beberapa strategi bawaan:
BodyOnlyIfStatusAtLeastStrategy
StatusAtLeastStrategy
WithoutBodyStrategy
Mulai versi 3.4.0, Logbook dilengkapi dengan fitur bernama Attribute Extractor . Atribut pada dasarnya adalah daftar pasangan kunci/nilai yang dapat diekstraksi dari permintaan dan/atau respons, dan dicatat bersama mereka. Ide ini muncul dari edisi 381, di mana sebuah fitur diminta untuk mengekstrak klaim subjek dari token JWT di header otorisasi.
Antarmuka AttributeExtractor
memiliki dua metode extract
: Metode yang dapat mengekstrak atribut hanya dari permintaan, dan metode yang memiliki permintaan dan respons pada info manfaatnya. Keduanya mengembalikan turunan dari kelas HttpAttributes
, yang pada dasarnya merupakan Map<String, Object>
yang mewah. Perhatikan bahwa karena nilai peta bertipe Object
, nilai tersebut harus memiliki metode toString()
yang tepat agar nilai tersebut muncul di log dengan cara yang bermakna. Alternatifnya, pemformat log dapat mengatasi hal ini dengan menerapkan logika serialisasi mereka sendiri. Misalnya, pemformat log bawaan JsonHttpLogFormatter
menggunakan ObjectMapper
untuk membuat serialisasi nilai.
Berikut ini contohnya:
final class OriginExtractor implements AttributeExtractor {
@ Override
public HttpAttributes extract ( final HttpRequest request ) {
return HttpAttributes . of ( "origin" , request . getOrigin ());
}
}
Buku catatan kemudian harus dibuat dengan mendaftarkan ekstraktor atribut ini:
final Logbook logbook = Logbook . builder ()
. attributeExtractor ( new OriginExtractor ())
. build ();
Ini akan menghasilkan log permintaan yang menyertakan sesuatu seperti:
"attributes":{"origin":"LOCAL"}
Untuk contoh lebih lanjut, lihat kelas JwtFirstMatchingClaimExtractor
dan JwtAllMatchingClaimsExtractor
. Yang pertama mengekstrak klaim pertama yang cocok dengan daftar nama klaim dari token JWT permintaan. Yang terakhir mengekstrak semua klaim yang cocok dengan daftar nama klaim dari token JWT permintaan.
Jika Anda perlu memasukkan beberapa AttributeExtractor
, Anda dapat menggunakan kelas CompositeAttributeExtractor
:
final List < AttributeExtractor > extractors = List . of (
extractor1 ,
extractor2 ,
extractor3
);
final Logbook logbook = Logbook . builder ()
. attributeExtractor ( new CompositeAttributeExtractor ( extractors ))
. build ();
Buku catatan bekerja dalam beberapa fase berbeda:
Setiap fase diwakili oleh satu atau lebih antarmuka yang dapat digunakan untuk penyesuaian. Setiap fase memiliki standar yang masuk akal.
Mencatat pesan HTTP dan menyertakan isinya adalah tugas yang agak mahal, jadi masuk akal untuk menonaktifkan logging untuk permintaan tertentu. Kasus penggunaan yang umum adalah mengabaikan permintaan pemeriksaan kondisi dari penyeimbang beban, atau permintaan apa pun ke titik akhir manajemen yang biasanya dikeluarkan oleh pengembang.
Mendefinisikan suatu kondisi semudah menulis Predicate
khusus yang memutuskan apakah suatu permintaan (dan respon terkaitnya) harus dicatat atau tidak. Alternatifnya, Anda dapat menggunakan dan menggabungkan predikat yang telah ditentukan sebelumnya:
Logbook logbook = Logbook . builder ()
. condition ( exclude (
requestTo ( "/health" ),
requestTo ( "/admin/**" ),
contentType ( "application/octet-stream" ),
header ( "X-Secret" , newHashSet ( "1" , "true" ):: contains )))
. build ();
Pola pengecualian, misalnya /admin/**
, secara longgar mengikuti gaya pola jalur Ant tanpa mempertimbangkan string kueri URL.
Tujuan Pemfilteran adalah untuk mencegah pencatatan bagian sensitif tertentu dari permintaan dan tanggapan HTTP. Ini biasanya mencakup header Otorisasi , namun bisa juga berlaku untuk kueri teks biasa atau parameter formulir tertentu — misalnya password .
Buku catatan mendukung berbagai jenis filter:
Jenis | Beroperasi pada | Berlaku untuk | Bawaan |
---|---|---|---|
QueryFilter | String kueri | meminta | access_token |
PathFilter | Jalur | meminta | tidak ada |
HeaderFilter | Header (pasangan nilai kunci tunggal) | keduanya | Authorization |
BodyFilter | Tipe Konten dan isi | keduanya | json: access_token dan refresh_token formulir: client_secret , password dan refresh_token |
RequestFilter | HttpRequest | meminta | Ganti badan biner, multipart, dan aliran. |
ResponseFilter | HttpResponse | tanggapan | Ganti badan biner, multipart, dan aliran. |
QueryFilter
, PathFilter
, HeaderFilter
dan BodyFilter
merupakan level yang relatif tinggi dan harus mencakup semua kebutuhan di ~90% dari semua kasus. Untuk pengaturan yang lebih rumit, kita harus kembali ke varian tingkat rendah, yaitu RequestFilter
dan ResponseFilter
(bersama dengan ForwardingHttpRequest
/ ForwardingHttpResponse
).
Anda dapat mengonfigurasi filter seperti ini:
import static org . zalando . logbook . core . HeaderFilters . authorization ;
import static org . zalando . logbook . core . HeaderFilters . eachHeader ;
import static org . zalando . logbook . core . QueryFilters . accessToken ;
import static org . zalando . logbook . core . QueryFilters . replaceQuery ;
Logbook logbook = Logbook . builder ()
. requestFilter ( RequestFilters . replaceBody ( message -> contentType ( "audio/*" ). test ( message ) ? "mmh mmh mmh mmh" : null ))
. responseFilter ( ResponseFilters . replaceBody ( message -> contentType ( "*/*-stream" ). test ( message ) ? "It just keeps going and going..." : null ))
. queryFilter ( accessToken ())
. queryFilter ( replaceQuery ( "password" , "<secret>" ))
. headerFilter ( authorization ())
. headerFilter ( eachHeader ( "X-Secret" :: equalsIgnoreCase , "<secret>" ))
. build ();
Anda dapat mengonfigurasi filter sebanyak yang Anda inginkan - filter tersebut akan berjalan secara berurutan.
Anda dapat menerapkan pemfilteran Jalur JSON ke badan JSON. Berikut beberapa contohnya:
import static org . zalando . logbook . json . JsonPathBodyFilters . jsonPath ;
import static java . util . regex . Pattern . compile ;
Logbook logbook = Logbook . builder ()
. bodyFilter ( jsonPath ( "$.password" ). delete ())
. bodyFilter ( jsonPath ( "$.active" ). replace ( "unknown" ))
. bodyFilter ( jsonPath ( "$.address" ). replace ( "X" ))
. bodyFilter ( jsonPath ( "$.name" ). replace ( compile ( "^( \ w).+" ), "$1." ))
. bodyFilter ( jsonPath ( "$.friends.*.name" ). replace ( compile ( "^( \ w).+" ), "$1." ))
. bodyFilter ( jsonPath ( "$.grades.*" ). replace ( 1.0 ))
. build ();
Perhatikan contoh berikut, sebelum dan sesudah pemfilteran diterapkan:
{
"id" : 1 ,
"name" : " Alice " ,
"password" : " s3cr3t " ,
"active" : true ,
"address" : " Anhalter Straße 17 13, 67278 Bockenheim an der Weinstraße " ,
"friends" : [
{
"id" : 2 ,
"name" : " Bob "
},
{
"id" : 3 ,
"name" : " Charlie "
}
],
"grades" : {
"Math" : 1.0 ,
"English" : 2.2 ,
"Science" : 1.9 ,
"PE" : 4.0
}
}
{
"id" : 1 ,
"name" : " Alice " ,
"active" : " unknown " ,
"address" : " XXX " ,
"friends" : [
{
"id" : 2 ,
"name" : " B. "
},
{
"id" : 3 ,
"name" : " C. "
}
],
"grades" : {
"Math" : 1.0 ,
"English" : 1.0 ,
"Science" : 1.0 ,
"PE" : 1.0
}
}
Buku catatan menggunakan id korelasi untuk menghubungkan permintaan dan tanggapan. Hal ini memungkinkan permintaan dan respons terkait kecocokan yang biasanya ditempatkan di tempat berbeda dalam file log.
Jika penerapan default id korelasi tidak mencukupi untuk kasus penggunaan Anda, Anda dapat memberikan penerapan khusus:
Logbook logbook = Logbook . builder ()
. correlationId ( new CustomCorrelationId ())
. build ();
Pemformatan pada dasarnya menentukan bagaimana permintaan dan respons akan diubah menjadi string. Pemformat tidak menentukan di mana permintaan dan tanggapan dicatat — penulislah yang melakukan pekerjaan itu.
Logbook hadir dengan dua formatter default yang berbeda: HTTP dan JSON .
HTTP adalah gaya pemformatan default, yang disediakan oleh DefaultHttpLogFormatter
. Ini terutama dirancang untuk digunakan untuk pengembangan dan debugging lokal, bukan untuk penggunaan produksi. Ini karena JSON tidak mudah dibaca mesin.
Incoming Request: 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b
GET http://example.org/test HTTP/1.1
Accept: application/json
Host: localhost
Content-Type: text/plain
Hello world!
Outgoing Response: 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b
Duration: 25 ms
HTTP/1.1 200
Content-Type: application/json
{ "value" : " Hello world! " }
JSON adalah gaya pemformatan alternatif, yang disediakan oleh JsonHttpLogFormatter
. Tidak seperti HTTP, ini terutama dirancang untuk penggunaan produksi — parser dan konsumen log dapat dengan mudah menggunakannya.
Membutuhkan ketergantungan berikut:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-json</ artifactId >
</ dependency >
{
"origin" : " remote " ,
"type" : " request " ,
"correlation" : " 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b " ,
"protocol" : " HTTP/1.1 " ,
"sender" : " 127.0.0.1 " ,
"method" : " GET " ,
"uri" : " http://example.org/test " ,
"host" : " example.org " ,
"path" : " /test " ,
"scheme" : " http " ,
"port" : null ,
"headers" : {
"Accept" : [ " application/json " ],
"Content-Type" : [ " text/plain " ]
},
"body" : " Hello world! "
}
{
"origin" : " local " ,
"type" : " response " ,
"correlation" : " 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b " ,
"duration" : 25 ,
"protocol" : " HTTP/1.1 " ,
"status" : 200 ,
"headers" : {
"Content-Type" : [ " text/plain " ]
},
"body" : " Hello world! "
}
Catatan: Badan bertipe application/json
(dan application/*+json
) akan dimasukkan ke dalam pohon JSON yang dihasilkan. Yaitu, isi respons JSON tidak akan di-escape dan direpresentasikan sebagai string:
{
"origin" : " local " ,
"type" : " response " ,
"correlation" : " 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b " ,
"duration" : 25 ,
"protocol" : " HTTP/1.1 " ,
"status" : 200 ,
"headers" : {
"Content-Type" : [ " application/json " ]
},
"body" : {
"greeting" : " Hello, world! "
}
}
Common Log Format (CLF) adalah format file teks standar yang digunakan oleh server web saat membuat file log server. Format ini didukung melalui CommonsLogFormatSink
:
185.85.220.253 - - [02/Aug/2019:08:16:41 0000] "GET /search?q=zalando HTTP/1.1" 200 -
Extended Log Format (ELF) adalah format file teks standar, seperti Common Log Format (CLF), yang digunakan oleh server web saat membuat file log, namun file ELF memberikan lebih banyak informasi dan fleksibilitas. Format ini didukung melalui ExtendedLogFormatSink
. Lihat juga dokumen W3C.
Bidang bawaan:
date time c-ip s-dns cs-method cs-uri-stem cs-uri-query sc-status sc-bytes cs-bytes time-taken cs-protocol cs(User-Agent) cs(Cookie) cs(Referrer)
Contoh keluaran log default:
2019-08-02 08:16:41 185.85.220.253 localhost POST /search ?q=zalando 200 21 20 0.125 HTTP/1.1 "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0" "name=value" "https://example.com/page?q=123"
Pengguna dapat mengganti bidang default dengan bidang khusus mereka melalui konstruktor ExtendedLogFormatSink
:
new ExtendedLogFormatSink ( new DefaultHttpLogWriter (), "date time cs(Custom-Request-Header) sc(Custom-Response-Header)" )
Untuk bidang header Http: cs(Any-Header)
dan sc(Any-Header)
, pengguna dapat menentukan header apa pun yang ingin mereka ekstrak dari permintaan.
Bidang lain yang didukung tercantum dalam nilai ExtendedLogFormatSink.Field
, yang dapat dimasukkan ke dalam ekspresi bidang khusus.
cURL adalah gaya pemformatan alternatif, yang disediakan oleh CurlHttpLogFormatter
yang akan merender permintaan sebagai perintah cURL
yang dapat dieksekusi. Tidak seperti JSON, ini terutama dirancang untuk manusia.
curl -v -X GET ' http://localhost/test ' -H ' Accept: application/json '
Lihat HTTP atau berikan cadangannya sendiri untuk mendapatkan tanggapan:
new CurlHttpLogFormatter ( new JsonHttpLogFormatter ());
Splunk adalah gaya pemformatan alternatif, yang disediakan oleh SplunkHttpLogFormatter
yang akan merender permintaan dan respons sebagai pasangan nilai kunci.
origin=remote type=request correlation=2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b protocol=HTTP/1.1 sender=127.0.0.1 method=POST uri=http://example.org/test host=example.org scheme=http port=null path=/test headers={Accept=[application/json], Content-Type=[text/plain]} body=Hello world!
origin=local type=response correlation=2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b duration=25 protocol=HTTP/1.1 status=200 headers={Content-Type=[text/plain]} body=Hello world!
Penulisan menentukan di mana permintaan dan tanggapan yang diformat ditulis. Logbook hadir dengan tiga implementasi: Logger, Stream, dan Chunking.
Secara default, permintaan dan tanggapan dicatat dengan logger slf4j yang menggunakan kategori org.zalando.logbook.Logbook
dan trace
tingkat log. Ini dapat disesuaikan:
Logbook logbook = Logbook . builder ()
. sink ( new DefaultSink (
new DefaultHttpLogFormatter (),
new DefaultHttpLogWriter ()
))
. build ();
Implementasi alternatifnya adalah mencatat permintaan dan respons ke PrintStream
, misalnya System.out
atau System.err
. Ini biasanya merupakan pilihan yang buruk untuk menjalankan produksi, namun terkadang berguna untuk pengembangan dan/atau penyelidikan lokal jangka pendek.
Logbook logbook = Logbook . builder ()
. sink ( new DefaultSink (
new DefaultHttpLogFormatter (),
new StreamHttpLogWriter ( System . err )
))
. build ();
ChunkingSink
akan membagi pesan panjang menjadi beberapa bagian yang lebih kecil dan akan menulisnya satu per satu sambil mendelegasikannya ke sink lain:
Logbook logbook = Logbook . builder ()
. sink ( new ChunkingSink ( sink , 1000 ))
. build ();
Kombinasi HttpLogFormatter
dan HttpLogWriter
cocok untuk sebagian besar kasus penggunaan, namun memiliki keterbatasan. Menerapkan antarmuka Sink
secara langsung memungkinkan kasus penggunaan yang lebih canggih, misalnya menulis permintaan/tanggapan ke penyimpanan persisten terstruktur seperti database.
Beberapa wastafel dapat digabungkan menjadi satu menggunakan CompositeSink
.
Anda harus mendaftarkan LogbookFilter
sebagai Filter
di rantai filter Anda — baik di file web.xml
Anda (harap dicatat bahwa pendekatan xml akan menggunakan semua default dan tidak dapat dikonfigurasi):
< filter >
< filter-name >LogbookFilter</ filter-name >
< filter-class >org.zalando.logbook.servlet.LogbookFilter</ filter-class >
</ filter >
< filter-mapping >
< filter-name >LogbookFilter</ filter-name >
< url-pattern >/*</ url-pattern >
< dispatcher >REQUEST</ dispatcher >
< dispatcher >ASYNC</ dispatcher >
</ filter-mapping >
atau secara terprogram, melalui ServletContext
:
context . addFilter ( "LogbookFilter" , new LogbookFilter ( logbook ))
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST , ASYNC ), true , "/*" );
Hati-hati : Pengiriman ERROR
tidak didukung. Anda sangat disarankan untuk menghasilkan respons kesalahan dalam pengiriman REQUEST
atau ASNYC
.
LogbookFilter
akan, secara default, memperlakukan permintaan dengan isi application/x-www-form-urlencoded
yang tidak berbeda dengan permintaan lainnya, yaitu Anda akan melihat isi permintaan di log. Kelemahan dari pendekatan ini adalah Anda tidak akan dapat menggunakan metode HttpServletRequest.getParameter*(..)
apa pun. Lihat edisi #94 untuk lebih jelasnya.
Pada Logbook 1.5.0, Anda kini dapat menentukan salah satu dari tiga strategi yang menentukan cara Logbook menangani situasi ini dengan menggunakan properti sistem logbook.servlet.form-request
:
Nilai | Kelebihan | Kontra |
---|---|---|
body (standar) | Tubuh dicatat | Kode hilir tidak dapat menggunakan getParameter*() |
parameter | Isi dicatat (tetapi direkonstruksi dari parameter) | Kode hilir tidak dapat menggunakan getInputStream() |
off | Kode hilir dapat memutuskan apakah akan menggunakan getInputStream() atau getParameter*() | Tubuh tidak dicatat |
Aplikasi yang aman biasanya memerlukan pengaturan yang sedikit berbeda. Secara umum, Anda harus menghindari pencatatan permintaan tidak sah, terutama bagian isi, karena hal ini dengan cepat memungkinkan penyerang membanjiri file log Anda — dan, akibatnya, ruang disk Anda yang berharga. Dengan asumsi bahwa aplikasi Anda menangani otorisasi di dalam filter lain, Anda memiliki dua pilihan:
Anda dapat dengan mudah mencapai pengaturan sebelumnya dengan menempatkan LogbookFilter
setelah filter keamanan Anda. Yang terakhir ini sedikit lebih canggih. Anda memerlukan dua instance LogbookFilter
— satu sebelum filter keamanan Anda, dan satu lagi setelahnya:
context . addFilter ( "SecureLogbookFilter" , new SecureLogbookFilter ( logbook ))
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST , ASYNC ), true , "/*" );
context . addFilter ( "securityFilter" , new SecurityFilter ())
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST ), true , "/*" );
context . addFilter ( "LogbookFilter" , new LogbookFilter ( logbook ))
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST , ASYNC ), true , "/*" );
Filter buku log pertama hanya akan mencatat permintaan yang tidak sah. Filter kedua akan mencatat permintaan resmi, seperti biasa.
Modul logbook-httpclient
berisi HttpRequestInterceptor
dan HttpResponseInterceptor
untuk digunakan dengan HttpClient
:
CloseableHttpClient client = HttpClientBuilder . create ()
. addInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. addInterceptorFirst ( new LogbookHttpResponseInterceptor ())
. build ();
Karena LogbookHttpResponseInterceptor
tidak kompatibel dengan HttpAsyncClient
ada cara lain untuk mencatat respons:
CloseableHttpAsyncClient client = HttpAsyncClientBuilder . create ()
. addInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. build ();
// and then wrap your response consumer
client . execute ( producer , new LogbookHttpAsyncResponseConsumer <>( consumer ), callback )
Modul logbook-httpclient5
berisi ExecHandler
untuk digunakan dengan HttpClient
:
CloseableHttpClient client = HttpClientBuilder . create ()
. addExecInterceptorFirst ( "Logbook" , new LogbookHttpExecHandler ( logbook ))
. build ();
Handler harus ditambahkan terlebih dahulu, sehingga kompresi dilakukan setelah logging dan dekompresi dilakukan sebelum logging.
Untuk menghindari perubahan yang dapat menyebabkan gangguan, ada juga HttpRequestInterceptor
dan HttpResponseInterceptor
untuk digunakan dengan HttpClient
, yang berfungsi dengan baik selama kompresi (atau ExecHandlers lainnya) tidak digunakan:
CloseableHttpClient client = HttpClientBuilder . create ()
. addRequestInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. addResponseInterceptorFirst ( new LogbookHttpResponseInterceptor ())
. build ();
Karena LogbookHttpResponseInterceptor
tidak kompatibel dengan HttpAsyncClient
ada cara lain untuk mencatat respons:
CloseableHttpAsyncClient client = HttpAsyncClientBuilder . create ()
. addRequestInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. build ();
// and then wrap your response consumer
client . execute ( producer , new LogbookHttpAsyncResponseConsumer <>( consumer ), callback )
Catatan
Dukungan untuk JAX-RS 2.x
Dukungan JAX-RS 2.x (lama) dihilangkan di Logbook 3.0 menjadi 3.6.
Pada Logbook 3.7, dukungan JAX-RS 2.x telah kembali.
Namun, Anda perlu menambahkan pengklasifikasi javax
untuk menggunakan modul Logbook yang tepat:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-jaxrs</ artifactId >
< version >${logbook.version}</ version >
< classifier >javax</ classifier >
</ dependency >
Anda juga harus memastikan bahwa dependensi berikut ada di classpath Anda. Secara default, logbook-jaxrs
mengimpor jersey-client 3.x
, yang tidak kompatibel dengan JAX-RS 2.x:
Modul logbook-jaxrs
berisi:
LogbookClientFilter
yang akan digunakan untuk aplikasi yang membuat permintaan HTTP
client . register ( new LogbookClientFilter ( logbook ));
LogbookServerFilter
untuk digunakan dengan server HTTP
resourceConfig . register ( new LogbookServerFilter ( logbook ));
Modul logbook-jdkserver
menyediakan dukungan untuk server HTTP JDK dan berisi:
LogbookFilter
untuk digunakan dengan server bawaan
httpServer . createContext ( path , handler ). getFilters (). add ( new LogbookFilter ( logbook ))
Modul logbook-netty
berisi:
LogbookClientHandler
untuk digunakan dengan HttpClient
:
HttpClient httpClient =
HttpClient . create ()
. doOnConnected (
( connection -> connection . addHandlerLast ( new LogbookClientHandler ( logbook )))
);
LogbookServerHandler
untuk digunakan dengan HttpServer
:
HttpServer httpServer =
HttpServer . create ()
. doOnConnection (
connection -> connection . addHandlerLast ( new LogbookServerHandler ( logbook ))
);
Pengguna Spring WebFlux dapat memilih salah satu opsi berikut:
NettyWebServer
(melewati HttpServer
)NettyServerCustomizer
khususReactorClientHttpConnector
(melewati HttpClient
)WebClientCustomizer
khususlogbook-spring-webflux
yang terpisah dan tidak bergantung pada konektor Pengguna Micronaut dapat mengikuti dokumen resmi tentang cara mengintegrasikan Logbook dengan Micronaut.
Modul logbook-okhttp2
berisi Interceptor
untuk digunakan dengan OkHttpClient
versi 2.x :
OkHttpClient client = new OkHttpClient ();
client . networkInterceptors (). add ( new LogbookInterceptor ( logbook ));
Jika Anda mengharapkan respons terkompresi gzip, Anda juga perlu mendaftarkan GzipInterceptor
kami. Dukungan gzip transparan yang terpasang di OkHttp akan berjalan setelah pencegat jaringan apa pun yang memaksa buku catatan untuk mencatat respons biner terkompresi.
OkHttpClient client = new OkHttpClient ();
client . networkInterceptors (). add ( new LogbookInterceptor ( logbook ));
client . networkInterceptors (). add ( new GzipInterceptor ());
Modul logbook-okhttp
berisi Interceptor
untuk digunakan dengan OkHttpClient
versi 3.x :
OkHttpClient client = new OkHttpClient . Builder ()
. addNetworkInterceptor ( new LogbookInterceptor ( logbook ))
. build ();
Jika Anda mengharapkan respons terkompresi gzip, Anda juga perlu mendaftarkan GzipInterceptor
kami. Dukungan gzip transparan yang terpasang di OkHttp akan berjalan setelah pencegat jaringan apa pun yang memaksa buku catatan untuk mencatat respons biner terkompresi.
OkHttpClient client = new OkHttpClient . Builder ()
. addNetworkInterceptor ( new LogbookInterceptor ( logbook ))
. addNetworkInterceptor ( new GzipInterceptor ())
. build ();
Modul logbook-ktor-client
berisi:
LogbookClient
untuk digunakan dengan HttpClient
:
private val client = HttpClient ( CIO ) {
install( LogbookClient ) {
logbook = logbook
}
}
Modul logbook-ktor-server
berisi:
LogbookServer
untuk digunakan dengan Application
:
private val server = embeddedServer( CIO ) {
install( LogbookServer ) {
logbook = logbook
}
}
Alternatifnya, Anda dapat menggunakan logbook-ktor
, yang mengirimkan modul logbook-ktor-client
dan logbook-ktor-server
.
Modul logbook-spring
berisi ClientHttpRequestInterceptor
untuk digunakan dengan RestTemplate
:
LogbookClientHttpRequestInterceptor interceptor = new LogbookClientHttpRequestInterceptor ( logbook );
RestTemplate restTemplate = new RestTemplate ();
restTemplate . getInterceptors (). add ( interceptor );
Logbook hadir dengan konfigurasi otomatis yang nyaman untuk pengguna Spring Boot. Ini mengatur semua bagian berikut secara otomatis dengan default yang masuk akal:
Daripada mendeklarasikan ketergantungan pada logbook-core
nyatakan ketergantungan pada Spring Boot Starter:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-spring-boot-starter</ artifactId >
< version >${logbook.version}</ version >
</ dependency >
Setiap kacang dapat ditimpa dan dikustomisasi jika diperlukan, misalnya seperti ini:
@ Bean
public BodyFilter bodyFilter () {
return merge (
defaultValue (),
replaceJsonStringProperty ( singleton ( "secret" ), "XXX" ));
}
Silakan merujuk ke LogbookAutoConfiguration
atau tabel berikut untuk melihat daftar kemungkinan titik integrasi:
Jenis | Nama | Bawaan |
---|---|---|
FilterRegistrationBean | secureLogbookFilter | Berdasarkan LogbookFilter |
FilterRegistrationBean | logbookFilter | Berdasarkan LogbookFilter |
Logbook | Berdasarkan kondisi, filter, formatter dan penulis | |
Predicate<HttpRequest> | requestCondition | Tanpa filter; kemudian digabungkan dengan logbook.exclude dan logbook.exclude |
HeaderFilter | Berdasarkan logbook.obfuscate.headers | |
PathFilter | Berdasarkan logbook.obfuscate.paths | |
QueryFilter | Berdasarkan logbook.obfuscate.parameters | |
BodyFilter | BodyFilters.defaultValue() , lihat pemfilteran | |
RequestFilter | RequestFilters.defaultValue() , lihat pemfilteran | |
ResponseFilter | ResponseFilters.defaultValue() , lihat pemfilteran | |
Strategy | DefaultStrategy | |
AttributeExtractor | NoOpAttributeExtractor | |
Sink | DefaultSink | |
HttpLogFormatter | JsonHttpLogFormatter | |
HttpLogWriter | DefaultHttpLogWriter |
Beberapa filter digabungkan menjadi satu.
logbook-spring
Beberapa kelas dari logbook-spring
disertakan dalam konfigurasi otomatis.
Anda dapat melakukan autowire LogbookClientHttpRequestInterceptor
dengan kode seperti:
private final RestTemplate restTemplate ;
MyClient ( RestTemplateBuilder builder , LogbookClientHttpRequestInterceptor interceptor ){
this . restTemplate = builder
. additionalInterceptors ( interceptor )
. build ();
}
Tabel berikut menunjukkan konfigurasi yang tersedia (diurutkan berdasarkan abjad):
Konfigurasi | Keterangan | Bawaan |
---|---|---|
logbook.attribute-extractors | Daftar AttributeExtractors, termasuk konfigurasi seperti type (saat ini JwtFirstMatchingClaimExtractor atau JwtAllMatchingClaimsExtractor ), claim-names dan claim-key . | [] |
logbook.filter.enabled | Aktifkan Filter LogbookFilter | true |
logbook.filter.form-request-mode | Menentukan bagaimana permintaan formulir ditangani | body |
logbook.filters.body.default-enabled | Mengaktifkan/menonaktifkan filter isi default yang dikumpulkan oleh java.util.ServiceLoader | true |
logbook.format.style | Gaya pemformatan ( http , json , curl atau splunk ) | json |
logbook.httpclient.decompress-response | Mengaktifkan/menonaktifkan proses dekompresi tambahan untuk HttpClient dengan isi yang dikodekan gzip (hanya untuk tujuan logging). Ini berarti dekompresi ekstra dan kemungkinan dampak kinerja. | false (dinonaktifkan) |
logbook.minimum-status | Status minimum untuk mengaktifkan pencatatan ( status-at-least dan body-only-if-status-at-least ) | 400 |
logbook.obfuscate.headers | Daftar nama header yang perlu dibingungkan | [Authorization] |
logbook.obfuscate.json-body-fields | Daftar bidang isi JSON yang akan dikaburkan | [] |
logbook.obfuscate.parameters | Daftar nama parameter yang memerlukan kebingungan | [access_token] |
logbook.obfuscate.paths | Daftar jalur yang memerlukan kebingungan. Periksa Pemfilteran untuk sintaksis. | [] |
logbook.obfuscate.replacement | Nilai yang akan digunakan, bukan nilai yang dikaburkan | XXX |
logbook.predicate.include | Hanya sertakan jalur dan metode tertentu (jika ditentukan) | [] |
logbook.predicate.exclude | Kecualikan jalur dan metode tertentu (mengganti logbook.predicate.include ) | [] |
logbook.secure-filter.enabled | Aktifkan SecureLogbookFilter | true |
logbook.strategy | Strategi ( default , status-at-least , body-only-if-status-at-least , without-body ) | default |
logbook.write.chunk-size | Membagi baris log menjadi potongan-potongan kecil dengan ukuran hingga chunk-size . | 0 (dinonaktifkan) |
logbook.write.max-body-size | Memotong isi hingga karakter max-body-size dan menambahkan ... .logbook.write.max-body-size | -1 (dinonaktifkan) |
logbook :
predicate :
include :
- path : /api/**
methods :
- GET
- POST
- path : /actuator/**
exclude :
- path : /actuator/health
- path : /api/admin/**
methods :
- POST
filter.enabled : true
secure-filter.enabled : true
format.style : http
strategy : body-only-if-status-at-least
minimum-status : 400
obfuscate :
headers :
- Authorization
- X-Secret
parameters :
- access_token
- password
write :
chunk-size : 1000
attribute-extractors :
- type : JwtFirstMatchingClaimExtractor
claim-names : [ "sub", "subject" ]
claim-key : Principal
- type : JwtAllMatchingClaimsExtractor
claim-names : [ "sub", "iat" ]
Untuk konfigurasi Logback dasar
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
konfigurasikan Buku Catatan dengan LogstashLogbackSink
HttpLogFormatter formatter = new JsonHttpLogFormatter();
LogstashLogbackSink sink = new LogstashLogbackSink(formatter);
untuk output seperti
{
"@timestamp" : "2019-03-08T09:37:46.239+01:00",
"@version" : "1",
"message" : "GET http://localhost/test?limit=1",
"logger_name" : "org.zalando.logbook.Logbook",
"thread_name" : "main",
"level" : "TRACE",
"level_value" : 5000,
"http" : {
// logbook request/response contents
}
}
Anda memiliki fleksibilitas untuk menyesuaikan tingkat logging default dengan menginisialisasi LogstashLogbackSink
dengan tingkat tertentu. Misalnya:
LogstashLogbackSink sink = new LogstashLogbackSink(formatter, Level.INFO);
getWriter
dan/atau getParameter*()
. Lihat Servlet untuk lebih jelasnya.ERROR
. Anda sangat dianjurkan untuk tidak menggunakannya untuk menghasilkan respons kesalahan. Jika Anda memiliki pertanyaan, kekhawatiran, laporan bug, dll., silakan ajukan masalah di Issue Tracker repositori ini.
Untuk berkontribusi, cukup buat pull request dan tambahkan deskripsi singkat (1-2 kalimat) tentang penambahan atau perubahan Anda. Untuk lebih jelasnya, periksa pedoman kontribusi.
Grand Turk, replika fregat tingkat 6 bertiang tiga dari zaman Nelson - buku catatan dan bagan oleh JoJan dilisensikan di bawah Creative Commons (Attribution-Share Alike 3.0 Unported).