답변:
일반적인 방법은 이름이 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()
메소드를 사용하여 프로그래밍 방식으로 설정할 수 있습니다 .
android.util.Config
더 이상 사용되지 않는 상수 를 언급하는 것처럼 보입니다 . API 문서에 지정된 하드 코딩 된 값은 빌드마다 다를 수 있으므로 쓸모가 없습니다. 따라서 ProGuard 경로는 우리에게 가장 적합한 솔루션 인 것 같습니다.
가장 쉬운 방법은 배포 전에 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 애플리케이션의 경우 다음을 사용할 수 있습니다.
logback-android
않기 때문에 사용해야 logback
합니다. logback-android-1.0.10-1.jar
는 429KB로 제공되는 기능을 고려하면 그리 나쁘지는 않지만 대부분의 개발자는 Proguard를 사용하여 응용 프로그램을 최적화합니다.
사용해야합니다
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)
로깅을 전혀 비활성화 하는 데 사용할 수 있습니다 .
이 Log 확장 클래스를 볼 수 있습니다 : https://github.com/dbauduin/Android-Tools/tree/master/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);