Cómo imprimir registros razonablemente en Java

1. El papel principal del registro

1) El registro es el "espejo espejo" de la operación del sistema, a través del cual puede reflejar el estado operativo del sistema en tiempo real;

Como se muestra en la figura anterior, el productor en el sistema A genera continuamente datos y los coloca en la cola de datos, y el remitente obtiene continuamente los datos de la cola de datos y los envía al receptor del sistema B descendente. Para el sistema A, la cantidad de datos que se enviarán en la cola de datos Es un indicador clave, realmente puede reflejar el estado operativo actual del sistema desde un lado. Si el número de elementos en la cola de datos excede el 90% de la capacidad, indica que el sistema puede no estar funcionando correctamente en este momento, y habrá colas El riesgo de congestión; si el número de elementos en la cola de datos es inferior al 10% de la capacidad, indica que el sistema está funcionando normalmente en este momento y el riesgo de congestión en la cola es bajo.

Si este indicador no se envía al registro, el personal de desarrollo y operación y mantenimiento no puede conocer el estado operativo actual del sistema A (por supuesto, hay otras formas de obtener este indicador, como la exposición a través de la interfaz http también es una de las formas).

2) Un buen registro es conveniente para que O&M posterior y los desarrolladores puedan localizar rápidamente los problemas en línea, acelerar el stop loss y reducir las pérdidas causadas por fallas del sistema;

3) El registro también tiene otra función: ser capaz de integrarse sin problemas con el sistema de monitoreo, recolectar registros a través del sistema de monitoreo y obtener los indicadores de desempeño relevantes de la operación del sistema, lo cual es útil para analizar el cuello de botella de desempeño del sistema y evitar riesgos de antemano;

Ejemplos:

Si hay un sistema de centro comercial, en la etapa inicial, la base de datos proporciona servicios a través de dos servidores (uno maestro, uno esclavo), y la mayoría de las interfaces pueden responder a las solicitudes de los usuarios en cuestión de segundos. Con el paso del tiempo, el número de usuarios en el sistema del centro comercial ha aumentado gradualmente, y ha habido un cierto aumento en las consultas y escrituras concurrentes. La cantidad de datos en la base de datos también ha aumentado lentamente, lo que resulta en una consulta cada vez más lenta de algunas declaraciones SQL. La máquina esclava de la base de datos fue arrastrada hacia abajo debido a demasiadas consultas lentas, tiempo de inactividad completo, lo que resultó en la falta de disponibilidad de los servicios del centro comercial.

Si el sistema del centro comercial registra en el registro la situación que consume mucho tiempo de cada solicitud http, configura la recopilación de registros a través del sistema de monitoreo y configura la alarma correspondiente, entonces puede encontrar el cuello de botella de rendimiento del sistema debido al crecimiento comercial por adelantado y optimizar el sistema por adelantado ( Como la expansión de la capacidad de la máquina, la optimización de sentencias SQL, la subtabla de la base de datos, etc.), para evitar riesgos.

4) Es conveniente para estadísticas de datos de índice relacionados con negocios, análisis de negocios relevantes y optimización de funciones.

Ejemplos:

Por ejemplo, un sistema de búsqueda desea contar la proporción de búsquedas utilizadas en diferentes regiones (como las regiones norte y sur) durante la semana pasada. Si la dirección IP de cada solicitud de consulta de búsqueda se imprime en el registro, es fácil contar, de lo contrario debe estar en línea y agregarse al registro para contar.

Por lo tanto, todos deben prestar atención a la estandarización de la escritura de registros en el proceso diario de escritura de código, dejar que juegue su valor debido y ayudar a garantizar el funcionamiento estable de nuestros servicios, al tiempo que mejora efectivamente la eficiencia del mantenimiento posterior del sistema.

2. ¿Cómo imprimir el registro del programa de manera estandarizada?

A continuación, hablaremos sobre cómo imprimir registros de forma estandarizada a partir de los siguientes aspectos.

  1. Nombre de archivo de registro
  2. Desplazamiento de registro
  3. Nivel de registro
  4. Selección del tiempo de impresión de registros

2.1 Nombrar archivos de registro

En términos generales, la denominación de los archivos de registro puede incluir la siguiente información clave:

  1. Identificación de tipo (logTypeName)
  2. Nivel de registro (logLevel)
  3. Tiempo de generación de registro (logCreateTime)
  4. Número de copia de seguridad del registro (logBackupNum)

Identificación de tipo: se refiere a la función o el propósito de este archivo de registro, como un servicio web. El registro que registra la solicitud HTTP generalmente se denomina request.log o access.log, request y access son la identificación de tipo, y el registro java gc generalmente se denomina gc. registro, para que pueda verlo de un vistazo; el registro generalmente utilizado para registrar la operación general del servicio generalmente se nombra después del nombre del servicio (serviceName, appKey) o el nombre de la máquina (hostName), como nginx.log;

Nivel de registro: es una forma más recomendada de distinguir el nivel directamente a través del archivo al imprimir el registro. Si registra todos los niveles en el mismo archivo de registro, debe ir al archivo para encontrar la operación al localizar el problema. Engorroso El nivel de registro generalmente incluye los cinco niveles de DEBUG, INFO, WARN, ERROR y FATAL. En el código de escritura real, se puede adoptar el modo de coincidencia estricta o el modo de coincidencia no estricta. El modo de coincidencia estricta significa que solo el registro de INFO y el registro de ERROR se imprimen en el archivo de registro de INFO. El archivo solo imprime el registro de ERROR; en el modo de coincidencia no estricto, el archivo de registro de INFO puede imprimir el registro de INFO, el registro de WARN, el registro de ERROR, el registro de FATAL, el archivo de registro de WARN puede imprimir el registro de WARN, el registro de ERROR, el registro de FATAL, etc.

Tiempo de generación de registro: el tiempo en que se crea el archivo de registro se agrega al nombre del archivo de registro, lo cual es conveniente para ordenar cuando se busca el archivo de registro;

Número de copia de seguridad del registro: cuando se corta el registro, si el tamaño del archivo se utiliza para desplazarse, puede agregar un número al final del nombre del archivo de registro;

2.2 Desplazamiento de registro

Aunque el registro puede guardar la información clave cuando el sistema se está ejecutando, pero debido al espacio limitado en el disco, no podemos mantener el registro sin límite, por lo que debe haber una estrategia de registro continuo. El registro de rodamiento generalmente tiene los siguientes modos:

  1. El primero: desplazarse por tiempo
  2. El segundo: rodar según el tamaño de un único archivo de registro
  3. El tercer tipo: desplazarse según la hora y el tamaño de un solo archivo de registro al mismo tiempo.

La rotación según el tiempo, es decir, la creación de un nuevo archivo de registro cada cierto tiempo, por lo general, se puede desplazar según el nivel de hora o nivel de día, dependiendo de la cantidad de impresión del registro del sistema. Si el registro del sistema es relativamente pequeño, puede pasar el nivel diario; y si el volumen diario del sistema es relativamente grande, se recomienda tomar el nivel horario.
Gire de acuerdo con el tamaño de un único archivo de registro, es decir, cuando un archivo de registro alcanza un cierto tamaño, se crea un nuevo archivo de registro. En general, se recomienda que el tamaño de un solo archivo de registro no supere los 500 M. Si el archivo de registro es demasiado grande, puede causar la supervisión o la solución de problemas del registro. Ciertamente afectado.

Según el tiempo y el tamaño de un único archivo de registro, este modo suele ser adecuado para escenarios en los que desea mantener registros durante un cierto período de tiempo, pero no desea que el archivo de registro sea demasiado grande.

Para la estrategia de registro continuo, hay dos parámetros críticos más: el número máximo de registros reservados y la huella máxima del disco. Recuerde configurar estos dos parámetros. Si no están configurados, es muy probable que el disco de la máquina en línea esté lleno.

2.3 Nivel de registro

Los niveles de registro suelen ser los siguientes:

depuración / rastreo 、 información 、 advertencia 、 error 、 fatal

Los programas serios de estos niveles de registro aumentan en orden:

  • depuración / rastreo: dado que los registros de nivel de depuración y rastreo tienen una gran cantidad de contenido de impresión, generalmente no son adecuados para el uso del entorno de producción en línea, y generalmente se usan para la depuración temprana del entorno fuera de línea. Incluso si se va a utilizar el entorno en línea, debe controlarse mediante un interruptor, y solo se activa al localizar y rastrear problemas en línea;

  • info: el registro de información se usa generalmente para registrar el estado crítico de la operación del sistema, la lógica comercial crítica o los nodos críticos de ejecución. Pero tenga en cuenta que no se debe abusar del registro de información. Si se abusa del registro de información, no es muy diferente del registro de depuración / rastreo.

  • advertencia: El registro de advertencia se usa generalmente para registrar algunas situaciones inesperadas cuando el sistema se está ejecutando. Como su nombre lo indica, se usa como una advertencia para recordarle al personal de desarrollo y operación y mantenimiento que preste atención, pero que se ocupe de él inmediatamente sin intervención humana.

  • error: el registro de errores generalmente se usa para registrar algunos errores comunes cuando el sistema se está ejecutando. Una vez que aparecen estos errores, significa que el acceso o uso normal del usuario se ha visto afectado, lo que generalmente significa que se requiere intervención humana. Sin embargo, en el entorno de producción, no siempre es necesario intervenir manualmente en el registro de errores cuando aparece.Por lo general, el número y la duración del registro de errores se combinan para hacer un juicio exhaustivo.

  • fatal: es un error fatal del sistema. Generalmente, significa que el sistema básicamente cuelga y requiere intervención manual.

Aquí hay un ejemplo simple para ilustrar, si tenemos ese escenario, tenemos un sistema de cálculo de salario, necesitamos obtener los datos de asistencia de todos los empleados de la compañía del sistema de asistencia de empleados el 1 de cada mes, y luego calcular el último mes debe basarse en los datos de asistencia Salario, entonces debe haber una función para obtener datos de asistencia de los empleados del sistema de asistencia:

public Map<Long, Double> getEmployeeWorkDaysFromAttendance(int year, int month, Set<Long> employeeList) throws BusiessException {
        // 入口关键日志,需要打印关键的参数,因为employeeList可能数量较大,所以次数没有直接打印employeeList列表内容,只打印了size
        logger.info("get employee work days, year:{}, month:{}, employeeList.size:{}", year, month, employeeList.size());
 
        // 如果需要临时检验员工列表,可以把debug日志开关打开
        if (debugOpen()) {
            logger.debug("employ list content:{}", JSON.toJsonString(employeeList));
        }
         
        int retry = 1;
        while (retry <= MAX_RETRY_TIMES) {
            try {
                Map<Long, Double> employeeWorkDays = employeeAttendanceRPC.getEmployeeWorkDays(year, month, employeeList);
                logger.info("get employee work days success, year:{}, month:{}, employeeList.size:{}, employeeWorkDays.size:{}", year, month, employeeList.size(), employeeWorkDays.size());
                return employeeWorkDays;
            } catch (Exception ex) {
                logger.warning("rpc invoke failed(employeeAttendanceRPC.getEmployeeWorkDays), retry times:{}, year:{}, month:{},  employeeList.size:{}", retry, year, month, employeeList.size(), ex);
                 
                // 连续重试失败之后,向上跑出异常
                // 对于没有异常机制的语言,此处应该打印error日志
                if (retry == MAX_RETRY_TIMES) {
                    throw new BusiessException(ex, "rpc invoke failed(employeeAttendanceRPC.getEmployeeWorkDays)");
                }
            }
            retry++;
        }
    }

2.4 Selección del tiempo de impresión del registro

Debido a que el registro debe facilitarnos comprender el estado operativo actual del sistema y localizar problemas en línea, el momento de la impresión del registro es muy importante. Si se abusa del registro, causará demasiado contenido del registro y afectará la eficiencia de la ubicación del problema; Es fácil causar la falta de registros de claves, y la causa raíz del problema no se puede encontrar al localizar el problema en línea. Por lo tanto, es muy importante comprender el momento de la impresión de registros. Los siguientes son tiempos comunes adecuados para imprimir registros:

1) llamada http o llamada de interfaz rpc

Cuando el programa llama a otros servicios o sistemas, se deben imprimir los parámetros de llamada de la interfaz y los resultados de la llamada (éxito / falla).

2) programa anormal

Cuando ocurre una excepción en el programa, usted elige lanzar una excepción hacia arriba o debe imprimir la información de la pila de excepciones en el bloque catch. Sin embargo, debe tenerse en cuenta que es mejor no imprimir repetidamente el registro de excepciones, como lanzar una excepción hacia arriba en el bloque catch e imprimir el registro de errores (excepto la entrada de la función de interfaz externa rpc).

3) Rama de condición especial

Cuando el programa ingresa algunas ramas condicionales especiales, como special else o switch branch. Por ejemplo, calculamos el salario en función de la antigüedad:

public double calSalaryByWorkingAge(int age) {
       if (age < 0) {
           logger.error("wrong age value, age:{}", age);
           return 0;
       }
       // ..
   }

En teoría, la duración del servicio no puede ser inferior a 0, por lo que es necesario imprimir esta situación inesperada. Por supuesto, también es posible lanzar una excepción.

4) Ruta de ejecución crítica y estado intermedio

También es necesario registrar información de registro clave en algunas rutas de ejecución críticas y estados intermedios. Por ejemplo, un algoritmo puede dividirse en muchos pasos. ¿Cuál es el resultado de salida intermedio de cada paso debe registrarse para facilitar el posicionamiento y el seguimiento posterior del estado de ejecución del algoritmo?

5) Solicitar entrada y salida

Los registros de entrada / salida deben imprimirse en la entrada / salida de la función o interfaz externa, lo que facilita las estadísticas de registro posteriores y también facilita el monitoreo del estado operativo del sistema.

2.5 Contenido de registro y formato

El momento de la impresión del registro determina que el problema puede ubicarse de acuerdo con el registro, y el contenido del registro determina si la causa del problema se puede encontrar rápidamente en función del registro, por lo que el contenido del registro también es crucial. En términos generales, un registro debe incluir al menos los siguientes componentes:

logTag 、 param 、 exceptionStacktrace

  • logTag es el identificador de registro, que se utiliza para identificar la escena o el motivo de la salida de este registro,
  • param es el parámetro de llamada de función,
  • exceptionStacktrace es una pila de excepciones.

Ejemplos:

buen caso

public class HttpClient {
        private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);
 
        private static int CONNECT_TIMEOUT = 5000;   // unit ms
        private static int READ_TIMEOUT = 10000;     // unit ms
 
        public static String sendPost(String url, String param) {
            OutputStream out = null;
            BufferedReader in = null;
            String result = "";
            try {
                URL realUrl = new URL(url);
                URLConnection conn = realUrl.openConnection();
                conn.setDoInput(true);
                conn.setDoOutput(true);
                conn.setConnectTimeout(CONNECT_TIMEOUT);
                conn.setReadTimeout(READ_TIMEOUT);
                conn.setRequestProperty("charset", "UTF-8");
                out = new PrintWriter(conn.getOutputStream());
                out.print(parm);
                out.flush();
                in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                String line;
                while ((line = in.readLine()) != null) {
                    result += line;
                }
            } catch (Exception ex) {
                // 有关键logTag,有参数信息,有错误堆栈
                LOG.error("post request error!!!, url:[[}], param:[{}]", url, param, ex);
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException ex) {
                    LOG.error("close stream error!!!, url:[[}], param:[{}]", url, param, ex);
                }
                return result;
            }
        }
    }

mal caso

public class HttpClient {
    private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);
 
    private static int CONNECT_TIMEOUT = 5000;   // unit ms
    private static int READ_TIMEOUT = 10000;     // unit ms
     
    public static String sendPost(String url, String param) {
        OutputStream out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setConnectTimeout(CONNECT_TIMEOUT);
            conn.setReadTimeout(READ_TIMEOUT);
            conn.setRequestProperty("charset", "UTF-8");
            out = new PrintWriter(conn.getOutputStream());
            out.print(parm);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception ex) {
            // 没有任何错误信息
            LOG.error("post request error!!!");
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                LOG.error("close stream error!!!");
            }
            return result;
        }
    }
}

Además, para la interfaz http externa o la interfaz rpc, es mejor tener un requestId para cada solicitud para rastrear todas las rutas de ejecución posteriores de cada solicitud.

¿Cómo iniciar sesión correctamente en el proyecto?

1. Definir correctamente el registro

2. Utilice la forma parametrizada {} marcador de posición, [] para el aislamiento de parámetros

LOG.debug("Save order with order no:[{}], and order amount:[{}]");

3. Salida de diferentes niveles de registros

Los niveles de registro más utilizados en el proyecto son ERROR, WARN, INFO y DEBUG ¿Cuáles son los escenarios de aplicación de estos cuatro?

Varias formas incorrectas de iniciar sesión

1. No utilizar System.out.print..

Al generar el registro, el registro solo se puede generar a través del marco de registro, en lugar de usar System.out.print ... para imprimir el registro. Esto solo se imprimirá en la consola de tomcat y no se registrará en el archivo de registro. No es conveniente administrar el registro. El registro se descarta después de iniciarse como servicio y no se puede encontrar el registro.

2. No utilizar e.printStackTrace()

En realidad, se emite a la consola tomcat usando System.err.

3. No envíe registros después de lanzar una excepción

Si se produce una excepción comercial personalizada después de detectar la excepción, no es necesario registrar un registro de errores en este momento, y la parte de captura final se encargará de la excepción. No puede volver a lanzar una excepción e imprimir el registro de errores, de lo contrario, provocará la salida repetida del registro.


try {
    // ...
} catch (Exception e) {
    // 错误
    LOG.error("xxx", e);
    throw new RuntimeException();
}

4. No todos los mensajes de error se envían

Mirando el siguiente código, esto no registrará información detallada de excepción de pila, sino solo la descripción básica del error, que no conduce a la resolución de problemas.

try {
    // ...
} catch (Exception e) {
    // 错误
    LOG.error('XX 发生异常', e.getMessage());
 
    // 正确
    LOG.error('XX 发生异常', e);
}

5. No use el nivel de registro incorrecto

Solía ​​localizar un problema en línea, y mis colegas me dijeron con confianza: claramente imprimo el registro, por qué no puedo encontrarlo ... Más tarde, fui a leer su código, así:

try {
    // ...
} catch (Exception e) {
    // 错误
    LOG.info("XX 发生异常...", e);
}

Use la información para registrar el registro de errores y el registro se enviará al archivo de registro de información. ¿Cómo pueden encontrarlo mis colegas en el archivo de registro de errores?

6. No imprima el registro en el bucle Melaleuca

¿Qué significa esto? Si su marco utiliza el marco Log4j con bajo rendimiento, entonces no imprima el registro en miles de bucles, ya que esto puede arrastrar su aplicación. Si el tiempo de respuesta de su programa es lento, Debe considerarse si el registro se imprime demasiado.

for(int i=0; i<2000; i++){
    LOG.info("XX");
}

7. Deshabilite la depuración en el entorno en línea

420 artículos originales publicados · 143 pulgares arriba · 890,000 vistas

Supongo que te gusta

Origin blog.csdn.net/jeikerxiao/article/details/99851611
Recomendado
Clasificación