GUI editor to tame mod_security rules

⌈⌋ branch:  modseccfg


Check-in [f0887760c8]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Bundle logfmt1 into sub project. Support for /usr/share/logfmt/ database and update scripts (apache version is a trimmed down modseccfg.vhosts extractor). Support both pip and deb package (differ to some extend).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: f0887760c86cf6ee08a7961533fbca4e7877ff11a69ef2c2f366a584d8196512
User & Date: mario 2020-12-16 10:35:59
Context
2020-12-16
10:39
Use prefix/whitespace prepending for whole block (some macros just got the first line indented). check-in: a58faea2e0 user: mario tags: trunk
10:35
Bundle logfmt1 into sub project. Support for /usr/share/logfmt/ database and update scripts (apache version is a trimmed down modseccfg.vhosts extractor). Support both pip and deb package (differ to some extend). check-in: f0887760c8 user: mario tags: trunk
2020-12-15
09:41
Support TransferLog (which nobody uses). Add pretty ▰▰▰▰▰▰▰▰▱▱▱▱▱ progressbar for *.conf traversal on startup. check-in: d7f672369e user: mario tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added logfmt1/README.md.















































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
**logfmt1** handles `*.log.fmt` files to transform LogFormat / placeholder
strings to regular expressions (named capture groups). Currently just comes
with rules for Apache definitions. It bundles a `logex` and `update-logfmt`
to create/rewrite `*.log.fmt` files globally.

    {
       "class": "apache combined",
       "record": "%h %l %u %t \"%r\" %>s %b",
    }

It's basically meant for universal log parsing, whilst reducing manual
configuration or the restrain on basic log variants. It originated in
[modseccfg](https://fossil.include-once.org/modseccfg/). This Python
package is mostly a stub. You should preferrably install the
[system package](https://apt.include-once.org/):

    apt install python3-logfmt1

This will yield the proper `/usr/share/logfmt/` structure and the run-parts
wrapper `update-logfmt`. The grok placeholders are supported, but remain
untested.


### logfmt1

To craft a regex:

    import logfmt1, json
    fmt = json.load(open("/.../access.log.fmt", "r"))
    rx = logfmt1.regex(fmt)
    rx = logfmt1.rx2re(rx)   # turn into Python regex

Or with plain old guesswork / presuming a standard log format:

    rx = logfmt1.regex({"class": "apache combined"})

Though that's of course not the intended use case, and hinges on
predefined formats in /usr/share/logfmt/.


### logex

Very crudementary extractor for log files:

    logex .../access.log --tab @host @date +id

Which of course handles the `.fmt` implicitly.


### update-logfmt

The Python package does bundle a run-parts wrapper, but just the apache
collector, and a local Python copy of the format database. It should discover
all `*.log` files nonetheless and pair them with `.fmt` declarations.

Added logfmt1/__init__.py.

















>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
# encoding: utf-8
# api: python
# title: logfmt1
# description: handle *.log.fmt specifiers and regex conversion
# type: functions
# depends: python (>= 3.6)

from logfmt1.logfmt1 import regex, rx2re, update, rulesdb, logopen, parsy_parse

Added logfmt1/grok2fmt1.























































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python3
# description: expand grok to .fmt
#
# output should go into /usr/share/logfmt/grok.fmt
# and could then be inherited by various `grok.appname.fmt`
# or in any ….log.fmt via {"class": "grok appname"}
# or via "record": "%{GROK:%{IP:ip} %{DATE:data}...}"
# or perhaps per "class":"grok" in fields/expand definitions
# 
#

import re, json

# moved to https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns
src = """
# 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]+)?)|(?:\.[0-9]+)))
NUMBER (?:%{BASE10NUM})
BASE16NUM (?<![0-9A-Fa-f])(?:[+-]?(?:0x)?(?:[0-9A-Fa-f]+))
BASE16FLOAT \b(?<![0-9A-Fa-f.])(?:[+-]?(?:0x)?(?:(?:[0-9A-Fa-f]+(?:\.[0-9A-Fa-f]*)?)|(?:\.[0-9A-Fa-f]+)))\b

POSINT \b(?:[1-9][0-9]*)\b
NONNEGINT \b(?:[0-9]+)\b
WORD \b\w+\b
NOTSPACE \S+
SPACE \s*
DATA .*?
GREEDYDATA .*
QUOTEDSTRING (?>(?<!\\)(?>"(?>\\.|[^\\"]+)+"|""|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``))
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 (?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])
IP (?:%{IPV6}|%{IPV4})
HOSTNAME \b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\.?|\b)
HOST %{HOSTNAME}
IPORHOST (?:%{HOSTNAME}|%{IP})
HOSTPORT %{IPORHOST}:%{POSINT}

# paths
PATH (?:%{UNIXPATH}|%{WINPATH})
UNIXPATH (?>/(?>[\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)?)
"""

grok = dict(re.findall("^([A-Z]\w+) (.+)", src, re.M))
def grok2rx(rx):
    rx = re.sub("%\{(\w+)\}", lambda m: grok[m.group(1)], rx)
    rx = re.sub("%\{(\w+):([\w.\-]+)\}", lambda m: f"(?<{m.group(2)}>" + grok[m.group(1)] + ")", rx)
    return rx
grok = {name:grok2rx(rx) for name,rx in grok.items()}
grok = {name:grok2rx(rx) for name,rx in grok.items()}
grok = {name:grok2rx(rx) for name,rx in grok.items()}
grok = {name:grok2rx(rx) for name,rx in grok.items()}


fmt = {
    "$license": "Apache-2.0",
    "$origin": "https://github.com/elastic/logstash/blob/v1.4.2/patterns/",
    "class": "grok",
    "separator": " ",
    "placeholder": "%\{\w+:([\w.-]+)\}",
    "rewrite": {},
    "alias": {},
    "fields": {},
    "expand": {
        "%\{GROK:((?:[^{}]+|\{[^{}]+\})+)\}": { "id": "", "class": "grok" }
    },
    "container": {},
    "glob": ["*.grok"],
}
for name, rx in grok.items():
    #if name in ("COMBINEDAPACHELOG", "COMMONAPACHELOG", "SYSLOGBASE",) or 
    if len(re.findall("\(\?<\w+", rx))>=5:
        fmt["fields"]["%{"+name+"}"] = {
           "rx": rx,
           "id": name.lower(),
           "grok": name,
        }
    else:
        fmt["expand"]["%{"+name+":([\w.\-]+)}"] = {
           "rx": rx,
           "id": '$1',
           "grok": name,
        }


print(json.dumps(fmt, indent=4))

Name change from modseccfg/scripts/logex.py to logfmt1/logex.py.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26










27


28
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/usr/bin/env python3
# encoding: utf-8
# title: logex
# description: extract fields from log (with .fmt)
# version: 0.2
# type: cli
# category: extract
#
#
# First parameter should be the log file. And a .log.fmt must exist
# alongside (see modseccfg→Install
#
# Syntax:
#   logex.py /var/log/apache2/access.log @request_path @request_time @host
#
# Other args:
#   --json / --tab / --csv
#   --iso8601 / --debug
#
# Where the @'s are decoration, and fields can be supplied as individual
# arguments (then the output became space-separated). Field names are
# application-type specific (internal) names. (e.g. @request_method, @host
# or @tm_wday for Apache logs. With some predefined aliases, e.g. for w3c
# field names.)
#
# Other field name prefixes (than @) are % or * to comma-join list entries,










# while + combines them with a plus, and # made the list field json.


#


import sys, re, json
import traceback, dateutil.parser
try:
    import logfmt1
except:
    from modseccfg import logfmt1


#-- args
argv = sys.argv
space = " "
if "--tab" in argv:
    space = "\t"
if "--csv" in argv:
    space = "," 
iso8601 = any(a in argv for a in ("--iso", "--iso8601", "--date", "--fixdates", "--die-apachedateformat-die"))
as_json = any(a in argv for a in ("--json", "--asjson", "--as-json"))
dodebug = any(a in argv for a in ("--debug", "-D")) or True
# remove --params
args = [a for a in argv if not re.match("^--\w+", a)]
# filename and field list
log_fn = argv[1]
output_fields = space.join(argv[2:])


#-- open log file
try:
    reader = logfmt1.logopen(log_fn, debug=dodebug, duplicate=False)
    if dodebug:
        sys.stdout.write(json.dumps(reader.__dict__, indent=2, default=lambda x:str(x))+"\n")
except Exception as e:
    sys.stderr.write(traceback.format_exc()+"\n")
    sys.stderr.write("Use `update-logfmt-apache` or modseccfg→File→Install→update_logfmt to generate a .fmt descriptor\n")
    sys.exit()


# extra aliases (for apache/httpd)










|


|





|
|
|
|
|

|
>
>
>
>
>
>
>
>
>
>
|
>
>


>


<
|
<
<











|

|








|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45


46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/env python3
# encoding: utf-8
# title: logex
# description: extract fields from log (with .fmt)
# version: 0.2
# type: cli
# category: extract
#
#
# First parameter should be the log file. And a .log.fmt must exist
# alongside (generate with `update-logfmt`).
#
# Syntax:
#   logex.py /var/log/apache2/access.log  request_path  request_time  @host
#
# Other args:
#   --json / --tab / --csv
#   --iso8601 / --debug
#
# Where any @'s are decoration, and fields can be supplied as individual
# arguments (become space-separated without --tab/--csv). Field names are
# application-type specific (internal) names. (E.g. @request_method, @host
# or @tm_wday for Apache logs. With some predefined aliases, e.g. the w3c
# extended log field names.)
#
# Field name prefixes are irrelevant for normal log entries.
# But may join list-entries from container fields:
#    @name   will just show the first entry
#    %name   space-separated list
#    *name   comma-separated list
#    +name   plus-joined list
#    #name   as json array
#     name   whatever
#
# Fields can be given as individual arguments, or as part of a string
# output groups:
#    logex fn.log --tab  @individual "@combined,@with,@comma" @tabagain
#    logex fn.log --csv  "@lone" "*multi" "#json"
# Though you usually don't wanna overcomplicate the log format again.
#


import sys, re, json
import traceback, dateutil.parser

import logfmt1




#-- args
argv = sys.argv
space = " "
if "--tab" in argv:
    space = "\t"
if "--csv" in argv:
    space = "," 
iso8601 = any(a in argv for a in ("--iso", "--iso8601", "--date", "--fixdates", "--die-apachedateformat-die"))
as_json = any(a in argv for a in ("--json", "--asjson", "--as-json"))
dodebug = any(a in argv for a in ("--debug", "-D"))
# remove --params
argv = [a for a in argv if not re.match("^--\w+$|^-\w$", a)]
# filename and field list
log_fn = argv[1]
output_fields = space.join(argv[2:])


#-- open log file
try:
    reader = logfmt1.logopen(log_fn, debug=dodebug, duplicate=False)
    #if dodebug:
    #    sys.stdout.write(json.dumps(reader.__dict__, indent=2, default=lambda x:str(x))+"\n")
except Exception as e:
    sys.stderr.write(traceback.format_exc()+"\n")
    sys.stderr.write("Use `update-logfmt-apache` or modseccfg→File→Install→update_logfmt to generate a .fmt descriptor\n")
    sys.exit()


# extra aliases (for apache/httpd)
80
81
82
83
84
85
86
87
88


89
90


91
92
93
94
95
96
97
98
}
alias.update(reader.alias)


# substitute occurences
def get_field(m, row):
    pfx, name = m.groups()
    val = row.get(alias.get(name, name), "-")
    if isinstance(val, list):  # how to handle lists (for unpacked [key "value"] fields)


        if pfx == "+":
            val = "+".join(val)


        elif pfx == "*" or pfx == "%":
            val = ",".join(val)
        elif pfx == "#":
            val = json.dumps(val)
        else:
            val = str(val)
    return val








|

>
>
|

>
>
|







90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
}
alias.update(reader.alias)


# substitute occurences
def get_field(m, row):
    pfx, name = m.groups()
    val = row.get(name) or row.get(alias.get(name)) or "-"
    if isinstance(val, list):  # how to handle lists (for unpacked [key "value"] fields)
        if pfx == "@":
            val = val[0]
        elif pfx == "+":
            val = "+".join(val)
        elif pfx == "%":
            val = " ".join(val)
        elif pfx == "*":
            val = ",".join(val)
        elif pfx == "#":
            val = json.dumps(val)
        else:
            val = str(val)
    return val

Name change from modseccfg/logfmt1.py to logfmt1/logfmt1.py.

1
2
3
4
5
6
7
8








9
10
11
12
13
14
15
# encoding: utf-8
# api: python
# title: logfmt1
# description: craft *.log.fmt regex
# type: transform
# category: io
# version: 0.4
# license: Apache-2.0








#
# Logging format strings to regex conversion.
#
# This is supposed to recognize .fmt files adjacent to logs,
# which document both the application type and log variant
# with the most current %p%l%ace%holder definition used.
# The purpose of this is to allow log extraction with exact


|
|




>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# encoding: utf-8
# api: python
# title: python3-logfmt1
# description: handle *.log.fmt specifiers and regex conversion
# type: transform
# category: io
# version: 0.4
# license: Apache-2.0
# pack:
#    logfmt1.py=/usr/lib/python3/dist-packages/
#    update-logfmt=/usr/bin/
#    ./logex.py=/usr/bin/logex
#    share=/usr/share/logfmt
# architecture: all
# depends: python (>= 3.6)
# url: https://fossil.include-once.org/modseccfg/wiki/logfmt1
#
# Logging format strings to regex conversion.
#
# This is supposed to recognize .fmt files adjacent to logs,
# which document both the application type and log variant
# with the most current %p%l%ace%holder definition used.
# The purpose of this is to allow log extraction with exact
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
        "alias": {
            "remote_address": "remote_addr",
            "ip": "remote_addr",
            "user": "remote_user",
            "file": "request_file",
            "size": "bytes_sent",
            "datetime": "request_time",
            "ctime": "request_time".
            "date": "request_time",
            "loglevel": "remote_logname",
            "module_name": "request_method",
            "request_flushed": "file_line",
            "requests_on_connection": "keepalives",
            "error": "apr_status",
            "request_flushed": "file_line",
        },

        # convert variant placeholders into fields beforehand,
        # possibly looking up other definitions (strftime) for marshalled placeholders
        "expand": {
            "%\{([^{}]+)\}t": {
                "id": "request_time",
                "class": "strftime" # different placeholders within \{...\}

            },
            "%[<>]?\{([\w\-]+)\}[Conexic]": {
                "id": "$1",
                "rx": "\S+"
            },
            "%\{([\w\-]+)\}\^t[io]": {
                "id": "$1",







|














|
>







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
        "alias": {
            "remote_address": "remote_addr",
            "ip": "remote_addr",
            "user": "remote_user",
            "file": "request_file",
            "size": "bytes_sent",
            "datetime": "request_time",
            "ctime": "request_time",
            "date": "request_time",
            "loglevel": "remote_logname",
            "module_name": "request_method",
            "request_flushed": "file_line",
            "requests_on_connection": "keepalives",
            "error": "apr_status",
            "request_flushed": "file_line",
        },

        # convert variant placeholders into fields beforehand,
        # possibly looking up other definitions (strftime) for marshalled placeholders
        "expand": {
            "%\{([^{}]+)\}t": {
                "id": "request_time",
                "class": "strftime", # different placeholders within \{...\}
                "record": "$1"
            },
            "%[<>]?\{([\w\-]+)\}[Conexic]": {
                "id": "$1",
                "rx": "\S+"
            },
            "%\{([\w\-]+)\}\^t[io]": {
                "id": "$1",
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252



253
254
255
256
257
258
259
            "%%": { "id": "percent", "rx": "%" },
        },
        "expand": {
            "%(\w)": "[\w\d.]+"
        }
    }
    
    grok = {
        # see dev/logfmt1/grok2fmt1.py
    }
    
    # return builtin definitions or from /usr/share/logfmt/*.*.fmt
    @staticmethod
    def get(cls):
        rules = {}
        cls = cls.split(" ")
        while cls:
            lookup = ".".join(cls)
            lookup_ = "_".join(cls)
            add = {}
            fn = f"/usr/share/logfmt/{lookup}.fmt"



            if os.path.exists(fn):
                add = open(fn, "r", encoding="utf-8")
                add = re.sub("^\s*#.+", "", add, re.M)
                add = json.loads(add)
            #elif *.grok: read, find primary regex:, or declare %GROK per cls=["grok", "-"]
            #elif *.lnav: get readymade "regex:"
            else:







<
<
<
<









|
>
>
>







241
242
243
244
245
246
247




248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
            "%%": { "id": "percent", "rx": "%" },
        },
        "expand": {
            "%(\w)": "[\w\d.]+"
        }
    }
    




    # return builtin definitions or from /usr/share/logfmt/*.*.fmt
    @staticmethod
    def get(cls):
        rules = {}
        cls = cls.split(" ")
        while cls:
            lookup = ".".join(cls)
            lookup_ = "_".join(cls)
            add = {}
            dir = "/usr/share/logfmt"
            if not os.path.exists(dir):  # kludge for Python package
                dir = re.sub("[\w.]$", "share", __file__) # use bundled ./share/ directory
            fn = f"{dir}/{lookup}.fmt"
            if os.path.exists(fn):
                add = open(fn, "r", encoding="utf-8")
                add = re.sub("^\s*#.+", "", add, re.M)
                add = json.loads(add)
            #elif *.grok: read, find primary regex:, or declare %GROK per cls=["grok", "-"]
            #elif *.lnav: get readymade "regex:"
            else:
268
269
270
271
272
273
274







275
276
277
278
279
280
281
            if isinstance(v, dict):
                if not k in rules:
                    rules[k] = {}
                rulesdb.merge(rules[k], v)
            elif not k in rules:
                rules[k] = v
        return rules









# should be the other way round: regex() is meant to be a subset of update()
def update(fmt):
    fmt["regex"] = regex(fmt, update=True)

# assemble regex for format string







>
>
>
>
>
>
>







276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
            if isinstance(v, dict):
                if not k in rules:
                    rules[k] = {}
                rulesdb.merge(rules[k], v)
            elif not k in rules:
                rules[k] = v
        return rules

    #@staticmethod
    #def extract_all():
    #  for key,val in self.__dict__.items():
    #    if isinstance(val, dict):
    #      open("share/{key}.fmt", "w").write(json.dumps(val, indent=4))



# should be the other way round: regex() is meant to be a subset of update()
def update(fmt):
    fmt["regex"] = regex(fmt, update=True)

# assemble regex for format string
307
308
309
310
311
312
313
314
315

316
317
318
319

320

321
322
323
324



325
326
327
328
329
330
331
332
333
334
335
336
337
    if "rewrite" in rules:
        for rx, repl in rules["rewrite"].items():
            record = re.sub(rx, repl.replace(r'$1', r'\1'), record)

    # create fields from variant placeholders
    if "expand" in rules:
        for rx, expand in rules["expand"].items():
            for is_quoted, key, id, *extra in re.findall(f"(\"?)({rx})", record):
                if not key in fields:

                    opt = copy(expand)
                    #subs = ["", key, id, *extra] replace any $…
                                     # $…
                    if opt["id"].find('$1') >= 0:

                        opt["id"] = opt["id"].replace('$1', re.sub("\W+", "_", id).lower())

                    if not "rx" in opt and "class" in opt:
                                                               # replace any $…
                        opt["rx"] = regex({"class":opt["class"], "record":id})
                        pass



                    if "rx" in opt:
                        if not is_quoted and opt["rx"] == '[^"]*':
                            opt["rx"] = "\S*"
                        elif is_quoted and opt["rx"] == "\S+":
                            opt["rx"] = "(?:[^\"]*|\\\\\")+"
                    fields[key] = opt
                        
    # catch-all \S+ for completely unknown placeholders
    if "placeholder" in rules:
        for ph in re.findall(rules["placeholder"], record):
            if not ph in fields:
                id = re.sub("\W+", "", ph)
                fields[ph] = { "id": id, "rx": "\S+" }







|
|
>
|
<
|
|
>
|
>
|
<
|
|
>
>
>
|
|
|
|
|
|







322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337
338

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
    if "rewrite" in rules:
        for rx, repl in rules["rewrite"].items():
            record = re.sub(rx, repl.replace(r'$1', r'\1'), record)

    # create fields from variant placeholders
    if "expand" in rules:
        for rx, expand in rules["expand"].items():
            for is_quoted, match, *uu in re.findall(f"(\"?)({rx})", record):
                if match in fields:
                    continue
                x = copy(expand)

                # id: is usually "$1", but might be "prefix_$2" or something
                if x["id"].find('$') >= 0:
                    x["id"] = rx_sub(rx, x["id"], match)
                    x["id"] = re.sub("\W+", "", x["id"]).lower()
                # recurse into other pattern types
                if not "rx" in x and "class" in x:

                    x["rx"] = regex({
                        "class": x["class"],
                        "record": rx_sub(rx, x.get("record") or "$1", match)
                    })
                # handle generic placeholders / quoted strings, somewhat apache-specific
                if "rx" in x:
                    if not is_quoted and x["rx"] == '[^"]*':
                        x["rx"] = "\S*"
                    elif is_quoted and x["rx"] == "\S+":
                        x["rx"] = "(?:[^\"]*|\\\\\")+"
                fields[match] = x
                        
    # catch-all \S+ for completely unknown placeholders
    if "placeholder" in rules:
        for ph in re.findall(rules["placeholder"], record):
            if not ph in fields:
                id = re.sub("\W+", "", ph)
                fields[ph] = { "id": id, "rx": "\S+" }
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373





374
375
376
377
378
379
380
        else:
            rx = f"(?<{id}>{rx})"
        return rx
    rx = re.sub(rules["placeholder"], sub_placeholder, record)
    rx = rename_duplicates(rx)
    return rx


# (?<duplicate2>)
def rename_duplicates(rx):
    fields = []
    def count_ph(m):
        s, i = m.group(1), 1
        while s in fields:
            i += 1
            s = f"{m.group(1)}{i}"
        fields.append(s)
        return s
    return re.sub("(?<=\(\?\<)(\w+)(?=\>)", count_ph, rx)


# (?<name>) to (?P<name>)
def rx2re(rx):
    return re.sub("\(\?<(?=\w+>)", "(?P<", rx)
    







# file-style wrapper that yields parsed dictionaries instead of string lines
class parsy_parse:

    def __init__(self, logfn="", fmt=None, debug=False, fail=False, duplicate=True):
        """







<












<



|
>
>
>
>
>







368
369
370
371
372
373
374

375
376
377
378
379
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
        else:
            rx = f"(?<{id}>{rx})"
        return rx
    rx = re.sub(rules["placeholder"], sub_placeholder, record)
    rx = rename_duplicates(rx)
    return rx


# (?<duplicate2>)
def rename_duplicates(rx):
    fields = []
    def count_ph(m):
        s, i = m.group(1), 1
        while s in fields:
            i += 1
            s = f"{m.group(1)}{i}"
        fields.append(s)
        return s
    return re.sub("(?<=\(\?\<)(\w+)(?=\>)", count_ph, rx)


# (?<name>) to (?P<name>)
def rx2re(rx):
    return re.sub("\(\?<(?=\w+>)", "(?P<", rx)

# allow for $1, $2, $3 in re.sub()
def rx_sub(pattern, replacement, source, flags=0):
    if replacement.find('$') >= 0:
        replacement = re.sub(r'\(?=[0-9])', '$', replacement)
    return re.sub(pattern, replacement, source, flags)


# file-style wrapper that yields parsed dictionaries instead of string lines
class parsy_parse:

    def __init__(self, logfn="", fmt=None, debug=False, fail=False, duplicate=True):
        """
427
428
429
430
431
432
433




434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457


458
459
460

461


462
463
464
465
466

467


468
469

470
471
472
473
474
475
476
            self.debug_rx(line)
            if self.fail:
                raise StopIteration()
        elif self.fail:
            raise StopIteration()
        else:
            pass # jusst try next line





    # add [key "value"] fields
    def container_expand(self, d):
        for k,opt in self.container.items():
            if k in d:
                for id,val in re.findall(opt["rx"], d[k]):
                    if not id in d:
                        d[id] = val
                    elif not isinstance(d[id], "list"):
                        d[id] = [d[id], val]
                    else:
                        d[id].append(val)

    # ANSI output for debugging regex/fmt string
    def debug_rx(self, line):
        rx = self.rx.pattern
        line = line.rstrip()
        rx_cut = re.compile("[\s\"\[]*\(\?P<\w+>([^()]+|\([^()]+\))*\)[\"\]\s]*$")
        # iteratively strip (?...) capture groups
        while len(rx) and rx.find("(?P<") >= 0:
            fail = rx_cut.search(rx)
            if fail:
                fail = fail.group(0)
            else:


                fail = "<unknown-last-capture>"
                break
            rx = rx_cut.sub("", rx)

            try:


                if re.match(rx, line):
                    break # works now, so `fail` was the culprit
            except:
                # likely broke regex nesting, try removing next (?...)
                pass

        matched = re.match(rx, line).group(0)


        print("\033[42m" + matched + "\033[41m" + line[len(matched):] + "\033[40;0m")
        print("\033[36m" + "failed regex section: \033[1;33;41m" + fail + "\033[40;0m")


# alias
logopen = parsy_parse


#-- test
def test():







>
>
>
>

















|


|
<
|
|
>
>
|
<
|
>

>
>





>
|
>
>
|

>







449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480

481
482
483
484
485

486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
            self.debug_rx(line)
            if self.fail:
                raise StopIteration()
        elif self.fail:
            raise StopIteration()
        else:
            pass # jusst try next line
    
    # pass .close() and similar to file object
    def __getattr__(self, name):
        return getattr(self.f, name)

    # add [key "value"] fields
    def container_expand(self, d):
        for k,opt in self.container.items():
            if k in d:
                for id,val in re.findall(opt["rx"], d[k]):
                    if not id in d:
                        d[id] = val
                    elif not isinstance(d[id], "list"):
                        d[id] = [d[id], val]
                    else:
                        d[id].append(val)

    # ANSI output for debugging regex/fmt string
    def debug_rx(self, line):
        rx = self.rx.pattern
        line = line.rstrip()
        #rx_cut = re.compile("[^)]*  \(\?P<\w+>  ( [^()]+ | \([^()]+\) )+  \)  [^()]* \Z", re.X)
        # iteratively strip (?...) capture groups
        while len(rx) and rx.find("(?P<") >= 0:
            #fail = rx_cut.search(rx)

            #if fail: fail = fail.group(0)
            #else: fail = "<unknown-last-capture>"; break
            last = rx.rindex("(?P<")
            if last < 1:
                fail = "<unknown-last-capture>"; break

            fail = rx[last:]
            #print(f"testfail: `{fail}`")
            try:
                rx = rx[0:last]
                rx = re.sub("[^)]*$", "", rx)
                if re.match(rx, line):
                    break # works now, so `fail` was the culprit
            except:
                # likely broke regex nesting, try removing next (?...)
                pass
        try:
            matched = re.match(rx, line)
            matched = matched.group(0)
        except:
            matched = ""
        print("\033[36m" + "failed regex section: \033[1;33;41m" + fail + "\033[40;0m")
        print("\033[42m" + matched + "\033[41m" + line[len(matched):] + "\033[40;0m")

# alias
logopen = parsy_parse


#-- test
def test():

Added logfmt1/setup.py.







































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env python3
# encoding: utf-8
# api: pip
# type: build
# title: config for setuptools
#
# Notably the .deb will install as just dist-packages/logfmt1.py.
# Whereas the .whl creates a logfmt1/__init__.py wrapper and
# directory structure.
# - share/ files shouldn't really reside within the pkg.
#

from pluginconf.setup import setup


setup(
    fn="./logfmt1.py",
    long_description="@README.rst",
    package_dir={"logfmt1": "./"},
    packages=["logfmt1"],
    package_data={
        "logfmt1": [
           "./share/*",
           "./share/update/*"
        ],
    },
    #data_files=[],
    entry_points={
        "console_scripts": [
            "logex=logfmt1.logex",
            "update-logfmt=logfmt1.update_logfmt_all",
        ]
    }
)

Added logfmt1/share/apache.combined.fmt.















>
>
>
>
>
>
>
1
2
3
4
5
6
7
{
    "class": "apache combined",
    "record": "%h %l %u %t \"%r\" %>s %b",
    "glob": [
        "*.access.log"
    ]
}

Added logfmt1/share/apache.fmt.











































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
{
    "class": "apache generic",
    "separator": " ",
    "rewrite": {
        "%[\\d!,]+": "%",
        "(?<!\\\\)([\\[\\]])": "\\\\$1",
        "%%": "%"
    },
    "placeholder": "%[<>]?(?:\\w*\\{[^\\}]+\\})?\\^?\\w+",
    "fields": {
        "%a": {
            "id": "remote_addr",
            "rx": "[\\d.:a-f]+"
        },
        "%{c}a": {
            "id": "remote_addr",
            "rx": "[\\d.:a-f]+"
        },
        "%h": {
            "id": "remote_host",
            "rx": "[\\w\\-.:]+"
        },
        "%{c}h": {
            "id": "remote_host",
            "rx": "[\\w\\-.:]+"
        },
        "%A": {
            "id": "local_address",
            "rx": "[\\d.:a-f]+"
        },
        "%u": {
            "id": "remote_user",
            "rx": "[\\-\\w@.]+"
        },
        "%l": {
            "id": "remote_logname",
            "rx": "[\\w\\-.:]+"
        },
        "%t": {
            "id": "request_time",
            "rx": "\\[(\\d[\\d:\\w\\s:./\\-+,;]+)\\]"
        },
        "%{u}t": {
            "id": "request_time",
            "rx": "\\d+/\\w+/\\d+:\\d+:\\d+:\\d+\\.\\d+\\s\\+\\d+"
        },
        "%{cu}t": {
            "id": "request_time",
            "rx": "\\d+-\\w+-\\d+\\s\\d+:\\d+:\\d+\\.\\d+"
        },
        "%{msec_frac}t": {
            "id": "msec_frac",
            "rx": "[\\d.]+"
        },
        "%{usec_frac}t": {
            "id": "usec_frac",
            "rx": "[\\d.]+"
        },
        "%f": {
            "id": "request_file",
            "rx": "[^\\s\"]+"
        },
        "%b": {
            "id": "bytes_sent",
            "rx": "\\d+|-"
        },
        "%B": {
            "id": "bytes_sent",
            "rx": "\\d+|-"
        },
        "%O": {
            "id": "bytes_out",
            "rx": "\\d+"
        },
        "%I": {
            "id": "bytes_in",
            "rx": "\\d+"
        },
        "%S": {
            "id": "bytes_combined",
            "rx": "\\d+"
        },
        "%E": {
            "id": "apr_status",
            "rx": "\\w+"
        },
        "%M": {
            "id": "message",
            "rx": ".+"
        },
        "%L": {
            "id": "log_id",
            "rx": "[\\w\\-\\.]+"
        },
        "%{c}L": {
            "id": "log_id",
            "rx": "[\\w\\-\\.]+"
        },
        "%{C}L": {
            "id": "log_id",
            "rx": "[\\w\\-\\.]*"
        },
        "%V": {
            "id": "server_name",
            "rx": "[\\w\\-\\.]+"
        },
        "%v": {
            "id": "virtual_host",
            "rx": "[\\w\\-\\.]+"
        },
        "%p": {
            "id": "server_port",
            "rx": "\\d+"
        },
        "%{local}p": {
            "id": "server_port",
            "rx": "\\d+"
        },
        "%{canonical}p": {
            "id": "canonical_port",
            "rx": "[\\w.]+"
        },
        "%{remote}p": {
            "id": "remote_port",
            "rx": "\\d+"
        },
        "%P": {
            "id": "pid",
            "rx": "\\d+"
        },
        "%{g}T": {
            "id": "tid",
            "rx": "\\d+"
        },
        "%{tid}P": {
            "id": "tid",
            "rx": "\\d+"
        },
        "%{pid}P": {
            "id": "pid",
            "rx": "\\d+"
        },
        "%{hextid}P": {
            "id": "tid",
            "rx": "\\w+"
        },
        "%{hexpid}P": {
            "id": "pid",
            "rx": "\\w+"
        },
        "%H": {
            "id": "request_protocol",
            "rx": "[\\w/\\d.]+"
        },
        "%m": {
            "id": "request_method",
            "rx": "[\\w.]+"
        },
        "%q": {
            "id": "request_query",
            "rx": "\\??\\S*"
        },
        "%F": {
            "id": "file_line",
            "rx": "[/\\w\\-.:(\\d)]+"
        },
        "%X": {
            "id": "connection_status",
            "rx": "[Xx+\\-.\\d]+"
        },
        "%k": {
            "id": "keepalives",
            "rx": "\\d+"
        },
        "%r": {
            "id": "request_line",
            "rx": "(?<request_method>\\w+) (?<request_path>\\S+) (?<request_protocol>[\\w/\\d.]+)"
        },
        "%D": {
            "id": "request_duration_microseconds",
            "rx": "\\d+"
        },
        "%T": {
            "id": "request_duration_scaled",
            "rx": "[\\d.]+"
        },
        "%{s}T": {
            "id": "request_duration_seconds",
            "rx": "\\d+"
        },
        "%{us}T": {
            "id": "request_duration_microseconds",
            "rx": "\\d+"
        },
        "%{ms}T": {
            "id": "request_duration_milliseconds",
            "rx": "\\d+"
        },
        "%U": {
            "id": "request_uri",
            "rx": "\\S+(?<!\")"
        },
        "%s": {
            "id": "status",
            "rx": "\\d+"
        },
        "%>s": {
            "id": "status",
            "rx": "-|\\d\\d\\d"
        },
        "%R": {
            "id": "handler",
            "rx": "[\\w:.\\-]+"
        },
        "%^FU": {
            "id": "ttfu",
            "rx": "-|\\d+"
        },
        "%^FB": {
            "id": "ttfb",
            "rx": "-|\\d+"
        },
        "%^\u0134S": {
            "id": "json",
            "rx": "\\{(?:[\\w:,\\s\\[\\]]+|\"(?:[^\\\\\"]+|\\\\.)*\")\\}"
        },
        "%{Referer}i": {
            "id": "referer",
            "rx": "[^\"]*"
        },
        "%{User-Agent}i": {
            "id": "user_agent",
            "rx": "(?:[^\"]+|\\\\\")*"
        }
    },
    "alias": {
        "remote_address": "remote_addr",
        "ip": "remote_addr",
        "user": "remote_user",
        "file": "request_file",
        "size": "bytes_sent",
        "datetime": "request_time",
        "ctime": "request_time",
        "date": "request_time",
        "loglevel": "remote_logname",
        "module_name": "request_method",
        "request_flushed": "file_line",
        "requests_on_connection": "keepalives",
        "error": "apr_status"
    },
    "expand": {
        "%\\{([^{}]+)\\}t": {
            "id": "request_time",
            "class": "strftime"
        },
        "%[<>]?\\{([\\w\\-]+)\\}[Conexic]": {
            "id": "$1",
            "rx": "\\S+"
        },
        "%\\{([\\w\\-]+)\\}\\^t[io]": {
            "id": "$1",
            "rx": "\\S+"
        }
    },
    "container": {
        "message": {
            "id": "$1",
            "value": "$2",
            "rx": "\\[(\\w+) \"(.*?)\"\\]",
            "class": "apache mod_security"
        }
    },
    "glob": [
        "/var/log/apache*/*err*.log",
        "/var/log/apache*/*acc*.log"
    ]
}

Added logfmt1/share/grok.fmt.







































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
{
    "$license": "Apache-2.0",
    "$origin": "https://github.com/elastic/logstash/blob/v1.4.2/patterns/",
    "class": "grok",
    "separator": " ",
    "placeholder": "%\\{\\w+:([\\w.-]+)\\}",
    "rewrite": {},
    "alias": {},
    "fields": {
        "%{SYSLOGBASE}": {
            "rx": "(?<timestamp>\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 +(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9]) (?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9])) (?:<(?<facility>\b(?:[0-9]+)\b).(?<priority>\b(?:[0-9]+)\b)> )?(?<logsource>(?:\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)|(?:((([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}))|:)))(%.+)?|(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])))) (?<program>(?:[\\w._/%-]+))(?:\\[(?<pid>\b(?:[1-9][0-9]*)\b)\\])?:",
            "id": "syslogbase",
            "grok": "SYSLOGBASE"
        },
        "%{COMMONAPACHELOG}": {
            "rx": "(?<clientip>(?:\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)|(?:((([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}))|:)))(%.+)?|(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])))) (?<ident>[a-zA-Z0-9._-]+) (?<auth>[a-zA-Z0-9._-]+) \\[(?<timestamp>(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])/\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/(?>\\d\\d){1,2}:(?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9]) (?:[+-]?(?:[0-9]+)))\\] \"(?:(?<verb>\b\\w+\b) (?<request>\\S+)(?: HTTP/(?<httpversion>(?:(?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+))))))?|(?<rawrequest>.*?))\" (?<response>(?:(?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+))))) (?:(?<bytes>(?:(?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+)))))|-)",
            "id": "commonapachelog",
            "grok": "COMMONAPACHELOG"
        },
        "%{COMBINEDAPACHELOG}": {
            "rx": "(?<clientip>(?:\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)|(?:((([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}))|:)))(%.+)?|(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])))) (?<ident>[a-zA-Z0-9._-]+) (?<auth>[a-zA-Z0-9._-]+) \\[(?<timestamp>(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])/\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/(?>\\d\\d){1,2}:(?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9]) (?:[+-]?(?:[0-9]+)))\\] \"(?:(?<verb>\b\\w+\b) (?<request>\\S+)(?: HTTP/(?<httpversion>(?:(?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+))))))?|(?<rawrequest>.*?))\" (?<response>(?:(?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+))))) (?:(?<bytes>(?:(?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+)))))|-) (?<referrer>(?>(?<!\\)(?>\"(?>\\.|[^\\\"]+)+\"|\"\"|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``))) (?<agent>(?>(?<!\\)(?>\"(?>\\.|[^\\\"]+)+\"|\"\"|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``)))",
            "id": "combinedapachelog",
            "grok": "COMBINEDAPACHELOG"
        }
    },
    "expand": {
        "%\\{GROK:((?:[^{}]+|\\{[^{}]+\\})+)\\}": {
            "id": "",
            "class": "grok"
        },
        "%{USERNAME:([\\w.\\-]+)}": {
            "rx": "[a-zA-Z0-9._-]+",
            "id": "$1",
            "grok": "USERNAME"
        },
        "%{USER:([\\w.\\-]+)}": {
            "rx": "[a-zA-Z0-9._-]+",
            "id": "$1",
            "grok": "USER"
        },
        "%{INT:([\\w.\\-]+)}": {
            "rx": "(?:[+-]?(?:[0-9]+))",
            "id": "$1",
            "grok": "INT"
        },
        "%{BASE10NUM:([\\w.\\-]+)}": {
            "rx": "(?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+)))",
            "id": "$1",
            "grok": "BASE10NUM"
        },
        "%{NUMBER:([\\w.\\-]+)}": {
            "rx": "(?:(?<![0-9.+-])(?>[+-]?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+))))",
            "id": "$1",
            "grok": "NUMBER"
        },
        "%{BASE16NUM:([\\w.\\-]+)}": {
            "rx": "(?<![0-9A-Fa-f])(?:[+-]?(?:0x)?(?:[0-9A-Fa-f]+))",
            "id": "$1",
            "grok": "BASE16NUM"
        },
        "%{BASE16FLOAT:([\\w.\\-]+)}": {
            "rx": "\b(?<![0-9A-Fa-f.])(?:[+-]?(?:0x)?(?:(?:[0-9A-Fa-f]+(?:\\.[0-9A-Fa-f]*)?)|(?:\\.[0-9A-Fa-f]+)))\b",
            "id": "$1",
            "grok": "BASE16FLOAT"
        },
        "%{POSINT:([\\w.\\-]+)}": {
            "rx": "\b(?:[1-9][0-9]*)\b",
            "id": "$1",
            "grok": "POSINT"
        },
        "%{NONNEGINT:([\\w.\\-]+)}": {
            "rx": "\b(?:[0-9]+)\b",
            "id": "$1",
            "grok": "NONNEGINT"
        },
        "%{WORD:([\\w.\\-]+)}": {
            "rx": "\b\\w+\b",
            "id": "$1",
            "grok": "WORD"
        },
        "%{NOTSPACE:([\\w.\\-]+)}": {
            "rx": "\\S+",
            "id": "$1",
            "grok": "NOTSPACE"
        },
        "%{SPACE:([\\w.\\-]+)}": {
            "rx": "\\s*",
            "id": "$1",
            "grok": "SPACE"
        },
        "%{DATA:([\\w.\\-]+)}": {
            "rx": ".*?",
            "id": "$1",
            "grok": "DATA"
        },
        "%{GREEDYDATA:([\\w.\\-]+)}": {
            "rx": ".*",
            "id": "$1",
            "grok": "GREEDYDATA"
        },
        "%{QUOTEDSTRING:([\\w.\\-]+)}": {
            "rx": "(?>(?<!\\)(?>\"(?>\\.|[^\\\"]+)+\"|\"\"|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``))",
            "id": "$1",
            "grok": "QUOTEDSTRING"
        },
        "%{UUID:([\\w.\\-]+)}": {
            "rx": "[A-Fa-f0-9]{8}-(?:[A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}",
            "id": "$1",
            "grok": "UUID"
        },
        "%{MAC:([\\w.\\-]+)}": {
            "rx": "(?:(?:(?:[A-Fa-f0-9]{4}\\.){2}[A-Fa-f0-9]{4})|(?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})|(?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}))",
            "id": "$1",
            "grok": "MAC"
        },
        "%{CISCOMAC:([\\w.\\-]+)}": {
            "rx": "(?:(?:[A-Fa-f0-9]{4}\\.){2}[A-Fa-f0-9]{4})",
            "id": "$1",
            "grok": "CISCOMAC"
        },
        "%{WINDOWSMAC:([\\w.\\-]+)}": {
            "rx": "(?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})",
            "id": "$1",
            "grok": "WINDOWSMAC"
        },
        "%{COMMONMAC:([\\w.\\-]+)}": {
            "rx": "(?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})",
            "id": "$1",
            "grok": "COMMONMAC"
        },
        "%{IPV6:([\\w.\\-]+)}": {
            "rx": "((([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}))|:)))(%.+)?",
            "id": "$1",
            "grok": "IPV6"
        },
        "%{IPV4:([\\w.\\-]+)}": {
            "rx": "(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])",
            "id": "$1",
            "grok": "IPV4"
        },
        "%{IP:([\\w.\\-]+)}": {
            "rx": "(?:((([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}))|:)))(%.+)?|(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9]))",
            "id": "$1",
            "grok": "IP"
        },
        "%{HOSTNAME:([\\w.\\-]+)}": {
            "rx": "\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)",
            "id": "$1",
            "grok": "HOSTNAME"
        },
        "%{HOST:([\\w.\\-]+)}": {
            "rx": "\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)",
            "id": "$1",
            "grok": "HOST"
        },
        "%{IPORHOST:([\\w.\\-]+)}": {
            "rx": "(?:\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)|(?:((([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}))|:)))(%.+)?|(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])))",
            "id": "$1",
            "grok": "IPORHOST"
        },
        "%{HOSTPORT:([\\w.\\-]+)}": {
            "rx": "(?:\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)|(?:((([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}))|:)))(%.+)?|(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9]))):\b(?:[1-9][0-9]*)\b",
            "id": "$1",
            "grok": "HOSTPORT"
        },
        "%{PATH:([\\w.\\-]+)}": {
            "rx": "(?:(?>/(?>[\\w_%!$@:.,-]+|\\.)*)+|(?>[A-Za-z]+:|\\)(?:\\[^\\?*]*)+)",
            "id": "$1",
            "grok": "PATH"
        },
        "%{UNIXPATH:([\\w.\\-]+)}": {
            "rx": "(?>/(?>[\\w_%!$@:.,-]+|\\.)*)+",
            "id": "$1",
            "grok": "UNIXPATH"
        },
        "%{TTY:([\\w.\\-]+)}": {
            "rx": "(?:/dev/(pts|tty([pq])?)(\\w+)?/?(?:[0-9]+))",
            "id": "$1",
            "grok": "TTY"
        },
        "%{WINPATH:([\\w.\\-]+)}": {
            "rx": "(?>[A-Za-z]+:|\\)(?:\\[^\\?*]*)+",
            "id": "$1",
            "grok": "WINPATH"
        },
        "%{URIPROTO:([\\w.\\-]+)}": {
            "rx": "[A-Za-z]+(\\+[A-Za-z+]+)?",
            "id": "$1",
            "grok": "URIPROTO"
        },
        "%{URIHOST:([\\w.\\-]+)}": {
            "rx": "(?:\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)|(?:((([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}))|:)))(%.+)?|(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])))(?::(?<port>\b(?:[1-9][0-9]*)\b))?",
            "id": "$1",
            "grok": "URIHOST"
        },
        "%{URIPATH:([\\w.\\-]+)}": {
            "rx": "(?:/[A-Za-z0-9$.+!*'(){},~:;=@#%_\\-]*)+",
            "id": "$1",
            "grok": "URIPATH"
        },
        "%{URIPARAM:([\\w.\\-]+)}": {
            "rx": "\\?[A-Za-z0-9$.+!*'|(){},~@#%&/=:;_?\\-\\[\\]]*",
            "id": "$1",
            "grok": "URIPARAM"
        },
        "%{URIPATHPARAM:([\\w.\\-]+)}": {
            "rx": "(?:/[A-Za-z0-9$.+!*'(){},~:;=@#%_\\-]*)+(?:\\?[A-Za-z0-9$.+!*'|(){},~@#%&/=:;_?\\-\\[\\]]*)?",
            "id": "$1",
            "grok": "URIPATHPARAM"
        },
        "%{URI:([\\w.\\-]+)}": {
            "rx": "[A-Za-z]+(\\+[A-Za-z+]+)?://(?:[a-zA-Z0-9._-]+(?::[^@]*)?@)?(?:(?:\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)|(?:((([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}))|:)))(%.+)?|(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])))(?::(?<port>\b(?:[1-9][0-9]*)\b))?)?(?:(?:/[A-Za-z0-9$.+!*'(){},~:;=@#%_\\-]*)+(?:\\?[A-Za-z0-9$.+!*'|(){},~@#%&/=:;_?\\-\\[\\]]*)?)?",
            "id": "$1",
            "grok": "URI"
        },
        "%{MONTH:([\\w.\\-]+)}": {
            "rx": "\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",
            "id": "$1",
            "grok": "MONTH"
        },
        "%{MONTHNUM:([\\w.\\-]+)}": {
            "rx": "(?:0?[1-9]|1[0-2])",
            "id": "$1",
            "grok": "MONTHNUM"
        },
        "%{MONTHNUM2:([\\w.\\-]+)}": {
            "rx": "(?:0[1-9]|1[0-2])",
            "id": "$1",
            "grok": "MONTHNUM2"
        },
        "%{MONTHDAY:([\\w.\\-]+)}": {
            "rx": "(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])",
            "id": "$1",
            "grok": "MONTHDAY"
        },
        "%{DAY:([\\w.\\-]+)}": {
            "rx": "(?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)",
            "id": "$1",
            "grok": "DAY"
        },
        "%{YEAR:([\\w.\\-]+)}": {
            "rx": "(?>\\d\\d){1,2}",
            "id": "$1",
            "grok": "YEAR"
        },
        "%{HOUR:([\\w.\\-]+)}": {
            "rx": "(?:2[0123]|[01]?[0-9])",
            "id": "$1",
            "grok": "HOUR"
        },
        "%{MINUTE:([\\w.\\-]+)}": {
            "rx": "(?:[0-5][0-9])",
            "id": "$1",
            "grok": "MINUTE"
        },
        "%{SECOND:([\\w.\\-]+)}": {
            "rx": "(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?)",
            "id": "$1",
            "grok": "SECOND"
        },
        "%{TIME:([\\w.\\-]+)}": {
            "rx": "(?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9])",
            "id": "$1",
            "grok": "TIME"
        },
        "%{DATE_US:([\\w.\\-]+)}": {
            "rx": "(?:0?[1-9]|1[0-2])[/-](?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])[/-](?>\\d\\d){1,2}",
            "id": "$1",
            "grok": "DATE_US"
        },
        "%{DATE_EU:([\\w.\\-]+)}": {
            "rx": "(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])[./-](?:0?[1-9]|1[0-2])[./-](?>\\d\\d){1,2}",
            "id": "$1",
            "grok": "DATE_EU"
        },
        "%{ISO8601_TIMEZONE:([\\w.\\-]+)}": {
            "rx": "(?:Z|[+-](?:2[0123]|[01]?[0-9])(?::?(?:[0-5][0-9])))",
            "id": "$1",
            "grok": "ISO8601_TIMEZONE"
        },
        "%{ISO8601_SECOND:([\\w.\\-]+)}": {
            "rx": "(?:(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?)|60)",
            "id": "$1",
            "grok": "ISO8601_SECOND"
        },
        "%{TIMESTAMP_ISO8601:([\\w.\\-]+)}": {
            "rx": "(?>\\d\\d){1,2}-(?:0?[1-9]|1[0-2])-(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])[T ](?:2[0123]|[01]?[0-9]):?(?:[0-5][0-9])(?::?(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))?(?:Z|[+-](?:2[0123]|[01]?[0-9])(?::?(?:[0-5][0-9])))?",
            "id": "$1",
            "grok": "TIMESTAMP_ISO8601"
        },
        "%{DATE:([\\w.\\-]+)}": {
            "rx": "(?:0?[1-9]|1[0-2])[/-](?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])[/-](?>\\d\\d){1,2}|(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])[./-](?:0?[1-9]|1[0-2])[./-](?>\\d\\d){1,2}",
            "id": "$1",
            "grok": "DATE"
        },
        "%{DATESTAMP:([\\w.\\-]+)}": {
            "rx": "(?:0?[1-9]|1[0-2])[/-](?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])[/-](?>\\d\\d){1,2}|(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])[./-](?:0?[1-9]|1[0-2])[./-](?>\\d\\d){1,2}[- ](?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9])",
            "id": "$1",
            "grok": "DATESTAMP"
        },
        "%{TZ:([\\w.\\-]+)}": {
            "rx": "(?:[PMCE][SD]T|UTC)",
            "id": "$1",
            "grok": "TZ"
        },
        "%{DATESTAMP_RFC822:([\\w.\\-]+)}": {
            "rx": "(?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?) \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 (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9]) (?>\\d\\d){1,2} (?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9]) (?:[PMCE][SD]T|UTC)",
            "id": "$1",
            "grok": "DATESTAMP_RFC822"
        },
        "%{DATESTAMP_RFC2822:([\\w.\\-]+)}": {
            "rx": "(?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?), (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9]) \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 (?>\\d\\d){1,2} (?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9]) (?:Z|[+-](?:2[0123]|[01]?[0-9])(?::?(?:[0-5][0-9])))",
            "id": "$1",
            "grok": "DATESTAMP_RFC2822"
        },
        "%{DATESTAMP_OTHER:([\\w.\\-]+)}": {
            "rx": "(?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?) \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 (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9]) (?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9]) (?:[PMCE][SD]T|UTC) (?>\\d\\d){1,2}",
            "id": "$1",
            "grok": "DATESTAMP_OTHER"
        },
        "%{DATESTAMP_EVENTLOG:([\\w.\\-]+)}": {
            "rx": "(?>\\d\\d){1,2}(?:0[1-9]|1[0-2])(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])(?:2[0123]|[01]?[0-9])(?:[0-5][0-9])(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?)",
            "id": "$1",
            "grok": "DATESTAMP_EVENTLOG"
        },
        "%{SYSLOGTIMESTAMP:([\\w.\\-]+)}": {
            "rx": "\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 +(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9]) (?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9])",
            "id": "$1",
            "grok": "SYSLOGTIMESTAMP"
        },
        "%{PROG:([\\w.\\-]+)}": {
            "rx": "(?:[\\w._/%-]+)",
            "id": "$1",
            "grok": "PROG"
        },
        "%{SYSLOGPROG:([\\w.\\-]+)}": {
            "rx": "(?<program>(?:[\\w._/%-]+))(?:\\[(?<pid>\b(?:[1-9][0-9]*)\b)\\])?",
            "id": "$1",
            "grok": "SYSLOGPROG"
        },
        "%{SYSLOGHOST:([\\w.\\-]+)}": {
            "rx": "(?:\b(?:[0-9A-Za-z][0-9A-Za-z-]{0,62})(?:\\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\\.?|\b)|(?:((([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}))|:)))(%.+)?|(?<![0-9])(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))(?![0-9])))",
            "id": "$1",
            "grok": "SYSLOGHOST"
        },
        "%{SYSLOGFACILITY:([\\w.\\-]+)}": {
            "rx": "<(?<facility>\b(?:[0-9]+)\b).(?<priority>\b(?:[0-9]+)\b)>",
            "id": "$1",
            "grok": "SYSLOGFACILITY"
        },
        "%{HTTPDATE:([\\w.\\-]+)}": {
            "rx": "(?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])/\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/(?>\\d\\d){1,2}:(?!<[0-9])(?:2[0123]|[01]?[0-9]):(?:[0-5][0-9])(?::(?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?))(?![0-9]) (?:[+-]?(?:[0-9]+))",
            "id": "$1",
            "grok": "HTTPDATE"
        },
        "%{QS:([\\w.\\-]+)}": {
            "rx": "(?>(?<!\\)(?>\"(?>\\.|[^\\\"]+)+\"|\"\"|(?>'(?>\\.|[^\\']+)+')|''|(?>`(?>\\.|[^\\`]+)+`)|``))",
            "id": "$1",
            "grok": "QS"
        },
        "%{LOGLEVEL:([\\w.\\-]+)}": {
            "rx": "([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)?)",
            "id": "$1",
            "grok": "LOGLEVEL"
        }
    },
    "container": {},
    "glob": [
        "*.grok"
    ]
}

Added logfmt1/share/strftime.fmt.









































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
{
    "class": "strftime",
    "placeholder": "%\\w",
    "rewrite": {
        "%[EO_^0#\\-]+(\\w)": "%$1"
    },
    "fields": {
        "%a": {
            "id": "tm_wday",
            "rx": "\\w+"
        },
        "%A": {
            "id": "tm_wday",
            "rx": "\\w+"
        },
        "%b": {
            "id": "tm_mon",
            "rx": "\\w+"
        },
        "%B": {
            "id": "tm_mon",
            "rx": "\\w+"
        },
        "%c": {
            "id": "tm_dt",
            "rx": "[-:/.\\w\\d]+"
        },
        "%C": {
            "id": "tm_cent",
            "rx": "\\d\\d"
        },
        "%d": {
            "id": "tm_mday",
            "rx": "\\d\\d"
        },
        "%D": {
            "id": "tm_mdy",
            "rx": "\\d+/\\d+/\\d+"
        },
        "%e": {
            "id": "tm_mday",
            "rx": "[\\d\\s]\\d"
        },
        "%F": {
            "id": "tm_date",
            "rx": "\\d\\d\\d\\d-\\d\\d-\\d\\d"
        },
        "%G": {
            "id": "tm_wyear",
            "rx": "\\d\\d\\d\\d"
        },
        "%g": {
            "id": "tm_wyearnc",
            "rx": "\\d\\d"
        },
        "%h": {
            "id": "tm_mon",
            "rx": "\\w+"
        },
        "%H": {
            "id": "tm_hour",
            "rx": "\\d\\d"
        },
        "%I": {
            "id": "tm_hour",
            "rx": "\\d\\d"
        },
        "%j": {
            "id": "tm_yday",
            "rx": "\\d\\d\\d"
        },
        "%k": {
            "id": "tm_hour",
            "rx": "\\d\\d"
        },
        "%l": {
            "id": "tm_hour",
            "rx": "[\\d\\s]\\d"
        },
        "%m": {
            "id": "tm_mon",
            "rx": "\\d\\d"
        },
        "%M": {
            "id": "tm_min",
            "rx": "\\d\\d"
        },
        "%n": {
            "id": "newline",
            "rx": "\\n"
        },
        "%p": {
            "id": "tm_ampm",
            "rx": "AM|PM"
        },
        "%P": {
            "id": "tm_ampm",
            "rx": "am|pm"
        },
        "%r": {
            "id": "tm_time",
            "rx": "\\d\\d:\\d\\d:\\d\\d [AMPM]{2}"
        },
        "%R": {
            "id": "tm_time",
            "rx": "\\d\\d:\\d\\d"
        },
        "%s": {
            "id": "tm_epoch",
            "rx": "\\d+"
        },
        "%S": {
            "id": "tm_sec",
            "rx": "\\d\\d"
        },
        "%t": {
            "id": "tab",
            "rx": "\\t"
        },
        "%T": {
            "id": "tm_time",
            "rx": "\\d\\d:\\d\\d:\\d\\d"
        },
        "%u": {
            "id": "tm_wday",
            "rx": "[1-7]"
        },
        "%U": {
            "id": "tm_yday",
            "rx": "[0-5]\\d|5[0123]"
        },
        "%V": {
            "id": "tm_yday",
            "rx": "\\d\\d"
        },
        "%w": {
            "id": "tm_wday",
            "rx": "[0-6]"
        },
        "%W": {
            "id": "tm_yday",
            "rx": "\\d\\d"
        },
        "%x": {
            "id": "tm_ldate",
            "rx": "[-./\\d]+"
        },
        "%X": {
            "id": "tm_ltime",
            "rx": "[:.\\d]+"
        },
        "%y": {
            "id": "tm_year",
            "rx": "\\d\\d"
        },
        "%Y": {
            "id": "tm_year",
            "rx": "\\d\\d\\d\\d"
        },
        "%z": {
            "id": "tm_tz",
            "rx": "[-+]\\d\\d\\d\\d"
        },
        "%Z": {
            "id": "tm_tz",
            "rx": "\\w+"
        },
        "%+": {
            "id": "tm_date",
            "rx": "[-/:. \\w\\d]+"
        },
        "%%": {
            "id": "percent",
            "rx": "%"
        }
    },
    "expand": {
        "%(\\w)": "[\\w\\d.]+"
    }
}

Added logfmt1/share/update/apache.

















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#!/usr/bin/env python3


import os, re, sys, random
import subprocess
import traceback
import json
from pprint import pprint
try:
    import logfmt1
except:
    from modseccfg import logfmt1


# extraction patterns
class rx:
    # a conf file '(*) /etc/apache2/main.conf'
    dump_includes = re.compile("^\s*\([\d*]+\)\s+(.+)$", re.M)
    # directives we care about (to detect relevant .conf files)
    interesting = re.compile("""
        ^ \s*
         ( (Error|Custom|Global|Forensic|Transfer)Log | (Error)?LogFormat )           # log directivess
        """,
        re.M|re.I|re.X
    )
    # extract directive line including line continuations (<\><NL>)
    configline = re.compile(
        """ ^
        [\ \\t]*                          # whitespace \h*
        # (?:Use \s{1,4})?                # optional: `Use␣` to find custom macros like `Use SecRuleRemoveByPath…`
        (
          \w+ |                           # alphanumeric directive 
          </?(?:File|Loc|Dir|If\\b)\w*    # or <Wrap> section
        )\\b
          [\ \\t]*                        # whitespace \h+
        (
          (?: [^\\n\\\\]+ | [\\\\]. )*    # literals, or backslash + anything
        )
        (?: $ | >.*$ )                    # account for line end or closing >
        """,
        re.M|re.S|re.X
    )
    # to strip <\><NL>
    escnewline = re.compile(
        """[\\\\][\\n]\s*"""              # escaped linkebreaks
    )
    # handle quoted/unquoted directive arguments (not entirely sure if Apache does \" escaped quotes within)
    split_args = re.compile(
        """
        (?:\s+)   |                       # skip whitespace (\K not supported in python re, so removing empty matches in postprocessing)
        \#.*$  |                          # skip trailing comment (which isn't technically allowed, but)
        " ((?:[^\\\\"]+|\\\\ .)+) "  |    # quoted arguments
        (?!\#) ([^"\s]+)                  # plain arguments (no quotes, no spaces)
        """,
        re.X
    ) 
    # envvars 
    shell_vars = re.compile(
    """
        ^\s* (?:export\s+)?  ([A-Z_]+)  =  ["']?  ([\w/\-.]+)  ["']?
    """, re.M|re.X)  #"
    

# temporary state variables
class tmp:

    env = {
        "APACHE_LOG_DIR": "/var/log/apache2"  #/var/log/httpd/
    }
    env_locations = [
        "/etc/apache2/envvars", "/etc/default/httpd"
    ]
    
    log_formats = {
        "error": "[%t] [%l] [pid %P] %F: %E: [client %a] %M",
        #"default": "%h %l %u %t "%r" %>s %b",
        "common": '%h %l %u %t "%r" %>s %O',
        "forensic": '+%{forensic-id}n|%r|Host:%H|%{UA}|%{H*}\n-%{forensic-id}n',
        #%t == [%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]
    }
    log_map = {
        #"../fn.log": "combined"
    }


# encapsulate properties of config file (either vhosts, SecCfg*, or secrule collections)
class vhost:
    """
        Represents a config/vhost or mod_security rules file.
        
        Parameters
        ----------
        fn : str
            *.conf filename
        src : str
            config file source
        
    """

    # split *.conf directives, dispatch onto assignment/extract methods
    def __init__(self, fn, src, cfg_only=False):
        self.logs = []
        self.extract(src, cfg_only=cfg_only)

    # extract directive lines
    def extract(self, src, cfg_only=False):
        for dir,args  in rx.configline.findall(src):    # or .finditer()? to record positions right away?
            dir = dir.lower()
            #log.debug(dir, args)
            if hasattr(self, dir):
                if cfg_only: #→ if run from SecOptions dialog, we don't actually want rules collected
                    continue
                func = getattr(self, dir)
                func(self.split_args(args))

    # strip \\ \n line continuations, split all "args"
    def split_args(self, args):
        args = re.sub(rx.escnewline, " ", args)
        args = rx.split_args.findall(args)
        args = [s[1] or s[0] for s in args]
        args = [s for s in args if len(s)]
        #args = [s.decode("unicode_escape") for s in args]   # don't strip backslashes
        return args
    # apply ${ENV} vars
    def var_sub(self, s):
        return re.sub('\$\{(\w+)\}', lambda m: tmp.env.get(m.group(1), ""), s)

    # apache: log directives
    def customlog(self, args):
        fn, ty = self.var_sub(args[0]), args[1]
        self.logs.append(fn)
        if ty.find("%") >= 0:  # turn literal placeholder format into temporary name
            ty, fmt = hex(hash(ty))[4:], ty
            self.logformat(ty, fmt)
        tmp.log_map[fn] = ty
    def errorlog(self, args):
        self.customlog([args[0], "error"])
    def forensiclog(self, args):
        self.customlog([args[0], "forensic"])
    def globallog(self, args):
        self.customlog([args[0], args[1] or "combined"])
    def transferlog(self, args):
        self.customlog([args[0], "transfer"])
    def logformat(self, args):
        if len(args) == 1: args[1] = "transfer"
        tmp.log_formats[args[1]] = args[0].replace('\\"', '"')
    def errorlogformat(self, args):
        self.logformat([args[0], "error"])


# scan for APACHE_ENV= vars
def read_env_vars():
    for fn in tmp.env_locations:
        if os.path.exists(fn):
            src = open(fn, "r", encoding="utf-8").read()
            tmp.env.update(
                dict(rx.shell_vars.findall(src))
            )

# iterate over all Apache config files, visit relevant ones (vhosts/mod_security configs)
def scan_all():
    read_env_vars()
    ls = apache_dump_includes()
    for i, fn in enumerate(ls):
        src = open(fn, "r", encoding="utf-8").read()
        if rx.interesting.search(src):
            vhost(fn, src)

# get *.conf list from apache2ctl
def apache_dump_includes():
    cmd = ["apache2ctl", "-t", "-D", "DUMP_INCLUDES"]
    stdout = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout
    return rx.dump_includes.findall(stdout.read().decode("utf-8"))



# traverse log files, create .fmt descriptor with current format string
def mk_fmt():

    for fn,ty in tmp.log_map.items():

        fn_fmt = f"{fn}.fmt"
        fmt_record = tmp.log_formats.get(ty)
        if not fmt_record:
            continue
        
        j = {}
        if os.path.exists(fn_fmt):
            try:
                j = json.loads(open(fn_fmt, "r", encoding="utf-8").read())
            except Exception as e:
                j = {}
                print(f"WARN: {fn_fmt} contained invalid json: {str(e)}")
        if not "class" in j:
            j["class"] = f"apache {ty}"
        if not "record" in j or j["record"] != fmt_record:
            j["record"] = fmt_record
            
        # add descriptors for known placeholders
        if not "fields" in j or True:
            j["regex"] = logfmt1.regex(j)

        print(f"→ {fn_fmt}")
        try:
            f = open(fn_fmt, "w")
            f.write(json.dumps(j, indent=4))
            f.close()
        except Exception as e:
            print("ERR: " + str(e))



scan_all()
mk_fmt()


Added logfmt1/update-logfmt.





>
>
1
2
#!/bin/sh
run-parts /usr/share/logfmt/update/

Added logfmt1/update_logfmt_all.py.





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python3
# encoding: utf-8
# title: update-logfmt
# description: invoke ./share/update/* scripts
# type: virtual
#
# Stub that reimplements run-parts

import os, re

for dir in [re.sub("[.\w]+$", "share/update", __file__), "/usr/share/logfmt/update"]:
    if os.path.exists(dir):
        os.system(f"run-parts {dir}")
        break