Bitácora sustantivo, /lɑɡ bʊk/: Libro en el que se registran las medidas del diario del barco, junto con otros detalles destacados del viaje.
Logbook es una biblioteca Java extensible que permite un registro completo de solicitudes y respuestas para diferentes tecnologías del lado del cliente y del servidor. Satisface una necesidad especial al a) permitir a los desarrolladores de aplicaciones web registrar cualquier tráfico HTTP que una aplicación reciba o envíe b) de una manera que facilite su conservación y análisis posterior. Esto puede resultar útil para el análisis de registros tradicionales, cumplir con los requisitos de auditoría o investigar problemas de tráfico históricos individuales.
Logbook está listo para usar de inmediato para las configuraciones más comunes. Incluso para aplicaciones y tecnologías poco comunes, debería ser sencillo implementar las interfaces necesarias para conectar una biblioteca/marco/etc. lo.
Agregue la siguiente dependencia a su proyecto:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-core</ artifactId >
< version >${logbook.version}</ version >
</ dependency >
Para compatibilidad con versiones anteriores de Spring 5/Spring Boot 2, agregue la siguiente importación:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-servlet</ artifactId >
< version >${logbook.version}</ version >
< classifier >javax</ classifier >
</ dependency >
Los módulos/artefactos adicionales de Logbook siempre comparten el mismo número de versión.
Alternativamente, puede importar nuestra lista de materiales ...
< 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 >
El registrador del libro de registro debe configurarse en el nivel de seguimiento para poder registrar las solicitudes y respuestas. Con Spring Boot 2 (usando Logback), esto se puede lograr agregando la siguiente línea a su application.properties
logging.level.org.zalando.logbook: TRACE
Todas las integraciones requieren una instancia de Logbook
que contenga toda la configuración y conecte todas las piezas necesarias. Puedes crear uno usando todos los valores predeterminados:
Logbook logbook = Logbook . create ();
o cree una versión personalizada usando 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 ();
El libro de registro solía tener una estrategia muy rígida sobre cómo realizar el registro de solicitudes/respuestas:
Algunas de esas restricciones podrían mitigarse con implementaciones personalizadas HttpLogWriter
, pero nunca fueron ideales.
A partir de la versión 2.0, el Libro de registro ahora viene con un patrón de estrategia en su núcleo. Asegúrese de leer la documentación de la interfaz Strategy
para comprender las implicaciones.
El libro de registro viene con algunas estrategias integradas:
BodyOnlyIfStatusAtLeastStrategy
StatusAtLeastStrategy
WithoutBodyStrategy
A partir de la versión 3.4.0, Logbook está equipado con una función llamada Attribute Extractor . Los atributos son básicamente una lista de pares clave/valor que se pueden extraer de la solicitud y/o respuesta, y registrarse con ellos. La idea surgió del número 381, donde se solicitó una función para extraer el reclamo del sujeto de los tokens JWT en el encabezado de autorización.
La interfaz AttributeExtractor
tiene dos métodos extract
: uno que puede extraer atributos únicamente de la solicitud y otro que tiene tanto la solicitud como la respuesta a su disposición. Ambos devuelven una instancia de la clase HttpAttributes
, que es básicamente un elegante Map<String, Object>
. Tenga en cuenta que, dado que los valores del mapa son de tipo Object
, deben tener un método toString()
adecuado para que aparezcan en los registros de manera significativa. Alternativamente, los formateadores de registros pueden solucionar este problema implementando su propia lógica de serialización. Por ejemplo, el formateador de registros integrado JsonHttpLogFormatter
usa ObjectMapper
para serializar los valores.
Aquí hay un ejemplo:
final class OriginExtractor implements AttributeExtractor {
@ Override
public HttpAttributes extract ( final HttpRequest request ) {
return HttpAttributes . of ( "origin" , request . getOrigin ());
}
}
Luego se debe crear el libro de registro registrando este extractor de atributos:
final Logbook logbook = Logbook . builder ()
. attributeExtractor ( new OriginExtractor ())
. build ();
Esto dará como resultado que los registros de solicitudes incluyan algo como:
"attributes":{"origin":"LOCAL"}
Para obtener ejemplos más avanzados, consulte las clases JwtFirstMatchingClaimExtractor
y JwtAllMatchingClaimsExtractor
. El primero extrae el primer reclamo que coincide con una lista de nombres de reclamos del token JWT de solicitud. Este último extrae todos los reclamos que coinciden con una lista de nombres de reclamos del token JWT de solicitud.
Si necesita incorporar varios AttributeExtractor
, puede utilizar la clase CompositeAttributeExtractor
:
final List < AttributeExtractor > extractors = List . of (
extractor1 ,
extractor2 ,
extractor3
);
final Logbook logbook = Logbook . builder ()
. attributeExtractor ( new CompositeAttributeExtractor ( extractors ))
. build ();
El libro de registro funciona en varias fases diferentes:
Cada fase está representada por una o más interfaces que se pueden utilizar para la personalización. Cada fase tiene un valor predeterminado sensato.
Registrar mensajes HTTP e incluir sus cuerpos es una tarea bastante costosa, por lo que tiene mucho sentido deshabilitar el registro para ciertas solicitudes. Un caso de uso común sería ignorar las solicitudes de verificación de estado de un balanceador de carga o cualquier solicitud a los puntos finales de administración que normalmente emiten los desarrolladores.
Definir una condición es tan fácil como escribir un Predicate
especial que decida si una solicitud (y su correspondiente respuesta) debe registrarse o no. Alternativamente, puedes usar y 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 ();
Los patrones de exclusión, por ejemplo /admin/**
, siguen vagamente el estilo de patrones de ruta de Ant sin tener en cuenta la cadena de consulta de la URL.
El objetivo del filtrado es evitar el registro de determinadas partes sensibles de las solicitudes y respuestas HTTP. Esto generalmente incluye el encabezado Autorización , pero también podría aplicarse a ciertas consultas de texto sin formato o parámetros de formulario, por ejemplo, contraseña .
El libro de registro admite diferentes tipos de filtros:
Tipo | Opera en | Se aplica a | Por defecto |
---|---|---|---|
QueryFilter | cadena de consulta | pedido | access_token |
PathFilter | Camino | pedido | n / A |
HeaderFilter | Encabezado (par clave-valor único) | ambos | Authorization |
BodyFilter | Tipo de contenido y cuerpo | ambos | json: access_token y refresh_token formulario: client_secret , password y refresh_token |
RequestFilter | HttpRequest | pedido | Reemplace cuerpos binarios, multiparte y de flujo. |
ResponseFilter | HttpResponse | respuesta | Reemplace cuerpos binarios, multiparte y de flujo. |
QueryFilter
, PathFilter
, HeaderFilter
y BodyFilter
son de nivel relativamente alto y deberían cubrir todas las necesidades en ~90% de todos los casos. Para configuraciones más complicadas, se debe recurrir a las variantes de bajo nivel, es decir, RequestFilter
y ResponseFilter
respectivamente (junto con ForwardingHttpRequest
/ ForwardingHttpResponse
).
Puedes 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 ();
Puede configurar tantos filtros como desee; se ejecutarán consecutivamente.
Puede aplicar el filtrado de ruta JSON a cuerpos JSON. A continuación se muestran algunos ejemplos:
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 ();
Mire el siguiente ejemplo, antes y después de aplicar el filtrado:
{
"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
}
}
El libro de registro utiliza una identificación de correlación para correlacionar solicitudes y respuestas. Esto permite solicitudes y respuestas relacionadas con coincidencias que normalmente se ubicarían en diferentes lugares del archivo de registro.
Si la implementación predeterminada de la identificación de correlación no es suficiente para su caso de uso, puede proporcionar una implementación personalizada:
Logbook logbook = Logbook . builder ()
. correlationId ( new CustomCorrelationId ())
. build ();
El formato define cómo las solicitudes y respuestas se transformarán básicamente en cadenas. Los formateadores no especifican dónde se registran las solicitudes y las respuestas; los escritores hacen ese trabajo.
Logbook viene con dos formateadores predeterminados diferentes: HTTP y JSON .
HTTP es el estilo de formato predeterminado, proporcionado por DefaultHttpLogFormatter
. Está diseñado principalmente para ser utilizado para el desarrollo local y la depuración, no para uso de producción. Esto se debe a que no es tan fácilmente legible por máquina como 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 es un estilo de formato alternativo, proporcionado por JsonHttpLogFormatter
. A diferencia de HTTP, está diseñado principalmente para uso en producción: los analizadores y consumidores de registros pueden consumirlo fácilmente.
Requiere la siguiente dependencia:
< 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: Los cuerpos de tipo application/json
(y application/*+json
) se insertarán en el árbol JSON resultante. Es decir, el cuerpo de una respuesta JSON no se escapará ni se representará como una cadena:
{
"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! "
}
}
El formato de registro común (CLF) es un formato de archivo de texto estandarizado utilizado por los servidores web al generar archivos de registro del servidor. El formato es compatible a través de CommonsLogFormatSink
:
185.85.220.253 - - [02/Aug/2019:08:16:41 0000] "GET /search?q=zalando HTTP/1.1" 200 -
El formato de registro extendido (ELF) es un formato de archivo de texto estandarizado, como el formato de registro común (CLF), que utilizan los servidores web al generar archivos de registro, pero los archivos ELF brindan más información y flexibilidad. El formato es compatible a través de ExtendedLogFormatSink
. Consulte también el documento del W3C.
Campos predeterminados:
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)
Ejemplo de salida de registro predeterminada:
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"
Los usuarios pueden anular los campos predeterminados con sus campos personalizados a través del constructor de ExtendedLogFormatSink
:
new ExtendedLogFormatSink ( new DefaultHttpLogWriter (), "date time cs(Custom-Request-Header) sc(Custom-Response-Header)" )
Para los campos de encabezado Http: cs(Any-Header)
y sc(Any-Header)
, los usuarios pueden especificar cualquier encabezado que deseen extraer de la solicitud.
Otros campos admitidos se enumeran en el valor de ExtendedLogFormatSink.Field
, que se puede colocar en la expresión del campo personalizado.
cURL es un estilo de formato alternativo, proporcionado por CurlHttpLogFormatter
, que representará las solicitudes como comandos cURL
ejecutables. A diferencia de JSON, está diseñado principalmente para humanos.
curl -v -X GET ' http://localhost/test ' -H ' Accept: application/json '
Consulte HTTP o proporcione su propia alternativa para las respuestas:
new CurlHttpLogFormatter ( new JsonHttpLogFormatter ());
Splunk es un estilo de formato alternativo, proporcionado por SplunkHttpLogFormatter
que representará las solicitudes y las respuestas como pares clave-valor.
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!
La escritura define dónde se escriben las solicitudes y respuestas formateadas. Logbook viene con tres implementaciones: Logger, Stream y Chunking.
De forma predeterminada, las solicitudes y respuestas se registran con un registrador slf4j que utiliza la categoría org.zalando.logbook.Logbook
y el trace
de nivel de registro. Esto se puede personalizar:
Logbook logbook = Logbook . builder ()
. sink ( new DefaultSink (
new DefaultHttpLogFormatter (),
new DefaultHttpLogWriter ()
))
. build ();
Una implementación alternativa es registrar solicitudes y respuestas en un PrintStream
, por ejemplo System.out
o System.err
. Esta suele ser una mala elección para ejecutar en producción, pero a veces puede ser útil para el desarrollo y/o la investigación local a corto plazo.
Logbook logbook = Logbook . builder ()
. sink ( new DefaultSink (
new DefaultHttpLogFormatter (),
new StreamHttpLogWriter ( System . err )
))
. build ();
ChunkingSink
dividirá los mensajes largos en fragmentos más pequeños y los escribirá individualmente mientras los delega a otro receptor:
Logbook logbook = Logbook . builder ()
. sink ( new ChunkingSink ( sink , 1000 ))
. build ();
La combinación de HttpLogFormatter
y HttpLogWriter
se adapta bien a la mayoría de los casos de uso, pero tiene limitaciones. La implementación directa de la interfaz Sink
permite casos de uso más sofisticados, por ejemplo, escribir solicitudes/respuestas en un almacenamiento persistente estructurado como una base de datos.
Se pueden combinar varios lavabos en uno usando CompositeSink
.
Tendrá que registrar LogbookFilter
como Filter
en su cadena de filtros, ya sea en su archivo web.xml
(tenga en cuenta que el enfoque xml utilizará todos los valores predeterminados y no es configurable):
< 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 >
o mediante programación, a través de ServletContext
:
context . addFilter ( "LogbookFilter" , new LogbookFilter ( logbook ))
. addMappingForUrlPatterns ( EnumSet . of ( REQUEST , ASYNC ), true , "/*" );
Cuidado : el envío ERROR
no es compatible. Se recomienda encarecidamente generar respuestas de error en el envío REQUEST
o ASNYC
.
LogbookFilter
, de forma predeterminada, tratará las solicitudes con un cuerpo application/x-www-form-urlencoded
que no es diferente de cualquier otra solicitud, es decir, verá el cuerpo de la solicitud en los registros. La desventaja de este enfoque es que no podrá utilizar ninguno de los métodos HttpServletRequest.getParameter*(..)
. Consulte el número 94 para obtener más detalles.
A partir de Logbook 1.5.0, ahora puede especificar una de las tres estrategias que definen cómo Logbook aborda esta situación mediante el uso de la propiedad del sistema logbook.servlet.form-request
:
Valor | Ventajas | Contras |
---|---|---|
body (predeterminado) | El cuerpo está registrado | El código descendente no puede utilizar getParameter*() |
parameter | El cuerpo está registrado (pero se reconstruye a partir de parámetros) | El código descendente no puede usar getInputStream() |
off | El código posterior puede decidir si usar getInputStream() o getParameter*() | El cuerpo no está registrado |
Las aplicaciones seguras suelen necesitar una configuración ligeramente diferente. En general, debe evitar registrar solicitudes no autorizadas, especialmente el cuerpo, porque permite rápidamente a los atacantes inundar su archivo de registro y, en consecuencia, su valioso espacio en disco. Suponiendo que su aplicación maneja la autorización dentro de otro filtro, tiene dos opciones:
Puede lograr fácilmente la configuración anterior colocando LogbookFilter
después de su filtro de seguridad. Este último es un poco más sofisticado. Necesitará dos instancias LogbookFilter
: una antes del filtro de seguridad y otra después:
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 , "/*" );
El primer filtro del libro de registro registrará únicamente las solicitudes no autorizadas. El segundo filtro registrará las solicitudes autorizadas, como siempre.
El módulo logbook-httpclient
contiene un HttpRequestInterceptor
y un HttpResponseInterceptor
para usar con HttpClient
:
CloseableHttpClient client = HttpClientBuilder . create ()
. addInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. addInterceptorFirst ( new LogbookHttpResponseInterceptor ())
. build ();
Dado que LogbookHttpResponseInterceptor
es incompatible con HttpAsyncClient
existe otra forma de registrar respuestas:
CloseableHttpAsyncClient client = HttpAsyncClientBuilder . create ()
. addInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. build ();
// and then wrap your response consumer
client . execute ( producer , new LogbookHttpAsyncResponseConsumer <>( consumer ), callback )
El módulo logbook-httpclient5
contiene un ExecHandler
para usar con HttpClient
:
CloseableHttpClient client = HttpClientBuilder . create ()
. addExecInterceptorFirst ( "Logbook" , new LogbookHttpExecHandler ( logbook ))
. build ();
El controlador debe agregarse primero, de modo que se realice una compresión después del registro y una descompresión antes del registro.
Para evitar un cambio importante, también hay un HttpRequestInterceptor
y un HttpResponseInterceptor
para usar con HttpClient
, que funciona bien siempre que no se use compresión (u otros ExecHandlers):
CloseableHttpClient client = HttpClientBuilder . create ()
. addRequestInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. addResponseInterceptorFirst ( new LogbookHttpResponseInterceptor ())
. build ();
Dado que LogbookHttpResponseInterceptor
es incompatible con HttpAsyncClient
existe otra forma de registrar respuestas:
CloseableHttpAsyncClient client = HttpAsyncClientBuilder . create ()
. addRequestInterceptorFirst ( new LogbookHttpRequestInterceptor ( logbook ))
. build ();
// and then wrap your response consumer
client . execute ( producer , new LogbookHttpAsyncResponseConsumer <>( consumer ), callback )
Nota
Soporte para JAX-RS 2.x
La compatibilidad con JAX-RS 2.x (heredado) se eliminó en Logbook 3.0 a 3.6.
A partir de Logbook 3.7, la compatibilidad con JAX-RS 2.x ha vuelto.
Sin embargo, es necesario agregar el clasificador javax
para usar el módulo de Libro de registro adecuado:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-jaxrs</ artifactId >
< version >${logbook.version}</ version >
< classifier >javax</ classifier >
</ dependency >
También debe asegurarse de que las siguientes dependencias estén en su classpath. De forma predeterminada, logbook-jaxrs
importa jersey-client 3.x
, que no es compatible con JAX-RS 2.x:
El módulo logbook-jaxrs
contiene:
Un LogbookClientFilter
que se utilizará para aplicaciones que realizan solicitudes HTTP
client . register ( new LogbookClientFilter ( logbook ));
Un LogbookServerFilter
para usar con servidores HTTP
resourceConfig . register ( new LogbookServerFilter ( logbook ));
El módulo logbook-jdkserver
proporciona soporte para el servidor HTTP JDK y contiene:
Un LogbookFilter
para usar con el servidor integrado
httpServer . createContext ( path , handler ). getFilters (). add ( new LogbookFilter ( logbook ))
El módulo logbook-netty
contiene:
Un LogbookClientHandler
que se utilizará con un HttpClient
:
HttpClient httpClient =
HttpClient . create ()
. doOnConnected (
( connection -> connection . addHandlerLast ( new LogbookClientHandler ( logbook )))
);
Un LogbookServerHandler
para usar con un HttpServer
:
HttpServer httpServer =
HttpServer . create ()
. doOnConnection (
connection -> connection . addHandlerLast ( new LogbookServerHandler ( logbook ))
);
Los usuarios de Spring WebFlux pueden elegir cualquiera de las siguientes opciones:
NettyWebServer
(pasando un HttpServer
)NettyServerCustomizer
personalizadoReactorClientHttpConnector
(pasando un HttpClient
)WebClientCustomizer
personalizadologbook-spring-webflux
Los usuarios de Micronaut pueden seguir los documentos oficiales sobre cómo integrar Logbook con Micronaut.
El módulo logbook-okhttp2
contiene un Interceptor
para usar con la versión 2.x de OkHttpClient
:
OkHttpClient client = new OkHttpClient ();
client . networkInterceptors (). add ( new LogbookInterceptor ( logbook ));
Si espera respuestas comprimidas con gzip, además debe registrar nuestro GzipInterceptor
. El soporte gzip transparente integrado en OkHttp se ejecutará después de cualquier interceptor de red que obligue al libro de registro a registrar respuestas binarias comprimidas.
OkHttpClient client = new OkHttpClient ();
client . networkInterceptors (). add ( new LogbookInterceptor ( logbook ));
client . networkInterceptors (). add ( new GzipInterceptor ());
El módulo logbook-okhttp
contiene un Interceptor
para usar con la versión 3.x de OkHttpClient
:
OkHttpClient client = new OkHttpClient . Builder ()
. addNetworkInterceptor ( new LogbookInterceptor ( logbook ))
. build ();
Si espera respuestas comprimidas con gzip, además debe registrar nuestro GzipInterceptor
. El soporte gzip transparente integrado en OkHttp se ejecutará después de cualquier interceptor de red que obligue al libro de registro a registrar respuestas binarias comprimidas.
OkHttpClient client = new OkHttpClient . Builder ()
. addNetworkInterceptor ( new LogbookInterceptor ( logbook ))
. addNetworkInterceptor ( new GzipInterceptor ())
. build ();
El módulo logbook-ktor-client
contiene:
Un LogbookClient
que se utilizará con un HttpClient
:
private val client = HttpClient ( CIO ) {
install( LogbookClient ) {
logbook = logbook
}
}
El módulo logbook-ktor-server
contiene:
Un LogbookServer
para utilizar con una Application
:
private val server = embeddedServer( CIO ) {
install( LogbookServer ) {
logbook = logbook
}
}
Alternativamente, puede usar logbook-ktor
, que incluye los módulos logbook-ktor-client
y logbook-ktor-server
.
El módulo logbook-spring
contiene un ClientHttpRequestInterceptor
para usar con RestTemplate
:
LogbookClientHttpRequestInterceptor interceptor = new LogbookClientHttpRequestInterceptor ( logbook );
RestTemplate restTemplate = new RestTemplate ();
restTemplate . getInterceptors (). add ( interceptor );
Logbook viene con una configuración automática conveniente para los usuarios de Spring Boot. Configura todas las siguientes partes automáticamente con valores predeterminados razonables:
En lugar de declarar una dependencia al logbook-core
declare una en Spring Boot Starter:
< dependency >
< groupId >org.zalando</ groupId >
< artifactId >logbook-spring-boot-starter</ artifactId >
< version >${logbook.version}</ version >
</ dependency >
Cada bean se puede anular y personalizar si es necesario, por ejemplo, así:
@ Bean
public BodyFilter bodyFilter () {
return merge (
defaultValue (),
replaceJsonStringProperty ( singleton ( "secret" ), "XXX" ));
}
Consulte LogbookAutoConfiguration
o la siguiente tabla para ver una lista de posibles puntos de integración:
Tipo | Nombre | Por defecto |
---|---|---|
FilterRegistrationBean | secureLogbookFilter | Basado en LogbookFilter |
FilterRegistrationBean | logbookFilter | Basado en LogbookFilter |
Logbook | Basado en condición, filtros, formateador y escritor. | |
Predicate<HttpRequest> | requestCondition | Sin filtro; luego se combina con logbook.exclude y logbook.exclude |
HeaderFilter | Basado en logbook.obfuscate.headers | |
PathFilter | Basado en logbook.obfuscate.paths | |
QueryFilter | Basado en logbook.obfuscate.parameters | |
BodyFilter | BodyFilters.defaultValue() , ver filtrado | |
RequestFilter | RequestFilters.defaultValue() , ver filtrado | |
ResponseFilter | ResponseFilters.defaultValue() , ver filtrado | |
Strategy | DefaultStrategy | |
AttributeExtractor | NoOpAttributeExtractor | |
Sink | DefaultSink | |
HttpLogFormatter | JsonHttpLogFormatter | |
HttpLogWriter | DefaultHttpLogWriter |
Varios filtros se fusionan en uno.
logbook-spring
Algunas clases de logbook-spring
están incluidas en la configuración automática.
Puede conectar automáticamente LogbookClientHttpRequestInterceptor
con código como:
private final RestTemplate restTemplate ;
MyClient ( RestTemplateBuilder builder , LogbookClientHttpRequestInterceptor interceptor ){
this . restTemplate = builder
. additionalInterceptors ( interceptor )
. build ();
}
Las siguientes tablas muestran la configuración disponible (ordenadas alfabéticamente):
Configuración | Descripción | Por defecto |
---|---|---|
logbook.attribute-extractors | Lista de AttributeExtractors, incluidas configuraciones como type (actualmente JwtFirstMatchingClaimExtractor o JwtAllMatchingClaimsExtractor ), claim-names y claim-key . | [] |
logbook.filter.enabled | Habilite el LogbookFilter | true |
logbook.filter.form-request-mode | Determina cómo se manejan las solicitudes de formulario | body |
logbook.filters.body.default-enabled | Habilita/deshabilita los filtros de cuerpo predeterminados recopilados por java.util.ServiceLoader | true |
logbook.format.style | Estilo de formato ( http , json , curl o splunk ) | json |
logbook.httpclient.decompress-response | Habilita/deshabilita el proceso de descompresión adicional para HttpClient con cuerpo codificado en gzip (solo para fines de registro). Esto significa una descompresión adicional y un posible impacto en el rendimiento. | false (deshabilitado) |
logbook.minimum-status | Estado mínimo para habilitar el registro ( status-at-least y body-only-if-status-at-least ) | 400 |
logbook.obfuscate.headers | Lista de nombres de encabezados que necesitan ofuscación | [Authorization] |
logbook.obfuscate.json-body-fields | Lista de campos del cuerpo JSON que se ofuscarán | [] |
logbook.obfuscate.parameters | Lista de nombres de parámetros que necesitan ofuscación | [access_token] |
logbook.obfuscate.paths | Lista de rutas que necesitan ofuscación. Marque Filtrado para ver la sintaxis. | [] |
logbook.obfuscate.replacement | Un valor que se utilizará en lugar de uno ofuscado | XXX |
logbook.predicate.include | Incluir solo ciertas rutas y métodos (si están definidos) | [] |
logbook.predicate.exclude | Excluir ciertas rutas y métodos (anula logbook.predicate.include ) | [] |
logbook.secure-filter.enabled | Habilite el SecureLogbookFilter | true |
logbook.strategy | Estrategia ( default , status-at-least , body-only-if-status-at-least , without-body ) | default |
logbook.write.chunk-size | Divide las líneas de troncos en trozos más pequeños de hasta chunk-size . | 0 (deshabilitado) |
logbook.write.max-body-size | Trunca el cuerpo hasta los caracteres max-body-size y agrega ... logbook.write.max-body-size | -1 (deshabilitado) |
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 configuración básica de inicio de sesión
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
configurar el libro de registro con un LogstashLogbackSink
HttpLogFormatter formatter = new JsonHttpLogFormatter();
LogstashLogbackSink sink = new LogstashLogbackSink(formatter);
para salidas 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
}
}
Tiene la flexibilidad de personalizar el nivel de registro predeterminado inicializando LogstashLogbackSink
con un nivel específico. Por ejemplo:
LogstashLogbackSink sink = new LogstashLogbackSink(formatter, Level.INFO);
getWriter
y/o getParameter*()
. Consulte Servlet para obtener más detalles.ERROR
. Le recomendamos encarecidamente que no lo utilice para generar respuestas de error. Si tiene preguntas, inquietudes, informes de errores, etc., presente un problema en el Rastreador de problemas de este repositorio.
Para contribuir, simplemente haga una solicitud de extracción y agregue una breve descripción (1 o 2 oraciones) de su adición o cambio. Para obtener más detalles, consulte las pautas de contribución.
Grand Turk, una réplica de una fragata de sexta categoría de tres mástiles de la época de Nelson: libro de registro y cartas de JoJan tiene licencia Creative Commons (Atribución-Compartir Igual 3.0 Unported).