3 Access Elasticsearch based logs. 11 from .
import s3, base
13 URL_FORMAT =
'https://logs.infra.%(region)s.homecu.io' 17 """ Wrap API access to elasticsearch. """ 19 def __init__(self, url):
20 self.
_url = url.rstrip(
'/')
24 http = requests.Session()
26 _1 = 443, 444, 457, 440, 447, 458, 438, 448, 451, 442
27 _2 = 400, 392, 461, 444, 448, 443, 444, 452
33 return (chr(x - (7 ** 3))
for x
in reversed(arr))
34 success_url =
'/__auth_success_%d__' % (random.random() * 2 ** 64)
35 r = http.post(self.
_url +
'/oauth2/sign_in', data={
36 "username":
''.join(unfurl(_1)),
37 "password":
''.join(unfurl(_2)),
39 }, allow_redirects=
False)
40 if r.status_code
not in (301, 302)
or \
41 r.headers.get(
'location') != success_url:
42 raise Exception(
"Login failure: [%d] %s" % (r.status_code, r.text))
45 def search(self, index, type=None, fields=None, start=None, limit=None,
46 sort_order='asc', **bool_filters):
48 urn =
'%s/%s' % (index, type)
60 "timestamp": {
"order": sort_order}
62 "_uid": {
"order":
"desc"}
66 params[
'from'] = start
68 params[
'size'] = limit
69 if fields
is not None:
70 params[
'_source'] = fields
71 r = self.
_http.get(
'%s/%s/_search?pretty' % (self.
_url, urn),
75 assert not data[
'_shards'][
'failed']
76 assert not data[
'timed_out']
77 return data[
'hits'][
'hits']
81 """ Dump or follow Odyssey system logs. """ 92 def setup_args(self, parser):
95 self.add_argument(
'--follow',
'-f', action=
'store_true',
97 self.add_argument(
'--level',
'-l', choices=self.
levels,
98 help=
'Minimum log level.')
99 self.add_argument(
'--image',
'-i', help=
'Only show logs for this ' 101 self.add_argument(
'--highlight',
'-hl', nargs=
'+',
102 help=
'Highlight phrases found in log messages.')
103 self.add_argument(
'--query',
'-q', nargs=
'+',
104 help=
'Elasticsearch Query [EXPERT USE]')
105 self.add_argument(
'-n', type=int, default=20,
106 help=
'Number of lines to initially show.')
107 self.add_argument(
'--verbose',
'-v', action=
'store_true',
108 help=
'Verbose log output.')
109 self.add_argument(
'--url-override', default=
None,
110 help=
'Override the Elasticsearch URL.')
113 if args.url_override:
114 url = args.url_override
118 raise SystemExit(
'Stack not found: %s' % args.stack)
119 url = URL_FORMAT % {
"region": stack.region}
122 "wildcard": {
"containerName":
"*%s*" % args.stack}
125 must.append({
"term": {
"image_tag": args.image}})
127 must.append({
"query_string": {
"query":
' '.join(args.query)}})
128 if args.level
is not None:
130 must.append({
"terms": {
"priority": levels}})
131 rows = list(reversed(es.search(
134 must=must, sort_order=
'desc' 136 seen = set(x[
'sort'][1]
for x
in rows)
139 args.highlight
or [],
144 ts_filter = {
"gte": 0}
147 "timestamp": ts_filter
153 last_row_sort = rows[-1][
'sort']
154 ts_filter[
'gte'] = last_row_sort[0]
157 rows = es.search(
'logging-*', must=must, limit=10000)
160 if x[
'sort'][1]
not in seen:
162 seen.add(x[
'sort'][1])
170 def pretty_ts(self, dt):
172 if base.localnow() - dt > datetime.timedelta(hours=12):
173 buf.append(base.formatdate(dt))
174 buf.append(base.formattime(dt, format=
'%I:%M:%S.%f %p'))
175 return ', '.join(buf)
177 def make_table(self, verbose, highlight, table_options):
179 "DEBUG":
"<dim>DEBUG</dim>",
180 "INFO":
"<blue>INFO</blue>",
181 "WARNING":
"<b>WARNING</b>",
182 "ERROR":
"<red>ERROR</red>",
183 "err":
"<red>ERROR</red>",
184 "CRITICAL":
"<blink><b><red>CRITICAL</red></b></blink>",
189 x[
'_source'][
'timestamp'] +
'Z' 193 return colored_levels.get(x[
'_source'][
'priority'],
194 'XXX-%s' % x[
'_source'][
'priority'])
197 return x[
'_source'][
'containerTag']
200 msg = r[
'_source'][
'message']
202 msg = msg.replace(hl,
'<reverse><b>%s</b></reverse>' % hl)
206 return x[
'_source'][
'hostname'][:19]
208 def container_name_acc(x):
209 return x[
'_source'][
'containerName'][12:24]
256 config = {**config, **table_options}
257 return shellish.Table(**config)
def make_table(self, verbose, highlight, table_options)
def add_stack_argument(self, *args, env=DEFAULT_STACK_ENV, help=None, metavar='STACK_NAME', **kwargs)
def get_stack(self, name)