Class: LogsCmd

Inherits:
Object
  • Object
show all
Includes:
MrMurano::Verbose
Defined in:
lib/MrMurano/commands/logs.rb

Overview

Because Ruby 2.0 does not support quoted keys, e.g., { ‘$eq’: ‘value’ }. rubocop:disable Style/HashSyntax

Constant Summary collapse

LOG_EMITTER_TYPES =
i[
  script
  call
  event
  config
  service
].freeze
LOG_SEVERITIES =
i[
  emergency
  alert
  critical
  error
  warning
  notice
  informational
  debug
].freeze
EXCLUDE_INDICATOR =

(lb): Ideally, we’d use /- and not /:, but rb-commander (or is it OptionParser?) double-parses things that look like switches. E.g., ‘murano logs –types -call` would set options.types to [“-call”] but would also set options.config to “all”. Just one more reason I do not think rb-commander should call itself a “complete solution”. (Note also we cannot use ’!‘ instead of ’-‘, because Bash.) Another option would be to use the “no-” option, e.g., “–[no-]types”, but then what do you do with the sindle character ’-T’ option?

':'
INCLUDE_INDICATOR =
'+'

Constants included from MrMurano::Verbose

MrMurano::Verbose::TABULARIZE_DATA_FORMAT_ERROR

Instance Method Summary collapse

Methods included from MrMurano::Verbose

ask_yes_no, #ask_yes_no, #assert, assert, cmd_confirm_delete!, #cmd_confirm_delete!, debug, #debug, dump_file_json, dump_file_plain, dump_file_yaml, #dump_output_file, #error, error, #error_file_format!, fancy_ticks, #fancy_ticks, #load_file_json, #load_file_plain, #load_file_yaml, #load_input_file, outf, #outf, #outformat_engine, #pluralize?, pluralize?, #prepare_hash_csv, #read_hashf!, #tabularize, tabularize, verbose, #verbose, warning, #warning, #whirly_interject, whirly_interject, #whirly_linger, whirly_linger, #whirly_msg, whirly_msg, #whirly_pause, whirly_pause, #whirly_start, whirly_start, #whirly_stop, whirly_stop, #whirly_unpause, whirly_unpause

Constructor Details

#initializeLogsCmd

Returns a new instance of LogsCmd.



55
56
57
58
59
60
# File 'lib/MrMurano/commands/logs.rb', line 55

def initialize
  @filter_severity = []
  @filter_types = []
  @filter_events = []
  @filter_endpoints = []
end

Instance Method Details

#assemble_in_or_nin_query(query_parts, field, terms, &block) ⇒ Object



525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/MrMurano/commands/logs.rb', line 525

def assemble_in_or_nin_query(query_parts, field, terms, &block)
  return if terms.empty?
  exclude = term_indicates_exclude?(terms[0])
  resolved_terms = []
  terms.each do |term|
    process_query_term(term, resolved_terms, exclude, field, terms, &block)
  end
  return if resolved_terms.empty?
  if !exclude
    operator = '$in'
  else
    operator = '$nin'
  end
  query_parts[field] = { operator.to_s => resolved_terms }
end

#assemble_queryObject



418
419
420
421
422
423
424
425
426
427
428
# File 'lib/MrMurano/commands/logs.rb', line 418

def assemble_query
  query_parts = {}
  assemble_query_severity(query_parts)
  assemble_query_types_array(query_parts)
  assemble_query_message(query_parts)
  assemble_query_service(query_parts)
  assemble_query_event(query_parts)
  assemble_query_endpoint(query_parts)
  # Assemble and return actual query string.
  assemble_query_string(query_parts)
end

#assemble_query_endpoint(query_parts) ⇒ Object



483
484
485
486
487
488
489
# File 'lib/MrMurano/commands/logs.rb', line 483

def assemble_query_endpoint(query_parts)
  terms = @filter_endpoints.map { |endp| format_endpoint(endp) }
  # FIXME: (lb): MUR-5446: Still unresolved: How to query endpoints.
  #   The RFC says to use 'endpoint', but I've also been told to use
  #   'data.section.' Neither work for me.
  assemble_string_search_many(query_parts, 'data.section', terms)
end

#assemble_query_event(query_parts) ⇒ Object



479
480
481
# File 'lib/MrMurano/commands/logs.rb', line 479

def assemble_query_event(query_parts)
  assemble_string_search_many(query_parts, 'event', @filter_events)
end

#assemble_query_message(query_parts) ⇒ Object



471
472
473
# File 'lib/MrMurano/commands/logs.rb', line 471

def assemble_query_message(query_parts)
  assemble_string_search_one(query_parts, 'message', @options.message, regex: true)
end

#assemble_query_service(query_parts) ⇒ Object



475
476
477
# File 'lib/MrMurano/commands/logs.rb', line 475

def assemble_query_service(query_parts)
  assemble_string_search_one(query_parts, 'service', @options.service)
end

#assemble_query_severity(query_parts) ⇒ Object



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
# File 'lib/MrMurano/commands/logs.rb', line 430

def assemble_query_severity(query_parts)
  filter_severity = @filter_severity.flatten
  return if filter_severity.empty?
  indices = []
  filter_severity.each do |sev|
    index = sev if sev =~ /^[0-9]$/
    index = LOG_SEVERITIES.find_index { |s| s.to_s =~ /^#{sev.downcase}/ } unless index
    if index
      indices.push index.to_i
    else
      parts = /^([0-9])-([0-9])$/.match(sev)
      if !parts.nil?
        start = parts[1].to_i
        finis = parts[2].to_i
        if start < finis
          more_indices = (start..finis).to_a
        else
          more_indices = (finis..start).to_a
        end
        indices += more_indices
      else
        warning "Invalid severity: #{sev}"
        exit 1
      end
    end
  end
  query_parts['severity'] = { '$in' => indices }
end

#assemble_query_string(query_parts) ⇒ Object



568
569
570
571
572
573
574
575
576
# File 'lib/MrMurano/commands/logs.rb', line 568

def assemble_query_string(query_parts)
  if query_parts.empty?
    ''
  else
    # (lb): I tried escaping parts of the query but it broke things.
    # So I'm assuming we don't need CGI.escape or URI.encode_www_form.
    query_parts.to_json
  end
end

#assemble_query_types_array(query_parts) ⇒ Object



459
460
461
462
463
464
465
466
467
468
469
# File 'lib/MrMurano/commands/logs.rb', line 459

def assemble_query_types_array(query_parts)
  assemble_in_or_nin_query(query_parts, 'type', @filter_types.flatten) do |type|
    index = LOG_EMITTER_TYPES.find_index { |s| s.to_s =~ /^#{type.downcase}/ }
    if index
      LOG_EMITTER_TYPES[index].to_s
    else
      warning "Invalid emitter type: #{type}"
      exit 1
    end
  end
end

#assemble_string_search_many(query_parts, field, arr_of_arrs) ⇒ Object



515
516
517
518
519
520
521
522
523
# File 'lib/MrMurano/commands/logs.rb', line 515

def assemble_string_search_many(query_parts, field, arr_of_arrs)
  terms = arr_of_arrs.flatten
  return if terms.empty?
  if terms.length == 1
    assemble_string_search_one(query_parts, field, terms[0])
  else
    assemble_in_or_nin_query(query_parts, field, terms)
  end
end

#assemble_string_search_one(query_parts, field, value, regex: false) ⇒ Object



500
501
502
503
504
505
506
507
508
509
510
511
512
513
# File 'lib/MrMurano/commands/logs.rb', line 500

def assemble_string_search_one(query_parts, field, value, regex: false)
  return if value.to_s.empty?
  if !regex
    # Note that some options support strict equality, e.g.,
    #   { 'key': { '$eq': 'value' } }
    # but post-processed keys like 'data.section' do not.
    # So we just do normal equality, e.g.,
    #   { 'key': 'value' }
    query_parts[field] = value
  else
    query_parts[field] = { :'$regex' => value }
    query_parts[field][:'$options'] = 'i' if @options.insensitive
  end
end

#cmd_add_examples(cmd) ⇒ Object



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
# File 'lib/MrMurano/commands/logs.rb', line 144

def cmd_add_examples(cmd)
  cmd.example %(
    View the last 100 product log entries
  ).strip, 'murano logs'

  cmd.example %(
    View the last 10 product log entries
  ).strip, 'murano logs --limit 10'

  cmd.example %(
    Stream the application logs, including the last 100 records
  ).strip, 'murano logs --follow'

  cmd.example %(
    Stream the logs generated by 'device2' events
  ).strip, 'murano logs --follow --event device2'

  cmd.example %(
    Stream the logs generated by the types, 'event' and 'script'
  ).strip, 'murano logs --follow --types event,script'

  cmd.example %(
    Stream the logs generated by the types, 'call' and 'config'
  ).strip, 'murano logs --follow --types call -T config'

  cmd.example %(
    Exclude the logs generated by the 'script' type
  ).strip, 'murano logs --follow -T :script'

  cmd.example %(
    Show last 100 logs with any severity level except DEBUG
  ).strip, 'murano logs --severity 0-6'

  cmd.example %(
    Stream only the logs with the DEBUG severity level
  ).strip, 'murano logs --follow -V --severity deB'

  cmd.example %(
    Stream logs with the severity levels ALERT, CRITICAL, WARNING, and DEBUG
  ).strip, 'murano logs --follow -V -l 1-2,WARN,7'

  cmd.example %(
    Show only log entries whose message contains the case-insensitive substring, "hello"
  ).strip, 'murano logs --message hello'

  cmd.example %(
    Show only log entries whose message contains the case-sensitive substring, "Hello"
  ).strip, 'murano logs --message Hello --no-insensitive'

  cmd.example %(
    Display logs using a custom timestamp format (see `man strftime` for format options)
  ).strip, %(murano logs --sprintf '%m/%d/%Y %Hh %Mm %Ss')

  cmd.example %(
    Stream logs using compact format, using one line per log entry
  ).strip, 'murano logs --follow --one-line'

  cmd.example %(
    Stream logs using two lines per log entry (1 header line and 1 message line)
  ).strip, 'murano logs --follow --message-only'

  cmd.example %(
    Format log entries as JSON (useful if you want to pipe the results, e.g., to `jq`)
  ).strip, 'murano logs --json'

  cmd.example %(
    Format log entries as YAML
  ).strip, 'murano logs --yaml'
end

#cmd_add_filter_option_endpoint(cmd) ⇒ Object



346
347
348
349
350
351
352
353
# File 'lib/MrMurano/commands/logs.rb', line 346

def cmd_add_filter_option_endpoint(cmd)
  cmd.option(
    '-e', '--endpoint ENDPOINT',
    %(Filter log entries by the endpoint (ENDPOINT is VERB:PATH))
  ) do |value|
    @filter_endpoints.push value
  end
end

#cmd_add_filter_option_event(cmd) ⇒ Object



337
338
339
340
341
342
343
344
# File 'lib/MrMurano/commands/logs.rb', line 337

def cmd_add_filter_option_event(cmd)
  cmd.option(
    '-E', '--event GLOB', Array,
    %(Filter log entries by the event)
  ) do |value|
    @filter_events.push value
  end
end

#cmd_add_filter_option_message(cmd) ⇒ Object



325
326
327
328
329
# File 'lib/MrMurano/commands/logs.rb', line 325

def cmd_add_filter_option_message(cmd)
  cmd.option '-m', '--message GLOB', %(
    Filter log entries by the message contents
  ).strip
end

#cmd_add_filter_option_service(cmd) ⇒ Object



331
332
333
334
335
# File 'lib/MrMurano/commands/logs.rb', line 331

def cmd_add_filter_option_service(cmd)
  cmd.option '-s', '--service GLOB', %(
    Filter log entries by the originating service
  ).strip
end

#cmd_add_filter_option_severity(cmd) ⇒ Object



296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/MrMurano/commands/logs.rb', line 296

def cmd_add_filter_option_severity(cmd)
  cmd.option(
    '-l', '--severity [NAME|LEVEL|RANGE[,NAME|LEVEL|RANGE...]]', Array,
    %(
Only show log entries of this severity.
May be specified by name, value, or range, e.g., WARN, 3, 1-4.
#{LOG_SEVERITIES.map.with_index { |s, i| "#{s}(#{i})" }.join(' ')}
    ).strip
  ) do |value|
    @filter_severity.push value
  end
end

#cmd_add_filter_option_type(cmd) ⇒ Object



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/MrMurano/commands/logs.rb', line 309

def cmd_add_filter_option_type(cmd)
  emitter_type_help = %(
Filter log entries by type (emitter system) of message.
EMITTERS is 1 or more comma-separated types:
#{LOG_EMITTER_TYPES.map(&:to_s)}
Use a "#{INCLUDE_INDICATOR}" or "#{EXCLUDE_INDICATOR}" prefix to include or exclude types, respectively.
  ).strip
  cmd.option('-T EMITTERS', '--types EMITTERS', Array, emitter_type_help) do |values|
    # This seems a little roundabout, but rb-commander only keeps last value.
    @filter_types.push values
    values.map do |val|
      val.sub(/^[#{INCLUDE_INDICATOR}#{EXCLUDE_INDICATOR}]/, '')
    end
  end
end

#cmd_add_filter_options(cmd) ⇒ Object



282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/MrMurano/commands/logs.rb', line 282

def cmd_add_filter_options(cmd)
  cmd_add_filter_option_severity(cmd)
  cmd_add_filter_option_type(cmd)
  cmd_add_filter_option_message(cmd)
  cmd_add_filter_option_service(cmd)
  cmd_add_filter_option_event(cmd)
  # FIXME/2018-01-12: MUR-5446: Backend issue supporting endpoint query.
  #   cmd_add_filter_option_endpoint(cmd)
  # Skipping: timestamp filter
  # Skipping: tracking_id filter
  # Skipping: module filter
  # Skipping: elapsed time filter (i.e., could do { elapsed: { $gt: 10 } })
end

#cmd_add_format_options(cmd) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/MrMurano/commands/logs.rb', line 224

def cmd_add_format_options(cmd)
  cmd_add_format_options_localtime(cmd)
  cmd_add_format_options_pretty(cmd)
  cmd_add_format_options_raw(cmd)
  cmd_add_format_options_message_only(cmd)
  cmd_add_format_options_omit_headers(cmd)
  cmd_add_format_options_one_line(cmd)
  cmd_add_format_options_align_columns(cmd)
  cmd_add_format_options_indent_body(cmd)
  cmd_add_format_options_separators(cmd)
  cmd_add_format_options_include_tracking(cmd)
  cmd_add_format_options_sprintf(cmd)
end

#cmd_add_format_options_align_columns(cmd) ⇒ Object



262
263
264
# File 'lib/MrMurano/commands/logs.rb', line 262

def cmd_add_format_options_align_columns(cmd)
  cmd.option '--[no-]align', %(Align columns in formatted output)
end

#cmd_add_format_options_include_tracking(cmd) ⇒ Object



274
275
276
# File 'lib/MrMurano/commands/logs.rb', line 274

def cmd_add_format_options_include_tracking(cmd)
  cmd.option '--[no-]tracking', %(Include tracking ID)
end

#cmd_add_format_options_indent_body(cmd) ⇒ Object



266
267
268
# File 'lib/MrMurano/commands/logs.rb', line 266

def cmd_add_format_options_indent_body(cmd)
  cmd.option '--[no-]indent', %(Indent body content in formatted output)
end

#cmd_add_format_options_localtime(cmd) ⇒ Object



238
239
240
# File 'lib/MrMurano/commands/logs.rb', line 238

def cmd_add_format_options_localtime(cmd)
  cmd.option '--[no-]localtime', %(Adjust Timestamps to be in local time)
end

#cmd_add_format_options_message_only(cmd) ⇒ Object



250
251
252
# File 'lib/MrMurano/commands/logs.rb', line 250

def cmd_add_format_options_message_only(cmd)
  cmd.option '-o', '--message-only', %(Show only the 'print' message, and maybe headers)
end

#cmd_add_format_options_omit_headers(cmd) ⇒ Object



254
255
256
# File 'lib/MrMurano/commands/logs.rb', line 254

def cmd_add_format_options_omit_headers(cmd)
  cmd.option '-O', '--omit-headers', %(Omit headers)
end

#cmd_add_format_options_one_line(cmd) ⇒ Object



258
259
260
# File 'lib/MrMurano/commands/logs.rb', line 258

def cmd_add_format_options_one_line(cmd)
  cmd.option '--one-line', %(Squeeze each log entry onto one line (wrapping as necessary))
end

#cmd_add_format_options_pretty(cmd) ⇒ Object



242
243
244
# File 'lib/MrMurano/commands/logs.rb', line 242

def cmd_add_format_options_pretty(cmd)
  cmd.option '--[no-]pretty', %(Reformat JSON blobs in logs)
end

#cmd_add_format_options_raw(cmd) ⇒ Object



246
247
248
# File 'lib/MrMurano/commands/logs.rb', line 246

def cmd_add_format_options_raw(cmd)
  cmd.option '--raw', %(Do not format the log data)
end

#cmd_add_format_options_separators(cmd) ⇒ Object



270
271
272
# File 'lib/MrMurano/commands/logs.rb', line 270

def cmd_add_format_options_separators(cmd)
  cmd.option '--[no-]separators', %(Show separators between log entries)
end

#cmd_add_format_options_sprintf(cmd) ⇒ Object



278
279
280
# File 'lib/MrMurano/commands/logs.rb', line 278

def cmd_add_format_options_sprintf(cmd)
  cmd.option '--sprintf FORMAT', %(Specify timestamp format (default: '%Y-%m-%d %H:%M:%S'))
end

#cmd_add_help(cmd) ⇒ Object



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
# File 'lib/MrMurano/commands/logs.rb', line 85

def cmd_add_help(cmd)
  cmd.description = %(
Get the logs for a solution.

Each log record contains a number of fields, including the following.

Severity
================================================================
The severity of the log message, as defined by rsyslog standard.

ID | Name          | Description
-- | ------------- | -----------------------------------------
0  | Emergency     | System is unusable
1  | Alert         | Action must be taken immediately
2  | Critical      | Critical conditions
3  | Error         | Error conditions
4  | Warning       | Warning conditions
5  | Notice        | Normal but significant condition
6  | Informational | Informational messages
7  | Debug         | Debug-level messages

Type
================================================================
The type (emitter system) of the message.

Name    | Description
------- | ----------------------------------------------------
Script  | Script Engine: When User Lua script calls `print()`
Call    | Dispatcher: On service calls from Lua
Event   | Dispatcher: On event trigger from services
Config  | API: On solution configuration change, or
        |      used service deprecation warning
Service | Services generated & transmitted to Dispatcher


Message
================================================================
Message can be up to 64kb containing plain text describing a log
of the event.

Service
================================================================
The service via which the event name is coming or the service of
which the function is called.

Event
================================================================
Depending on the type:

Event, Script => Event name
Call          => operationId

Tracking ID
================================================================
End to end Murano processing id.
Used to group logs together for one endpoint request.
  ).strip
end

#cmd_add_logs_meta(cmd) ⇒ Object



77
78
79
80
81
82
83
# File 'lib/MrMurano/commands/logs.rb', line 77

def cmd_add_logs_meta(cmd)
  cmd.syntax = %(murano logs [--options])
  cmd.summary = %(Get the logs for a solution)
  cmd.must_not_be_managed = true
  cmd_add_help(cmd)
  cmd_add_examples(cmd)
end

#cmd_add_logs_options(cmd) ⇒ Object



214
215
216
217
218
219
220
221
222
# File 'lib/MrMurano/commands/logs.rb', line 214

def cmd_add_logs_options(cmd)
  cmd.option '-f', '--[no-]follow', %(Follow logs from server)
  cmd.option '-r', '--retry', %(Always retry the connection)
  cmd.option(
    '-i', '--[no-]insensitive',
    %(Use case-insensitive matching (default: true))
  )
  cmd.option '-N', '--limit LIMIT', Integer, %(Retrieve this many existing logs at start of command (only works with --no-follow))
end

#cmd_default_logs_optionsObject



365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/MrMurano/commands/logs.rb', line 365

def cmd_default_logs_options
  @options.default(
    type: :application,
    follow: false,
    retry: false,
    insensitive: true,
    limit: nil,
    localtime: true,
    pretty: true,
    raw: false,
    message_only: false,
    omit_headers: false,
    one_line: false,
    tracking: false,
    sprintf: '%Y-%m-%d %H:%M:%S',
    align: false,
    indent: false,
    separators: false,
    severity: nil,
    types: [],
    message: nil,
    service: nil,
    event: nil,
    endpoint: nil,
  )
end

#cmd_get_sol!Object



407
408
409
410
411
412
413
414
415
416
# File 'lib/MrMurano/commands/logs.rb', line 407

def cmd_get_sol!
  if @options.type == :application
    MrMurano::Application.new
  elsif @options.type == :product
    MrMurano::Product.new
  else
    error "Unknown --type specified: #{@options.type}"
    exit 1
  end
end

#cmd_verify_logs_options!Object



392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/MrMurano/commands/logs.rb', line 392

def cmd_verify_logs_options!
  n_formatting = 0
  n_formatting += 1 if @options.raw
  n_formatting += 1 if @options.message_only
  n_formatting += 1 if @options.one_line
  # Global options should really be checked elsewhere. Oh, well.
  n_formatting += 1 if @options.json
  n_formatting += 1 if @options.yaml
  n_formatting += 1 if @options.pp
  return unless n_formatting > 1
  format_options = '--raw, --message-only, --one-line, --json, --yaml, or --pp'
  warning "Try using just one of #{format_options}, but not two or more."
  exit 1
end

#command_logs(cmd) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/MrMurano/commands/logs.rb', line 62

def command_logs(cmd)
  cmd_add_logs_meta(cmd)
  # Add global solution flag: --type [application|product].
  cmd_add_solntype_pickers(cmd, exclude_all: true)
  cmd_add_logs_options(cmd)
  cmd_add_format_options(cmd)
  cmd_add_filter_options(cmd)
  cmd.project_not_required = true
  cmd.action do |args, options|
    @options = options
    cmd.verify_arg_count!(args)
    logs_action
  end
end

#determine_format(line) ⇒ Object



670
671
672
673
674
675
676
677
678
679
# File 'lib/MrMurano/commands/logs.rb', line 670

def determine_format(line)
  # FIXME/2018-01-05 (landonb): On bizapi-dev, I see new-format logs,
  # but on bizapi-staging, I see old school format. So deal with it.
  # Logs V1 have 4 entries: type, timestamp, subject, data
  # Logs V2 have lots more entries, including type, timestamp and data.
  # We could use presence of subject to distinguish; or we could check
  # timestamp, which is seconds in V1 but msecs in V2; or we could check
  # data, which is string in V1, and Hash in V2.
  @log_format_is_v2 = !(line[:data].is_a? String)
end

#fetch_formatterObject



642
643
644
645
646
647
648
# File 'lib/MrMurano/commands/logs.rb', line 642

def fetch_formatter
  if pretty_printing
    method(:print_pretty)
  else
    method(:print_raw)
  end
end

#format_endpoint(endp) ⇒ Object



491
492
493
494
495
496
497
498
# File 'lib/MrMurano/commands/logs.rb', line 491

def format_endpoint(endp)
  # E.g., ?query={"data.section":"get_/set"}
  # The format the user is most likely to use is with a colon, but
  # we can let the user be a little sloppy, too, e.g., "get /set".
  parts = endp.split(' ', 2)
  parts = endp.split(':', 2) unless parts.length > 1
  parts.join('_').downcase
end

#logs_actionObject



355
356
357
358
359
360
361
362
363
# File 'lib/MrMurano/commands/logs.rb', line 355

def logs_action
  cmd_default_logs_options
  cmd_verify_logs_options!
  cmd_defaults_solntype_pickers(@options, :application)
  @query = assemble_query
  verbose %(query: #{@query})
  sol = cmd_get_sol!
  logs_display(sol)
end

#logs_display(sol) ⇒ Object



578
579
580
581
582
583
584
# File 'lib/MrMurano/commands/logs.rb', line 578

def logs_display(sol)
  if !@options.follow
    logs_once(sol)
  else
    logs_follow(sol)
  end
end

#logs_follow(sol) ⇒ Object

LATER/2017-12-14 (landonb): Show logs from all associated solutions.

We'll have to wire all the WebSockets from within the EM.run block.


617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
# File 'lib/MrMurano/commands/logs.rb', line 617

def logs_follow(sol)
  formatter = fetch_formatter
  keep_running = true
  while keep_running
    keep_running = @options.retry
    logs = MrMurano::Logs::Follow.new(@query, @options.limit)
    logs.run_event_loop(sol) do |line|
      log_entry = parse_logs_line(line)
      if log_entry[:statusCode] == 400
        warning "Query error: #{log_entry}"
      else
        formatter.call(log_entry) unless log_entry.nil?
      end
    end
  end
end

#logs_once(sol) ⇒ Object



586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
# File 'lib/MrMurano/commands/logs.rb', line 586

def logs_once(sol)
  ret = sol.get("/logs#{query_string}")
  if ret.is_a?(Hash) && ret.key?(:items)
    ret[:items].reverse.each do |line|
      if pretty_printing
        print_pretty(line)
      else
        print_raw(line)
      end
    end
  else
    sol.error "Could not get logs: #{ret}"
    exit 1
  end
end

#parse_logs_line(line) ⇒ Object



634
635
636
637
638
639
640
# File 'lib/MrMurano/commands/logs.rb', line 634

def parse_logs_line(line)
  log_entry = JSON.parse(line)
  elevate_hash(log_entry)
rescue StandardError => err
  warning "Not JSON: #{err} / #{line}"
  nil
end

#pretty_printingObject



650
651
652
# File 'lib/MrMurano/commands/logs.rb', line 650

def pretty_printing
  !@options.raw && ($cfg['tool.outformat'] == 'best')
end


658
659
660
661
662
663
664
665
666
667
668
# File 'lib/MrMurano/commands/logs.rb', line 658

def print_pretty(line)
  determine_format(line) if @log_format_is_v2.nil?
  if @log_format_is_v2
    puts MrMurano::Pretties.MakePrettyLogsV2(line, @options)
  else
    puts MrMurano::Pretties.MakePrettyLogsV1(line, @options)
  end
rescue StandardError => err
  error "Failed to parse log: #{err} / #{line}"
  raise
end


654
655
656
# File 'lib/MrMurano/commands/logs.rb', line 654

def print_raw(line)
  outf line
end

#process_query_term(term, resolved_terms, exclude, field, terms) ⇒ Object



549
550
551
552
553
554
# File 'lib/MrMurano/commands/logs.rb', line 549

def process_query_term(term, resolved_terms, exclude, field, terms)
  verify_term_plus_minux_prefix!(term, exclude, field, terms)
  term = term.sub(/^[#{INCLUDE_INDICATOR}#{EXCLUDE_INDICATOR}]/, '')
  term = yield term if block_given?
  resolved_terms.push term
end

#query_stringObject



602
603
604
605
606
607
608
609
610
611
612
613
# File 'lib/MrMurano/commands/logs.rb', line 602

def query_string
  # NOTE (lb): Unsure whether we need to encode parts of the query,
  # possibly with CGI.escape. Note that http.get calls
  # URI.encode_www_form(query), which the server will accept,
  # but it will not produce any results.
  params = []
  params += ["query=#{@query}"] unless @query.empty?
  params += ["limit=#{@options.limit}"] unless @options.limit.nil?
  querys = params.join('&')
  querys = "?#{querys}" unless querys.empty?
  querys
end

#term_indicates_exclude?(term) ⇒ Boolean

Returns:

  • (Boolean)


541
542
543
544
545
546
547
# File 'lib/MrMurano/commands/logs.rb', line 541

def term_indicates_exclude?(term)
  if term.start_with? EXCLUDE_INDICATOR
    true
  else
    false
  end
end

#verify_term_plus_minux_prefix!(term, exclude, field, terms) ⇒ Object



556
557
558
559
560
561
562
563
564
565
566
# File 'lib/MrMurano/commands/logs.rb', line 556

def verify_term_plus_minux_prefix!(term, exclude, field, terms)
  return unless term =~ /^[#{INCLUDE_INDICATOR}#{EXCLUDE_INDICATOR}]/
  return unless (
    (!exclude && term.start_with?(EXCLUDE_INDICATOR)) ||
    (exclude && term.start_with?(INCLUDE_INDICATOR))
  )
  warning(
    %(You cannot mix + and ! for "#{field}": #{terms.join(',')})
  )
  exit 1
end