Module: LogBrew::Trace

Defined in:
lib/logbrew/trace.rb

Overview

Request-local trace context for app-owned logs, errors, actions, and metrics.

Class Method Summary collapse

Class Method Details

.activate(context) ⇒ Object



34
35
36
37
38
39
40
41
42
# File 'lib/logbrew/trace.rb', line 34

def activate(context)
  unless context.is_a?(TraceContext)
    raise SdkError.new("validation_error", "trace context must be a LogBrew::TraceContext")
  end

  scope_id = Object.new.object_id
  stack << { id: scope_id, context: context }
  TraceScope.new(scope_id)
end

.add_metadata(target, context = current) ⇒ Object



125
126
127
128
129
130
131
132
# File 'lib/logbrew/trace.rb', line 125

def (target, context = current)
  return target unless context

  (context).each do |key, value|
    target[key] = value unless target.key?(key) || target.key?(key.to_sym)
  end
  target
end

.close_scope(scope_id) ⇒ Object



51
52
53
54
55
# File 'lib/logbrew/trace.rb', line 51

def close_scope(scope_id)
  entries = stack
  index = entries.rindex { |entry| entry[:id] == scope_id }
  entries.delete_at(index) unless index.nil?
end

.continue_or_create(traceparent) ⇒ Object



57
58
59
60
61
62
63
64
# File 'lib/logbrew/trace.rb', line 57

def continue_or_create(traceparent)
  text = traceparent.to_s.strip
  return create_root if text.empty?

  from_traceparent(text)
rescue SdkError
  create_root
end

.create(trace_id:, span_id:, trace_flags: "01", parent_span_id: nil) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/logbrew/trace.rb', line 80

def create(trace_id:, span_id:, trace_flags: "01", parent_span_id: nil)
  normalized_traceparent = Traceparent.create(trace_id: trace_id, span_id: span_id, trace_flags: trace_flags)
  _version, normalized_trace_id, normalized_span_id, normalized_flags = normalized_traceparent.split("-")
  normalized_parent_span_id = nil
  unless parent_span_id.nil?
    parent_traceparent = Traceparent.create(
      trace_id: normalized_trace_id,
      span_id: parent_span_id,
      trace_flags: normalized_flags
    )
    normalized_parent_span_id = parent_traceparent.split("-")[2]
  end

  TraceContext.new(
    trace_id: normalized_trace_id,
    span_id: normalized_span_id,
    parent_span_id: normalized_parent_span_id,
    trace_flags: normalized_flags,
    sampled: (normalized_flags.to_i(16) & 1) == 1
  )
end

.create_headers(context = current) ⇒ Object



102
103
104
105
106
107
108
109
110
# File 'lib/logbrew/trace.rb', line 102

def create_headers(context = current)
  return {} unless context

  Traceparent.create_headers(
    trace_id: context.trace_id,
    span_id: context.span_id,
    trace_flags: context.trace_flags
  )
end

.create_rootObject



76
77
78
# File 'lib/logbrew/trace.rb', line 76

def create_root
  create(trace_id: generate_trace_id, span_id: generate_span_id, trace_flags: "01")
end

.currentObject



29
30
31
32
# File 'lib/logbrew/trace.rb', line 29

def current
  entry = stack.last
  entry && entry[:context]
end

.from_rack_env(env) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/logbrew/trace.rb', line 151

def from_rack_env(env)
  existing = env_value(env, "logbrew.trace")
  return existing if existing.is_a?(TraceContext)

  traceparent = env_value(env, "HTTP_TRACEPARENT") || env_value(env, "traceparent") || env_value(env, "logbrew.traceparent")
  return continue_or_create(traceparent) unless traceparent.nil? || traceparent.empty?

  trace_id = env_value(env, "logbrew.trace_id")
  span_id = env_value(env, "logbrew.span_id")
  if trace_id && span_id
    begin
      return create(
        trace_id: trace_id,
        span_id: span_id,
        parent_span_id: env_value(env, "logbrew.parent_span_id"),
        trace_flags: env_value(env, "logbrew.trace_flags") || "01"
      )
    rescue SdkError
      nil
    end
  end

  create_root
end

.from_traceparent(traceparent) ⇒ Object



66
67
68
69
70
71
72
73
74
# File 'lib/logbrew/trace.rb', line 66

def from_traceparent(traceparent)
  context = Traceparent.parse(traceparent)
  create(
    trace_id: context.trace_id,
    span_id: generate_span_id,
    parent_span_id: context.parent_span_id,
    trace_flags: context.trace_flags
  )
end

.generate_span_idObject



183
184
185
186
187
188
# File 'lib/logbrew/trace.rb', line 183

def generate_span_id
  loop do
    value = SecureRandom.hex(8)
    return value unless value.delete("0").empty?
  end
end

.generate_trace_idObject



176
177
178
179
180
181
# File 'lib/logbrew/trace.rb', line 176

def generate_trace_id
  loop do
    value = SecureRandom.hex(16)
    return value unless value.delete("0").empty?
  end
end

.merge_attributes(attributes, context = current) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/logbrew/trace.rb', line 134

def merge_attributes(attributes, context = current)
  return attributes unless context && attributes.is_a?(Hash)

   = attributes.key?("metadata") ? attributes["metadata"] : attributes[:metadata]
  return attributes unless .nil? || .is_a?(Hash)

  copied = attributes.dup
   = .nil? ? {} : .dup
  (, context)
  if copied.key?(:metadata) && !copied.key?("metadata")
    copied[:metadata] = 
  else
    copied["metadata"] = 
  end
  copied
end

.metadata(context = current) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/logbrew/trace.rb', line 112

def (context = current)
  return {} unless context

  {
    "traceId" => context.trace_id,
    "spanId" => context.span_id,
    "traceFlags" => context.trace_flags,
    "traceSampled" => context.sampled
  }.tap do |payload|
    payload["parentSpanId"] = context.parent_span_id unless context.parent_span_id.nil?
  end
end

.with_context(context) ⇒ Object



44
45
46
47
48
49
# File 'lib/logbrew/trace.rb', line 44

def with_context(context)
  scope = activate(context)
  yield context
ensure
  scope.close if scope
end