воскресенье, 25 апреля 2010 г.
Введение в Log4php, часть 2. Использование
суббота, 24 апреля 2010 г.
Введение в Log4php, часть 1. Конфигурация
Log4php относится к тому же семейству, что и любимый мной log4net, так что многие понятия повторяются. И, кстати, у log4php имеется достаточно хорошая документация. Настолько хорошая, что в этой моей статье, в общем-то, нет ровно ничего оригинального.
четверг, 16 октября 2008 г.
Знакомство с Android. Часть 1: Простое приложение для Android
UPDATE
Серия статей по игре Life обновлена 03.11.2010. Исправлены ошибки и проведена адаптация под Android версии 2.2. Всем спасибо за замечания и дополнения!Постановка задачи
Первым нашим приложением для Android будет реализация всем известной игры Life. Местом дейтвия будет прямоугольное клеточное поле, размеры которого запрашиваются у пользователя. Также у пользователя запрашивается начальное количество клеток. Первое поколение расставляется по карте случайным образом. Последующие поколения получаются по следующим правилам:- Если у живой клетки меньше двух или больше трёх соседей, то она погибает.
- Если у пустой клетки ровно три соседки, она оживает.
Для разработки была использована среда Eclipse и Android plugin для неё.
Статья будет из четырех частей:
- Часть 1 — Простое приложение для Android
- Часть 2 — Переходы между формами
- Часть 3 — Использование диалогов
- Часть 4 — Использование GridView
В этой части
Мы создадим проект, рассмотрим его структуру и напишем простое приложение, состоящее из одной формы. На форме будет интерфейс для ввода данных и кнопка Run.Создание и обзор проекта
На установке Android SDK и плагина для Eclipse останавливаться не будем, т.к. это достаточно подробно описано в официальном мануале. Создаем в Eclipse новый Android Project:После нажатия на кнопку Finish создастся новый проект с такой структурой файлов:
Рассмотрим эту структуру внимательнее.
/res/drawable-dpi
Сюда помещаются все графические файлы, используемые в приложении, для разных разрешений экрана. На данный момент там есть только файл icon.png - главная иконка приложения./res/layout
В эту папку помещаются файлы, в которых в формате XML описывается внешний вид форм, расположение контролов и т.д. (как dfm-ки в Дельфи). Плагин даже создал разметку для нашей единственной формы и назвал её main.xml. Позже мы рассмотрим ее подробнее./res/values
В этой папке хранятся общие константы для всего приложения, как то: текст, используемый элементами управления, цвета, стили и т.д.. Например, если мы хотим вывести "Hello World" в TextView, можно это сделать явно в разметке, как мы всю жизнь делали в тех же dfm-ках или aspx; либо создать вstrings.xml
константу hello
со значением "Hello World", после чего пойти обратно в разметку и в атрибутах этого TextView прописать android:text="@string/hello"
./gen/R.java
Это такой специальный сгенерированный класс, посредством которого осуществляется доступ к ресурсам приложения (т.е. ко всему тому, что есть в папкеres
). Например, R.string.hello
возвращает константу с именем hello
из strings.xml
.StartActivity.java
Это нам плагин сгенерировал класс для главной (и пока что единственной) формы приложения. Там пока содержится единственный обработчикonCreate
, и написано там только setContentView(R.layout.main);
. С помощью этой строчки к данной форме привязывается разметка, описанная в файле /res/layout/main.xml
AndroidManifest.xml
В этом файле перечисляются общие свойства проекта (версия, package и прочее), а также все формы (Activities), входящие в проект.Разметка формы (Layout)
Элементы управления в Android называются Views и наследуются от классаView
или ViewGroup
. Класс ViewGroup
также унаследован от View
, но его отличие в том, что в него могут быть вложены другие View
или ViewGroup
.
Плагин создал простейшую разметку для нашей единственной формы (
main.xml
):main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout>
LinearLayout
значит, что они идут друг за другом сверху вниз (android:orientation="vertical"
). Бывают и другие Layout-ы: TableLayout
, с помощью которого можно выстроить контролы в таблицу; FrameLayout
, который ставит контролы один на другой; и т.д.Мы воспользуемся
TableLayout
Сделаем вот такую разметку:
main.xml
<?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:stretchColumns="1" android:padding="10dip" > <TableRow android:paddingBottom="5dip"> <TextView android:text="@string/columns_title" android:paddingRight="10dip" android:gravity="right" android:textStyle="bold" /> <EditText android:id="@+id/columns_count" android:text="25" android:inputType="number" /> </TableRow> <TableRow android:paddingBottom="5dip"> <TextView android:text="@string/rows_title" android:paddingRight="10dip" android:gravity="right" android:textStyle="bold" /> <EditText android:id="@+id/rows_count" android:text="35" android:inputType="number" /> </TableRow> <TableRow android:paddingBottom="5dip"> <TextView android:text="@string/cells_title" android:paddingRight="10dip" android:gravity="right" android:textStyle="bold" /> <EditText android:id="@+id/cells_count" android:text="100" android:inputType="number" /> </TableRow> <TableRow> <Button android:id="@+id/run" android:text="@string/run_title" android:textStyle="bold" android:layout_span="2" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </TableRow> </TableLayout>
res/values/strings.xml
при этом нужно добавить следующие строки:strings.xml
<string name="run_title">Run!</string> <string name="columns_title">Columns:</string> <string name="rows_title">Rows:</string> <string name="cells_title">Cells:</string>
Рассмотрим некоторые атрибуты, использованные в разметке
android:id
Идентификатор элемента. Если он указан, то в дальнейшем его можно найти на форме с помощью методаfindViewById(id)
. Для контролов, которых мы не планируем в дальнейшем трогать (например, для заголовков), можно это свойство и вовсе не указывать. Идентификаторы можно складывать в файл ids.xml
, но вместо этого обычно применяется синтаксис @+id/View1
. Это означает, что идентификатор View1
добавляется в константы прямо на ходу. В R.java
соответствующие поля также добавляются автоматически.android:layout_width и android:layout_heigth
Свойстваlayout_width
и layout_heigth
обозначают, какую часть родительского контрола будет занимать данный элемент управления: всю (fill_parent
или match_parent
, начиная с 2.2) или ровно столько, сколько требуется (wrap_content
). Можно также задавать численные значения.android:inputType
Это атрибутEditText
. С его помощью можно устанавливать формат вводимого значения. Есть ряд предопределенных форматов (date
, phone
, etc). Значение number
значит, что в это поле можно вводить только целые положительные числа.android:gravity, android:layout_gravity
Устанавливает выравнивание в данном элементе управления. Отличие в том, чтоgravity
задает выравнивание дочерних контролов, а layout_gravity
задает выравнивание самого контрола.Заключение
Итак, мы создали проект для Android, рассмотрели его структуру, составили разметку для нашей единственной формы.Исходники примера
Знакомство с Android. Часть 2: Переходы между формами
Run
. Также сделаем так, чтобы параметры, которые были введены в первой форме, передавались во вторую (они там ещё пригодятся). Однако, ничего страшного мы пока с ними делать не будем, а просто напишем "Введены такие-то числа"Знакомство с Android. Часть 3: Использование диалогов
- Число столбцов должно быть не меньше 5 и не больше 25.
- Число строк должно быть не меньше 5 и не больше 35.
- Начальное количество клеток должно быть не больше, чем ячеек на поле.
Кроме того, мы добавим кнопку Close, при нажатии на которую приложение будет закрываться, спрашивая вначале согласие пользователя.
Знакомство с Android. Часть 4: Использование GridView
среда, 9 апреля 2008 г.
Использование log4net
Подключение
Web Application
- Добавить log4net.dll в References приложения
- Добавить в
AssemblyInfo.cs
строку[assembly: log4net.Config.XmlConfigurator(Watch = true)]
Web Site
Просто переписать log4net.dll в каталог Bin
Настройка
Логгер настраивается в web.Config. Вначале в раздел configSections следует добавить строчку:
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net">
Затем, нужно добавить раздел:
<log4net debug="true"> ... </log4net>
В этом разделе идет основная настройка логгера. В разделе могут быть потомки типа appender, logger и root. Назначение элемента logger пока что осталось для меня загадкой, так что рассмотрим остальные.
Appender
Для того, чтобы заставить log4net что-то куда-то писать, нужно определить Appender. Подходящее русское обозначение мне придумать не удалось, но суть в том, что он хранит настройки одного логгера. Типов appender-ов существует много (см. документацию), но мне довелось работать только с RollingFileAppender
, который определяет логгер, пишущий в файл. Другие типы appender-ов позволяют писать в базу данных(AdoNetAppender
), Windows EventLog (EventLogAppender
) и т.д. Рассмотрим пример:
<appender name="ERRORFileAppender" type="log4net.Appender.RollingFileAppender"> <lockingmodel type="log4net.Appender.FileAppender+MinimalLock"/> <file value="\Logs\errors.log" /> <appendtofile value="true" /> <rollingstyle value="Size" /> <maxsizerollbackups value="10" /> <maximumfilesize value="200MB" /> <staticlogfilename value="true" /> <filter> ... </filter> <layout> ... </layout> </appender>
Назначение перечисленных Appender-а, в принципе, понятно. Следует отметить параметр lockingModel
и его значение MinimalLock
. Дело в том, что log4net во время работы приложения жестко лочит файл с логом, его нельзя ни открыть (разве что редактором Far-а), ни удалить. Поэтому, если очень хочется удалить лог web-приложения, нужно перезапускать IIS, что не есть хорошо. Данная настройка "разлочивает" лог, его свободно можно удалять и открывать хоть для записи.
Ключевыми настройками Appender-а являются Filter и Layout.
Filter
Данная настройка позволяет отфильтровывать сообщения по некоторому признаку. Для фильтра обязательно указывается тип. Существует несколько типов фильтров, можно также писать свои. Наиболее часто используемые фильтры - по типу сообщения (LevelRangeFilter
и LevelMatchFilter
) и по тексту сообщения (StringMatchFilter
). Для начала, остановимся на типах (уровнях) сообщений. Сообщения в log4net делятся на несколько уровней:
- ALL
- DEBUG
- INFO
- WARN
- ERROR
- FATAL
- OFF
Разработчик выбирает, какой тип сообщений в каком случае ему использовать. Логгер же можно настроить таким образом, чтобы, к примеру, ошибки выводились отдельно, а отладочные данные - отдельно. Для это и пишутся фильтры. Рассмотрим на примерах.
Фильтр, выбирающий только ошибочные и фатальные сообщения:
<filter type="log4net.Filter.LevelRangeFilter"> <levelMin value="ERROR"/> <levelMax value="FATAL"/> </filter>
Фильтр, выбирающий только отладочные сообщения
<filter type="log4net.Filter.LevelMatchFilter"> <levelToMatch value="DEBUG"/> <acceptOnMatch value="true"/> </filter>
Layout
В разделе Layout определяется формат вывода сообщений в файл. Как ранее было сказано, log4net, помимо самого сообщения, может выводить кучу полезной информации. Layout может выглядеть, например, таким образом:
<layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%-5p [%d] [%C.%M] %m%n"/> </layout>
В conversionPattern указано, какие значения и в каком формате будут выводиться в лог. Выходной лог с таким Layout-ом получится примерно таким:
DEBUG [2008-04-08 17:45:14,662] [TableTemplates.GetTemplate] Is visible: [visible]
DEBUG [2008-04-08 17:45:14,662] [TableTemplates.GetTemplate] Report Footer (rows:1)
DEBUG [2008-04-08 17:45:14,662] [TableTemplates.GetTemplate] Number of cells in row [2]
INFO [2008-04-08 17:45:14,663] [ClientDataObjects.get_ClientObjects] Client object found in session
Значения ключей conversionPattern:
%p
— Тип события лога (DEBUG, ERROR и т.д.)%d
— Дата и время, когда логгирующая функция была вызвана. Можно задавать различные форматы вывода даты и времени. К примеру,%d{HH:mm:ss,SSS}
отображает только время%C
— Имя класса, в котором была вызвана какая-либо функция лога%M
— Имя метода%m
— Собственно, сообщение в лог%F
— Имя файла%l
— Номер строки%r
— Количество миллисекунд, прошедших с начала работы программы%t
— Имя потока%n
— Переход на новую строку%%
— Символ"%"
Root
Написать Appender-ы недостаточно для того, чтобы log4net начал писать логи. Нужно еще зарегистрировать их в корне лога, вот таким вот образом:
<root> <level value="WARN" /> <appender-ref ref="ERRORFileAppender" /> <appender-ref ref="DEBUGFileAppender" /> </root>
level
- уровень, сообщения ниже которого не выводятся ни в один лог. Приведенная запись значит, что в логи не будут выводиться сообщения типа DEBUG и INFO.
Использование
Я делаю так. Пишу класс:
using log4net; using log4net.Config; using System; /// /// Logger - shell for log4net logger /// public static class Logger { private static readonly ILog log = LogManager.GetLogger(typeof(Logger)); public static ILog Log { get { return log; } } public static void InitLogger() { XmlConfigurator.Configure(); } }
Потом в Global.asax в метод Application_Start добавляю строчку:
Logger.InitLogger();
И подключение лога в код готово. После этого в любом месте кода можно написать что-то вроде:
Logger.Log.Debug("Some message");
ILog
представляет различные методы для вывода сообщений различных типов. Рассмотрим некоторые методы:
Функции | Описание | Пример |
---|---|---|
Info(String message) Debug(String message) Warn(String message) Error(String message) Fatal(String message) |
Вывод в лог сообщения message
|
Logger.Log.Info("Sample log message"); |
InfoFormat(String format, params object[] args) DebugFormat(String format, params object[] args) WarnFormat(String format, params object[] args) ErrorFormat(String format, params object[] args) FatalFormat(String format, params object[] args) |
Вывод в лог сообщения c параметрами. Аналогично выводу сообщения String.Format(format, args) .
|
Logger.Log.DebugFormat("Key={0}, Value={1}", key, value); |
Info(String message, Exception e) Debug(String message, Exception e) Warn(String message, Exception e) Error(String message, Exception e) Fatal(String message, Exception e) |
Вывод в лог сообщения с исключением. В лог помещается целиком текст исключения и stack trace. |
try { a = b / c; } catch (Exception e) { Logger.Log.Error("Exception catched while calculation.", e); } |