Substantivo do diário de bordo , /lɑɡ bʊk/: Um livro no qual são registradas as medidas do diário de bordo do navio, junto com outros detalhes importantes da viagem.
Logbook é uma biblioteca Java extensível para permitir o registro completo de solicitações e respostas para diferentes tecnologias do lado do cliente e do servidor. Ele satisfaz uma necessidade especial ao a) permitir que desenvolvedores de aplicativos da Web registrem qualquer tráfego HTTP que um aplicativo receba ou envie b) de uma forma que facilite sua persistência e análise posterior. Isso pode ser útil para análise de log tradicional, atendimento a requisitos de auditoria ou investigação de problemas históricos de tráfego individuais.
O Logbook está pronto para uso nas configurações mais comuns. Mesmo para aplicações e tecnologias incomuns, deve ser simples implementar as interfaces necessárias para conectar uma biblioteca/framework/etc. para isso.
Adicione a seguinte dependência ao seu projeto:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-core</ artifactId >
< version >${logbook.version}</ version >
</ dependency >
Para compatibilidade com versões anteriores do Spring 5/Spring Boot 2, adicione a seguinte importação:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-servlet</ artifactId >
< version >${logbook.version}</ version >
< classifier >javax</ classifier >
</ dependency >
Módulos/artefatos adicionais do Logbook sempre compartilham o mesmo número de versão.
Alternativamente, você pode importar nossa lista de materiais ...
< 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 >
O criador de logs deve ser configurado no nível de rastreio para registrar as solicitações e respostas. Com Spring Boot 2 (usando Logback), isso pode ser feito adicionando a seguinte linha ao seu application.properties
logging.level.org.zalando.logbook: TRACE
Todas as integrações requerem uma instância do Logbook
que mantém todas as configurações e conecta todas as peças necessárias. Você pode criar um usando todos os padrões:
Logbook logbook = Logbook . create ();
ou crie uma versão personalizada usando o 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 ();
O Logbook costumava ter uma estratégia muito rígida sobre como fazer o registro de solicitações/respostas:
Algumas dessas restrições poderiam ser atenuadas com implementações personalizadas HttpLogWriter
, mas nunca foram ideais.
A partir da versão 2.0, o Logbook agora vem com um padrão Strategy em seu núcleo. Certifique-se de ler a documentação da interface Strategy
para entender as implicações.
O Logbook vem com algumas estratégias integradas:
BodyOnlyIfStatusAtLeastStrategy
StatusAtLeastStrategy
WithoutBodyStrategy
A partir da versão 3.4.0, o Logbook é equipado com um recurso chamado Attribute Extractor . Os atributos são basicamente uma lista de pares chave/valor que podem ser extraídos da solicitação e/ou resposta e registrados com eles. A ideia surgiu a partir da edição 381, onde foi solicitado um recurso para extrair a declaração do assunto dos tokens JWT no cabeçalho de autorização.
A interface AttributeExtractor
possui dois métodos extract
: um que pode extrair atributos apenas da solicitação e outro que tem solicitação e resposta à sua disposição. Ambos retornam uma instância da classe HttpAttributes
, que é basicamente um Map<String, Object>
sofisticado. Observe que, como os valores do mapa são do tipo Object
, eles devem ter um método toString()
adequado para que apareçam nos logs de maneira significativa. Alternativamente, os formatadores de log podem contornar isso implementando sua própria lógica de serialização. Por exemplo, o formatador de log integrado JsonHttpLogFormatter
usa ObjectMapper
para serializar os valores.
Aqui está um exemplo:
final class OriginExtractor implements AttributeExtractor {
@ Override
public HttpAttributes extract ( final HttpRequest request ) {
return HttpAttributes . of ( "origin" , request . getOrigin ());
}
}
O Logbook deve então ser criado registrando este extrator de atributos:
final Logbook logbook = Logbook . builder ()
. attributeExtractor ( new OriginExtractor ())
. build ();
Isso resultará em logs de solicitação para incluir algo como:
"attributes":{"origin":"LOCAL"}
Para exemplos mais avançados, consulte as classes JwtFirstMatchingClaimExtractor
e JwtAllMatchingClaimsExtractor
. O primeiro extrai a primeira declaração que corresponde a uma lista de nomes de declarações do token JWT da solicitação. O último extrai todas as declarações que correspondem a uma lista de nomes de declarações do token JWT da solicitação.
Se você precisar incorporar vários AttributeExtractor
s, poderá usar a classe CompositeAttributeExtractor
:
final List < AttributeExtractor > extractors = List . of (
extractor1 ,
extractor2 ,
extractor3
);
final Logbook logbook = Logbook . builder ()
. attributeExtractor ( new CompositeAttributeExtractor ( extractors ))
. build ();
O Logbook funciona em diversas fases diferentes:
Cada fase é representada por uma ou mais interfaces que podem ser utilizadas para customização. Cada fase tem um padrão sensato.
Registrar mensagens HTTP e incluir seus corpos é uma tarefa bastante cara, por isso faz muito sentido desabilitar o registro para determinadas solicitações. Um caso de uso comum seria ignorar solicitações de verificação de integridade de um balanceador de carga ou qualquer solicitação para endpoints de gerenciamento normalmente emitida por desenvolvedores.
Definir uma condição é tão fácil quanto escrever um Predicate
especial que decide se uma solicitação (e sua resposta correspondente) deve ser registrada ou não. Alternativamente, você pode usar e combinar predicados predefinidos:
Logbook logbook = Logbook . builder ()
. condition ( exclude (
requestTo ( "/health" ),
requestTo ( "/admin/**" ),
contentType ( "application/octet-stream" ),
header ( "X-Secret" , newHashSet ( "1" , "true" ):: contains )))
. build ();
Os padrões de exclusão, por exemplo /admin/**
, seguem vagamente o estilo de padrões de caminho do Ant, sem levar em consideração a string de consulta da URL.
O objetivo da Filtragem é impedir o registro de determinadas partes confidenciais de solicitações e respostas HTTP. Isso geralmente inclui o cabeçalho Authorization , mas também pode se aplicar a determinadas consultas de texto simples ou parâmetros de formulário — por exemplo, password .
O Logbook suporta diferentes tipos de filtros:
Tipo | Opera em | Aplica-se a | Padrão |
---|---|---|---|
QueryFilter | Cadeia de consulta | solicitar | access_token |
PathFilter | Caminho | solicitar | n / D |
HeaderFilter | Cabeçalho (par chave-valor único) | ambos | Authorization |
BodyFilter | Tipo de conteúdo e corpo | ambos | json: access_token e refresh_token formulário: client_secret , password e refresh_token |
RequestFilter | HttpRequest | solicitar | Substitua corpos binários, multipartes e de fluxo. |
ResponseFilter | HttpResponse | resposta | Substitua corpos binários, multipartes e de fluxo. |
QueryFilter
, PathFilter
, HeaderFilter
e BodyFilter
são de nível relativamente alto e devem cobrir todas as necessidades em aproximadamente 90% de todos os casos. Para configurações mais complicadas, deve-se recorrer às variantes de baixo nível, ou seja, RequestFilter
e ResponseFilter
respectivamente (em conjunto com ForwardingHttpRequest
/ ForwardingHttpResponse
).
Você pode configurar filtros como este:
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 ();
Você pode configurar quantos filtros desejar - eles serão executados consecutivamente.
Você pode aplicar a filtragem de caminho JSON a corpos JSON. Aqui estão alguns exemplos:
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 ();
Dê uma olhada no exemplo a seguir, antes e depois da aplicação da filtragem:
{
"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
}
}
O Logbook usa um ID de correlação para correlacionar solicitações e respostas. Isso permite solicitações e respostas relacionadas à correspondência que normalmente estariam localizadas em locais diferentes no arquivo de log.
Se a implementação padrão do ID de correlação for insuficiente para seu caso de uso, você poderá fornecer uma implementação personalizada:
Logbook logbook = Logbook . builder ()
. correlationId ( new CustomCorrelationId ())
. build ();
A formatação define basicamente como as solicitações e respostas serão transformadas em strings. Os formatadores não especificam onde as solicitações e respostas são registradas – os gravadores fazem esse trabalho.
O Logbook vem com dois formatadores padrão diferentes: HTTP e JSON .
HTTP é o estilo de formatação padrão, fornecido pelo DefaultHttpLogFormatter
. Ele foi projetado principalmente para ser usado para desenvolvimento e depuração local, não para uso em produção. Isso ocorre porque não é tão legível por máquina quanto o JSON.
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 é um estilo de formatação alternativo, fornecido pelo JsonHttpLogFormatter
. Ao contrário do HTTP, ele é projetado principalmente para uso em produção – analisadores e consumidores de log podem consumi-lo facilmente.
Requer a seguinte dependência:
< 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! "
}
Nota: Corpos do tipo application/json
(e application/*+json
) serão incorporados na árvore JSON resultante. Ou seja, um corpo de resposta JSON não terá escape e será representado como uma 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! "
}
}
O Common Log Format (CLF) é um formato de arquivo de texto padronizado usado por servidores web ao gerar arquivos de log do servidor. O formato é compatível com CommonsLogFormatSink
:
185.85.220.253 - - [02/Aug/2019:08:16:41 0000] "GET /search?q=zalando HTTP/1.1" 200 -
O Extended Log Format (ELF) é um formato de arquivo de texto padronizado, como Common Log Format (CLF), que é usado por servidores web ao gerar arquivos de log, mas os arquivos ELF fornecem mais informações e flexibilidade. O formato é compatível com ExtendedLogFormatSink
. Veja também o documento W3C.
Campos padrão:
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)
Exemplo de saída de log padrão:
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"
Os usuários podem substituir os campos padrão por seus campos personalizados por meio do construtor de ExtendedLogFormatSink
:
new ExtendedLogFormatSink ( new DefaultHttpLogWriter (), "date time cs(Custom-Request-Header) sc(Custom-Response-Header)" )
Para campos de cabeçalho Http: cs(Any-Header)
e sc(Any-Header)
, os usuários podem especificar quaisquer cabeçalhos que desejam extrair da solicitação.
Outros campos suportados estão listados no valor de ExtendedLogFormatSink.Field
, que pode ser colocado na expressão do campo customizado.
cURL é um estilo de formatação alternativo, fornecido pelo CurlHttpLogFormatter
, que renderizará solicitações como comandos cURL
executáveis. Ao contrário do JSON, ele foi projetado principalmente para humanos.
curl -v -X GET ' http://localhost/test ' -H ' Accept: application/json '
Consulte HTTP ou forneça seu próprio substituto para respostas:
new CurlHttpLogFormatter ( new JsonHttpLogFormatter ());
Splunk é um estilo de formatação alternativo, fornecido pelo SplunkHttpLogFormatter
, que renderizará solicitações e respostas como pares de valores-chave.
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!
A escrita define onde as solicitações e respostas formatadas são gravadas. Logbook vem com três implementações: Logger, Stream e Chunking.
Por padrão, solicitações e respostas são registradas com um criador de logs slf4j que usa a categoria org.zalando.logbook.Logbook
e o nível de log trace
. Isso pode ser personalizado:
Logbook logbook = Logbook . builder ()
. sink ( new DefaultSink (
new DefaultHttpLogFormatter (),
new DefaultHttpLogWriter ()
))
. build ();
Uma implementação alternativa é registrar solicitações e respostas em um PrintStream
, por exemplo, System.out
ou System.err
. Esta é geralmente uma má escolha para execução em produção, mas às vezes pode ser útil para desenvolvimento local e/ou investigação de curto prazo.
Logbook logbook = Logbook . builder ()
. sink ( new DefaultSink (
new DefaultHttpLogFormatter (),
new StreamHttpLogWriter ( System . err )
))
. build ();
O ChunkingSink
dividirá mensagens longas em partes menores e as escreverá individualmente enquanto delegará para outro coletor:
Logbook logbook = Logbook . builder ()
. sink ( new ChunkingSink ( sink , 1000 ))
. build ();
A combinação de HttpLogFormatter
e HttpLogWriter
é adequada para a maioria dos casos de uso, mas tem limitações. A implementação direta da interface Sink
permite casos de uso mais sofisticados, por exemplo, escrever solicitações/respostas em um armazenamento persistente estruturado como um banco de dados.
Vários coletores podem ser combinados em um usando CompositeSink
.
Você terá que registrar o LogbookFilter
como um Filter
na sua cadeia de filtros - no seu arquivo web.xml
(observe que a abordagem xml usará todos os padrões e não é configurável):
< 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 >
ou programaticamente, por meio do ServletContext
:
context . addFilter ( "LogbookFilter" , new LogbookFilter ( logbook ))
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST , ASYNC ), true , "/*" );
Cuidado : o envio ERROR
não é suportado. É altamente recomendável produzir respostas de erro no envio REQUEST
ou ASNYC
.
O LogbookFilter
irá, por padrão, tratar solicitações com um corpo application/x-www-form-urlencoded
não diferente de qualquer outra solicitação, ou seja, você verá o corpo da solicitação nos logs. A desvantagem dessa abordagem é que você não poderá usar nenhum dos métodos HttpServletRequest.getParameter*(..)
. Consulte a edição nº 94 para obter mais detalhes.
A partir do Logbook 1.5.0, agora você pode especificar uma das três estratégias que definem como o Logbook lida com esta situação usando a propriedade de sistema logbook.servlet.form-request
:
Valor | Prós | Contras |
---|---|---|
body (padrão) | O corpo está registrado | O código downstream não pode usar getParameter*() |
parameter | O corpo está registrado (mas é reconstruído a partir de parâmetros) | O código downstream não pode usar getInputStream() |
off | O código downstream pode decidir se deve usar getInputStream() ou getParameter*() | O corpo não está registrado |
Os aplicativos seguros geralmente precisam de uma configuração ligeiramente diferente. Geralmente, você deve evitar registrar solicitações não autorizadas, especialmente o corpo, porque isso permite que invasores inundem rapidamente seu arquivo de log — e, consequentemente, seu precioso espaço em disco. Supondo que seu aplicativo lide com a autorização dentro de outro filtro, você tem duas opções:
Você pode facilmente obter a configuração anterior colocando o LogbookFilter
após o filtro de segurança. Este último é um pouco mais sofisticado. Você precisará de duas instâncias LogbookFilter
— uma antes do filtro de segurança e outra depois dele:
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 , "/*" );
O primeiro filtro do logbook registrará somente solicitações não autorizadas. O segundo filtro registrará as solicitações autorizadas, como sempre.
O módulo logbook-httpclient
contém um HttpRequestInterceptor
e um HttpResponseInterceptor
para usar com o HttpClient
:
CloseableHttpClient client = HttpClientBuilder . create ()
. addInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. addInterceptorFirst ( new LogbookHttpResponseInterceptor ())
. build ();
Como o LogbookHttpResponseInterceptor
é incompatível com o HttpAsyncClient
há outra maneira de registrar respostas:
CloseableHttpAsyncClient client = HttpAsyncClientBuilder . create ()
. addInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. build ();
// and then wrap your response consumer
client . execute ( producer , new LogbookHttpAsyncResponseConsumer <>( consumer ), callback )
O módulo logbook-httpclient5
contém um ExecHandler
para usar com o HttpClient
:
CloseableHttpClient client = HttpClientBuilder . create ()
. addExecInterceptorFirst ( "Logbook" , new LogbookHttpExecHandler ( logbook ))
. build ();
O manipulador deve ser adicionado primeiro, de modo que uma compactação seja executada após o registro e a descompactação seja executada antes do registro.
Para evitar uma alteração significativa, há também um HttpRequestInterceptor
e um HttpResponseInterceptor
para usar com o HttpClient
, que funciona bem desde que a compactação (ou outros ExecHandlers) não seja usada:
CloseableHttpClient client = HttpClientBuilder . create ()
. addRequestInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. addResponseInterceptorFirst ( new LogbookHttpResponseInterceptor ())
. build ();
Como o LogbookHttpResponseInterceptor
é incompatível com o HttpAsyncClient
há outra maneira de registrar respostas:
CloseableHttpAsyncClient client = HttpAsyncClientBuilder . create ()
. addRequestInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. build ();
// and then wrap your response consumer
client . execute ( producer , new LogbookHttpAsyncResponseConsumer <>( consumer ), callback )
Observação
Suporte para JAX-RS 2.x
O suporte ao JAX-RS 2.x (legado) foi eliminado no Logbook 3.0 para 3.6.
A partir do Logbook 3.7, o suporte ao JAX-RS 2.x está de volta.
No entanto, você precisa adicionar o classificador javax
para usar o módulo Logbook adequado:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-jaxrs</ artifactId >
< version >${logbook.version}</ version >
< classifier >javax</ classifier >
</ dependency >
Você também deve certificar-se de que as seguintes dependências estejam em seu caminho de classe. Por padrão, logbook-jaxrs
importa jersey-client 3.x
, que não é compatível com JAX-RS 2.x:
O módulo logbook-jaxrs
contém:
Um LogbookClientFilter
a ser usado para aplicativos que fazem solicitações HTTP
client . register ( new LogbookClientFilter ( logbook ));
Um LogbookServerFilter
para ser usado com servidores HTTP
resourceConfig . register ( new LogbookServerFilter ( logbook ));
O módulo logbook-jdkserver
fornece suporte para servidor JDK HTTP e contém:
Um LogbookFilter
para ser usado com o servidor integrado
httpServer . createContext ( path , handler ). getFilters (). add ( new LogbookFilter ( logbook ))
O módulo logbook-netty
contém:
Um LogbookClientHandler
a ser usado com um HttpClient
:
HttpClient httpClient =
HttpClient . create ()
. doOnConnected (
( connection -> connection . addHandlerLast ( new LogbookClientHandler ( logbook )))
);
Um LogbookServerHandler
para uso com um HttpServer
:
HttpServer httpServer =
HttpServer . create ()
. doOnConnection (
connection -> connection . addHandlerLast ( new LogbookServerHandler ( logbook ))
);
Os usuários do Spring WebFlux podem escolher qualquer uma das seguintes opções:
NettyWebServer
(passando um HttpServer
)NettyServerCustomizer
personalizadoReactorClientHttpConnector
(passando um HttpClient
)WebClientCustomizer
personalizadologbook-spring-webflux
Os usuários do Micronaut podem seguir os documentos oficiais sobre como integrar o Logbook ao Micronaut.
O módulo logbook-okhttp2
contém um Interceptor
para usar com a versão 2.x do OkHttpClient
:
OkHttpClient client = new OkHttpClient ();
client . networkInterceptors (). add ( new LogbookInterceptor ( logbook ));
Se você está esperando respostas compactadas com gzip, você também precisa registrar nosso GzipInterceptor
. O suporte gzip transparente integrado ao OkHttp será executado após qualquer interceptador de rede que force o logbook a registrar respostas binárias compactadas.
OkHttpClient client = new OkHttpClient ();
client . networkInterceptors (). add ( new LogbookInterceptor ( logbook ));
client . networkInterceptors (). add ( new GzipInterceptor ());
O módulo logbook-okhttp
contém um Interceptor
para usar com a versão 3.x do OkHttpClient
:
OkHttpClient client = new OkHttpClient . Builder ()
. addNetworkInterceptor ( new LogbookInterceptor ( logbook ))
. build ();
Se você está esperando respostas compactadas com gzip, você também precisa registrar nosso GzipInterceptor
. O suporte gzip transparente integrado ao OkHttp será executado após qualquer interceptador de rede que force o logbook a registrar respostas binárias compactadas.
OkHttpClient client = new OkHttpClient . Builder ()
. addNetworkInterceptor ( new LogbookInterceptor ( logbook ))
. addNetworkInterceptor ( new GzipInterceptor ())
. build ();
O módulo logbook-ktor-client
contém:
Um LogbookClient
a ser usado com um HttpClient
:
private val client = HttpClient ( CIO ) {
install( LogbookClient ) {
logbook = logbook
}
}
O módulo logbook-ktor-server
contém:
Um LogbookServer
a ser usado com um Application
:
private val server = embeddedServer( CIO ) {
install( LogbookServer ) {
logbook = logbook
}
}
Alternativamente, você pode usar logbook-ktor
, que fornece os módulos logbook-ktor-client
e logbook-ktor-server
.
O módulo logbook-spring
contém um ClientHttpRequestInterceptor
para usar com RestTemplate
:
LogbookClientHttpRequestInterceptor interceptor = new LogbookClientHttpRequestInterceptor ( logbook );
RestTemplate restTemplate = new RestTemplate ();
restTemplate . getInterceptors (). add ( interceptor );
O Logbook vem com uma configuração automática conveniente para usuários do Spring Boot. Ele configura todas as seguintes partes automaticamente com padrões razoáveis:
Em vez de declarar uma dependência para logbook-core
declare uma para o Spring Boot Starter:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-spring-boot-starter</ artifactId >
< version >${logbook.version}</ version >
</ dependency >
Cada bean pode ser substituído e personalizado, se necessário, por exemplo:
@ Bean
public BodyFilter bodyFilter () {
return merge (
defaultValue (),
replaceJsonStringProperty ( singleton ( "secret" ), "XXX" ));
}
Consulte LogbookAutoConfiguration
ou a tabela a seguir para ver uma lista de possíveis pontos de integração:
Tipo | Nome | Padrão |
---|---|---|
FilterRegistrationBean | secureLogbookFilter | Baseado em LogbookFilter |
FilterRegistrationBean | logbookFilter | Baseado em LogbookFilter |
Logbook | Com base na condição, filtros, formatador e gravador | |
Predicate<HttpRequest> | requestCondition | Sem filtro; é posteriormente combinado com logbook.exclude e logbook.exclude |
HeaderFilter | Baseado em logbook.obfuscate.headers | |
PathFilter | Baseado em logbook.obfuscate.paths | |
QueryFilter | Baseado em logbook.obfuscate.parameters | |
BodyFilter | BodyFilters.defaultValue() , consulte filtragem | |
RequestFilter | RequestFilters.defaultValue() , consulte filtragem | |
ResponseFilter | ResponseFilters.defaultValue() , consulte filtragem | |
Strategy | DefaultStrategy | |
AttributeExtractor | NoOpAttributeExtractor | |
Sink | DefaultSink | |
HttpLogFormatter | JsonHttpLogFormatter | |
HttpLogWriter | DefaultHttpLogWriter |
Vários filtros são mesclados em um.
logbook-spring
Algumas classes do logbook-spring
estão incluídas na configuração automática.
Você pode conectar automaticamente LogbookClientHttpRequestInterceptor
com código como:
private final RestTemplate restTemplate ;
MyClient ( RestTemplateBuilder builder , LogbookClientHttpRequestInterceptor interceptor ){
this . restTemplate = builder
. additionalInterceptors ( interceptor )
. build ();
}
As tabelas a seguir mostram a configuração disponível (classificada em ordem alfabética):
Configuração | Descrição | Padrão |
---|---|---|
logbook.attribute-extractors | Lista de AttributeExtractors, incluindo configurações como type (atualmente JwtFirstMatchingClaimExtractor ou JwtAllMatchingClaimsExtractor ), claim-names e claim-key . | [] |
logbook.filter.enabled | Habilite o LogbookFilter | true |
logbook.filter.form-request-mode | Determina como as solicitações de formulário são tratadas | body |
logbook.filters.body.default-enabled | Ativa/desativa filtros de corpo padrão coletados por java.util.ServiceLoader | true |
logbook.format.style | Estilo de formatação ( http , json , curl ou splunk ) | json |
logbook.httpclient.decompress-response | Ativa/desativa o processo de descompactação adicional para HttpClient com corpo codificado em gzip (somente para fins de registro). Isso significa descompressão extra e possível impacto no desempenho. | false (desativado) |
logbook.minimum-status | Status mínimo para ativar o log ( status-at-least e body-only-if-status-at-least ) | 400 |
logbook.obfuscate.headers | Lista de nomes de cabeçalho que precisam de ofuscação | [Authorization] |
logbook.obfuscate.json-body-fields | Lista de campos do corpo JSON a serem ofuscados | [] |
logbook.obfuscate.parameters | Lista de nomes de parâmetros que precisam de ofuscação | [access_token] |
logbook.obfuscate.paths | Lista de caminhos que precisam de ofuscação. Verifique Filtragem para sintaxe. | [] |
logbook.obfuscate.replacement | Um valor a ser usado em vez de um valor ofuscado | XXX |
logbook.predicate.include | Incluir apenas determinados caminhos e métodos (se definidos) | [] |
logbook.predicate.exclude | Excluir determinados caminhos e métodos (substitui logbook.predicate.include ) | [] |
logbook.secure-filter.enabled | Habilite o SecureLogbookFilter | true |
logbook.strategy | Estratégia ( default , status-at-least , body-only-if-status-at-least , without-body ) | default |
logbook.write.chunk-size | Divide as linhas de log em pedaços menores de tamanho até chunk-size . | 0 (desativado) |
logbook.write.max-body-size | Trunca o corpo até caracteres max-body-size e acrescenta ... .logbook.write.max-body-size | -1 (desativado) |
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" ]
Para configuração básica do Logback
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
configurar o Logbook com um LogstashLogbackSink
HttpLogFormatter formatter = new JsonHttpLogFormatter();
LogstashLogbackSink sink = new LogstashLogbackSink(formatter);
para saídas como
{
"@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
}
}
Você tem a flexibilidade de personalizar o nível de log padrão inicializando LogstashLogbackSink
com um nível específico. Por exemplo:
LogstashLogbackSink sink = new LogstashLogbackSink(formatter, Level.INFO);
getWriter
e/ou getParameter*()
. Consulte Servlet para obter mais detalhes.ERROR
. Você é fortemente encorajado a não usá-lo para produzir respostas de erro. Se você tiver dúvidas, preocupações, relatórios de bugs, etc., registre um problema no Issue Tracker deste repositório.
Para contribuir, basta fazer uma solicitação pull e adicionar uma breve descrição (1 a 2 frases) de sua adição ou alteração. Para mais detalhes, verifique as diretrizes de contribuição.
Grand Turk, uma réplica de uma fragata de 6ª categoria de três mastros da época de Nelson - diário de bordo e gráficos de JoJan é licenciada sob Creative Commons (Atribuição-Compartilhamento pela mesma Licença 3.0 Não Portada).