Biblioteca Java de código abierto con filtrado de seguimiento de pila, análisis de cadenas silenciosas y comparación de versiones

Durante algún tiempo en diferentes trabajos, me encontré con la necesidad de varias utilidades que no pude encontrar disponibles en ese momento. Y vi que los necesitaba varias veces una y otra vez. Entonces escribí mi propia biblioteca pequeña que encontré muy útil. Así que lo publiqué como una biblioteca Java de código abierto.

Aquí está el enlace de Github

Javadoc en línea está disponible aquí

Además, esta biblioteca está disponible en Maven Central. Estos son los artefactos de Maven (la versión 1.5.0.8 es la más reciente al momento de escribir este artículo, pero podría cambiar en el futuro. Para verificar la última versión, busque el artefacto "MgntUtils" en http: //search.maven. org / ):

<dependency> <groupId>com.github.michaelgantman</groupId> <artifactId>MgntUtils</artifactId> <version>1.5.0.8</version> </dependency> <dependency> <groupId>com.github.michaelgantman</groupId> <artifactId>MgntUtils</artifactId> <version>1.5.0.8</version> <classifier>javadoc</classifier> </dependency> <dependency> <groupId>com.github.michaelgantman</groupId> <artifactId>MgntUtils</artifactId> <version>1.5.0.8</version> <classifier>sources</classifier> </dependency> 

A continuación hay una breve explicación de lo que hay allí. La biblioteca viene con un JavaDoc bien escrito (espero) con una descripción detallada. Así que aquí está la lista de características:

Filtro de ruido Stacktrace


En mi experiencia, esta característica fue la más genérica y útil para mí. Stacktrace es un salvavidas al depurar o tratar de averiguar qué salió mal en su aplicación. Sin embargo, cuando trabaje con registros en el lado del servidor, puede encontrar un enorme stacktrace que contiene la cola inútil más larga de varios marcos y paquetes relacionados con Application Server. Y en algún lugar de este montón, hay varias líneas de una traza relevante y pueden estar en diferentes segmentos separados por información inútil. Se convierte en una pesadilla buscar cosas relevantes. Aquí hay un enlace que describe el mismo problema con ejemplos de la vida real (no para los pusilánimes) https://dzone.com/articles/filtering-stack-trace-hell . Entonces, en mi biblioteca, hay una clase llamada TextUtils y tiene el método getStacktrace () con varias firmas sobrecargadas. Toma una instancia de Throwable y permite establecer un prefijo de paquete de los paquetes que son relevantes.

Además, la misma utilidad (a partir de la versión 1.5.0.3) tiene el método getStacktrace () que toma la interfaz CharSequence en lugar de Throwable y, por lo tanto, permite filtrar y acortar el stacktrace almacenado como una cadena de la misma manera que un stacktrace extraído de Throwable. Por lo tanto, esencialmente los seguimientos de pila se pueden filtrar "sobre la marcha" en tiempo de ejecución o más tarde desde cualquier fuente de texto, como un registro. (Solo para aclarar: la utilidad no admite el análisis y la modificación de todo el archivo de registro. Admite el filtrado de solo un stacktrace que se pasa como una Cadena. Por lo tanto, si alguien quiere filtrar excepciones en el archivo de registro, tendría que analizar el archivo de registro y extraer stacktrace (s) como cadenas separadas y luego puede usar esta utilidad para filtrar cada stacktrace individual).

Aquí hay un ejemplo de uso. Supongamos que el código de su empresa siempre reside en paquetes que comienzan con "com.plain. *". Así que establezca dicho prefijo y haga esto

 logger.info(TextUtils.getStacktrace(e,true,"com.plain.")); 

esto filtrará de manera muy inteligente todas las partes inútiles de la traza, dejándolo con una traza de pila muy concisa. Además, me pareció muy conveniente preestablecer el prefijo y luego usar el método de conveniencia

 TextUtils.getStacktrace(e); 

Hará lo mismo. Para preestablecer el prefijo solo use el método

 setRelevantPackage("com.plain."); 

Si desea preestablecer este valor mediante la configuración, a partir de la versión de biblioteca 1.1.0.1, puede establecer la Variable de entorno " MGNT_RELEVANT_PACKAGE " o la Propiedad del sistema " mgnt.relevant.package " en el valor " com.plain " . Y la propiedad se establecerá en ese valor sin invocar el método TextUtils.setRelevantPackage ("com.plain."); explícitamente en su código. Tenga en cuenta que el valor de la propiedad del sistema tendría prioridad sobre la variable de entorno si ambos estuvieran establecidos. Solo un recordatorio de que con la propiedad del sistema puede agregarlo en su línea de comando usando la bandera -D:

"-Dmgnt.relevant.package = com.plain".

Además, si utiliza una versión anterior a la 1.1.0.1 de esta biblioteca o no desea utilizar la variable de entorno o la propiedad del sistema mencionada anteriormente y está utilizando el entorno Spring, puede preestablecer el prefijo agregando el siguiente segmento a su configuración Spring:

 <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetClass" value="com.mgnt.utils.TextUtils"/> <property name="targetMethod" value="setRelevantPackage"/> <property name="arguments" value="com.plain."/> </bean> 

Javadoc explica todo en detalle. Pero aquí hay un pequeño adelanto: obtendrá un seguimiento de pila siguiente:

 at com.plain.BookService.listBooks() at com.plain.BookService$$FastClassByCGLIB$$e7645040.invoke() at net.sf.cglib.proxy.MethodProxy.invoke() ... at com.plain.LoggingAspect.logging() at sun.reflect.NativeMethodAccessorImpl.invoke0() ... at com.plain.BookService$$EnhancerByCGLIB$$7cb147e4.listBooks() at com.plain.web.BookController.listBooks() 

en lugar de

 at com.plain.BookService.listBooks() at com.plain.BookService$$FastClassByCGLIB$$e7645040.invoke() at net.sf.cglib.proxy.MethodProxy.invoke() at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint() at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed() at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed() at com.plain.LoggingAspect.logging() at sun.reflect.NativeMethodAccessorImpl.invoke0() at sun.reflect.NativeMethodAccessorImpl.invoke() at sun.reflect.DelegatingMethodAccessorImpl.invoke() at java.lang.reflect.Method.invoke() at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs() at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod() at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke() at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed() at org.springframework.aop.interceptor.AbstractTraceInterceptor.invoke() at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed() at org.springframework.transaction.interceptor.TransactionInterceptor.invoke() at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed() at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke() at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed() at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept() at com.plain.BookService$$EnhancerByCGLIB$$7cb147e4.listBooks() at com.plain.web.BookController.listBooks() 

Análisis de cadena silenciosa en entero, largo, etc.


La misma clase TextUtils proporciona métodos para analizar String de forma silenciosa (sin excepción alguna), en tipos de Double, Long, Integer, Short y Byte. Los métodos se llaman parseStringToLong () parseStringToInteger () etc. Todos toman 4 argumentos:

  1. Una cadena para ser analizada
  2. Una implementación de Número (Doble, Largo, Entero, Corto o Byte) para servir como valor predeterminado si ocurriera algún problema de análisis
  3. Una cadena que se imprimirá como mensaje de error en el registro si el primer argumento es nulo (puede ser nulo y luego no se imprime ningún error)
  4. Una cadena que se imprimirá como mensaje de error si se produjo NumberFormatException (puede ser nulo y no se imprime ningún error)

Análisis de cadena a intervalo de tiempo


En la misma clase TextUtils , hay un método parseStringToTimeInterval (valor de cadena) . Este método analiza una cadena que se espera que contenga algún valor de intervalo de tiempo, un valor numérico con sufijo de unidad de tiempo opcional. Por ejemplo, la cadena "38s" se analizará como 38 segundos, "24m" - 24 minutos "4h" - 4 horas, "3d" - 3 días y "45" como 45 milisegundos. Los sufijos admitidos son " s " por segundos, " m " por minutos, " h " por horas y " d " por días. Se considera que una cadena sin sufijo contiene un valor en milisegundos. Los sufijos no distinguen entre mayúsculas y minúsculas. Si la cadena proporcionada contiene un sufijo no admitido o contiene un valor numérico negativo o cero o contiene un valor no numérico, se genera IllegalArgumentException. Este método devuelve la clase TimeInterval, una clase también definida en esta biblioteca. Esencialmente, posee dos propiedades con getters y setters relevantes: "value" largo y java.util.concurrent.TimeUnit. Pero además de getters y setters, esta clase tiene métodos toMillis () , toSeconds () , toMinutes () , toHours () toDays () . Esos métodos devuelven un valor largo en la escala de tiempo especificada (de la misma manera que los métodos correspondientes en la clase java.util.concurrent.TimeUnit )

Este método puede ser muy útil para analizar las propiedades del intervalo de tiempo, como los tiempos de espera o los períodos de espera de los archivos de configuración. Elimina cálculos innecesarios de diferentes escalas de tiempo a milisegundos de ida y vuelta. Tenga en cuenta que tiene una propiedad methodInvokingInterval que debe establecer durante 5 días. Por lo tanto, para establecer el valor de milisegundos, deberá calcular que 5 días son 432000000 milisegundos (obviamente no es una tarea imposible pero molesta y propensa a errores) y luego cualquier otra persona que vea el valor 432000000 tendrá que volver a calcularlo en 5 días que es frustrante. Pero usando este método tendrá un valor de propiedad establecido en "5d" e invocando el código

 long milliseconds = TextUtils.parsingStringToTimeInterval("5d").toMillis(); 

Resolverá su problema de conversión. Obviamente, esta no es una característica demasiado compleja, pero podría agregar simplicidad y claridad en sus archivos de configuración y ahorrar algo de frustración y error de cálculo "estúpido" en errores de milisegundos.

Comparar versiones


Esta utilidad permite convertir String a versión y viceversa y comparar versiones y rangos de versión correctamente. A menudo, si necesita comparar versiones, simplemente compara las cadenas. Entonces, digamos que la versión "1.5.3" será mayor que la versión "1.3.1". Sin embargo, si compara las cadenas "1.4.2" y "1.12.3", la cadena "1.4.2" será erróneamente mayor. Por lo tanto, esta utilidad se ocupa de dicho problema y, además, presenta la clase VersionRange y permite operaciones en rangos de versiones. (Vea los métodos compareVersions en la clase TextUtils y la clase VersionRange )

Convertidor de cadenas Unicode


Class StringUnicodeEncoderDecoder tiene métodos que pueden convertir una cadena (en cualquier idioma) en una secuencia de caracteres Unicode y viceversa. Por ejemplo, una cadena "Hello World" se convertirá en

"\ u0048 \ u0065 \ u006c \ u006c \ u006f \ u0020 \ u0057 \ u006f \ u0072 \ u006c \ u0064"

y puede ser restaurado de nuevo.

Gestión del ciclo de vida (fábricas auto-instanciadas)


Esta característica es un paquete que contiene una pequeña infraestructura que simplifica y automatiza el trabajo con fábricas que proporcionan implementaciones concretas de una interfaz. El paquete contiene solo 2 clases: BaseEntityFactory y BaseEntity . En resumen, lo que hace esta infraestructura es que si crea una fábrica que extiende BaseEntityFactory y alguna interfaz con todas sus implementaciones concretas que extienden BaseEntity , cada una de sus instancias de clase de implementación concretas se insertará automáticamente en su fábrica. No tendrá que preocuparse por cómo y cuándo poblar su fábrica. La infraestructura lo hará por usted cuando se invoque al constructor de su clase de implementación concreta. Entonces, todo lo que tendrá que hacer es crear cualquier número de clases de implementación concretas y asegurarse de que se invoque para cada constructor. Después de eso, puede usar su fábrica para obtener cualquiera de sus clases de implementación concretas en cualquier parte de su código. Esta es una breve explicación. Hay algunos detalles más pero no tantos. Para obtener una descripción detallada de este paquete, consulte la API Javadoc del paquete com.mgnt.lifecycle.management description. Además, el código fuente contiene el paquete com.mgnt.lifecycle.management.example que contiene ejemplos de código bien comentados y detallados sobre cómo usar esta función.

En cuanto al uso práctico, lo encontré muy útil para el framework Spring. Recuerde que Spring crea una instancia de todos los beans definidos durante su inicialización. Entonces, dentro del contexto de Spring, si simplemente declaramos nuestras implementaciones concretas como Spring beans, Spring las instanciaría por nosotros, inicializando así su fábrica automáticamente. Esto podría ser muy conveniente. Imagine que tiene algún bean que tiene una propiedad del tipo de interfaz definida por el usuario que tiene varias implementaciones, pero qué implementación real sería necesaria se determina en tiempo de ejecución. Entonces, en ese momento, puede usar el método getInstance (java.lang.String) de su Factory para acceder a la implementación necesaria. Esto le permitirá no inyectar TODAS sus instancias concretas en su bean y no tendrá que usar Spring Bean Factory para acceder a un bean definido por Spring ya que eso violaría la no intrusión de Spring (lo que significa que puede escribir componentes que no dependen de Primavera). Además, si en algún momento posterior necesita agregar implementaciones más concretas, todo lo que tendrá que hacer es agregar sus clases de implementación a su código y declararlas como beans Spring. ¡El resto lo hará Spring y esta infraestructura!

Utilidades de archivo


Esta utilidad permite leer un archivo como una cadena con o sin especificar el conjunto de caracteres o simplemente leerlo como una matriz de bytes. Esta utilidad utiliza nio package y Buffers para un rendimiento más rápido. El tamaño máximo de archivo para esta utilidad es 2G.

Tiempo util


Este es solo un método de conveniencia único que proporciona la misma funcionalidad que Thread.sleep () pero es silencioso. Es decir, "traga" IterruptedException. Además, se necesitan 2 argumentos: tiempo para dormir y unidad de medida. Entonces en lugar de

 try { Thread.sleep(2000); } catch (InterruptedException ie) { } 

Solo puedes escribir:

 TimeUtils.sleepFor(2, TimeUnit.SECONDS); 

Pensé que era lindo

Entonces esto es todo. Siéntase libre de descargar esta biblioteca. Viene con la licencia MIT, una de las licencias más permisivas que conozco. El código fuente, Javadoc y la versión binaria están disponibles en el enlace de arriba. El código fue compilado por JDK 8 pero no tiene características de la versión 8. Definitivamente debería funcionar con Java 7, sospecho que funcionará con Java 6 e incluso 5, pero no lo he probado con esas versiones. Siéntase libre de usarlo como mejor le parezca, cuénteles a los demás si lo desea y no dude en enviarme una nota a michael_gantman@yahoo.com si tiene algún comentario.

Source: https://habr.com/ru/post/480268/


All Articles