Author Archive

Исследование Keycloak

Если у вас уже установлен keycloak и настроен realm, то запросить open-id конфигурацию данного realm можно командой:

curl http://{KEYCLOAK_HOST}:{KEYCLOAK_PORT}/auth/realms/{YOUR_REALM}/.well-known/openid-configuration | jq '.'

Результат будет представлен в виде JSON. Чтобы удобочитаемо отформатировать JSON, можно воспользоваться утилитой jq (https://stedolan.github.io/jq/), которую нужно будет дополнительно установить.

Небольшое описание полученных данных

issuer - базовый адрес realm'а;

authorization_endpoint - конечная точка авторизации; Получить публичный ключ для проверки подписи токена:

curl http://{KEYCLOAK_HOST}:{KEYCLOAK_PORT}/auth/realms/{YOUR_REALM}/protocol/openid-connect/certs | jq '.'

TAG: ,

Highload табличка

Часто возникает необходимость оценки скорости работы системы или какой-нибудь утилитки. Приходится брать калькулятор делить/умножать на секунды, минуты, часы и дни. Расчеты получаются, конечно же, приблизительные. Поэтому хотелось бы иметь примерную табличку, чтобы реже пользоваться калькулятором.
Кол-во операций,
транзакций и т.д.
За 24 часа
(86 400 секунд)
За 10 часов
(36 000 секунд)
За 10 часов
(600 минут)
1 000 000 11.6 в секунду 27.8 в секунду 1 666.7 в минуту
10 000 000 115.7 в секунду 277.8 в секунду 16 666.7 в минуту
100 000 000 1 157.4 в секунду 2 777.8 в секунду 166 666.7 в минуту
300 000 000 3 472.2 в секунду 8 333.3 в секунду 500 000.0 в минуту
500 000 000 5 787.0 в секунду 13 888.9 в секунду 833 333.3 в минуту
700 000 000 8 101.9 в секунду 19 444.4 в секунду 1 166 666.7 в минуту
1 000 000 000 11 574.1 в секунду 27 777.8 в секунду 1 666 666.7 в минуту

Слетели и не работают bash alias

В один день на серваке почему-то перестали работать алиасы (alias)

Долго так промучались.

Пока не нашел такое решение.

Выполняем команду и алиасы работают:

source ~/.bashrc

Чтобы это работало при новом входе:

Добавляем:

source ~/.bashrc

в файл ~/.bash_profile

Сборка и установка турника 3 в 1

Выложим на ровную поверхность компоненты турника:

Распакуем все детали:

Наденем ручки. Половину ручек нужно разрезать пополам. Чтобы дело пошло быстрее смачиваем ручки водой, а сам турник мыльным раствором:

В сборке турник выглядит следующим образом: <<<тут будет фото>>>

Размечаем на стене отверстия для креплений гвоздем или чем-то еще, чтобы была выемка для точного высверливания отверстия. Вcего четыре крепления, каждое крепится двумя анкерами. Расстояние между центрами креплений 45см (желательно пользоваться уровнем(ватерпасом)):

Под местом сверления вешаем пакет на скотч, чтобы меньше пылить и мусорить:

При сверлении отверстий возникла 1-ая проблема стена оказалась не бетонной, а из газобетона (газоблока). Такая стена менее прочная и легко сверлится. С турником шли анкера 12x70, стало понятно, что такие анкера не удержат турник. Пришлось гуглить и смотреть ютуб...

В итоге решил вешать турник на анкера 12x100, но перед этим залить в отверстия "жидкие гвозди" (строительный/монтажный клей). Как выяснилось, применение "жидких гвоздей" значительно усиливает соединение анкера со стеной, заполняя микрополости.

Далее возникла следующая проблема: в строй-магазине были только анкера 12x120, что даже лучше (чем длинне анкер, тем больше площадь соприкосновения со стеной), но продавец положил шурупы к анкеру длинной 100мм. Обнаружив это только дома, пришлось искать шурупы под анкера на 120, не найдя таких, решил укоротить анкера до 100мм, отрезав верхнюю часть:

После высверливания 8 отверстий 12x100, обязательно выскабливаем остатки пыли ершиком или как я - шляпкой шурупа меньшего диаметра:

Дополнительно, можно пройтись пылесосом:

Все отверстия готовы:

Далее по очереди работаем над каждым креплением: заливаем клей в два отверстия, забиваем два анкера и на два шурупа прикручиваем крепление.

Отверстие лучше заполнять клеем на половину, иначе избыток клея заполнит все пространство, сочясь из середины анкера, и будет очень мешать забить анкер.

Все крепления прикручены:

Ждем 3-5 суток до полного отвердевания клея...

Проверка DNS-балансировки

host -t a google.com

PostgreSQL размер таблиц и индексов

Надыбал такой запрос, который вытягивает размеры таблиц и их индексов, а также суммарный размер:
SELECT
    TABLE_NAME,
    pg_size_pretty(table_size) AS table_size,
    pg_size_pretty(indexes_size) AS indexes_size,
    pg_size_pretty(total_size) AS total_size
FROM (
    SELECT
        TABLE_NAME,
        pg_table_size(TABLE_NAME) AS table_size,
        pg_indexes_size(TABLE_NAME) AS indexes_size,
        pg_total_relation_size(TABLE_NAME) AS total_size
    FROM (
        SELECT ('"' || table_schema || '"."' || TABLE_NAME || '"') AS TABLE_NAME
        FROM information_schema.tables
    ) AS all_tables
    ORDER BY total_size DESC
) AS pretty_sizes

Суффиксный массив на Java за N*log^2(N)

public static int[] getSuffixArray(String s) {
 
    System.out.println(s);
 
    int N = s.length();
 
    int steps = Integer.bitCount(Integer.highestOneBit(N) - 1);
 
    int rank[][] = new int[steps + 1][N];
 
    for (int i = 0; i &lt; N; i++) {
        rank[0][i] = s.charAt(i) - 'a';
    }
 
    Tuple tuples[] = new Tuple[N];
 
    for (int step = 1, cnt = 1; step &lt;= steps; step++, cnt &lt;&lt;= 1) {
        for (int i = 0; i &lt; N; i++) {
            Tuple tuple = new Tuple();
            tuple.firstHalf = rank[step - 1][i];
            tuple.secondHalf = i + cnt &lt; N ? rank[step - 1][i + cnt] : -1;
            tuple.originalIndex = i;
 
            tuples[i] = tuple;
        }
 
        Arrays.sort(tuples);
 
        rank[step][tuples[0].originalIndex] = 0;
 
        for (int i = 1, currRank = 0; i &lt; N; i++) {
            if(!tuples[i - 1].firstHalf.equals(tuples[i].firstHalf)
                    || tuples[i - 1].secondHalf.equals(tuples[i].secondHalf)) {
                ++currRank;
            }
            rank[step][tuples[i].originalIndex] = currRank;
        }
 
 
    }
 
    int suffixArray[] = new int[N];
 
    for (int i = 0; i &lt; N; i++) {
        suffixArray[i] = tuples[i].originalIndex;
    }
 
    return suffixArray;
}
 
static class Tuple implements Comparable<tuple> {
    Integer originalIndex;  // хранит оригинальный индекс суффикса
    Integer firstHalf;      // хранит ранг первой половины суффикса
    Integer secondHalf;     // хранит ранг второй половины суффикса
 
 
    @Override
    public int compareTo(Tuple tuple) {
        return this.firstHalf.equals(tuple.firstHalf)
                ? this.secondHalf.compareTo(tuple.secondHalf)
                : this.firstHalf.compareTo(tuple.firstHalf);
    }
 
    @Override
    public String toString() {
        return "Tuple{" +
                "originalIndex=" + originalIndex +
                ", firstHalf=" + firstHalf +
                ", secondHalf=" + secondHalf +
                '}';
    }
}
 
</tuple>

Перевод времени на сервере

Когда мы тестируем нашу систему, часто возникает необходимость в переводе времени на сервере. У нас используются операционные системы Ubuntu и CentOS, поэтому команды будут для них. Ubuntu # Отключаем службу NTP (если она установлена и включена), которая синхронизирует время и помещает вам перевести время
sudo timedatectl set-ntp off
# Переводим времени, время указывается в формате ЧЧ:ММ:CC
sudo date +%T -s "11:14:00"
CentOS # Смена текущей даты в формате ГГГГММДД (время при этом обнуляется 00:00:00)
sudo date +%Y%m%d -s "20190413"
# Переводим времени, время указывается в формате ЧЧ:ММ:CC
sudo date +%T -s "10:48:56"
TAG: , ,

Пишем на C++ свою агрегационную функцию для HP Vertica

Предыстория В проекте возникла необходимость отобразить товары, которые поставляются или могут поставляться в определенные регионы Казахстана. У каждого поставщика есть перечень товаров с указанием регионов поставки. Есть таблица товаров, назовем ее product. Регионов в Казахстане всего 17, поэтому я решил хранить данные о регионах поставки в одном числовом поле c помощью битов. Таблица product выглядит следующим образом:
CREATE TABLE product (
	code VARCHAR(20),
	name VARCHAR(50),
	regions BIGINT,
	company_code VARCHAR(20),
	company_name VARCHAR(50)
);
code - код продукта; name - наименование продукта; regions - регионы поставки; company_code - код компании; company_name - наименование компании; Теперь о битовом хранение регионов. Каждому региону соответствует свой бит:
Число Битовое представление числа Наименование региона
1 000000000000000001 По всей территории РК
2 000000000000000010 г. Астана
4 000000000000000100 г. Алматы
8 000000000000001000 Акмолинская область
16 000000000000010000 Актюбинская область
32 000000000000100000 Алматинская область
64 000000000001000000 Атырауская область
128 000000000010000000 Западно-Казахстанская область
256 000000000100000000 Жамбылская область
512 000000001000000000 Карагандинская область
1024 000000010000000000 Костанайская область
2048 000000100000000000 Кызылординская область
4096 000001000000000000 Мангистауская область
8192 000010000000000000 Туркестанская область
16384 000100000000000000 Павлодарская область
32768 001000000000000000 Северо-Казахстанская область
65536 010000000000000000 Восточно-Казахстанская область
131072 100000000000000000 г. Шымкент
Например, если нужно указать, что товар поставляется в три региона: г. Астана, г. Алматы и Карагандинскую область, то нужно применить бинарную операцию побитового "ИЛИ" к битам регионов, в итоге регионы закодируются в число 518:
ИЛИ 000000000000000010 2
000000000000000100 4
000000001000000000 512
Результат 000000001000000110 518
Заполним таблицу тестовыми данными:
INSERT INTO product VALUES ('001', 'Трансформатор тока', 6, 'abc', 'Energy Corp.');
INSERT INTO product VALUES ('001', 'Трансформатор тока', 2, 'xyz', 'Power Inc.');
INSERT INTO product VALUES ('002', 'Усилитель низкой частоты', 1, 'abc', 'Energy Corp.');
INSERT INTO product VALUES ('003', 'Набор резисторов', 20, 'abc', 'Energy Corp.');
INSERT INTO product VALUES ('003', 'Набор резисторов', 160, 'volt', 'Volt Group');
INSERT INTO product VALUES ('003', 'Набор резисторов', 36, 'xyz', 'Power Inc.');
Данные готовы, но проблема в том, что в Vertica нет подходящей агрегационной функции, позволяющей делать побитовое "ИЛИ" чисел. В Vertica есть функция bit_or, но по ряду причин она не подходит:
  1. Функция bit_or работает с бинарными данными
  2. Напрямую преобразовать число в бинарный вид нет возможности, только через hex
  3. Функция bit_or производит побитовые операции слева направо, поэтому если битов не хватает правая часть дополняется нулями, что дает не тот результат, который мы ожидаем.
Таким образом, мы пришли к необходимости создания своей функции. Vertica позволяется писать свои функции на разных языках: C++, Java и Python. Но именного агрегационные функции User Defined Aggregate Function (UDAF) можно почему-то писать только на С++. В каталоге, где вы установили Vertica, есть SDK с примерами. Например, в CentOS 7 это директория: /opt/vertica/sdk/examples У вас должна быть установлена среда разработки g++. Чтобы установить среду разработки g++, запустите:
yum install gcc gcc-c++ make

Если у вас Ubuntu, библиотеки могу собираться, но не работать выдавая ошибки вида:
Failure in UDx RPC call InvokeGetLibraryManifest()
Насколько я понял, это из-за версий gcc и g++, которые в Ubuntu по умолчанию 5.4.0, а в Centos 7 они по умолчанию 4.8.5. Поэтому, если у вас возникли подобные проблемы, можно доставить нужные версии gcc и g++:
sudo apt-get install gcc-4.8
sudo apt-get install g++-4.8
Создайте файл OrSum.cpp со следующим содержимым:
#include "Vertica.h"
#include <time.h>
#include <sstream>
#include <iostream>
 
using namespace Vertica;
 
class OrSum : public AggregateFunction
{
 
public:
 
    virtual void initAggregate(ServerInterface &amp;srvInterface,
                               IntermediateAggs &amp;aggs)
    {
        vint &amp;sum = aggs.getIntRef(0);
        sum = 0;
    }
 
    void aggregate(ServerInterface &amp;srvInterface,
                   BlockReader &amp;arg_reader,
                   IntermediateAggs &amp;aggs)
    {
        vint &amp;sum = aggs.getIntRef(0);
        do {
            const vint &amp;val = arg_reader.getIntRef(0);
            if (val != Vertica::vint_null) {
                sum |= val;
            }
        } while (arg_reader.next());
    }
 
    virtual void combine(ServerInterface &amp;srvInterface,
                         IntermediateAggs &amp;aggs,
                         MultipleIntermediateAggs &amp;aggs_other)
    {
        vint &amp;sum = aggs.getIntRef(0);
 
        do {
            const vint &amp;otherSum = aggs_other.getIntRef(0);
            if (otherSum != Vertica::vint_null) {
                sum |= otherSum;
            }
        } while (aggs_other.next());
    }
 
    virtual void terminate(ServerInterface &amp;srvInterface,
                           BlockWriter &amp;res_writer,
                           IntermediateAggs &amp;aggs)
    {
        res_writer.setInt(aggs.getIntRef(0));
    }
 
    InlineAggregate()
};
 
class OrSumFactory : public AggregateFunctionFactory
{
    virtual void getPrototype(ServerInterface &amp;srvInterface,
                              ColumnTypes &amp;argTypes,
                              ColumnTypes &amp;returnType)
    {
        argTypes.addInt();
        returnType.addInt();
    }
 
    virtual void getReturnType(ServerInterface &amp;srvInterface,
                               const SizedColumnTypes &amp;input_types,
                               SizedColumnTypes &amp;output_types)
    {
        output_types.addInt();
    }
 
    virtual void getIntermediateTypes(ServerInterface &amp;srvInterface,
                                      const SizedColumnTypes &amp;input_types,
                                      SizedColumnTypes &amp;intermediateTypeMetaData)
    {
        intermediateTypeMetaData.addInt();
    }
 
    virtual AggregateFunction *createAggregateFunction(ServerInterface &amp;srvInterface)
    { return vt_createFuncObject<orsum>(srvInterface.allocator); }
};
 
RegisterFactory(OrSumFactory);
</orsum></iostream></sstream></time.h>
Сборка библиотеки В разработке... Создание функции
CREATE OR REPLACE LIBRARY KESH_LIB AS '/opt/vertica/sdk/examples/build/KeshFunctions.so' LANGUAGE 'C++';
CREATE OR REPLACE AGGREGATE FUNCTION KESH_OR_SUM AS LANGUAGE 'C++' NAME 'KeshOrSumFactory' LIBRARY EPROC_LIB;
Использование функции
SELECT code, kesh_or_sum(regions) FROM product GROUP BY code
Удаление функции
DROP AGGREGATE FUNCTION KESH_OR_SUM(BIGINT);
DROP LIBRARY KESH_LIB;

Оптимизация Grok (Logstash)

Повествование о том, как неэффективный разбор grok забил процессоры на 100%. Немного предыстории. С начала года у нас было настроено расширение к PostgreSQL под названием pg_audit (см. https://github.com/pgaudit/pgaudit) для аудита доступа к таблицам и колонкам таблиц. Логи pg_audit отправляются filebeat'ом в logstash, где разбираются через grok. На тот момент grok-выражение было следующим: %{TIMESTAMP_ISO8601:pg_aud_log_date} %{DATA:pg_aud_time_zone} %{DATA:pg_aud_db_name} %{DATA:pg_aud_db_user} %{DATA:pg_aud_db_host}: LOG:%{SPACE}AUDIT: %{DATA:pg_aud_audit_type},%{INT:pg_aud_statement_id},%{INT:pg_aud_sub_statement_id},%{DATA:pg_aud_class},%{DATA:pg_aud_command},%{DATA:pg_aud_object_type},%{DATA:pg_aud_object_name},%{GREEDYDATA:pg_aud_statement}> В течении 5 месяцев все было нормально, пока в последние несколько дней CPU не достиг 100% и не спадал. В логах logstash были следующие ошибки:
Timeout executing grok
Остро встала задача оптимизиции. Почему-то в интернете тяжело найти grok-паттерны. Чтобы это исправить, приложу две ссылки и сами grok-паттерны внизу данной статьи. Финальное оттюненное выражение имеет вид: %{TIMESTAMP_ISO8601:pg_aud_log_date} %{NOTSPACE:pg_aud_time_zone} %{NOTSPACE:pg_aud_db_name} %{NOTSPACE:pg_aud_db_user} %{IPORHOST:pg_aud_db_host}: LOG:%{SPACE}AUDIT: %{WORD:pg_aud_audit_type},%{INT:pg_aud_statement_id},%{INT:pg_aud_sub_statement_id},%{WORD:pg_aud_class},%{WORD:pg_aud_command},%{WORD:pg_aud_object_type},%{USERNAME:pg_aud_object_name},%{GREEDYDATA:pg_aud_statement} Видно, что заменены все DATA на более жесткие паттерны и оставлен только один GREEDYDATA. Если у вас тоже проблемы с производительностью разбора grok, постарайтесь свести к минимуму использование паттернов DATA и GREEDYDATA. Описанные мероприятия нормализовали работу logstash: При создании grok-выражений очень помогает онлайн grokdebug: http://grokdebug.herokuapp.com/ Совет Начинайте писать выражение с
%{GREEDYDATA:pg_aud_statement}
постепенно выделяя нужные фрагменты записи лога. Grok-паттерны https://github.com/hpcugent/logstash-patterns/blob/master/files/grok-patterns https://github.com/elastic/logstash/blob/v1.4.2/patterns/grok-patterns
USERNAME [a-zA-Z0-9._-]+
USER %{USERNAME}
INT (?:[+-]?(?:[0-9]+))
BASE10NUM (?[+-]?(?:(?:[0-9]+(?:\.[0-9]+)?)|(?:\.[0-9]+)))
NUMBER (?:%{BASE10NUM})
BASE16NUM (?(?"(?>\\.|[^\\"]+)+"|""|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``))
UUID [A-Fa-f0-9]{8}-(?:[A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}

# Networking
MAC (?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC})
CISCOMAC (?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4})
WINDOWSMAC (?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})
COMMONMAC (?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})
IPV6 ((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?
IPV4 (?/(?>[\w_%!$@:.,-]+|\\.)*)+
TTY (?:/dev/(pts|tty([pq])?)(\w+)?/?(?:[0-9]+))
WINPATH (?>[A-Za-z]+:|\\)(?:\\[^\\?*]*)+
URIPROTO [A-Za-z]+(\+[A-Za-z+]+)?
URIHOST %{IPORHOST}(?::%{POSINT:port})?
# uripath comes loosely from RFC1738, but mostly from what Firefox
# doesn't turn into %XX
URIPATH (?:/[A-Za-z0-9$.+!*'(){},~:;=@#%_\-]*)+
#URIPARAM \?(?:[A-Za-z0-9]+(?:=(?:[^&]*))?(?:&(?:[A-Za-z0-9]+(?:=(?:[^&]*))?)?)*)?
URIPARAM \?[A-Za-z0-9$.+!*'|(){},~@#%&/=:;_?\-\[\]]*
URIPATHPARAM %{URIPATH}(?:%{URIPARAM})?
URI %{URIPROTO}://(?:%{USER}(?::[^@]*)?@)?(?:%{URIHOST})?(?:%{URIPATHPARAM})?

# Months: January, Feb, 3, 03, 12, December
MONTH \b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\b
MONTHNUM (?:0?[1-9]|1[0-2])
MONTHNUM2 (?:0[1-9]|1[0-2])
MONTHDAY (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])

# Days: Monday, Tue, Thu, etc...
DAY (?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)

# Years?
YEAR (?>\d\d){1,2}
HOUR (?:2[0123]|[01]?[0-9])
MINUTE (?:[0-5][0-9])
# '60' is a leap second in most time standards and thus is valid.
SECOND (?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?)
TIME (?!<[0-9])%{HOUR}:%{MINUTE}(?::%{SECOND})(?![0-9])
# datestamp is YYYY/MM/DD-HH:MM:SS.UUUU (or something like it)
DATE_US %{MONTHNUM}[/-]%{MONTHDAY}[/-]%{YEAR}
DATE_EU %{MONTHDAY}[./-]%{MONTHNUM}[./-]%{YEAR}
ISO8601_TIMEZONE (?:Z|[+-]%{HOUR}(?::?%{MINUTE}))
ISO8601_SECOND (?:%{SECOND}|60)
TIMESTAMP_ISO8601 %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?
DATE %{DATE_US}|%{DATE_EU}
DATESTAMP %{DATE}[- ]%{TIME}
TZ (?:[PMCE][SD]T|UTC)
DATESTAMP_RFC822 %{DAY} %{MONTH} %{MONTHDAY} %{YEAR} %{TIME} %{TZ}
DATESTAMP_RFC2822 %{DAY}, %{MONTHDAY} %{MONTH} %{YEAR} %{TIME} %{ISO8601_TIMEZONE}
DATESTAMP_OTHER %{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{TZ} %{YEAR}
DATESTAMP_EVENTLOG %{YEAR}%{MONTHNUM2}%{MONTHDAY}%{HOUR}%{MINUTE}%{SECOND}

# Syslog Dates: Month Day HH:MM:SS
SYSLOGTIMESTAMP %{MONTH} +%{MONTHDAY} %{TIME}
PROG (?:[\w._/%-]+)
SYSLOGPROG %{PROG:program}(?:\[%{POSINT:pid}\])?
SYSLOGHOST %{IPORHOST}
SYSLOGFACILITY <%{NONNEGINT:facility}.%{NONNEGINT:priority}>
HTTPDATE %{MONTHDAY}/%{MONTH}/%{YEAR}:%{TIME} %{INT}

# Shortcuts
QS %{QUOTEDSTRING}

# Log formats
SYSLOGBASE %{SYSLOGTIMESTAMP:timestamp} (?:%{SYSLOGFACILITY} )?%{SYSLOGHOST:logsource} %{SYSLOGPROG}:
COMMONAPACHELOG %{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:timestamp}\] "(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})" %{NUMBER:response} (?:%{NUMBER:bytes}|-)
COMBINEDAPACHELOG %{COMMONAPACHELOG} %{QS:referrer} %{QS:agent}

# Log Levels
LOGLEVEL ([Aa]lert|ALERT|[Tt]race|TRACE|[Dd]ebug|DEBUG|[Nn]otice|NOTICE|[Ii]nfo|INFO|[Ww]arn?(?:ing)?|WARN?(?:ING)?|[Ee]rr?(?:or)?|ERR?(?:OR)?|[Cc]rit?(?:ical)?|CRIT?(?:ICAL)?|[Ff]atal|FATAL|[Ss]evere|SEVERE|EMERG(?:ENCY)?|[Ee]merg(?:ency)?)