Programing

Android에서 로그 수준을 활성화 / 비활성화하는 방법

lottogame 2020. 6. 12. 22:07
반응형

Android에서 로그 수준을 활성화 / 비활성화하는 방법


예를 들어 디버깅 할 로깅 문이 많이 있습니다.

Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");

장치 전화 에이 응용 프로그램을 배포하는 동안 로깅을 활성화 / 비활성화 할 수있는 곳에서 자세한 로깅을 끄고 싶습니다.


일반적인 방법은 이름이 loglevel 인 int를 만들고 로그 수준을 기반으로 디버그 수준을 정의하는 것입니다.

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을 변경할 수 있습니다.


안드로이드 문서는 로그 레벨에 대한 다음 말한다 :

Verbose는 개발 중을 제외하고는 응용 프로그램으로 컴파일해서는 안됩니다. 디버그 로그는 컴파일되지만 런타임에 제거됩니다. 오류, 경고 및 정보 로그는 항상 유지됩니다.

따라서 다른 답변에서 제안한대로 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()메소드를 사용하여 프로그래밍 방식으로 설정할 수 있습니다 .


가장 쉬운 방법은 배포 전에 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 애플리케이션의 경우 다음을 사용할 수 있습니다.

  1. Android Logger 는 가볍지 만 구성하기 쉬운 SLF4J 구현입니다 (<50 Kb).
  2. LOGBack은 가장 강력하고 최적화 된 구현이지만 크기는 약 1Mb입니다.
  3. 당신의 취향에 의한 다른 것들 : slf4j-android, slf4android.

사용해야합니다

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "my log message");
    }

proguard (@Christopher의 답변 참조)로 로깅을 제거하는 것은 쉽고 빠르지 만 파일에 디버그 로깅이 있으면 프로덕션의 스택 추적이 소스와 일치하지 않습니다.

대신, proguard가 프로덕션에만 사용된다고 가정 할 때 개발과 프로덕션에서 서로 다른 로깅 수준을 사용하는 기술이 있습니다. 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);
    }
}

Log4j 또는 slf4j는 logcat과 함께 Android에서 로깅 프레임 워크로 사용될 수도 있습니다. android에서 android-logging-log4j 또는 log4j 지원 프로젝트를 참조하십시오


표준 안드로이드 로그 클래스에 대한 작은 드롭 인 대체품이 있습니다-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)로깅을 전혀 비활성화 하는 사용할 수 있습니다 .


https://github.com/dbauduin/Android-Tools/tree/master/logs 로그 확장 클래스를 볼 수 있습니다 .

로그를 세부적으로 제어 할 수 있습니다. 예를 들어 모든 로그를 비활성화하거나 일부 패키지 또는 클래스의 로그 만 비활성화 할 수 있습니다.

또한 유용한 기능을 추가합니다 (예 : 각 로그에 태그를 전달하지 않아도 됨).


이 문제와 로깅과 관련된 다른 일반적인 문제를 해결하는 유틸리티 / 래퍼를 만들었습니다.

다음과 같은 기능을 가진 디버깅 유틸리티 :

  • Log 클래스가 제공하는 일반적인 기능은 LogMode로 감싸 져 있습니다 .
  • 메소드 진입 종료 로그 : 스위치로 끌 수 있음
  • 선택적 디버깅 : 특정 클래스를 디버깅합니다.
  • Method Execution-Time Measurement : 개별 메소드의 실행 시간과 클래스의 모든 메소드에 소요되는 총 시간을 측정합니다.

사용하는 방법?

  • 프로젝트에 수업을 포함 시키십시오.
  • android.util.Log 메소드를 사용하는 것처럼 사용하십시오.
  • 앱의 메소드 시작 및 종료시 entry_log ()-exit_log () 메소드를 호출하여 Entry-Exit logs 기능을 사용하십시오.

설명서를 충분하게 만들려고 노력했습니다.

이 유틸리티를 개선하기위한 제안을 환영합니다.

무료 사용 / 공유

GitHub 에서 다운로드하십시오 .


더 복잡한 해결책이 있습니다. 전체 스택 추적이 발생하고 필요한 경우에만 성능 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); 

디버깅 목적으로 개발 중에 문자 그대로 콘솔에 쓰려고하는 매우 간단한 로깅 시나리오에서는 프로덕션을 빌드하고 로그 또는 시스템에 대한 모든 호출을 주석 처리하기 전에 검색하고 교체하는 것이 가장 쉽습니다. out.println.

예를 들어 "로그"를 사용하지 않았다고 가정합니다. Log.d 또는 Log.e 등을 호출하지 않는 곳이면 어디에서나 "Log"를 대체하기 위해 전체 솔루션에서 찾기 및 바꾸기를 수행 할 수 있습니다. "// Log"로 모든 로깅 호출을 주석 처리하거나 제 경우에는 System.out.println을 어디에서나 사용하고 있으므로 프로덕션으로 이동하기 전에 "System.out.println"에 대한 전체 검색 및 바꾸기를 수행하고 "//System.out.println".

나는 이것이 이상적이지 않다는 것을 알고 있으며 Log와 System.out.println에 대한 호출을 찾아서 주석 처리하는 기능이 Eclipse에 내장되어 있다면 좋을 것입니다. 그러나 그렇게 할 때까지 가장 쉽고 빠르며 가장 좋은 방법은 검색 및 바꾸기로 주석 처리합니다. 이렇게하면 소스 코드를 편집하고 일부 로그 수준 구성 등을 확인하여 오버 헤드를 추가하지 않기 때문에 스택 추적 행 번호가 일치하지 않을까 걱정할 필요가 없습니다.


내 응용 프로그램에는 "state"라는 정적 부울 변수가있는 Log 클래스를 래핑하는 클래스가 있습니다. 내 코드 전체에서 실제로 Log에 쓰기 전에 정적 메서드를 사용하여 "state"변수의 값을 확인합니다. 그런 다음 "state"변수를 설정하는 정적 메소드를 사용하여 앱이 생성 한 모든 인스턴스에서 값이 공통되도록합니다. 즉, 앱이 실행 중일 때도 한 번의 호출로 앱의 모든 로깅을 활성화 또는 비활성화 할 수 있습니다. 지원 전화에 유용합니다 ... 디버깅 할 때 총을 고수해야하지만 표준 로그 클래스를 사용하여 회귀하지 않아야합니다 ...

Java가 부울 var에 값이 할당되지 않은 경우 false로 해석하는 것이 유용합니다 (매우 편리함). 로깅을 켜야 할 때까지 false로 남겨 둘 수 있습니다.


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);
    }

여기서 message는 for string이며 args인쇄하려는 값입니다.


나를 위해 종종 각 TAG에 대해 다른 로그 수준을 설정할 수있는 것이 유용합니다.

이 매우 간단한 래퍼 클래스를 사용하고 있습니다.

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);

다른 방법은 로그를 열고 닫는 기능이있는 로깅 플랫폼을 사용하는 것입니다. 예를 들어 다음과 같은 문제에 따라 로그가 열려 있고 닫혀있는 프로덕션 앱에서도 유연성이 크게 향상 될 수 있습니다.


However, System.setProperty() stores a totally different set of properties compared to the properties that can be accessed by the getprop command that can be issued at the ADB shell. Hence even if you try to use the System.setProperty() to store the log level for a tag, it will not work.

As was mentioned you can use adb shell setprop

adb shell setprop log.tag.<YOUR_LOG_TAG> <LEVEL>

Main Log levels

Log Level Value 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 

Read more here

참고URL : https://stackoverflow.com/questions/2018263/how-do-i-enable-disable-log-levels-in-android

반응형