-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
moved faraday middleware into the zipkin client gem
- Loading branch information
Showing
11 changed files
with
352 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--color | ||
--require spec_helper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# 0.5.0 | ||
Added Faraday middleware to the repo | ||
|
||
# 0.4.0 | ||
Use Thread safe Finagle version to store the traces |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,113 +1,2 @@ | ||
# Copyright 2012 Twitter Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
require 'finagle-thrift' | ||
require 'finagle-thrift/trace' | ||
require 'scribe' | ||
|
||
require 'zipkin-tracer/careless_scribe' | ||
|
||
if RUBY_PLATFORM == 'java' | ||
require 'hermann/producer' | ||
require 'zipkin-tracer/zipkin_kafka_tracer' | ||
end | ||
|
||
module ZipkinTracer extend self | ||
|
||
class RackHandler | ||
B3_REQUIRED_HEADERS = %w[HTTP_X_B3_TRACEID HTTP_X_B3_PARENTSPANID HTTP_X_B3_SPANID HTTP_X_B3_SAMPLED] | ||
B3_OPT_HEADERS = %w[HTTP_X_B3_FLAGS] | ||
|
||
def initialize(app, config=nil) | ||
@app = app | ||
@lock = Mutex.new | ||
|
||
config ||= app.config.zipkin_tracer # if not specified, try on app (e.g. Rails 3+) | ||
@service_name = config[:service_name] | ||
@service_port = config[:service_port] | ||
|
||
::Trace.tracer = if config[:scribe_server] && defined?(::Scribe) | ||
scribe = config[:scribe_server] ? Scribe.new(config[:scribe_server]) : Scribe.new() | ||
scribe_max_buffer = config[:scribe_max_buffer] ? config[:scribe_max_buffer] : 10 | ||
::Trace::ZipkinTracer.new(CarelessScribe.new(scribe), scribe_max_buffer) | ||
elsif config[:zookeeper] && RUBY_PLATFORM == 'java' && defined?(::Hermann) | ||
kafkaTracer = ::Trace::ZipkinKafkaTracer.new | ||
kafkaTracer.connect(config[:zookeeper]) | ||
kafkaTracer | ||
end | ||
|
||
@sample_rate = config[:sample_rate] ? config[:sample_rate] : 0.1 | ||
@annotate_plugin = config[:annotate_plugin] # call for trace annotation | ||
@filter_plugin = config[:filter_plugin] # skip tracing if returns false | ||
@whitelist_plugin = config[:whitelist_plugin] # force sampling if returns true | ||
end | ||
|
||
def call(env) | ||
# skip certain requests | ||
return @app.call(env) if filtered?(env) | ||
|
||
::Trace.default_endpoint = ::Trace.default_endpoint.with_service_name(@service_name).with_port(@service_port) | ||
::Trace.sample_rate=(@sample_rate) | ||
whitelisted = force_sample?(env) | ||
id = get_or_create_trace_id(env, whitelisted) # note that this depends on the sample rate being set | ||
tracing_filter(id, env, whitelisted) { @app.call(env) } | ||
end | ||
|
||
private | ||
def annotate(env, status, response_headers, response_body) | ||
@annotate_plugin.call(env, status, response_headers, response_body) if @annotate_plugin | ||
end | ||
|
||
def filtered?(env) | ||
@filter_plugin && !@filter_plugin.call(env) | ||
end | ||
|
||
def force_sample?(env) | ||
@whitelist_plugin && @whitelist_plugin.call(env) | ||
end | ||
|
||
def tracing_filter(trace_id, env, whitelisted=false) | ||
@lock.synchronize do | ||
::Trace.push(trace_id) | ||
::Trace.set_rpc_name(env["REQUEST_METHOD"]) # get/post and all that jazz | ||
::Trace.record(::Trace::BinaryAnnotation.new("http.uri", env["PATH_INFO"], "STRING", ::Trace.default_endpoint)) | ||
::Trace.record(::Trace::Annotation.new(::Trace::Annotation::SERVER_RECV, ::Trace.default_endpoint)) | ||
::Trace.record(::Trace::Annotation.new('whitelisted', ::Trace.default_endpoint)) if whitelisted | ||
end | ||
status, headers, body = yield if block_given? | ||
ensure | ||
@lock.synchronize do | ||
::Trace.record(::Trace::Annotation.new(::Trace::Annotation::SERVER_SEND, ::Trace.default_endpoint)) | ||
annotate(env, status, headers, body) | ||
::Trace.pop | ||
end | ||
end | ||
|
||
private | ||
def get_or_create_trace_id(env, whitelisted, default_flags = ::Trace::Flags::EMPTY) | ||
trace_parameters = if B3_REQUIRED_HEADERS.all? { |key| env.has_key?(key) } | ||
env.values_at(*B3_REQUIRED_HEADERS) | ||
else | ||
new_id = Trace.generate_id | ||
[new_id, nil, new_id, ("true" if whitelisted || Trace.should_sample?)] | ||
end | ||
trace_parameters[3] = (trace_parameters[3] == "true") | ||
|
||
trace_parameters += env.values_at(*B3_OPT_HEADERS) # always check flags | ||
trace_parameters[4] = (trace_parameters[4] || default_flags).to_i | ||
|
||
Trace::TraceId.new(*trace_parameters) | ||
end | ||
end | ||
|
||
end | ||
require 'zipkin-tracer/rack/zipkin-tracer' | ||
require 'zipkin-tracer/faraday/zipkin-tracer' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
require 'faraday' | ||
require 'finagle-thrift' | ||
require 'finagle-thrift/trace' | ||
require 'uri' | ||
|
||
module ZipkinTracer | ||
class FaradayHandler < ::Faraday::Middleware | ||
B3_HEADERS = { | ||
:trace_id => "X-B3-TraceId", | ||
:parent_id => "X-B3-ParentSpanId", | ||
:span_id => "X-B3-SpanId", | ||
:sampled => "X-B3-Sampled", | ||
:flags => "X-B3-Flags" | ||
}.freeze | ||
|
||
def initialize(app, service_name=nil) | ||
@app = app | ||
@service_name = service_name | ||
end | ||
|
||
def call(env) | ||
# handle either a URI object (passed by Faraday v0.8.x in testing), or something string-izable | ||
url = env[:url].respond_to?(:host) ? env[:url] : URI.parse(env[:url].to_s) | ||
service_name = @service_name || url.host.split('.').first # default to url-derived service name | ||
endpoint = ::Trace::Endpoint.new(host_ip_for(url.host), url.port, service_name) | ||
|
||
::Trace.unwind do | ||
trace_id = ::Trace.id | ||
::Trace.push(trace_id.next_id) | ||
B3_HEADERS.each do |method, header| | ||
env[:request_headers][header] = ::Trace.id.send(method).to_s | ||
end | ||
|
||
# annotate with method (GET/POST/etc.) and uri path | ||
::Trace.set_rpc_name(env[:method].to_s.upcase) | ||
::Trace.record(::Trace::BinaryAnnotation.new("http.uri", url.path, "STRING", endpoint)) | ||
::Trace.record(::Trace::Annotation.new(::Trace::Annotation::CLIENT_SEND, endpoint)) | ||
result = @app.call(env).on_complete do |renv| | ||
# record HTTP status code on response | ||
::Trace.record(::Trace::BinaryAnnotation.new("http.status", [renv[:status]].pack('n'), "I16", endpoint)) | ||
end | ||
::Trace.record(::Trace::Annotation.new(::Trace::Annotation::CLIENT_RECV, endpoint)) | ||
result | ||
end | ||
end | ||
|
||
# get host IP for specified hostname, catching exceptions | ||
def host_ip_for(hostname) | ||
::Trace::Endpoint.host_to_i32(hostname) | ||
rescue | ||
# default to 0.0.0.0 if lookup fails | ||
0x00000000 | ||
end | ||
private :host_ip_for | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# Copyright 2012 Twitter Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
require 'finagle-thrift' | ||
require 'finagle-thrift/trace' | ||
require 'scribe' | ||
|
||
require 'zipkin-tracer/careless_scribe' | ||
|
||
if RUBY_PLATFORM == 'java' | ||
require 'hermann/producer' | ||
require 'zipkin-tracer/zipkin_kafka_tracer' | ||
end | ||
|
||
module ZipkinTracer extend self | ||
|
||
class RackHandler | ||
B3_REQUIRED_HEADERS = %w[HTTP_X_B3_TRACEID HTTP_X_B3_PARENTSPANID HTTP_X_B3_SPANID HTTP_X_B3_SAMPLED] | ||
B3_OPT_HEADERS = %w[HTTP_X_B3_FLAGS] | ||
|
||
def initialize(app, config=nil) | ||
@app = app | ||
@lock = Mutex.new | ||
|
||
config ||= app.config.zipkin_tracer # if not specified, try on app (e.g. Rails 3+) | ||
@service_name = config[:service_name] | ||
@service_port = config[:service_port] | ||
|
||
::Trace.tracer = if config[:scribe_server] && defined?(::Scribe) | ||
scribe = config[:scribe_server] ? Scribe.new(config[:scribe_server]) : Scribe.new() | ||
scribe_max_buffer = config[:scribe_max_buffer] ? config[:scribe_max_buffer] : 10 | ||
::Trace::ZipkinTracer.new(CarelessScribe.new(scribe), scribe_max_buffer) | ||
elsif config[:zookeeper] && RUBY_PLATFORM == 'java' && defined?(::Hermann) | ||
kafkaTracer = ::Trace::ZipkinKafkaTracer.new | ||
kafkaTracer.connect(config[:zookeeper]) | ||
kafkaTracer | ||
end | ||
|
||
@sample_rate = config[:sample_rate] ? config[:sample_rate] : 0.1 | ||
@annotate_plugin = config[:annotate_plugin] # call for trace annotation | ||
@filter_plugin = config[:filter_plugin] # skip tracing if returns false | ||
@whitelist_plugin = config[:whitelist_plugin] # force sampling if returns true | ||
end | ||
|
||
def call(env) | ||
# skip certain requests | ||
return @app.call(env) if filtered?(env) | ||
|
||
::Trace.default_endpoint = ::Trace.default_endpoint.with_service_name(@service_name).with_port(@service_port) | ||
::Trace.sample_rate=(@sample_rate) | ||
whitelisted = force_sample?(env) | ||
id = get_or_create_trace_id(env, whitelisted) # note that this depends on the sample rate being set | ||
tracing_filter(id, env, whitelisted) { @app.call(env) } | ||
end | ||
|
||
private | ||
def annotate(env, status, response_headers, response_body) | ||
@annotate_plugin.call(env, status, response_headers, response_body) if @annotate_plugin | ||
end | ||
|
||
def filtered?(env) | ||
@filter_plugin && !@filter_plugin.call(env) | ||
end | ||
|
||
def force_sample?(env) | ||
@whitelist_plugin && @whitelist_plugin.call(env) | ||
end | ||
|
||
def tracing_filter(trace_id, env, whitelisted=false) | ||
@lock.synchronize do | ||
::Trace.push(trace_id) | ||
::Trace.set_rpc_name(env["REQUEST_METHOD"]) # get/post and all that jazz | ||
::Trace.record(::Trace::BinaryAnnotation.new("http.uri", env["PATH_INFO"], "STRING", ::Trace.default_endpoint)) | ||
::Trace.record(::Trace::Annotation.new(::Trace::Annotation::SERVER_RECV, ::Trace.default_endpoint)) | ||
::Trace.record(::Trace::Annotation.new('whitelisted', ::Trace.default_endpoint)) if whitelisted | ||
end | ||
status, headers, body = yield if block_given? | ||
ensure | ||
@lock.synchronize do | ||
::Trace.record(::Trace::Annotation.new(::Trace::Annotation::SERVER_SEND, ::Trace.default_endpoint)) | ||
annotate(env, status, headers, body) | ||
::Trace.pop | ||
end | ||
end | ||
|
||
private | ||
def get_or_create_trace_id(env, whitelisted, default_flags = ::Trace::Flags::EMPTY) | ||
trace_parameters = if B3_REQUIRED_HEADERS.all? { |key| env.has_key?(key) } | ||
env.values_at(*B3_REQUIRED_HEADERS) | ||
else | ||
new_id = Trace.generate_id | ||
[new_id, nil, new_id, ("true" if whitelisted || Trace.should_sample?)] | ||
end | ||
trace_parameters[3] = (trace_parameters[3] == "true") | ||
|
||
trace_parameters += env.values_at(*B3_OPT_HEADERS) # always check flags | ||
trace_parameters[4] = (trace_parameters[4] || default_flags).to_i | ||
|
||
Trace::TraceId.new(*trace_parameters) | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.