Регистрация лучших практик и мыслей

Я собираюсь провести некоторый рефакторинг своего приложения, и я подумал об этой простой, но сложной теме, о ведении журнала, как может быть так сложно вести чистый, эффективный и информативный журнал ...

Когда вы читаете документацию по ведению журнала, вы часто видите этот фрагмент

if (BuildConfig.DEBUG) {
    Log.d(TAG + "message");
}

и это заставляет меня задаться вопросом, для чего это нужно? Согласно документации Android Developer - Log сообщения журнала отладки компилируется, но удаляется во время выполнения, поэтому вам не нужно иметь вызов журнала внутри этого оператора if. Или я что-то не понимаю?

Тогда мне также интересно, какова реальная выгода от использования любых других вызовов Log.x (), кроме отладки, поскольку записи журнала не будут видны пользователю или зарегистрированы в каком-либо файле ошибок, поэтому они будут скомпилированы и выполнены в производственной среде вообще без всякой цели? Возможно, это был случай использования оператора if раньше?

Я упоминал ранее, что запись журнала не регистрируется в файле. Почему это не встроенная функция в Android? Это из-за проблем с производительностью, ненужного использования разрешений или чего-то еще? Я реализовал эту функцию в своем собственном классе ведения журнала, но теперь мне интересно, не плохая ли это практика? Но также приятно иметь журналы с важными записями?

Подводя итог, можно реализовать чистое, эффективное и информативное ведение журнала как во время разработки, так и в процессе производства. Какие лучшие практики?


person Robert    schedule 29.09.2014    source источник


Ответы (5)


Это создаст чистые теги отладки в этом формате ClasssName[MethodName] - LineNumber с отражением.

Полный код со встроенными комментариями доступен как здесь.

import android.util.Log;

public class Logger {

    public enum LOGGER_DEPTH {
        ACTUAL_METHOD(4),
        LOGGER_METHOD(3),
        STACK_TRACE_METHOD(1),
        JVM_METHOD(0);

        private final int value;

        private LOGGER_DEPTH(final int newValue) {
            value = newValue;
        }

        public int getValue() {
            return value;
        }
    }

    private static final String personalTAG = "Logger";

    private StringBuilder sb;

    private Logger() {
        if (LoggerLoader.instance != null) {
            Log.e(personalTAG, "Error: Logger already instantiated");
            throw new IllegalStateException("Already Instantiated");
        } else {
            this.sb = new StringBuilder(255);
        }
    }

    public static Logger getLogger() {
        return LoggerLoader.instance;
    }

    private String getTag(LOGGER_DEPTH depth) {
        try {
            String className = Thread.currentThread().getStackTrace()[depth.getValue()].getClassName();
            sb.append(className.substring(className.lastIndexOf(".") + 1));
            sb.append("[");
            sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getMethodName());
            sb.append("] - ");
            sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getLineNumber());
            return sb.toString();
        } catch (Exception ex) {
            ex.printStackTrace();
            Log.d(personalTAG, ex.getMessage());
        } finally {
            sb.setLength(0);
        }
        return null;
    }

    public void d(String msg) {
        try {
            Log.d(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void d(String msg, LOGGER_DEPTH depth) {
        try {
            Log.d(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void d(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.d(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void e(String msg) {
        try {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void e(String msg, LOGGER_DEPTH depth) {
        try {
            Log.e(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void e(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.e(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void w(String msg) {
        try {
            Log.w(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void w(String msg, LOGGER_DEPTH depth) {
        try {
            Log.w(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void w(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.w(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void v(String msg) {
        try {
            Log.v(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void v(String msg, LOGGER_DEPTH depth) {
        try {
            Log.v(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void v(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.v(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void i(String msg) {
        try {
            Log.i(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void i(String msg, LOGGER_DEPTH depth) {
        try {
            Log.i(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void i(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.i(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void wtf(String msg) {
        try {
            Log.wtf(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void wtf(String msg, LOGGER_DEPTH depth) {
        try {
            Log.wtf(getTag(depth), msg);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    public void wtf(String msg, Throwable t, LOGGER_DEPTH depth) {
        try {
            Log.wtf(getTag(depth), msg, t);
        } catch (Exception exception) {
            Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage());
        }
    }

    private static class LoggerLoader {
        private static final Logger instance = new Logger();
    }
}
person Eefret    schedule 29.09.2014
comment
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - person Dan; 30.09.2014
comment
@Dan, это мой личный Gist, срок его действия не истечет, если я не удалю его, так что не волнуйтесь - person Eefret; 30.09.2014
comment
// ЗАДАЧИ Создание подробного Enum для определения уровня детализации журнала. // ЗАДАЧА Создать метод, который останавливает все возможные журналы на основе режима разработки Пример: (ПРОИЗВОДСТВО, РАЗРАБОТКА, ОТЛАДКА), который может управлять тем, что может быть, а что не может регистрироваться. фрагмент для TODO, пожалуйста - person Hassan Tareq; 12.05.2017
comment
* @ см. com.motube.app.util.Logger.LOGGER_DEPTH? - person Hassan Tareq; 12.05.2017

Я столкнулся с той же проблемой с тех пор, как начал работать в Android и создал этот проект с открытым исходным кодом (Android Studio Macros), который позволяет вам делать то, что вы хотите, а также некоторые более сложные вещи, используя теги "//‹ # DEBUG_AREA> и // ‹# / DEBUG_AREA>" в вашем коде, основная идея заключается в том, что все в этих тегах будут прокомментированы, когда вы измените варианты сборки, например, если у вас есть что-то вроде этого в цикле for:

       //=========This piece of code is only for logging purposes...=========
        Log.e("LogUserInfo", "Name: " + name);
        Log.e("LogUserInfo", "Id: " + user.getId());
        Log.e("LogUserInfo", "Id: " + user.getDistance());
        //====================================================================

Вместо этого:

if(DEBUG){
      Log.e("LogginUserInfo", "Name: " + name);
      Log.e("LogginUserInfo", "Id: " + user.getId());
      Log.e("LogginUserInfo", "Id: " + user.getDistance());
 }

С помощью этого макроса вы можете сделать это (полный метод):

private List<String> getNamesOfUsersNearMe(String zipCode){
    List<User> users = mBusinessLogic.getUsersByZipcode(zipCode);
    if(users == null || users.size() < 1){
        return null;
    }

    List<String> names = new ArrayList<String>();
    int totalUsers = users.size();
    for(int i = 0; i < totalUsers; i++){
        User user = users.get(i);
        String name = user.getName();
        names.add(name);
        //<#DEBUG_AREA>
        Log.e("LogginUserInfo", "Name: " + name);
        Log.e("LogginUserInfo", "Id: " + user.getId());
        Log.e("LogginUserInfo", "Id: " + user.getDistance());
        //</#DEBUG_AREA>
    }
    return names;
}

И когда вы измените свой вариант сборки на выпуск, он станет примерно таким:

private List<String> getNamesOfUsersNearMe(String zipCode){
    List<User> users = mBusinessLogic.getUsersByZipcode(zipCode);
    if(users == null || users.size() < 1){
        return null;
    }

    List<String> names = new ArrayList<String>();
    int totalUsers = users.size();
    for(int i = 0; i < totalUsers; i++){
        User user = users.get(i);
        String name = user.getName();
        names.add(name);
        /*<#DEBUG_OFF>
            Log.e("LogginUserInfo", "Name: " + name);
            Log.e("LogginUserInfo", "Id: " + user.getId());
            Log.e("LogginUserInfo", "Id: " + user.getDistance());
        </#DEBUG_OFF>*/
    }

    return names;
}

Что намного лучше по производительности для длинных циклов и делает ваш код более чистым, избавляясь от ненужного кода в режиме «выпуска», конечно, если вы вернетесь к «отладке», он раскомментирует область и оставит ее исходной. был с тегами «‹ #DEBUG_AREA> »...

Также пытается соответствовать наиболее распространенным сценариям, кажется, бывают случаи, когда вам не нужна полная область, от которой нужно избавляться, а вместо этого нужен только один журнал, поэтому для этого случая в проекте также есть класс-оболочка журнала, который вы можете использовать как следует:

if(users == null || users.size() < 1){
    ASDebuggerMacroLog.e("LogUserInfo", "There's no users available near me...");
    return null;
}

Строка кода, используемая классом «ASDebuggerMacroLog», будет прокомментирована после перехода в режим «выпуска» в вашей Android Studio.

Надеюсь, поможет!

С Уважением!

person Martin Cazares    schedule 22.02.2015

Стандартные выходные данные не должны использоваться напрямую для записи чего-либо (squid: S106)

При регистрации сообщения необходимо выполнить несколько важных требований:

  • Пользователь должен иметь возможность легко получать журналы
  • Формат всех регистрируемых сообщений должен быть единообразным, чтобы пользователь мог легко читать журнал.
  • Зарегистрированные данные должны быть фактически записаны
  • Конфиденциальные данные должны регистрироваться только надежно.

Если программа напрямую записывает данные в стандартные выходные данные, нет абсолютно никакого способа выполнить эти требования. Вот почему настоятельно рекомендуется определить и использовать специальный регистратор.

Источник: Sonarcloud

person Ganesa Vijayakumar    schedule 30.05.2019

Я настоятельно рекомендую использовать библиотеку Timber: https://github.com/JakeWharton/timber

Это очень небольшая библиотека поверх класса Android Log, которая легко выполняет все требования к журналированию. некоторые особенности:

  • Он автоматически определяет, какой класс вызывается, и использует его имя как ТЕГ
  • Вы можете сажать разные деревья для каждого типа сборки.
  • Все бревна проходят через центральное место в Tree. поэтому вы их обрабатываете или загружаете куда-нибудь, если нужно.
person Ali Mehrpour    schedule 09.09.2019

person    schedule
comment
Полностью согласен с Александром. Как и в случае с обработкой исключений, если вы обрабатываете исключение, обработайте иначе, просто добавьте его с информативными данными, если хотите. Хранение файлов журнала в хранилище так же, как и перехват исключений и бездействие. - person huseyin; 24.01.2017