Pull to refresh

Простая и очевидная замена android.util.Log

Reading time3 min
Views17K
Однажды я писал один проект. Проект выдался большим и писал я его долго.
Там было все что только можно было запихнуть — и retrolambda/java8, и пара десятков
других библиотек (жадность заказчика до новых фич не знала границ, а потому
росло число зависимостей).

Но речь даже не об этом. Настало время делать релиз. И оказалось что в проекте
множество логов и неплохо бы их из релизной сборки убрать. Всем известный
способ с ProGuard не сработал с первого раза. С каждым новым "-keep"
приложение падало в каком-то новом месте. Так что ProGuard пришлось отключить
до лучших времен.

Все это время меня не покидало ощущуение, что управление уровнем логировния c
помощью изменения байт-кода — это нелепо. И тогда за полчаса я написал свой
примитивный логгер.

Обычно я редко довожу свои проекты до конца, но тут он был настолько прост — что теперь он лежит на гитхабе, желающие — пользуйтесь.

Совместимое API — залог простой миграции


Одна из причин почему я не стал использовать Timber — потому что мои пальцы
привыкли печатать Log.d. Свой класс я назвал тоже «Log», а методы сделал совместимыми с «android.util.Log».

Это значит, что если вы захотите соскочить со стандартного логгера — просто
замените импорты. Это можно сделать с помощью sed, или с помощью вашей любимой
IDE.

А в чем преимущество?


Ну вот несколько отрывков кода, показывающие основные фичи логгера:

// Весь ваш старый код останется работать как и прежде
Log.d(tag, "X equals " + x);
// Но теперь можно будет выводить несколько значений через запятую
Log.d(tag, "X", x)


// Можно будет не писать теги, по умолчанию они возьмутся из имени класса
class Foo {
		public void foo() {
				Log.d("Hello"); // выведет 'D/Foo: Hello'
		}
}

// Разве только в классе будет свой атрибут "tag" (или "TAG")
class Foo {
		private final static String TAG = "Bar";
		public void foo() {
				Log.d("Hello");      // выведет 'D/Bar: Hello'
				Log.d(TAG, "Hello"); // выведет 'D/Bar: Hello', не 'D/Bar: Bar Hello'
		}
}

// Конечно, все это настаиваемо и атрибут тега может называться как угодно
class Foo {
		static {
				Log.useTags(new String[]{"tag", "TAG", "MYTAG", "_TAG", "iLoveLongFieldName"});
		}
		private final static String _TAG = "Bar";
		...
}


// Можно выводить экспешены, для них будет напечатан стек-трейс
Exception e = new Exception("foo");
Log.d("Something bad happened", someObject, "error:", e);

// Длинные сообщения можно писать через точку
Log
		.d("First")
		.d("Second")
		.d("Third line")

// То с чего все начиналось - контроль за уровнем логирования
Log.level(Log.I);
Log.d("foo"); // ничего не выведет

// Можно использовать форматные стоки, как в String.format()
Log.useFormat(true);
Log.d("X equals %d", x); // выведет 'X equals 42'
// Если в первой строке нет процентов - будет использоваться вывод через запятую
Log.d("Value of X", x); // выведет 'Value of X 42'

// Длинные строки переносятся по '\n'. Непереносимые строки длиннее 4000
// символов будет переноситься по пробелам или пунктуации. Можно смело
// выводить длинные JSON-ы или HTML
Log.d("Hello\nworld"); // выведет 'D/SomeTag: Hello', затем 'D/SomeTag: world'

// Логгер работает как в андроиде, так и в JVM.
// В андроиде логирование ведется через стандартный логгер, в обычной джаве -
// через System.out.println
// Но все это можно настроить вручную
Log.usePrinter(Log.SYSTEM.true).usePrinter(Log.ANDROID, false).d("hello"); // использовать System.out, в том числе и в андроиде

// А еще можно использовать свои "принтеры", например для Crashlytics
Log.usePrinter(mCrashlyricsPrinter, true);


Уговорили, где взять?


Исходники на гитхабе: github.com/zserge/log

В build.gradle библиотека подключается как обычно:

repositories {
	jcenter() // или mavenCentral()
}
dependencies {
	compile 'co.trikita:log:1.1.5'
}

Вот так можно заменить импорты:

$ find -name "*.java" -type f -exec sed -i 's/import android.util.Log/import trikita.log.Log/g' {} \;


Логгер под лицензией MIT, используйте на здоровье. Там один класс без
зависимостей, всего на 250 строк, так что проект ваш тяжелей/тормозней не станет.

Любые пожелания или багрепорты (особенно с патчами) приветствуются!

UPD: Всем спасибо за код-ревью и конструктивные замечания. Благодаря вам выложил версию 1.1.5.
  • Потокобезопасность пришлось сделать в лоб, через synchronized. Померял скорость — практически не замедлилась, зато многострочные сообщения не разрываются если их печатать из разных потоков. Да и так спокойнее если вдруг принтер не многопоточный
  • Проверил бенчмарки — да, мой логгер медленнее чем android.util.Log за счет рефлексии. Однако он ничуть не медленнее Timber'а. Вообщем, если вы пишете меньше чем 10000 логов в секунду — проблем с производительностью не будет
  • Добавил приватный конструктор, ну и там по мелочам — где названия привел в порядок, где документацию
Tags:
Hubs:
+8
Comments28

Articles

Change theme settings