たとえば、デバッグするログステートメントがたくさんあります。
Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");
デバイスの電話にこのアプリケーションを展開している間、ログを有効/無効にすることができる場所から詳細なログをオフにします。
一般的な方法は、loglevelという名前のintを作成し、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
後で、すべてのデバッグ出力レベルのLOGLEVELを変更するだけです。
Androidのドキュメントには、ログレベル について次のことが記載されています。
開発中を除いて、冗長をアプリケーションにコンパイルしないでください。デバッグログはコンパイルされますが、実行時に削除されます。エラー、警告、および情報ログは常に保持されます。
したがって、 多分別の回答 で提案されているようにProGuardを使用して、ログの詳細なログを削除することを検討する必要があります。
ドキュメントによると、システムプロパティを使用して開発デバイスのログを構成できます。設定するプロパティはlog.tag.<YourTag>
であり、VERBOSE
、DEBUG
、INFO
、WARN
、ERROR
、ASSERT
、またはSUPPRESS
のいずれかの値に設定する必要があります。 これに関する詳細は、isLoggable()
メソッドのドキュメントで入手できます。
setprop
コマンドを使用して、プロパティを一時的に設定できます。例えば:
C:\Android>adb Shell setprop log.tag.MyAppTag WARN
C:\Android>adb Shell getprop log.tag.MyAppTag
WARN
または、次のようにファイル '/data/local.prop'でそれらを指定できます。
log.tag.MyAppTag=WARN
Android の後のバージョンでは、/ data/local.propを読み取り専用 にする必要があるようです。このファイルはブート時に読み込まれるため、更新後に再起動する必要があります。 /data/local.prop
が誰でも書き込み可能な場合、おそらく無視されます。
最後に、 System.setProperty()
method を使用してプログラムで設定できます。
最も簡単な方法は、おそらく、展開前に ProGuard を使用してコンパイル済みのJARを実行し、次のような構成を使用することです。
-assumenosideeffects class Android.util.Log {
public static int v(...);
}
これにより、他のすべてのProGuard最適化とは別に、詳細なログステートメントがバイトコードから直接削除されます。
単純なルートを取りました-可変パラメーターリストも使用するラッパークラスを作成します。
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
より良い方法は、SLF4J API +その実装の一部を使用することです。
Androidアプリケーションの場合、次を使用できます。
あなたが使用する必要があります
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "my log message");
}
Proguard(@Christopherの回答を参照)を使用してログを削除するのは簡単で高速でしたが、ファイルにデバッグログがある場合、本番からのスタックトレースがソースと一致しませんでした。
代わりに、プロガードがプロダクションでのみ使用されると仮定して、開発とプロダクションで異なるログレベルを使用する手法を紹介します。 proguardが特定のクラス名の名前を変更したかどうかを確認することにより、本番環境を認識します(この例では、「com.foo.Bar」を使用します。これは、proguardによって名前が変更されることがわかっている完全修飾クラス名に置き換えます)。
この手法は、コモンズロギングを利用します。
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);
}
}
標準のAndroid Logクラスの小さなドロップイン置換があります- https://github.com/zserge/log
基本的にあなたがしなければならないのは、インポートをAndroid.util.Log
からtrikita.log.Log
に置き換えることです。次に、Application.onCreate()
または静的イニシャライザーでBuilConfig.DEBUG
またはその他のフラグを確認し、Log.level(Log.D)
またはLog.level(Log.E)
を使用して最小ログレベルを変更します。 Log.useLog(false)
を使用して、ロギングをまったく無効にすることができます。
Log4jまたはslf4jは、logcatと一緒にAndroidのロギングフレームワークとしても使用できます。プロジェクトを参照してください Android-logging-log4j または Androidでのlog4jサポート
次のログ拡張クラスを見ることができます: https://github.com/dbauduin/Android-Tools/tree/master/logs 。
これにより、ログを細かく制御できます。たとえば、すべてのログを無効にしたり、一部のパッケージまたはクラスのログのみを無効にしたりできます。
さらに、いくつかの便利な機能が追加されます(たとえば、各ログにタグを渡す必要はありません)。
より複雑なソリューションを次に示します。完全なスタックトレースを取得し、メソッドtoString()は必要な場合にのみ呼び出されます(パフォーマンス)。 BuildConfig.DEBUG属性は実稼働モードではfalseになるため、すべてのトレースログとデバッグログは削除されます。ホットスポットコンパイラは、最終的な静的プロパティがオフになっているため、呼び出しを削除する可能性があります。
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();
}
}
次のように使用します:
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);
この問題とロギングに関するその他の一般的な問題を解決するユーティリティ/ラッパーを作成しました。
次の機能を備えたデバッグユーティリティ:
使い方?
私はドキュメントを自給自足させようとしました。
このユーティリティを改善する提案を歓迎します。
無料で使用/共有できます。
GitHub からダウンロードします。
私のアプリには、「state」と呼ばれる静的ブール変数を持つLogクラスをラップするクラスがあります。コード全体で、実際にログに書き込む前に、静的メソッドを使用して「状態」変数の値をチェックします。次に、アプリによって作成されたすべてのインスタンスで値が共通であることを保証する「状態」変数を設定する静的メソッドがあります。つまり、アプリの実行中であっても、アプリのすべてのログを1回の呼び出しで有効または無効にできます。サポートの呼び出しに役立ちます...それは、デバッグ時に銃に固執しなければならないことを意味しますが、標準のLogクラスを使用することに回帰しません...
値が割り当てられていない場合、Javaがブール変数をfalseとして解釈することも便利です(つまり、ロギングをオンにする必要があるまでfalseのままにしておくことができます:-)
デバッグ目的で開発中に文字通りコンソールに書き込もうとしている非常に単純なロギングシナリオでは、実稼働ビルドの前に検索と置換を行い、LogまたはSystemへのすべての呼び出しをコメントアウトするのが最も簡単な場合があります。 out.println。
たとえば、「ログ」を使用しなかったと仮定します。 Log.dやLog.eなどの呼び出し以外の場所では、ソリューション全体で検索と置換を行うだけで、「Log」を置換できます。 「// Log」すべてのロギング呼び出しをコメントアウトするか、私の場合はすべての場所でSystem.out.printlnを使用しているので、実稼働に行く前に「System.out.println」を完全に検索して置き換え、 「//System.out.println」。
これは理想的ではないことはわかっています。LogおよびSystem.out.printlnの呼び出しを検索してコメントアウトする機能がEclipseに組み込まれていればいいのですが、それが実現するまではこれが最も簡単で最速で最良の方法です。検索および置換によりコメントアウトします。これを行うと、ソースコードを編集しているため、スタックトレースの行番号の不一致を心配する必要はありません。また、ログレベルの構成などを確認してオーバーヘッドを追加することもありません。
ローカルコンポーネントでクラスLog
を使用し、メソッドをv/i/e/dとして定義できます。必要に応じて、さらに電話をかけることができます。
例を以下に示します。
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);
}
ここでのメッセージはstring
であり、args
は印刷する値です。
ただし、System.setProperty()
には、ADBシェルで発行できるgetprop
コマンドでアクセスできるプロパティとはまったく異なるプロパティのセットが格納されます。したがって、System.setProperty()
を使用してタグのログレベルを保存しようとしても、notは機能しません。
前述のとおり、 adb Shell setprop を使用できます
adb Shell setprop log.tag.<YOUR_LOG_TAG> <LEVEL>
メインログレベル
ログレベル値の注意
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
続きを読む こちら
別の方法は、ログを開いたり閉じたりする機能を備えたロギングプラットフォームを使用することです。これにより、ログが開かれている必要があり、たとえば、どの問題があるかに応じて閉じられる運用アプリでも、多くの柔軟性が得られる場合があります。
私にとっては、タグごとに異なるログレベルを設定できると便利です。
私はこの非常に単純なラッパークラスを使用しています。
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);
}
}
次に、各クラスの開始時にTAGごとにログレベルを設定します。
Log2.setLogLevel(TAG, LogLevels.INFO);