Module: Trailblazer::Developer::Trace

Defined in:
lib/trailblazer/developer/trace.rb,
lib/trailblazer/developer/trace/tree.rb,
lib/trailblazer/developer/trace/stack.rb,
lib/trailblazer/developer/trace/present.rb,
lib/trailblazer/developer/trace/debugger.rb,
lib/trailblazer/developer/trace/debugger/normalizer.rb

Defined Under Namespace

Modules: Debugger, Present Classes: Captured, Stack, Tree

Class Method Summary collapse

Class Method Details

.arguments_for_call(activity, options, original_flow_options, **original_circuit_options) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/trailblazer/developer/trace.rb', line 16

def arguments_for_call(activity, (options, original_flow_options), **original_circuit_options)
  default_flow_options = {
    stack:                  Trace::Stack.new,
    input_data_collector:   Trace.method(:default_input_data_collector),
    output_data_collector:  Trace.method(:default_output_data_collector),
  }

  flow_options = {**default_flow_options, **Hash(original_flow_options)}

  default_circuit_options = {
    wrap_runtime:  ::Hash.new(Trace.task_wrap_extensions), # DISCUSS: this overrides existing {:wrap_runtime}.
  }

  circuit_options = {**original_circuit_options, **default_circuit_options}

  return activity, [options, flow_options], circuit_options
end

.call(activity, ctx, flow_options, **circuit_options) ⇒ Object Also known as: invoke

Public entry point to activate tracing when running activity.



6
7
8
9
10
11
12
# File 'lib/trailblazer/developer/trace.rb', line 6

def call(activity, (ctx, flow_options), **circuit_options)
  activity, (ctx, flow_options), circuit_options = Trace.arguments_for_call( activity, [ctx, flow_options], **circuit_options ) # only run once for the entire circuit!

  signal, (ctx, flow_options) = Trailblazer::Activity::TaskWrap.invoke(activity, [ctx, flow_options], **circuit_options)

  return flow_options[:stack], signal, [ctx, flow_options]
end

.capture_args(wrap_config, ctx, flow), circuit_options) ⇒ Object

It’s important to understand that :stack is mutated by design. This is needed so in case of exceptions we still have a “global” trace - unfortunately Ruby doesn’t allow us a better way. taskWrap step to capture incoming arguments of a step.



51
52
53
54
55
56
57
58
59
# File 'lib/trailblazer/developer/trace.rb', line 51

def capture_args(wrap_config, ((ctx, flow), circuit_options))
  original_args = [[ctx, flow], circuit_options]

  captured_input = Captured(Captured::Input, flow[:input_data_collector], wrap_config, original_args)

  flow[:stack] << captured_input

  return wrap_config, original_args
end

.capture_return(wrap_config, ctx, flow), circuit_options) ⇒ Object

taskWrap step to capture outgoing arguments from a step.



62
63
64
65
66
67
68
69
70
# File 'lib/trailblazer/developer/trace.rb', line 62

def capture_return(wrap_config, ((ctx, flow), circuit_options))
  original_args = [[ctx, flow], circuit_options]

  captured_output = Captured(Captured::Output, flow[:output_data_collector], wrap_config, original_args)

  flow[:stack] << captured_output

  return wrap_config, original_args
end

.Captured(captured_class, data_collector, wrap_config, ctx, flow), circuit_options) ⇒ Object



72
73
74
75
76
77
78
79
80
# File 'lib/trailblazer/developer/trace.rb', line 72

def Captured(captured_class, data_collector, wrap_config, ((ctx, flow), circuit_options))
  collected_data = data_collector.call(wrap_config, [[ctx, flow], circuit_options])

  captured_class.new( # either Input or Output
    wrap_config[:task],
    circuit_options[:activity],
    collected_data
  ).freeze
end

.default_input_data_collector(wrap_config, ctx, _), _) ⇒ Object

Called in #Captured. DISCUSS: this is where to start for a new Inspector implementation.



84
85
86
87
88
89
90
91
92
# File 'lib/trailblazer/developer/trace.rb', line 84

def default_input_data_collector(wrap_config, ((ctx, _), _)) # DISCUSS: would it be faster to access ctx via {original_args[0][0]}?
  # mutable, old_ctx = ctx.decompose
  # mutable, old_ctx = ctx, nil

  {
    # ctx: ctx.to_h.freeze,
    ctx_snapshot: ctx.to_h.collect { |k,v| [k, v.inspect] }.to_h,
  } # TODO: proper snapshot!
end

.default_output_data_collector(wrap_config, ctx, _), _) ⇒ Object

Called in #Captured.



95
96
97
98
99
100
101
102
103
104
# File 'lib/trailblazer/developer/trace.rb', line 95

def default_output_data_collector(wrap_config, ((ctx, _), _))
  returned_ctx, _ = wrap_config[:return_args]

  # FIXME: snapshot!
  {
    # ctx: ctx.to_h.freeze,
    ctx_snapshot: returned_ctx.to_h.collect { |k,v| [k, v.inspect] }.to_h,
    signal: wrap_config[:return_signal]
  }
end

.task_wrap_extensionsObject

Insertions for the trace tasks that capture the arguments just before calling the task, and before the TaskWrap is finished.



40
41
42
43
44
45
# File 'lib/trailblazer/developer/trace.rb', line 40

def task_wrap_extensions
  Trailblazer::Activity::TaskWrap.Extension(
    [Trace.method(:capture_args),   id: "task_wrap.capture_args",   prepend: "task_wrap.call_task"],
    [Trace.method(:capture_return), id: "task_wrap.capture_return", append: nil], # append to the very end of tW.
  )
end

.Tree(stack_end, level: 0, parent: nil) ⇒ Object

Builds a tree graph from a linear stack. Consists of Trailblazer::Developer::Trace::Tree::Node structures.



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
# File 'lib/trailblazer/developer/trace/tree.rb', line 49

def self.Tree(stack_end, level: 0, parent: nil)
  processed = []
  nodes     = []

  # for {captured_input} we're gonna build a {Node}!
  captured_input, remaining = stack_end[0], stack_end[1..-1]

      raise unless captured_input.is_a?(Captured::Input)

  while next_captured = remaining[0]
    if next_captured.is_a?(Captured::Input)

      bla, _processed = Tree(remaining, level: level+1)
      nodes += [bla]
      processed += _processed


      remaining = remaining - processed

    else # Captured::Output

      raise unless next_captured.is_a?(Captured::Output)
      raise if next_captured.activity != captured_input.activity

      node = Tree::Node.new(level, captured_input, next_captured, nodes)

      return node,
        [captured_input, *processed, next_captured] # what nodes did we process here?

    end

  end


end