Spring Boot y el sistema de logging

Logo Spring

Resumen del funcionamiento del sistema de logging en Spring Boot 1.4.0.RELEASE

Principales características

  • Spring Boot utiliza internamente Commons Logging
  • Existe configuraciones para Java Util Logging, Log4J2 y Logback
  • Por defecto se utiliza la Consola para mostrar los logs. Opcionalmente podemos escribir en fichero
  • Logback es el sistema de log utilizado si utilizamos los Spring Boot Starters (spring-boot-starter-parent, etc)
  • Logback tiene niveles ERROR, WARN, INFO, DEBUG o TRACE pero no tiene FATAL
  • Se permite extender el comportamiento de logback mediante extensiones
  • Por defecto se muestran los niveles ERROR, WARN e INFO

Activación del nivel DEBUG

Como cualquier aplicación de Spring Boot podemos activar el nivel de DEBUG como cualquier configuración más. Eso significa que podemos indicarselo como argumento al arrancar la aplicación, como variable de entorno, en el application.properties o application.yml, etc

Es importante mencionar que la activacion del nivel de DEBUG solo afecta internamente a Spring Boot, no a los logs de nuestra aplicación, los cuales los tenemos que configurar mediante otro mecanismo.

Por ejemplo,

En el arranque de la aplicación

$ java -jar my-application.jar --debug

En el fichero application.properties o application.yml

...
debug=true
...
debug: true

Escribir logs a fichero

Por defecto Spring Boot escribe por consola pero opcionalmente le podemos decir que escriba a fichero.

Al igual que la activación del nivel a DEBUG la configuración para activar el fichero de log sigue el mismo principo de configuración de Spring Boot.

Los ficheros rotan por defecto cada 10Mb.

Las propiedades para indicar el fichero donde escribir los logs son:

  • logging.file
  • logging.path

Modificar los niveles de logs de nuestra aplicación o de librerías de terceros

Siguiendo los mismos principios de configuración de Spring Boot la modificación de los nieves de log de nuestra aplicación o de librerías de terceros es bastante fácil.

Por ejemplo,

En el arranque de la aplicación

java -jar my-application.jar --logging.level.org.myapp=DEBUG

En el fichero de configuración application.properties o application.yml

logging.level.org.myapp=DEBUG
...
logging:
   level:
     org:
       myapp: DEBUG

Activar otros sistemas de log

Se acivan por defecto al incluirlos en el classpath.

Podemos forzar a Spring Boot a utilzar un sistema de logging diferente al por defecto, configurando la propiedad de sistema:

org.springframework.boot.logging.LoggingSystem=Nombre completo de la clase que implementa el sistema de logging

o bien desactivar por completo el sistema de logging

org.springframework.boot.logging.LoggingSystem=none

Configuración de estos sistemas de logging

Una vez hemos cambiado el sistema de logging, Spring Boot se encargaría de leer lo ficheros de configuración de esos sistemas, aunque se recomienda que se utilicen la versión para Spring Boot.

Es decir, para los sistemas de logging como

  • log4j2 el fichero de configuración es log4j2.xml
  • Para Java Util Logging el fichero de configuración es logging.properties

pero Spring boot recomienda que utilicemos la versión para Spring Boot que serían respectivamente:

  • log4j2-spring.xml
  • logging-spring.properties
Anuncios

El mecanismo de heartbeat en kafka 0.9

Kafka

Es el mecanismo seguido por Apache Kafka 0.9 para determinar si un consumidor no se encuentra disponible, bien por un fallo en la aplicación, por un fallo de red o por un fallo de la máquina donde se está ejecutnado.

Recordemos que un grupo de consumidores comienzan a consumir mensajes de los topics en los que se ha subscrito. Cada topic suele estar formado por 1 log de varias particiones cada uno, y el broker que hace de coordinador del grupo, le asigna a cada consumidor una sola partición de un topic.

Por ejemplo si 3 consumidores se subscriben a un topic de 5 particiones, cada consumidor estará consumiendo mensaje de 1 sola partición. Nunca podremos tener más de un consumidor consumiendo más de 1 partición de un mismo topic.

En el ejemplo podríamos tener:

Sigue leyendo

Implementación de un consumidor de Kafka basado en la versión 0.9

Kafka

En Kafka 0.9 se ha rediseñado la implementación de los consumidores. Ahora se catacterizan por

  • poder implementar consumidores simples y más complejos
  • reimplementados en Java, evitando la dependencia con Scala y Zookeeper
  • mejora en la seguridad, soportanto SSL y Sasl/Kerberos
  • nuevos protocolos para mejorar la tolerancia a fallos de un grupo de consumidores. Esto facilita también la implementación de clientes en otros lenguajes de programación

Conceptos básicos de Kafka

Topic

Un topic se encuentra dividido en un conjunto de logs llamados particiones. Sobre estos topic se escriben y se consumen los mensajes.

Productor

Un productor publica o escribe mensajes al final de estos logs

Broker

Un broker es un nodo del cluster de kafka.

Consumidor

Un consumidor consume o lee los mensajes de estos logs según necesite, comentanzo normalmente desde el comienzo de ellos o desde el último procesado con éxito

Grupo de consumidores o consumer group

Un consumer group es un grupo de consumidores identificados por un identificador único y que se encuentran consumiendo mensajes de un topic. Usando un grupo de consumidores es el mecanismo de escalado de lectura de topics en Kafka, ya que cada consumidor se encuentra consumiendo mensajes de una de las particiones del topic.

Sigue leyendo

Cómo detectar si un fichero ha sido creado, modificado o borrado utilizando Java

Java

Desde hace tiempo estamos acostumbrados a que nuestros IDEs y otros gestores de archivos nos avisen cuando un fichero ha cambiado.

La razón del cambio puede ser desde la modificación o creación del fichero, hasta el borrado del mismo.

La forma más eficiente de conseguir esto es recibiendo los eventos de estas situaciones directamente del sistema nativo de notificación de eventos del SO.

Pues bien, desde Java podemos conseguir este comportamiento utilizando la interface WatchService.

Veamos en qué consiste

Para empezar partimos de un directorio sobre el que queremos ser notificados de eventos que se produzcan sobre él. Partimos entonces de un objeto que implementa la interface

  • java.nio.file.Watchable.

como es el caso de los objetos de tipo

  • java.nio.file.Path.

Y partimos de los tipos de eventos que queremos ser notificados, como son los

  • StandardWatchEventKinds.ENTRY_CREATE
  • StandardWatchEventKinds.ENTRY_DELETE
  • StandardWatchEventKinds.ENTRY_MODIFY
  • StandardWatchEventKinds.OVERFLOW

Entonces creamos el objeto Watchable y le registramos el servicio WatchService junto con los eventos en los que estamos interesados.

Este proceso de registro devuelve un objeto WatchKey, que es sobre el que recibiremos todos los eventos que se produzcan. Todos los eventos que se produzcan serán encolados en este objeto y los recibiremos utilizando el método poll() o take() del servicio WatchService.

  • poll()
  • take()

Ambos recuperan el WatchKey con los eventos producidos. La diferencia entre ambos métodos es que take() espera a que exista al menos un WatchKey con eventos, mientras que poll() si no hay ninguno devolvería null.

Una vez hemos procesado todos los eventos encolados en el WatchKey debemos resetearlo para que pueda seguir recibiendo y encolando más eventos.

En el caso de que un sistema de ficheros sea más rápido informando de los eventos que la implementación que los procesa, cuando procesemos dichos eventos a través del WatchKey recibiremos un evento del tipo OVERFLOW.

El servicio WatchService se puede utilizar de forma concurrente por varios consumidores. El único cuidado que tenemos que tener en estos casos es llamar al método reset() solo después de que sus eventos del WatchEvent hayan sido todos procesados.

Finalmente llamaremos al método close() del servicio WatchService para finalizar con la monitorización del directorio en el que estábamos interesados.

Código de ejemplo

Sigue leyendo

Importar un Certificado de una CA en el cacerts de la Jdk

Java

Es habitual encontrarnos en situaciones donde una empresa tiene su propia autoridad de certificación (Certificate authority o CA) con la que firma los certificados de sus servidores o bien nos encontramos con servicios REST o web services de sites cuyo certificado esta firmado por una CA que no la tenemos en nuestro almacén de CAs de confianza.

En estos casos para que nuestras aplicaciones puedan establecer una conexión segura a estos servicios, necesitamos incorporar el certificado de la CA en nuestro almacén de CAs de confianza.

De esta forma, siempre y cuando nos encontremos con un certificado firmado por cualquiera de esas nuevas CAs, confiaremos automáticamente en ellos.

Como sabemos una CA firma los certificados que emite con su clave privada y es mediante el certificado de la CA con lo que podemos verificar la firma. Esta firma va incluida en el certificado emitido. Por ese motivo incorporamos el certificado de la CA entre los que confiamos.

En la Jdk el almacen de CAs de confianza lo vamos a encontrar en:

$JAVA_HOME/jre/lib/security/cacerts

Para incorporar el Certificado de la CA en el cacerts necesitamos importarlo.

Importar un certificado de una CA en nuestro almacén de CAs de confianza o cacerts

$JAVA_HOME/bin/keytool -import -alias NuevoCA -file NuevoCertificadoCA.crt -keystore $JAVA_HOME/jre/lib/security/cacerts

La contraseña por defecto del cacerts es: changeit.

Este comando nos pedirá confirmación de que queremos importar este nuevo certificado en el almacén de CAs.

Importar un certificado de una CA en un almacén de CAs de confianza

keytool -import -trustcacerts -alias NuevoCA -file NuevoCertificadoCA.crt -keystore cacerts_app

-trustcacerts: Se utiliza cuando el -keystore no es el cacerts, y queremos que al importar el certificado, keytool tenga en cuenta los Certificados de las CAs que tenemos en el fichero cacerts

Herramienta gráfica para administrar un almacén de certificados

Si no queremos utiliza la línea de comando siempre podemos recurrir a alguna de las herramientas gráficas existente para manejar y administrar certificados.

Portecle es una de ellas. Muy simple y sencilla de utilizar.

Portecle

Excepción habitual que pretendemos evitar al incorpotar el Certificado de la CA

Cuando accedemos a un servicio por conexión segura y el certificado es autofirmado o no tenemos el certificado de la CA que lo firma en nuestro cacerts, obtendremos la siguiente excepción:

Exception in thread "main" javax.xml.ws.WebServiceException: Fallo al acceder al WSDL en: https://localhost/myservice. Ha fallado con: 
    sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target.
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(RuntimeWSDLParser.java:250)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:231)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:194)
    at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:163)
    ...
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1937)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
    ...

Incorporando el Certificado de la CA evitaremos este tipo de excepciones.

Convertir coordenadas en grados, minutos y segundos a su valor en grados decimales en Java

Java

Existen varias formas de convertir las coordenadas representadas en grados, minutos y segundos a su representación en grados decimales.

El objetivo es pasar de

  • latitud: 40° 20.2′ 55.68
  • longitud: 3° 21′ 3.29

a

  • latitud: 40.352133333333335
  • longitud: 3.350913888888889

Ejemplo de clase Java

public class LatLongUtils {

    public static Double[] toDecimal(String latitude, String longitude) {
        try {
            String[] lat = latitude.replaceAll("[^0-9.\\s-]", "").split(" ");
            String[] lng = longitude.replaceAll("[^0-9.\\s-]", "").split(" ");
            Double dlat = toDecimal(lat); 
            Double dlng = toDecimal(lng);
            return new Double[]{dlat, dlng};
        } catch(Exception ex) {
            log.error(String.format("Error en el formato de las coordenadas: %s %s", new Object[]{latitude, longitude}), ex);
            return null;
        }
    }

    public static Double toDecimal(String latOrLng) {
        try {
            String[] latlng = latOrLng.replaceAll("[^0-9.\\s-]", "").split(" ");
            Double dlatlng = toDecimal(latlng); 
            return dlatlng;
        } catch(Exception ex) {
            log.error(String.format("Error en el formato de las coordenadas: %s ", new Object[]{latOrLng}), ex);
            return null;
        }
    }

    public static Double toDecimal(String[] coord) {
        double d = Double.parseDouble(coord[0]);
        double m = Double.parseDouble(coord[1]);
        double s = Double.parseDouble(coord[2]);
        double signo = 1;
        if (coord[0].startsWith("-"))
            signo = -1;
        return signo * (Math.abs(d) + (m / 60.0) + (s / 3600.0));
    }

    public static void main(String[] args) {
        Double[] coord = toDecimal("40° 20.2' 55.68\"", "3° 21' 3.29\"");
        System.out.println(coord[0] + " " + coord[1]);
    }

}

Eliminar acentos y diacríticos de un String en Java

Java

Todos nos hemos encontrado en la situación de tener que eliminar las tildes y diacrípticos de una cadena de texto o String en una aplicación Java. Los motivos por los que hacemos esto pueden ser muy diversos:

  • para realizar algún tipo de comparación entre cadenas de texto
  • para intentar normalizar de alguna manera la cadena de texto antes de guardarlos en una base de datos
  • etc

En cualquier caso el código que nos resuelve esto es muy sencillo

Código Java

package example.string;

import java.text.Normalizer;

public class Norm {

    public static void main(String[] args) throws Exception {
        String cadena = "áéíóú";
        System.out.println(cadena + " = " + cleanString(cadena));
        cadena = "àèìòù";
        System.out.println(cadena + " = " + cleanString(cadena)); 
        cadena = "äëïöü";
        System.out.println(cadena + " = " + cleanString(cadena));
        cadena = "âêîôû";
        System.out.println(cadena + " = " + cleanString(cadena));
        cadena = "2² + 2³";
        System.out.println(cadena + " = " + cleanString(cadena));
    }

    public static String cleanString(String texto) {
        texto = Normalizer.normalize(texto, Normalizer.Form.NFD);
        texto = texto.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
        return texto;
    }
}

Resultado de la ejecución

áéíóú = aeiou
àèìòù = aeiou
äëïöü = aeiou
âêîôû = aeiou

Normalizer.normalize(texto, Normalizer.Form.NFD);

Según el API de la Jdk los métodos normalize de java.text.Normalizer transforman un texto Unicode en su representación compuesta o descompuesta, lo que nos permite poder buscar y ordenar textos de una forma más sencilla después de la transformación.

La transformación está recogida en un estándar de Unicode que lo podemos consultar en Unicode Standard Annex #15 Unicode Normalization Forms.

De este estándar nos interesa la transformación llamada Canonical decomposition.

Esta transformación transforma los caracteres con tildes y diacríticos separandolos en dos caracteres. Por ejemplo

á = a´
ä = a¨
â = a^

y con lo que el código

Normalizer.normalize(texto, Normalizer.Form.NFD);

convertimos los textos a su forma Canonical decomposition que utilizaremos en el siguiente paso para lograr nuestro objetivo.

\p{InCombiningDiacriticalMarks}

Lo utilizamos en expresiones regulares, y es independiente del lenguaje, por lo que lo podemos utilizar desde Java, Javascript, Php, Perl, Node.js y todo aquel lenguaje con una implementación básica de expresiones regulares.

Esta expresión representa los caracteres UTF-8 comprendidos entre

  • U+0300 hasta U+036F

entre los que se encuentran las tildes y todos los diacríticos en los que estamos interesados.

Entonces el código

texto = texto.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");

lo que hace es eliminar todos esos caracteres del texto, consiguiendo finalmente el resultado esperado.

Tabla con los caracteres comprendidos entre U+0300 y U+036F

U+0300  ̀   ̀   ̀   ̀   ̀   COMBINING GRAVE ACCENT
U+0301  ́   ́   ́   ́   ́   COMBINING ACUTE ACCENT
U+0302  ̂   ̂   ̂   ̂   ̂   COMBINING CIRCUMFLEX ACCENT
U+0303  ̃   ̃   ̃   ̃   ̃   COMBINING TILDE
U+0304  ̄   ̄   ̄   ̄   ̄   COMBINING MACRON
U+0305  ̅   ̅   ̅   ̅   ̅   COMBINING OVERLINE
U+0306  ̆   ̆   ̆   ̆   ̆   COMBINING BREVE
U+0307  ̇   ̇   ̇   ̇   ̇   COMBINING DOT ABOVE
U+0308  ̈   ̈   ̈   ̈   ̈   COMBINING DIAERESIS
U+0309  ̉   ̉   ̉   ̉   ̉   COMBINING HOOK ABOVE
U+030A  ̊   ̊   ̊   ̊   ̊   COMBINING RING ABOVE
U+030B  ̋   ̋   ̋   ̋   ̋   COMBINING DOUBLE ACUTE ACCENT
U+030C  ̌   ̌   ̌   ̌   ̌   COMBINING CARON
U+030D  ̍   ̍   ̍   ̍   ̍   COMBINING VERTICAL LINE ABOVE
U+030E  ̎   ̎   ̎   ̎   ̎   COMBINING DOUBLE VERTICAL LINE ABOVE
U+030F  ̏   ̏   ̏   ̏   ̏   COMBINING DOUBLE GRAVE ACCENT
U+0310  ̐   ̐   ̐   ̐   ̐   COMBINING CANDRABINDU
U+0311  ̑   ̑   ̑   ̑   ̑   COMBINING INVERTED BREVE
U+0312  ̒   ̒   ̒   ̒   ̒   COMBINING TURNED COMMA ABOVE
U+0313  ̓   ̓   ̓   ̓   ̓   COMBINING COMMA ABOVE
U+0314  ̔   ̔   ̔   ̔   ̔   COMBINING REVERSED COMMA ABOVE
U+0315  ̕   ̕   ̕   ̕   ̕   COMBINING COMMA ABOVE RIGHT
U+0316  ̖   ̖   ̖   ̖   ̖   COMBINING GRAVE ACCENT BELOW
U+0317  ̗   ̗   ̗   ̗   ̗   COMBINING ACUTE ACCENT BELOW
U+0318  ̘   ̘   ̘   ̘   ̘   COMBINING LEFT TACK BELOW
U+0319  ̙   ̙   ̙   ̙   ̙   COMBINING RIGHT TACK BELOW
U+031A  ̚   ̚   ̚   ̚   ̚   COMBINING LEFT ANGLE ABOVE
U+031B  ̛   ̛   ̛   ̛   ̛   COMBINING HORN
U+031C  ̜   ̜   ̜   ̜   ̜   COMBINING LEFT HALF RING BELOW
U+031D  ̝   ̝   ̝   ̝   ̝   COMBINING UP TACK BELOW
U+031E  ̞   ̞   ̞   ̞   ̞   COMBINING DOWN TACK BELOW
U+031F  ̟   ̟   ̟   ̟   ̟   COMBINING PLUS SIGN BELOW
U+0320  ̠   ̠   ̠   ̠   ̠   COMBINING MINUS SIGN BELOW
U+0321  ̡   ̡   ̡   ̡   ̡   COMBINING PALATALIZED HOOK BELOW
U+0322  ̢   ̢   ̢   ̢   ̢   COMBINING RETROFLEX HOOK BELOW
U+0323  ̣   ̣   ̣   ̣   ̣   COMBINING DOT BELOW
U+0324  ̤   ̤   ̤   ̤   ̤   COMBINING DIAERESIS BELOW
U+0325  ̥   ̥   ̥   ̥   ̥   COMBINING RING BELOW
U+0326  ̦   ̦   ̦   ̦   ̦   COMBINING COMMA BELOW
U+0327  ̧   ̧   ̧   ̧   ̧   COMBINING CEDILLA
U+0328  ̨   ̨   ̨   ̨   ̨   COMBINING OGONEK
U+0329  ̩   ̩   ̩   ̩   ̩   COMBINING VERTICAL LINE BELOW
U+032A  ̪   ̪   ̪   ̪   ̪   COMBINING BRIDGE BELOW
U+032B  ̫   ̫   ̫   ̫   ̫   COMBINING INVERTED DOUBLE ARCH BELOW
U+032C  ̬   ̬   ̬   ̬   ̬   COMBINING CARON BELOW
U+032D  ̭   ̭   ̭   ̭   ̭   COMBINING CIRCUMFLEX ACCENT BELOW
U+032E  ̮   ̮   ̮   ̮   ̮   COMBINING BREVE BELOW
U+032F  ̯   ̯   ̯   ̯   ̯   COMBINING INVERTED BREVE BELOW
U+0330  ̰   ̰   ̰   ̰   ̰   COMBINING TILDE BELOW
U+0331  ̱   ̱   ̱   ̱   ̱   COMBINING MACRON BELOW
U+0332  ̲   ̲   ̲   ̲   ̲   COMBINING LOW LINE
U+0333  ̳   ̳   ̳   ̳   ̳   COMBINING DOUBLE LOW LINE
U+0334  ̴   ̴   ̴   ̴   ̴   COMBINING TILDE OVERLAY
U+0335  ̵   ̵   ̵   ̵   ̵   COMBINING SHORT STROKE OVERLAY
U+0336  ̶   ̶   ̶   ̶   ̶   COMBINING LONG STROKE OVERLAY
U+0337  ̷   ̷   ̷   ̷   ̷   COMBINING SHORT SOLIDUS OVERLAY
U+0338  ̸   ̸   ̸   ̸   ̸   COMBINING LONG SOLIDUS OVERLAY
U+0339  ̹   ̹   ̹   ̹   ̹   COMBINING RIGHT HALF RING BELOW
U+033A  ̺   ̺   ̺   ̺   ̺   COMBINING INVERTED BRIDGE BELOW
U+033B  ̻   ̻   ̻   ̻   ̻   COMBINING SQUARE BELOW
U+033C  ̼   ̼   ̼   ̼   ̼   COMBINING SEAGULL BELOW
U+033D  ̽   ̽   ̽   ̽   ̽   COMBINING X ABOVE
U+033E  ̾   ̾   ̾   ̾   ̾   COMBINING VERTICAL TILDE
U+033F  ̿   ̿   ̿   ̿   ̿   COMBINING DOUBLE OVERLINE
U+0340  ̀   ̀   ̀   ̀   ̀   COMBINING GRAVE TONE MARK
U+0341  ́   ́   ́   ́   ́   COMBINING ACUTE TONE MARK
U+0342  ͂   ͂   ͂   ͂   ͂   COMBINING GREEK PERISPOMENI
U+0343  ̓   ̓   ̓   ̓   ̓   COMBINING GREEK KORONIS
U+0344  ̈́   ̈́   ̈́  ̈́  ̈́  COMBINING GREEK DIALYTIKA TONOS
U+0345  ͅ   ͅ   ͅ   ͅ   ͅ   COMBINING GREEK YPOGEGRAMMENI
U+0346  ͆   ͆   ͆   ͆   ͆   COMBINING BRIDGE ABOVE
U+0347  ͇   ͇   ͇   ͇   ͇   COMBINING EQUALS SIGN BELOW
U+0348  ͈   ͈   ͈   ͈   ͈   COMBINING DOUBLE VERTICAL LINE BELOW
U+0349  ͉   ͉   ͉   ͉   ͉   COMBINING LEFT ANGLE BELOW
U+034A  ͊   ͊   ͊   ͊   ͊   COMBINING NOT TILDE ABOVE
U+034B  ͋   ͋   ͋   ͋   ͋   COMBINING HOMOTHETIC ABOVE
U+034C  ͌   ͌   ͌   ͌   ͌   COMBINING ALMOST EQUAL TO ABOVE
U+034D  ͍   ͍   ͍   ͍   ͍   COMBINING LEFT RIGHT ARROW BELOW
U+034E  ͎   ͎   ͎   ͎   ͎   COMBINING UPWARDS ARROW BELOW
U+034F  ͏   ͏   ͏   ͏   ͏   COMBINING GRAPHEME JOINER
U+0350  ͐   ͐   ͐   ͐   ͐   COMBINING RIGHT ARROWHEAD ABOVE
U+0351  ͑   ͑   ͑   ͑   ͑   COMBINING LEFT HALF RING ABOVE
U+0352  ͒   ͒   ͒   ͒   ͒   COMBINING FERMATA
U+0353  ͓   ͓   ͓   ͓   ͓   COMBINING X BELOW
U+0354  ͔   ͔   ͔   ͔   ͔   COMBINING LEFT ARROWHEAD BELOW
U+0355  ͕   ͕   ͕   ͕   ͕   COMBINING RIGHT ARROWHEAD BELOW
U+0356  ͖   ͖   ͖   ͖   ͖   COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW
U+0357  ͗   ͗   ͗   ͗   ͗   COMBINING RIGHT HALF RING ABOVE
U+0358  ͘   ͘   ͘   ͘   ͘   COMBINING DOT ABOVE RIGHT
U+0359  ͙   ͙   ͙   ͙   ͙   COMBINING ASTERISK BELOW
U+035A  ͚   ͚   ͚   ͚   ͚   COMBINING DOUBLE RING BELOW
U+035B  ͛   ͛   ͛   ͛   ͛   COMBINING ZIGZAG ABOVE
U+035C  ͜   ͜   ͜   ͜   ͜   COMBINING DOUBLE BREVE BELOW
U+035D  ͝   ͝   ͝   ͝   ͝   COMBINING DOUBLE BREVE
U+035E  ͞   ͞   ͞   ͞   ͞   COMBINING DOUBLE MACRON
U+035F  ͟   ͟   ͟   ͟   ͟   COMBINING DOUBLE MACRON BELOW
U+0360  ͠   ͠   ͠   ͠   ͠   COMBINING DOUBLE TILDE
U+0361  ͡   ͡   ͡   ͡   ͡   COMBINING DOUBLE INVERTED BREVE
U+0362  ͢   ͢   ͢   ͢   ͢   COMBINING DOUBLE RIGHTWARDS ARROW BELOW
U+0363  ͣ   ͣ   ͣ   ͣ   ͣ   COMBINING LATIN SMALL LETTER A
U+0364  ͤ   ͤ   ͤ   ͤ   ͤ   COMBINING LATIN SMALL LETTER E
U+0365  ͥ   ͥ   ͥ   ͥ   ͥ   COMBINING LATIN SMALL LETTER I
U+0366  ͦ   ͦ   ͦ   ͦ   ͦ   COMBINING LATIN SMALL LETTER O
U+0367  ͧ   ͧ   ͧ   ͧ   ͧ   COMBINING LATIN SMALL LETTER U
U+0368  ͨ   ͨ   ͨ   ͨ   ͨ   COMBINING LATIN SMALL LETTER C
U+0369  ͩ   ͩ   ͩ   ͩ   ͩ   COMBINING LATIN SMALL LETTER D
U+036A  ͪ   ͪ   ͪ   ͪ   ͪ   COMBINING LATIN SMALL LETTER H
U+036B  ͫ   ͫ   ͫ   ͫ   ͫ   COMBINING LATIN SMALL LETTER M
U+036C  ͬ   ͬ   ͬ   ͬ   ͬ   COMBINING LATIN SMALL LETTER R
U+036D  ͭ   ͭ   ͭ   ͭ   ͭ   COMBINING LATIN SMALL LETTER T
U+036E  ͮ   ͮ   ͮ   ͮ   ͮ   COMBINING LATIN SMALL LETTER V
U+036F  ͯ   ͯ   ͯ   ͯ   ͯ   COMBINING LATIN SMALL LETTER X