16 января 2016 г.

Code Review: JMeter + GitLab

Входные параметры:
  • маленькие короткие проекты разработки нагрузочных тестов;
  • для разработки нагрузочных тестов используется JMeter;
  • уже успешно используется GitLab для хранения кода;
  • команда проекта два человека: опытный и ученик.
Нужно было придумать способ рецензирования кода JMeter-скриптов.

Был опыт работы с TFS, где рецензирование сделано отлично:
  • делаешь commit;
  • указываешь список рецензентов;
  • рецензенты проверяют файлы, затронутые в commit-е;
  • оставленные рецензентами примечания приходят почтой и отображаются в TFS.
В GitLab оставлять комментарии к файлам, затронутым в commit-е нельзя, но можно оставлять примечания к блокам изменений (частям файлов) и ко всему набору изменений (commit-у), возможно, так даже удобнее.

Процесс рецензирования:
  1. Зайти в список Commits (https://gitlab-url.ru/userName/projectName/commits/branchName).
  2. Открыть ссылку на рецензируемый commit, ссылки справа вверху, выглядят например так - 6af0b83c (https://gitlab-url.ru/userName/projectName/commit/6af0b83cCommitID7c0e04).
  3. Просмотреть код JMeter-скрипта глазами, тут нужно привыкнуть, удобно иметь два монитора и слева запускать JMeter а справа запускать браузер и смотреть содержимое commit-а с исходником скрипта.
  4. Оставить комментарии к коду, используется Markdown форматирование, можно цитировать код, прикладывать файлы - широкие возможности, шире, чем при рецензировании кода через Visual Studio в TFS.
  5. Автору commit-а придёт почтовое уведомление со ссылкой на оставленный комментарий.
Пример добавления комментария к строке текста
Пример письма с уведомлением о примечании к набору изменений. В тексте письма есть ссылка на комментарий в GitLab

А если объём изменения небольшой или наоборот очень большой и в нём сложно ориентироваться. То комментарии можно оставлять к самому commit-у, форма для добавления комментария к commit-у находится в самом низу.
Пример добавления комментария ко всему набору изменений


Под коммитом также можно писать:
  • Хороший commit, всё сделано правильно, молодец. 
Автор набора изменений получит этот отклик и будет знать, что рецензирование кода было выполнено.

Для JMeter нет такой среды разработки, которая бы поддерживала интеграцию с системами контроля версий и рецензирование кода. Но для задачи хранения и рецензирования кода можно использовать:
  • GitLab для рецензирования;
  • WinMerge для сравнения наборов изменений;
  • SourceTree для удобной работы с репозиторием;
  • Почтовый клиент для просмотра результатов рецензирования.
 Эти инструменты доступны, с их помощью можно организовать работу команды.

14 января 2016 г.

JMeter + GitLab => SourceTree + WinMerge или организация разработки нагрузочных тестов

В Apache JMeter результатом разработки является файл jmx. Это XML-файл.
Для организации процесса разработки удобно использовать систему контроля версий.

Наиболее удачными инструментами для сохранения результатов разработки JMeter в git считаю:
WinMerge позволяет задать такие настройки, при которых отображение отличий в XML-документах выглядит наиболее органичным и минималистичным.

Максимально гибкие настройки сравнения в WinMerge


А в SourceTree есть возможность легко подключить WinMerge в качестве инструмента сравнения.
Интеграция SourceTree и WinMerge

 Теперь если в SourceTree выделить изменённый файл со скриптом Apache.JMeter и нажать Ctrl+D, то откроется WinMerge и покажет изменения в скрипте. Которые легко отразить в комментарии.
SourceTree
WinMerge
В SourceTree и GitLab есть встроенное средство сравнения - diff. Из-за того, что утилита diff для сравнения XML не приспособлена, при отображении измеенений для XML-файла она показыает слишком много отличий. Особенно если изменений накопилось много.

Поэтому фиксировать изменения нужно часто:
  • каждое изменение фиксировать, не накапливать большие изменения;
  • под каждую задачу создавать ветку.
Иначе будет так, что WinMerge показывает что изменены три строки, а diff будет показывать, что было удалено 1000 строк и добавлено 1003 строки - набор изменений будет огромным.

BSOD при запуске VirtualBox 5 если есть КриптоПро CSP 3.6 R4 или USB 3.0 VGA Adapter

День первый

На Windows 7 x64 были совместно установлены Virtual Box (версии 4 и 5) с расширением Extension Pack и CryptoPro CSP (КриптоПро CSP 3.6 R4 для Windows).

При запуске виртуальной машины через Virtual Box система падала в синий экран. Причина в невозможности совместной работы КриптоПро CSP и Virtual Box.

Чтобы не было синего экрана при запуске виртуальной машины VirtualBox удалил КриптоПро CSP и перезагрузил компьютер. Решение временное, не сумел разобраться как подружить два продукта, а VirtualBox был нужнее, чем КриптоПро.

Помогло выйти на связь VirtualBox + КриптоПро == BSOD сообщение на форуме:
На форуме Крипто Про также есть обсуждение:
Отчёты из программы blueScreenView:

Bug Check String SYSTEM_SERVICE_EXCEPTION
Bug Check Code 0x0000003b
Parameter 1 00000000`c0000005
Parameter 2 fffff800`032735af
Parameter 3 fffff880`0412eb90
Parameter 4 00000000`00000000
Caused By Driver ntoskrnl.exe
Caused By Address ntoskrnl.exe+73c40
File Description NT Kernel & System
File Version 6.1.7601.19110 (win7sp1_gdr.151230-0600)
Major Version 15
Minor Version 7601
A problem has been detected and Windows has been shut down to prevent damage
to your computer.

The problem seems to be caused by the following file: ntoskrnl.exe

SYSTEM_SERVICE_EXCEPTION

Technical Information:

*** STOP: 0x0000003b (0x00000000c0000005, 0xfffff800032735af, 0xfffff8800412eb90, 
0x0000000000000000)

*** ntoskrnl.exe - Address 0xfffff80003292c40 base at 0xfffff8000321f000 DateStamp 
0x5625815c

Вывод - Virtual Box (версии 4 и 5) и CryptoPro CSP (КриптоПро CSP 3.6 R4 для Windows) пока несовместимы на Windows 7 x64 SP1.

================

День второй

Подключил к первому монитору второй монитор, используя USB 3.0 VGA Adapter. Модель устройства - Fresco Logic FL200 USB Display Adapter. Если подключить адаптер до включения виртуальной машины, то порядок. А если подключать адаптер во время работы виртуальной машины Virtual Box, то будет снова синий экран.


Bug Check StringIRQL_NOT_LESS_OR_EQUAL
Bug Check Code0x0000000a
Parameter 100000000`00000088
Parameter 200000000`00000002
Parameter 300000000`00000001
Parameter 4fffff800`032579e6
Caused By Driverntoskrnl.exe
Caused By Addressntoskrnl.exe+73c00
File DescriptionNT Kernel & System
File Version6.1.7601.19110 (win7sp1_gdr.151230-0600)
Major Version15
Minor Version7601

A problem has been detected and Windows has been shut down to prevent damage
to your computer.

The problem seems to be caused by the following file: ntoskrnl.exe

IRQL_NOT_LESS_OR_EQUAL

Technical Information:

*** STOP: 0x0000000a (0x0000000000000088, 0x0000000000000002, 0x0000000000000001, 
0xfffff800032579e6)

*** ntoskrnl.exe - Address 0xfffff80003276c00 base at 0xfffff80003203000 DateStamp 
0x5684191c

Из того, что видно перед ошибкой - диспетчер устройств (HAL) сообщает, что подключено новое USB 2.0 устройство, а затем система падает. Диспетчер устройств не прав, так как подключается USB 3.0 устройство в USB 3.0 порт.

Возможно, просто совпадение, два BSOD-а подряд. И причина BSOD в реализации драйверов для Fresco Logic FL200 USB Display Adapter.

Но есть предположение, что причина ошибок в реализации поддержки USB 2.0 / USB 3.0 в VirtualBox. А особые возможности поддержки USB появляются при установке Oracle VM VirtualBox Extension Pack.

Удалить VirtualBox не хотелось бы, иногда бывает нужен. А удалить Extension Pack могу запросто. Надеюсь, что после удаления VirtualBox Extension Pack синих экранов больше не будет. И можно будет повторно установить КриптоПро CSP 3.6 R4 для Windows и пользоваться двумя мониторами.


12 января 2016 г.

log4j (v.1) пример настройки трассировки (TRACE) нужного модуля

Настройка логирования для log4j версии 1
Пусть есть приложение, которое использует библиотеку log4j-1.2.17.jar

Параметры для log4j удобно передавать через внешний файл. Внешний файл удобно редактировать.

Запуск приложения с указанием конфига логов

Можно создать командный файл start.SampleConsoleServer.bat с текстом:
java -jar ^
    -Dlog4j.configuration=file:log4j.properties ^
    Server.jar ^
    com.blogger.server.SampleConsoleServer

Предполагается, что в одном каталоге находится три файла:
  • Server.jar - java-приложение, реализуещее класс com.blogger.server.SampleConsoleServer;
  • log4j.properties - файл настроек логирования для log4j версии 1;
  • start.SampleConsoleServer.bat - командный файл для запуска Server.jar.
Если бы файл log4j.properties находится не в текущем каталоге, а в каталоге на уровень выше, то надо бы было написать:
java -jar ^
    -Dlog4j.configuration=file:../log4j.properties ^
    Server.jar ^
    com.blogger.server.SampleConsoleServer

Пример содержимого файла log4j.properties

# Default logging level, log on console only (add ',file' to log on file as well)
#log4j.rootLogger=TRACE, stdout, fileTrace

log4j.logger.com.blogger.server.SampleConsoleServer=TRACE, stdout, fileTraceServer
log4j.logger.com.blogger.client=TRACE, stdout, fileTraceClient 

#Общий вывод в консоль
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p %c{1}:%L - %m%n#log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t%c{1}:%L\t-\t%C\t-\t%l\t-\t%M\t-\t%m%n
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t-\t%l\t-\t%m%n

#Лог сервера
log4j.appender.fileTraceServer=org.apache.log4j.RollingFileAppender
log4j.appender.fileTraceServer.maxFileSize=100MB
log4j.appender.fileTraceServer.maxBackupIndex=50
log4j.appender.fileTraceServer.file=trace.file.server.log
log4j.appender.fileTraceServer.layout=org.apache.log4j.PatternLayout
#log4j.appender.fileTraceServer.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t%c{1}:%L\t-\t%C\t-\t%l\t-\t%M\t-\t%m%n
log4j.appender.fileTraceServer.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t-\t%l\t-\t%m%n 

#Лог клиента
log4j.appender.fileTraceClient=org.apache.log4j.RollingFileAppender
log4j.appender.fileTraceClient.maxFileSize=100MB
log4j.appender.fileTraceClient.maxBackupIndex=50 
log4j.appender.fileTraceClient.file=trace.file.client.log 
log4j.appender.fileTraceClient.layout=org.apache.log4j.PatternLayout 
#log4j.appender.fileTraceClient.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t%c{1}:%L\t-\t%C\t-\t%l\t-\t%M\t-\t%m%n 
log4j.appender.fileTraceClient.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t-\t%l\t-\t%m%n

Описание конфигурационного файла log4j.properties


1. Указываются настройки для логгеров с разными именами

# Default logging level, log on console only (add ',file' to log on file as well)
#log4j.rootLogger=TRACE, stdout, fileTrace

log4j.logger.com.blogger.server.SampleConsoleServer=TRACE, stdout, fileTraceServer
log4j.logger.com.blogger.client=TRACE, stdout, fileTraceClient

Настройки логирования для всех логгеров

Можно настроить логирование которое будет применяться логгерам log4j с любым именем. Ключевым словом тут является rootLogger. Далее указываются Уровень логирования и куда записывать логи. В примере выше такая настройка закомментивана.
# Default logging level, log on console only (add ',file' to log on file as well)
log4j.rootLogger=TRACE, stdout, fileTrace

Но если бы она не была закомментирована, то сообщения с уровнем TRACE и выше для любого логгера записывались бы в лог согласно настройкам
log4j.appender.stdout
и
log4j.appender.fileTrace

Настройки логирования для конкретного класса и его подклассов

Обычно, логгеры создаются с именем, соответствующим имени класса. Пример java-кода ниже.
package com.blogger.server;

import org.apache.log4j.Logger;

public class SampleConsoleServer {
    protected static final Logger LOG = Logger.getLogger(SampleConsoleServer.class);

    public void processRequest(String request) {
        if (LOG.isTraceEnabled()) LOG.trace("processRequest(String request)");
        //...
        if (request == null) {
            LOG.fatal("Request is null");
            throw new NullPointerException();
        }
        //...
    }
}

Если логгер создавался по описанию класса, то имя логгера соотвествует полному имени класса: com.blogger.server.SampleConsoleServer.
В файле log4j.properties настройки для такого именованного логгера задаются после указания имени логгера:

log4j.logger.{Имя логгера}={Уровень}, {Обработчик 1}, {Обработчик 2}, ...
В имени логгера могут быть точки. Наличие точек в имени логгера и создаёт особую магию, позволяя задать настройки для конкретного класса или для логгеров всех классов из пакета, но об этом позже.

Пример настроек логирования для конкретного класса в примере выше это строка:
log4j.logger.com.blogger.server.SampleConsoleServer=TRACE, stdout, fileTraceServer

Тут говорится для логгера с именем com.blogger.server.SampleConsoleServer нужно все сообщения с уровнем TRACE и выше записывать в лог согласно настройкам с именами stdout и fileTraceServer.

Если бы в классе  SampleConsoleServer был подкласс ServerSettings с логгером имя которого com.blogger.server.SampleConsoleServer.ServerSetting, то также бы использовались настройки логирования:
log4j.logger.com.blogger.server.SampleConsoleServer=TRACE, stdout, fileTraceServer

Так как они соответствуют имени логгера.

Настройки логирования для всех классов из указанного пакета

Также в примере есть настройка для логгера com.blogger.client.
log4j.logger.com.blogger.client=TRACE, stdout, fileTraceClient 

Тут предполагается, что com.blogger.client - пакет, а не название класса. Что в этом пакете есть классы:
  • com.blogger.client.Client1,
  • com.blogger.client.Client1.Settings,
  • com.blogger.client.DefaultClient,
  • com.blogger.client.AbstractClient.
Что для этих классов созданы логгеры с соотвествующими именами. И все эти логгеры будут использовать настройки логирования com.blogger.client.

2. Задаются настройки обработчиков логов

#Общий вывод в консоль
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p %c{1}:%L - %m%n#log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t%c{1}:%L\t-\t%C\t-\t%l\t-\t%M\t-\t%m%n
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t-\t%l\t-\t%m%n

#Лог сервера
log4j.appender.fileTraceServer=org.apache.log4j.RollingFileAppender
log4j.appender.fileTraceServer.maxFileSize=100MB
log4j.appender.fileTraceServer.maxBackupIndex=50
log4j.appender.fileTraceServer.file=trace.file.server.log
log4j.appender.fileTraceServer.layout=org.apache.log4j.PatternLayout
#log4j.appender.fileTraceServer.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t%c{1}:%L\t-\t%C\t-\t%l\t-\t%M\t-\t%m%n
log4j.appender.fileTraceServer.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t-\t%l\t-\t%m%n 

#Лог клиента
log4j.appender.fileTraceClient=org.apache.log4j.RollingFileAppender
log4j.appender.fileTraceClient.maxFileSize=100MB
log4j.appender.fileTraceClient.maxBackupIndex=50 
log4j.appender.fileTraceClient.file=trace.file.client.log 
log4j.appender.fileTraceClient.layout=org.apache.log4j.PatternLayout 
#log4j.appender.fileTraceClient.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t%c{1}:%L\t-\t%C\t-\t%l\t-\t%M\t-\t%m%n 
log4j.appender.fileTraceClient.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}\t%p\t-\t%l\t-\t%m%n

Имена обработчиков логов

У обработчиков логов есть имена. В примере выше это:
  • stdout;
  • fileTraceServer;
  • fileTraceClient.

Тип  обработчика логов

Для лога указывается тип. Самые ходовые типы в моей практике:
  • org.apache.log4j.ConsoleAppender - вывод на консоль;
  • org.apache.log4j.RollingFileAppender - вывод в файл.

Формат лога

Для большинства ситуаций отладочного лога достаточно короткого формата:
%d{yyyy-MM-dd HH:mm:ss}\t%p\t-\t%l\t-\t%m%n

В лог запишутся:
  • %d дата в формате yyyy-MM-dd HH:mm:ss;
  • %p уровень события;
  • %l строка кода с указанием класса, метода, имени исходного файла и номера строки;
  • %m добавляемое в лог сообщение;
  • %n перевод строки;
  • \t символы табуляции;
  • - разделители.
Пример строк лога, соотвествующих указанному формату:
2015-12-14 18:53:15 TRACE - com.blogger.client.Handler.read(Handler.java:330) - 
2015-12-14 18:53:15 TRACE - com.blogger.client.Handler.read(Handler.java:346) - Read token duration: 185048 ms
2015-12-14 18:53:15 ERROR - com.blogger.client.Handler.read(Handler.java:360) - ProxyServer closed channel
2015-12-14 18:53:15 FATAL - com.blogger.client.Client2.run(Client2.java:164) - ProxyServer closed read channelData
com.blogger.exception.ReadControlChannelException: ProxyServer closed read channelData
 at com.blogger.client.Handler.read(Handler.java:361)
 at com.blogger.client.Handler.process(Handler.java:219)
 at com.blogger.client.Client2.run(Client2.java:151)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 at java.lang.Thread.run(Thread.java:745)
2015-12-14 18:53:15 TRACE - com.blogger.client.Client2.run(Client2.java:170) - Close all
2015-12-14 18:53:15 TRACE - com.blogger.client.Client2.tryClose(Client2.java:183) - Close channel java.nio.channels.SocketChannel[connected local=/192.168.150.58:55851 remote=/77.88.99.00:1234]

или без подсветки смысловых частей:
2015-12-14 18:53:15 TRACE - com.blogger.client.Handler.read(Handler.java:330) - 
2015-12-14 18:53:15 TRACE - com.blogger.client.Handler.read(Handler.java:346) - Read token duration: 185048 ms
2015-12-14 18:53:15 ERROR - com.blogger.client.Handler.read(Handler.java:360) - ProxyServer closed channel
2015-12-14 18:53:15 FATAL - com.blogger.client.Client2.run(Client2.java:164) - ProxyServer closed read channelData
com.blogger.exception.ReadControlChannelException: ProxyServer closed read channelData
 at com.blogger.client.Handler.read(Handler.java:361)
 at com.blogger.client.Handler.process(Handler.java:219)
 at com.blogger.client.Client2.run(Client2.java:151)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
 at java.lang.Thread.run(Thread.java:745)
2015-12-14 18:53:15 TRACE - com.blogger.client.Client2.run(Client2.java:170) - Close all
2015-12-14 18:53:15 TRACE - com.blogger.client.Client2.tryClose(Client2.java:183) - Close channel java.nio.channels.SocketChannel[connected local=/192.168.150.58:55851 remote=/77.88.99.00:1234]

По такому логу можно увидеть трассировку выполнения всех методов. Ошибки. Номера строк, в которых произошли логируемые события. Удобно, понятно.