Во время развития, сейчас или потом, кто-то хочет распечатать что-то на консоли его IDE. Ну, с Groovy это легко: println «Hello World!». Но когда дело доходит до серьезной разработки и ведения журнала, вы должны соответствовать следующим требованиям:
- сохраняйте лог-операторы в коде для более быстрого их включения
- временные метки для профилирования
- log log … кто входит?
- файл журнала для отслеживания ошибок
- ну и так далее …
Одно из многих хороших лог-решений: log4j
На работе наша компания использует log4j как часть сервера приложений JBoss, поэтому я к этому привык. Некоторое время назад, прежде чем я начал работать с Groovy, я наткнулся на статью в grovvy zone: Groovy и Log4j
Фактически, эта статья заставила меня наконец начать работать с Groovy, после нескольких месяцев молчаливого следования сообществу. Ну, я немного развивался, каждый день с новыми классными и «Groovy» функциями, но вскоре после этого я запустил еще один Groovy-проект, и это стало второстепенным со вчерашнего дня, когда я собирался внедрить ведение журнала для своего проекта. Итак, я выкопал свой код и немного отполировал его, и вот, вот мое решение с log4j.
Почему я говорю вам использовать log4j? Я не. Я не хочу показывать никаких плюсов и минусов. Я просто хочу предоставить простое и отличное решение для удовлетворения требований к журналированию, для этого примера с log4j. Так что снабдите ваш проект log4j и запустите ваши двигатели, дамы и господа …
Groovy путь
Из упомянутой статьи я впервые придумал это решение для тестирования log4j:
package log4jimport org.apache.log4j.*class HelloWorldNoLog { public static String PATTERN = "%d{ABSOLUTE} %-5p [%c{1}] %m%n" static void main(args) { def simple = new PatternLayout(PATTERN) BasicConfigurator.configure(new ConsoleAppender(simple)) LogManager.rootLogger.level = Level.INFO Logger log = Logger.getInstance(HelloWorldNoLog.class) def name = "World" log.info "Hello $name!" log.warn "Groovy " + "logging " + "ahead..." def x = 42 log.setLevel Level.DEBUG if (log.isDebugEnabled()) { log.debug "debug statement for var x: " + x } }}
дает тебе:
23:01:40,062 INFO [HelloWorldNoLog] Hello World!23:01:40,078 WARN [HelloWorldNoLog] Groovy logging ahead...23:01:40,078 DEBUG [HelloWorldNoLog] debug statement for var x: 42
Хороший, простой, но не очень хороший ИМХО, на самом деле здесь есть более-менее некрасивые вещи:
- У вас есть код начальной загрузки, но это обычно не реальная проблема
- Вам нужен экземпляр объекта Logger, и вы должны предоставить свой класс
- Лучше проверить, включен ли уровень для трассировки, отладки (и информации), прежде чем регистрировать
- Вы + присоединили строки вместо GString, как в первом примере кода для нескольких параметров
Настоящий заводной способ
Ну, разве не было бы проще иметь:
package log4jclass HelloWorldLog { static void main(args) { def name = "World" Log.info "Hello $name!" Log.warn "Groovy ", "logging ", "arrived!" def x = 42 Log.setLevel "debug" Log.debug "debug statement for var x: ", x }}
чтобы дать вам:
23:10:46,359 INFO [HelloWorldLog] Hello World!23:10:46,359 WARN [HelloWorldLog] Groovy logging arrived!23:10:46,375 DEBUG [HelloWorldLog] debug statement for var x: 42
Довольно классно, а? Но что здесь произошло? Где весь код? Давайте проясним некоторые вещи: поскольку «Log.info …» начинается с заглавной буквы, это должен быть класс в том же пакете. Имея этот класс в ваших пакетах, вы устраняете необходимость:
- Код шаблона
- Получение экземпляра Logger
- Предоставление класса ?
- Проверка, включен ли уровень
- Перечисление для изменения уровня и, следовательно, не требуется импорт log4j
Для этого действительно нужны только три вещи: библиотека log4j в classpath, вспомогательный класс Log и импорт при записи чего-либо (также может быть статический импорт …). Хорошо, хорошо, на следующей странице появляется магический класс …
Log.groovy
package log4jimport org.apache.log4j.*class Log { public static String PATTERN = "%d{ABSOLUTE} %-5p [%c{1}] %m%n" public static Level LEVEL = Level.INFO private static boolean initialized = false private static Logger logger() { def caller = Thread.currentThread().stackTrace.getAt(42) if (!initialized) basic() return Logger.getInstance(caller.className) } static basic() { def simple = new PatternLayout(PATTERN) BasicConfigurator.configure(new ConsoleAppender(simple)) LogManager.rootLogger.level = LEVEL initialized = true } static setLevel(level) { def Level l = null if (level instanceof Level) { l = level } else if (level instanceof String) { l = (Level."${level.toUpperCase()}")?: null } if (l) LogManager.rootLogger.level = l } static trace(Object... messages) { log("Trace", null, messages) } static trace(Throwable t, Object... messages) { log("Trace", t, messages) } static debug(Object... messages) { log("Debug", null, messages) } static debug(Throwable t, Object... messages) { log("Debug", t, messages) } static info(Object... messages) { log("Info", null, messages) } static info(Throwable t, Object... messages) { log("Info", t, messages) } static warn(Object... messages) { log("Warn", null, messages) } static warn(Throwable t, Object... messages) { log("Warn", t, messages) } static error(Object... messages) { log("Error", null, messages) } static error(Throwable t, Object... messages) { log("Error", t, messages) } static fatal(Object... messages) { log("Fatal", null, messages) } static fatal(Throwable t, Object... messages) { log("Fatal", t, messages) } private static log(String level, Throwable t, Object... messages) { if (messages) { def log = logger() if (level.equals("Warn") || level.equals("Error") || level.equals("Fatal") || log."is${level}Enabled" ()) { log."${level.toLowerCase()}" (messages.join(), t) } } }}
Когда вы вызываете любой из методов trace, debug и т. Д. (С Throwable или без него), вызывается общий метод log () с уровнем, начинающимся с заглавной строки в виде заглавной буквы (также обратите внимание на использование varargs):
private static log(String level, Throwable t, Object... messages) { if (messages) { def log = logger() if (level.equals("Warn") || level.equals("Error") || level.equals("Fatal") || log."is${level}Enabled" ()) { log."${level.toLowerCase()}" (messages.join(), t) } }}
- Метод сначала получает сам регистратор (все еще не нужен класс ?
- Затем он проверяет, является ли уровень предупреждением, ошибкой или фатальным для прямой регистрации
- В противном случае вызывается метод isTraceEnabled, isDebugEnable или isInfoEnabled (поэтому первая буква заглавная)
- Само ведение журнала происходит с нижним регистром как динамический вызов метода
- Сообщения объединяются автоматически (вы можете использовать запятую здесь для поддержки нескольких способов регистрации)
Теперь для объекта Logger этот метод выполняет всю магию:
private static Logger logger() { def caller = Thread.currentThread().stackTrace.getAt(42) if (!initialized) basic() return Logger.getInstance(caller.className) }
Заметки:
- Чтобы получить класс для логгера log4j, используйте трассировку стека текущего потока
- Поскольку путь стека к этому методу не изменяется внутренне, вызывающий абонент может быть найден в фиксированной позиции.
- Таким образом, экземпляр Logger был создан с использованием простого имени класса
Остальное здесь установлено только на уровне. Так долго, так здорово.
Что дальше
Ну, я думаю, что это довольно тонкое решение для легкой регистрации, но в конце концов это только базовый пример. С этого момента можно расширить это многими (отличными) способами, например:
- Поддержка log4j.xml для конфигурации (из classpath или из каталога пользователя)
- Разрешить Appenders, Категории и остальное
- Или создайте DSL для конфигурации, подобной этой (только что написал, не проверял!):
def log4jDSL = { configuration(debug: false) { appender(name: "file", type: "DailyRolling") { errorHandler (type: "OnlyOnce") params() { File "groovy.log" Append false DatePattern ".yyyy-MM-dd" Threshold "debug" layout (type: "PatternLayout") { param (type: "ConversationPattern", value: "%d %-5p [%c] %m%n") } } } category(name: "org.codehaus") { priority (value: "debug") } root() { appenders() { file() } } }}
Вывод
Для меня это чистый подход, и его можно расширять и использовать везде, где Groovy и регистрация ведутся. Приложив немного больше усилий, вы можете прочитать разметку xmls или groovy и в итоге получить надежное, но настраиваемое решение для вашего пакета утилит Groovy (ну, вы можете использовать его и в качестве категории, и в качестве статической …)
Надеюсь, вам понравился мой первый пост так же, как мне понравилось писать этот совет или трюк!
Обо мне
Я из Германии, и у меня Groovy зависимость, так как я попробовал это месяц назад, как уже упоминалось. Я работаю в средней компании по разработке программного обеспечения J2EE в Людвигсбурге, недалеко от Штутгарта, и в то же время я представил Groovy, который помог там успешно решить некоторые задачи по разработке ?
редактировать
Основываясь на комментарии Рональда, я обновил соответствующие части этой истории (подробности см. В комментариях)