Je vais avoir beaucoup d'instructions de journalisation à déboguer par exemple.
Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");
lors du déploiement de cette application sur un téléphone portable, je souhaite désactiver la journalisation détaillée à partir de laquelle je peux activer/désactiver la journalisation.
Une méthode courante consiste à créer un int nommé loglevel et à définir son niveau de débogage en fonction de loglevel.
public static int LOGLEVEL = 2;
public static boolean ERROR = LOGLEVEL > 0;
public static boolean WARN = LOGLEVEL > 1;
...
public static boolean VERBOSE = LOGLEVEL > 4;
if (VERBOSE) Log.v(TAG, "Message here"); // Won't be shown
if (WARN) Log.w(TAG, "WARNING HERE"); // Still goes through
Plus tard, vous pouvez simplement changer le niveau de journal pour tous les niveaux de sortie de débogage.
Le La documentation Android dit ce qui suit à propos des niveaux de journal :
Verbose ne doit jamais être compilé dans une application, sauf pendant le développement. Les journaux de débogage sont compilés dans mais supprimés au moment de l'exécution. Les journaux d’erreurs, d’avertissements et d’informations sont toujours conservés.
Donc, vous voudrez peut-être envisager de supprimer le journal des instructions de journalisation Verbose, en utilisant éventuellement ProGuard comme suggéré dans une autre réponse .
Selon la documentation, vous pouvez configurer la journalisation sur un périphérique de développement à l'aide de Propriétés système. La propriété à définir est log.tag.<YourTag>
et doit être définie sur l'une des valeurs suivantes: VERBOSE
, DEBUG
, INFO
, WARN
, ERROR
, ASSERT
ou SUPPRESS
. Plus d'informations à ce sujet sont disponibles dans la documentation de la méthode isLoggable()
.
Vous pouvez définir des propriétés temporairement à l'aide de la commande setprop
. Par exemple:
C:\Android>adb Shell setprop log.tag.MyAppTag WARN
C:\Android>adb Shell getprop log.tag.MyAppTag
WARN
Vous pouvez également les spécifier dans le fichier '/data/local.prop' comme suit:
log.tag.MyAppTag=WARN
Les versions ultérieures d'Android semblent nécessiter que /data/local.prop soit en lecture seule . Ce fichier est lu au démarrage, vous devrez donc le redémarrer après l'avoir mis à jour. Si /data/local.prop
est accessible en écriture dans le monde entier, il sera probablement ignoré.
Enfin, vous pouvez les définir par programme en utilisant la méthode System.setProperty()
.
Le moyen le plus simple est probablement d’exécuter votre JAR compilé via ProGuard avant le déploiement, avec une configuration comme:
-assumenosideeffects class Android.util.Log {
public static int v(...);
}
En plus de toutes les autres optimisations de ProGuard, cela supprimera toutes les instructions de journal détaillées directement du code-octet.
J'ai choisi un itinéraire simple: créer une classe wrapper qui utilise également des listes de paramètres variables.
public class Log{
public static int LEVEL = Android.util.Log.WARN;
static public void d(String tag, String msgFormat, Object...args)
{
if (LEVEL<=Android.util.Log.DEBUG)
{
Android.util.Log.d(tag, String.format(msgFormat, args));
}
}
static public void d(String tag, Throwable t, String msgFormat, Object...args)
{
if (LEVEL<=Android.util.Log.DEBUG)
{
Android.util.Log.d(tag, String.format(msgFormat, args), t);
}
}
//...other level logging functions snipped
Le meilleur moyen consiste à utiliser l'API SLF4J + une partie de son implémentation.
Pour les applications Android, vous pouvez utiliser les éléments suivants:
Tu devrais utiliser
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "my log message");
}
Supprimer la journalisation avec proguard (voir la réponse de @Christopher) était simple et rapide, mais les traces de pile issues de la production ne correspondaient pas à la source s'il existait une journalisation de débogage dans le fichier.
Au lieu de cela, voici une technique qui utilise différents niveaux de journalisation en développement par rapport à la production, en supposant que proguard est utilisé uniquement en production. Il reconnaît la production en vérifiant si proguard a renommé un nom de classe donné (dans l'exemple, j'utilise "com.foo.Bar" - vous devez remplacer ce nom par un nom de classe complet dont vous savez qu'il sera renommé par proguard).
Cette technique utilise la journalisation commune.
private void initLogging() {
Level level = Level.WARNING;
try {
// in production, the shrinker/obfuscator proguard will change the
// name of this class (and many others) so in development, this
// class WILL exist as named, and we will have debug level
Class.forName("com.foo.Bar");
level = Level.FINE;
} catch (Throwable t) {
// no problem, we are in production mode
}
Handler[] handlers = Logger.getLogger("").getHandlers();
for (Handler handler : handlers) {
Log.d("log init", "handler: " + handler.getClass().getName());
handler.setLevel(level);
}
}
Il existe un petit remplacement instantané pour la classe Android Log standard - https://github.com/zserge/log
En gros, tout ce que vous avez à faire est de remplacer les importations de Android.util.Log
par trikita.log.Log
. Ensuite, dans votre Application.onCreate()
ou dans certains initalizer statiques, recherchez le BuilConfig.DEBUG
ou tout autre indicateur et utilisez Log.level(Log.D)
ou Log.level(Log.E)
pour modifier le niveau de consignation minimal. Vous pouvez utiliser Log.useLog(false)
pour désactiver la journalisation.
Log4j ou slf4j peuvent également être utilisés comme infrastructures de journalisation sous Android avec logcat. Voir le projet Android-logging-log4j ou support log4j dans Android
Peut-être que vous pouvez voir cette classe d’extension de journal: https://github.com/dbauduin/Android-Tools/tree/master/logs .
Il vous permet d’avoir un contrôle précis sur les journaux . Vous pouvez par exemple désactiver tous les journaux ou uniquement les journaux de certains packages ou classes.
De plus, il ajoute des fonctionnalités utiles (par exemple, vous n'avez pas besoin de passer une balise pour chaque journal).
Voici une solution plus complexe. Vous obtiendrez une trace complète de la pile et la méthode toString () sera appelée uniquement si nécessaire (Performance). L'attribut BuildConfig.DEBUG sera false en mode production, ainsi tous les journaux de trace et de débogage seront supprimés. Le compilateur de points chauds a la possibilité de supprimer les appels en raison de propriétés statiques finales.
import Java.io.ByteArrayOutputStream;
import Java.io.PrintStream;
import Android.util.Log;
public class Logger {
public enum Level {
error, warn, info, debug, trace
}
private static final String DEFAULT_TAG = "Project";
private static final Level CURRENT_LEVEL = BuildConfig.DEBUG ? Level.trace : Level.info;
private static boolean isEnabled(Level l) {
return CURRENT_LEVEL.compareTo(l) >= 0;
}
static {
Log.i(DEFAULT_TAG, "log level: " + CURRENT_LEVEL.name());
}
private String classname = DEFAULT_TAG;
public void setClassName(Class<?> c) {
classname = c.getSimpleName();
}
public String getClassname() {
return classname;
}
public boolean isError() {
return isEnabled(Level.error);
}
public boolean isWarn() {
return isEnabled(Level.warn);
}
public boolean isInfo() {
return isEnabled(Level.info);
}
public boolean isDebug() {
return isEnabled(Level.debug);
}
public boolean isTrace() {
return isEnabled(Level.trace);
}
public void error(Object... args) {
if (isError()) Log.e(buildTag(), build(args));
}
public void warn(Object... args) {
if (isWarn()) Log.w(buildTag(), build(args));
}
public void info(Object... args) {
if (isInfo()) Log.i(buildTag(), build(args));
}
public void debug(Object... args) {
if (isDebug()) Log.d(buildTag(), build(args));
}
public void trace(Object... args) {
if (isTrace()) Log.v(buildTag(), build(args));
}
public void error(String msg, Throwable t) {
if (isError()) error(buildTag(), msg, stackToString(t));
}
public void warn(String msg, Throwable t) {
if (isWarn()) warn(buildTag(), msg, stackToString(t));
}
public void info(String msg, Throwable t) {
if (isInfo()) info(buildTag(), msg, stackToString(t));
}
public void debug(String msg, Throwable t) {
if (isDebug()) debug(buildTag(), msg, stackToString(t));
}
public void trace(String msg, Throwable t) {
if (isTrace()) trace(buildTag(), msg, stackToString(t));
}
private String buildTag() {
String tag ;
if (BuildConfig.DEBUG) {
StringBuilder b = new StringBuilder(20);
b.append(getClassname());
StackTraceElement stackEntry = Thread.currentThread().getStackTrace()[4];
if (stackEntry != null) {
b.append('.');
b.append(stackEntry.getMethodName());
b.append(':');
b.append(stackEntry.getLineNumber());
}
tag = b.toString();
} else {
tag = DEFAULT_TAG;
}
}
private String build(Object... args) {
if (args == null) {
return "null";
} else {
StringBuilder b = new StringBuilder(args.length * 10);
for (Object arg : args) {
if (arg == null) {
b.append("null");
} else {
b.append(arg);
}
}
return b.toString();
}
}
private String stackToString(Throwable t) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(500);
baos.toString();
t.printStackTrace(new PrintStream(baos));
return baos.toString();
}
}
utiliser comme ceci:
Loggor log = new Logger();
Map foo = ...
List bar = ...
log.error("Foo:", foo, "bar:", bar);
// bad example (avoid something like this)
// log.error("Foo:" + " foo.toString() + "bar:" + bar);
J'ai créé un utilitaire/wrapper qui résout ce problème ainsi que d'autres problèmes courants liés à la journalisation.
Un utilitaire de débogage avec les fonctionnalités suivantes:
Comment utiliser?
J'ai essayé de rendre la documentation suffisante.
Les suggestions pour améliorer cet utilitaire sont les bienvenues.
Libre d'utiliser/partager.
Téléchargez-le à partir de GitHub .
Dans mes applications, j'ai une classe qui englobe la classe Log qui a une variable booléenne statique appelée "état". Tout au long de mon code, je vérifie la valeur de la variable "state" à l'aide d'une méthode statique avant d'écrire dans le journal. J'ai ensuite une méthode statique pour définir la variable "state" qui garantit que la valeur est commune à toutes les instances créées par l'application. Cela signifie que je peux activer ou désactiver toute la journalisation de l'application en un seul appel, même lorsque l'application est en cours d'exécution. Utile pour les appels d'assistance ... Cela signifie que vous devez vous en tenir à vos armes lors du débogage et ne pas régresser à l'aide de la classe de journalisation standard ...
Il est également utile (pratique) que Java interprète une variable booléenne comme étant fausse si aucune valeur n'a été affectée, ce qui signifie qu'elle peut être laissée comme telle jusqu'à ce que vous deviez activer la journalisation :-)
Dans un scénario de journalisation très simple, dans lequel vous essayez littéralement d'écrire sur la console pendant le développement à des fins de débogage, il peut être plus simple d'effectuer une recherche et un remplacement avant votre génération de production et de commenter tous les appels à Log ou System. out.println.
Par exemple, en supposant que vous n'utilisiez pas le "journal". n'importe où en dehors d'un appel à Log.d ou Log.e, etc., vous pouvez simplement rechercher et remplacer l'ensemble de la solution pour remplacer "Journal". avec "// Log." pour commenter tous vos appels de journalisation, ou dans mon cas, j'utilise simplement System.out.println partout, alors avant de passer à la production, je vais simplement faire une recherche complète et remplacer "System.out.println" par " "//System.out.println".
Je sais que ce n’est pas idéal et que ce serait bien si la possibilité de trouver et de commenter les appels à Log et System.out.println était intégrée à Eclipse, mais jusqu’à ce que cela se produise, le moyen le plus simple, le plus rapide et le plus simple de le faire est: commenter par rechercher et remplacer. Si vous faites cela, vous n’aurez plus à vous soucier de la non-concordance des numéros de ligne de trace de pile, car vous modifiez votre code source et vous n’ajoutez aucune surcharge en vérifiant une configuration au niveau du journal, etc.
Nous pouvons utiliser la classe Log
dans notre composant local et définir les méthodes v/i/e/d . En fonction des besoins, nous pouvons faire un appel plus approfondi.
exemple est présenté ci-dessous.
public class Log{
private static boolean TAG = false;
public static void d(String enable_tag, String message,Object...args){
if(TAG)
Android.util.Log.d(enable_tag, message+args);
}
public static void e(String enable_tag, String message,Object...args){
if(TAG)
Android.util.Log.e(enable_tag, message+args);
}
public static void v(String enable_tag, String message,Object...args){
if(TAG)
Android.util.Log.v(enable_tag, message+args);
}
}
if we do not need any print(s), at-all make TAG as false for all else
remove the check for type of Log (say Log.d).
as
public static void i(String enable_tag, String message,Object...args){
// if(TAG)
Android.util.Log.i(enable_tag, message+args);
}
ici le message est pour string
et et args
est la valeur que vous voulez imprimer.
Cependant, System.setProperty()
stocke un ensemble de propriétés totalement différent des propriétés accessibles via la commande getprop
pouvant être émise sur le shell ADB. Par conséquent, même si vous essayez d'utiliser la System.setProperty()
pour stocker le niveau de journalisation d'une balise, cela fonctionnera not .
Comme mentionné précédemment, vous pouvez utiliser adb Shell setprop
adb Shell setprop log.tag.<YOUR_LOG_TAG> <LEVEL>
Niveaux principaux du journal
Niveau du journal Valeur Notes
ERROR 6 Errors<br/>
WARN 5 Warnings
INFO 4 Information messages <-Default level of any tag (any level above and including INFO)
DEBUG 3 Debug output
VERBOSE 2 For development only
En savoir plus ici
Une autre méthode consiste à utiliser une plate-forme de journalisation dotée des fonctionnalités d'ouverture et de fermeture des journaux. Cela peut parfois apporter beaucoup de flexibilité, même sur une application de production. Les journaux doivent être ouverts et fermés en fonction des problèmes que vous rencontrez Par exemple:
Pour moi, il est souvent utile de pouvoir définir différents niveaux de journalisation pour chaque TAG.
J'utilise cette classe très simple:
public class Log2 {
public enum LogLevels {
VERBOSE(Android.util.Log.VERBOSE), DEBUG(Android.util.Log.DEBUG), INFO(Android.util.Log.INFO), WARN(
Android.util.Log.WARN), ERROR(Android.util.Log.ERROR);
int level;
private LogLevels(int logLevel) {
level = logLevel;
}
public int getLevel() {
return level;
}
};
static private HashMap<String, Integer> logLevels = new HashMap<String, Integer>();
public static void setLogLevel(String tag, LogLevels level) {
logLevels.put(tag, level.getLevel());
}
public static int v(String tag, String msg) {
return Log2.v(tag, msg, null);
}
public static int v(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > Android.util.Log.VERBOSE) {
return -1;
}
}
return Log.v(tag, msg, tr);
}
public static int d(String tag, String msg) {
return Log2.d(tag, msg, null);
}
public static int d(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > Android.util.Log.DEBUG) {
return -1;
}
}
return Log.d(tag, msg);
}
public static int i(String tag, String msg) {
return Log2.i(tag, msg, null);
}
public static int i(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > Android.util.Log.INFO) {
return -1;
}
}
return Log.i(tag, msg);
}
public static int w(String tag, String msg) {
return Log2.w(tag, msg, null);
}
public static int w(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > Android.util.Log.WARN) {
return -1;
}
}
return Log.w(tag, msg, tr);
}
public static int e(String tag, String msg) {
return Log2.e(tag, msg, null);
}
public static int e(String tag, String msg, Throwable tr) {
if (logLevels.containsKey(tag)) {
if (logLevels.get(tag) > Android.util.Log.ERROR) {
return -1;
}
}
return Log.e(tag, msg, tr);
}
}
Maintenant, définissez le niveau de journalisation par balise au début de chaque classe:
Log2.setLogLevel(TAG, LogLevels.INFO);