android - studio - open logcat
Registro de mejores prácticas y pensamientos (3)
Estoy a punto de hacer algunas refacciones en mi aplicación y pensé en este tema simple pero complejo, el registro, ¿cómo puede ser tan difícil hacer un registro limpio, efectivo e informativo ...
Cuando lees la documentación sobre el registro, a menudo ves este fragmento
if (BuildConfig.DEBUG) {
Log.d(TAG + "message");
}
¿Y me hace preguntarme cuál es el propósito con eso? De acuerdo con la documentación, Android Developer - Log , los mensajes de registro de depuración se compilan pero se eliminan en el tiempo de ejecución, por lo que no es necesario tener la llamada de registro dentro de esa declaración if. ¿O estoy mal entendiendo algo?
Luego también me pregunto cuál es el beneficio real con el uso de cualquier otra llamada Log.x (), excepto la depuración, ya que el usuario no podrá ver las entradas del registro o iniciar sesión en algún archivo de errores, por lo que se compilarán y ejecutarán. en el entorno de producción sin ningún propósito en absoluto? Este es quizás un caso de uso para la sentencia if antes?
Anteriormente mencioné que la entrada de registro no está registrada en un archivo. ¿Por qué no es una característica incorporada en Android? ¿Es debido a problemas de rendimiento, uso innecesario de permisos u otra cosa? He implementado esta funcionalidad en mi propia clase de registro, pero ahora me pregunto si es una mala práctica. Pero también es bueno tener registros con entradas de registro importantes.
Entonces, para resumir, implementar un registro limpio, efectivo e informativo, tanto durante el desarrollo como durante la producción. ¿Cuáles son las mejores prácticas?
Esto generará etiquetas de depuración limpias con este formato ClasssName[MethodName] - LineNumber
con reflexión.
El código completo con comentarios en línea está disponible como una idea aquí .
import android.util.Log;
public class Logger {
public enum LOGGER_DEPTH {
ACTUAL_METHOD(4),
LOGGER_METHOD(3),
STACK_TRACE_METHOD(1),
JVM_METHOD(0);
private final int value;
private LOGGER_DEPTH(final int newValue) {
value = newValue;
}
public int getValue() {
return value;
}
}
private static final String personalTAG = "Logger";
private StringBuilder sb;
private Logger() {
if (LoggerLoader.instance != null) {
Log.e(personalTAG, "Error: Logger already instantiated");
throw new IllegalStateException("Already Instantiated");
} else {
this.sb = new StringBuilder(255);
}
}
public static Logger getLogger() {
return LoggerLoader.instance;
}
private String getTag(LOGGER_DEPTH depth) {
try {
String className = Thread.currentThread().getStackTrace()[depth.getValue()].getClassName();
sb.append(className.substring(className.lastIndexOf(".") + 1));
sb.append("[");
sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getMethodName());
sb.append("] - ");
sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getLineNumber());
return sb.toString();
} catch (Exception ex) {
ex.printStackTrace();
Log.d(personalTAG, ex.getMessage());
} finally {
sb.setLength(0);
}
return null;
}
public void d(String msg) {
try {
Log.d(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void d(String msg, LOGGER_DEPTH depth) {
try {
Log.d(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void d(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.d(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void e(String msg) {
try {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void e(String msg, LOGGER_DEPTH depth) {
try {
Log.e(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void e(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.e(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void w(String msg) {
try {
Log.w(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void w(String msg, LOGGER_DEPTH depth) {
try {
Log.w(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void w(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.w(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void v(String msg) {
try {
Log.v(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void v(String msg, LOGGER_DEPTH depth) {
try {
Log.v(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void v(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.v(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void i(String msg) {
try {
Log.i(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void i(String msg, LOGGER_DEPTH depth) {
try {
Log.i(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void i(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.i(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void wtf(String msg) {
try {
Log.wtf(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void wtf(String msg, LOGGER_DEPTH depth) {
try {
Log.wtf(getTag(depth), msg);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
public void wtf(String msg, Throwable t, LOGGER_DEPTH depth) {
try {
Log.wtf(getTag(depth), msg, t);
} catch (Exception exception) {
Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
}
}
private static class LoggerLoader {
private static final Logger instance = new Logger();
}
}
Los registros solo se necesitaron para depurar las aplicaciones durante el desarrollo, para garantizar que la función funcione como se espera y produzca los resultados deseados. Creo que la mejor práctica es realizar el registro de la forma que le resulte más conveniente y le permita encontrar y resolver problemas de la manera más rápida y eficiente posible.
Anteriormente mencioné que la entrada de registro no está registrada en un archivo. ¿Por qué no es una característica incorporada en Android?
¿Quién (excepto un programador en la etapa de desarrollo) querría que una aplicación desperdicia espacio de almacenamiento limitado en el dispositivo con datos inútiles? Los usuarios no ven, no leen, no usan registros. No necesitan esta basura. La aplicación en producción no debe producir ningún registro y, por supuesto, no debe guardarlos en archivos.
El único registro que debe implementarse en la aplicación liberada es el registro de excepciones no manejadas. Además, es responsabilidad de la aplicación manejar estos registros si sugiere enviar informes de errores y eliminarlos después de que se haya enviado el informe.
Otra razón por la que las aplicaciones publicadas no deben crear registros es que pueden contener datos y resultados confidenciales que requieren la autorización del usuario, lo que introduce fallas en la seguridad.
Creo que la mejor práctica es eliminar todos los registros tan pronto como el módulo o la característica se haya implementado completamente y se haya probado exhaustivamente, antes de implementarlo en producción. La introducción de la condición if (BuildConfig.DEBUG)
ayuda a garantizar que esto se haya logrado.
Me he topado con ese mismo problema desde que empecé a trabajar en Android y he creado este proyecto de código abierto ( Android Studio Macros ) que te permite hacer lo que deseas y algunas cosas más complicadas utilizando "// <# DEBUG_AREA> y // <# / DEBUG_AREA> "etiquetas dentro de su código, la idea básica es que cualquier cosa dentro de esas etiquetas será comentada cuando cambie sus variantes de compilación, por ejemplo, si tiene algo como esto en un bucle for:
//=========This piece of code is only for logging purposes...=========
Log.e("LogUserInfo", "Name: " + name);
Log.e("LogUserInfo", "Id: " + user.getId());
Log.e("LogUserInfo", "Id: " + user.getDistance());
//====================================================================
En lugar de hacer esto:
if(DEBUG){
Log.e("LogginUserInfo", "Name: " + name);
Log.e("LogginUserInfo", "Id: " + user.getId());
Log.e("LogginUserInfo", "Id: " + user.getDistance());
}
Con esta macro puedes hacer esto (método completo):
private List<String> getNamesOfUsersNearMe(String zipCode){
List<User> users = mBusinessLogic.getUsersByZipcode(zipCode);
if(users == null || users.size() < 1){
return null;
}
List<String> names = new ArrayList<String>();
int totalUsers = users.size();
for(int i = 0; i < totalUsers; i++){
User user = users.get(i);
String name = user.getName();
names.add(name);
//<#DEBUG_AREA>
Log.e("LogginUserInfo", "Name: " + name);
Log.e("LogginUserInfo", "Id: " + user.getId());
Log.e("LogginUserInfo", "Id: " + user.getDistance());
//</#DEBUG_AREA>
}
return names;
}
Y cuando cambies tu variante de construcción para lanzarla, se convertirá en algo como esto:
private List<String> getNamesOfUsersNearMe(String zipCode){
List<User> users = mBusinessLogic.getUsersByZipcode(zipCode);
if(users == null || users.size() < 1){
return null;
}
List<String> names = new ArrayList<String>();
int totalUsers = users.size();
for(int i = 0; i < totalUsers; i++){
User user = users.get(i);
String name = user.getName();
names.add(name);
/*<#DEBUG_OFF>
Log.e("LogginUserInfo", "Name: " + name);
Log.e("LogginUserInfo", "Id: " + user.getId());
Log.e("LogginUserInfo", "Id: " + user.getDistance());
</#DEBUG_OFF>*/
}
return names;
}
Lo que es mucho mejor en rendimiento para bucles largos y hace que su código sea más limpio al deshacerse del código innecesario mientras está en el modo de "liberación", por supuesto, si vuelve a "depurar", descomentará el área y la dejará tal como estaba originalmente. estaba con las etiquetas "<#DEBUG_AREA>" ...
Además, al tratar de adaptarse a los escenarios más comunes, parece que hay ocasiones en las que no necesita un área completa para deshacerse de ellos, sino un solo registro, por lo que, para ese caso, el proyecto también tiene una clase de contenedor de registros que puede usar como sigue:
if(users == null || users.size() < 1){
ASDebuggerMacroLog.e("LogUserInfo", "There''s no users available near me...");
return null;
}
La línea de código utilizada por la clase "ASDebuggerMacroLog" se comentará después de cambiar al modo "liberar" en su Android Studio.
¡Espero eso ayude!
¡Saludos!