Skip to content

Commit

Permalink
CCAP-239: Add request logging and instrumentation (#5)
Browse files Browse the repository at this point in the history
* Updated API tests to verify they are instrumented.
* Added correlation and request id to requests
* Updated dependencies.
  • Loading branch information
jamesiarmes authored Jul 10, 2024
1 parent 06b4c4f commit aae4fde
Showing 20 changed files with 567 additions and 42 deletions.
8 changes: 8 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -12,6 +12,14 @@ AllCops:
FactoryBot/FactoryAssociationWithStrategy:
Enabled: false

# Don't let long declarations and invocations to contribute to method length.
# This is preferable over squeezing them onto a single line.
Metrics/MethodLength:
CountAsOne:
- array
- method_call
- hash

# Count multi-line hashes and arrays in examples as one line.
RSpec/ExampleLength:
CountAsOne:
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ RUN bundle config --global frozen 1

# Install application depedencies.
RUN bundle install
RUN bundle binstubs --all

# Copy the application code.
COPY . .
121 changes: 97 additions & 24 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -17,8 +17,14 @@ PATH
grape-swagger (~> 2.1)
grape-swagger-entity (~> 0.5)
httparty (~> 0.22)
opentelemetry-exporter-otlp (~> 0.27)
opentelemetry-instrumentation-faraday (~> 0.24)
opentelemetry-instrumentation-grape (~> 0.1)
opentelemetry-instrumentation-rack (~> 0.24)
opentelemetry-sdk (~> 1.4)
rack (~> 3.0)
rackup (~> 2.1)
semantic_logger (~> 4.15)
statsd-instrument (~> 3.7)

GEM
@@ -42,7 +48,7 @@ GEM
base64 (0.2.0)
bigdecimal (3.1.8)
builder (3.3.0)
concurrent-ruby (1.3.1)
concurrent-ruby (1.3.3)
connection_pool (2.4.1)
csv (3.3.0)
diff-lcs (1.5.1)
@@ -51,7 +57,7 @@ GEM
dry-core (1.0.1)
concurrent-ruby (~> 1.0)
zeitwerk (~> 2.6)
dry-inflector (1.0.0)
dry-inflector (1.1.0)
dry-logic (1.5.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
@@ -65,10 +71,31 @@ GEM
zeitwerk (~> 2.6)
factory_bot (6.4.6)
activesupport (>= 5.0.0)
faraday (2.9.1)
faraday (2.10.0)
faraday-net_http (>= 2.0, < 3.2)
logger
faraday-net_http (3.1.0)
net-http
google-protobuf (4.27.2)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-aarch64-linux)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-arm64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-x86-linux)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-x86_64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-x86_64-linux)
bigdecimal
rake (>= 13)
googleapis-common-protos-types (1.15.0)
google-protobuf (>= 3.18, < 5.a)
grape (2.0.0)
activesupport (>= 5)
builder
@@ -94,9 +121,9 @@ GEM
json (2.7.2)
jwt (1.5.6)
language_server-protocol (3.17.0.3)
logger (1.6.0)
mini_mime (1.1.5)
mini_portile2 (2.8.7)
minitest (5.23.1)
minitest (5.24.1)
multi_json (1.15.0)
multi_xml (0.7.1)
bigdecimal (~> 3.1)
@@ -107,17 +134,57 @@ GEM
mutex_m (0.2.0)
net-http (0.4.1)
uri
nokogiri (1.16.5)
mini_portile2 (~> 2.8.2)
nokogiri (1.16.6-aarch64-linux)
racc (~> 1.4)
nokogiri (1.16.6-arm-linux)
racc (~> 1.4)
nokogiri (1.16.5-arm64-darwin)
nokogiri (1.16.6-arm64-darwin)
racc (~> 1.4)
parallel (1.24.0)
parser (3.3.2.0)
nokogiri (1.16.6-x86-linux)
racc (~> 1.4)
nokogiri (1.16.6-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.16.6-x86_64-linux)
racc (~> 1.4)
opentelemetry-api (1.2.5)
opentelemetry-common (0.21.0)
opentelemetry-api (~> 1.0)
opentelemetry-exporter-otlp (0.28.0)
google-protobuf (>= 3.18)
googleapis-common-protos-types (~> 1.3)
opentelemetry-api (~> 1.1)
opentelemetry-common (~> 0.20)
opentelemetry-sdk (~> 1.2)
opentelemetry-semantic_conventions
opentelemetry-instrumentation-base (0.22.4)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.21)
opentelemetry-registry (~> 0.1)
opentelemetry-instrumentation-faraday (0.24.5)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-grape (0.2.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rack (~> 0.21)
opentelemetry-instrumentation-rack (0.24.5)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-registry (0.3.1)
opentelemetry-api (~> 1.1)
opentelemetry-sdk (1.4.1)
opentelemetry-api (~> 1.1)
opentelemetry-common (~> 0.20)
opentelemetry-registry (~> 0.2)
opentelemetry-semantic_conventions
opentelemetry-semantic_conventions (1.10.0)
opentelemetry-api (~> 1.0)
parallel (1.25.1)
parser (3.3.3.0)
ast (~> 2.4.1)
racc
racc (1.8.0)
rack (3.0.11)
rack (3.1.6)
rack-accept (0.4.5)
rack (>= 0.4)
rack-test (2.1.0)
@@ -128,15 +195,15 @@ GEM
rainbow (3.1.1)
rake (13.2.1)
regexp_parser (2.9.2)
rexml (3.2.8)
strscan (>= 3.0.9)
rexml (3.3.1)
strscan
rspec (3.13.0)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.0)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.0)
rspec-expectations (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-github (2.4.0)
@@ -158,40 +225,46 @@ GEM
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.3)
parser (>= 3.3.1.0)
rubocop-capybara (2.20.0)
rubocop (~> 1.41)
rubocop-factory_bot (2.25.1)
rubocop-capybara (2.21.0)
rubocop (~> 1.41)
rubocop-factory_bot (2.26.1)
rubocop (~> 1.61)
rubocop-rake (0.6.0)
rubocop (~> 1.0)
rubocop-rspec (2.30.0)
rubocop-rspec (2.31.0)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
rubocop-rspec_rails (~> 2.28)
rubocop-rspec_rails (2.28.3)
rubocop (~> 1.40)
rubocop-rspec_rails (2.29.1)
rubocop (~> 1.61)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
semantic_logger (4.16.0)
concurrent-ruby (~> 1.0)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
statsd-instrument (3.7.0)
statsd-instrument (3.8.0)
strscan (3.1.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
uri (0.13.0)
uri_template (0.7.0)
webrick (1.8.1)
zeitwerk (2.6.15)
zeitwerk (2.6.16)

PLATFORMS
arm64-darwin-23
ruby
aarch64-linux
arm-linux
arm64-darwin
x86-linux
x86_64-darwin
x86_64-linux

DEPENDENCIES
document-transfer-service!
31 changes: 31 additions & 0 deletions config.ru
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
# frozen_string_literal: true

require 'opentelemetry/sdk'
require 'opentelemetry-exporter-otlp'
require 'opentelemetry/instrumentation/faraday'
require 'opentelemetry/instrumentation/grape'
require 'opentelemetry/instrumentation/rack'
require 'semantic_logger'

require_relative 'lib/document_transfer'
require_relative 'lib/api/api'
require_relative 'lib/api/middleware/correlation_id'
require_relative 'lib/api/middleware/instrument'
require_relative 'lib/api/middleware/request_id'
require_relative 'lib/api/middleware/request_logging'

# Configure the logger.
SemanticLogger.default_level = ENV.fetch('LOG_LEVEL',
DocumentTransfer::DEFAULT_LOG_LEVEL)
SemanticLogger.application = DocumentTransfer::NAME
SemanticLogger.add_appender(io: $stdout, formatter: :json)

# Configure telemetry reporting.
OpenTelemetry::SDK.configure do |c|
c.service_name = DocumentTransfer::NAME
c.service_version = DocumentTransfer::VERSION
c.use_all
end

# Include Rack middleware.
use Rack::RewindableInput::Middleware
use DocumentTransfer::API::Middleware::RequestId
use DocumentTransfer::API::Middleware::CorrelationId
use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args)
use DocumentTransfer::API::Middleware::RequestLogging
use DocumentTransfer::API::Middleware::Instrument

run DocumentTransfer::API::API
6 changes: 6 additions & 0 deletions document-transfer-service.gemspec
Original file line number Diff line number Diff line change
@@ -28,7 +28,13 @@ Gem::Specification.new do |s|
s.add_runtime_dependency 'grape-swagger', '~> 2.1'

Check notice on line 28 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L28

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'grape-swagger-entity', '~> 0.5'

Check notice on line 29 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L29

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'httparty', '~> 0.22'

Check notice on line 30 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L30

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'opentelemetry-exporter-otlp', '~> 0.27'

Check notice on line 31 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L31

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'opentelemetry-instrumentation-faraday', '~> 0.24'

Check notice on line 32 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L32

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'opentelemetry-instrumentation-grape', '~> 0.1'

Check notice on line 33 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L33

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'opentelemetry-instrumentation-rack', '~> 0.24'

Check notice on line 34 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L34

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'opentelemetry-sdk', '~> 1.4'

Check notice on line 35 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L35

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'rack', '~> 3.0'

Check notice on line 36 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L36

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'rackup', '~> 2.1'

Check notice on line 37 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L37

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'semantic_logger', '~> 4.15'

Check notice on line 38 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L38

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
s.add_runtime_dependency 'statsd-instrument', '~> 3.7'

Check notice on line 39 in document-transfer-service.gemspec

GitHub Actions / RuboCop Results

document-transfer-service.gemspec#L39

Use `add_dependency` instead of `add_runtime_dependency`. [Gemspec/AddRuntimeDependency]
end
6 changes: 6 additions & 0 deletions lib/api/api.rb
Original file line number Diff line number Diff line change
@@ -13,6 +13,12 @@ module API
class API < Grape::API
format :json

# Rescue all exceptions and return a JSON response with the message.
rescue_from :all do |e|
error!({ status: 'error', message: e.message },
e.respond_to?(:code) ? e.code : 500)
end

mount DocumentTransfer::API::Health
mount DocumentTransfer::API::Transfer

6 changes: 2 additions & 4 deletions lib/api/health.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
# frozen_string_literal: true

require 'grape'
require 'statsd-instrument'

require_relative '../response/health_status'

module DocumentTransfer
module API
# Health check endpoint for the API.
class Health < Grape::API
desc 'Check system health', success: DocumentTransfer::Response::HealthStatus
desc 'Check system health', success: DocumentTransfer::Response::HealthStatus,
endpoint_name: 'health'
get :health do
StatsD.increment('health_check')

present DocumentTransfer::Response::HealthStatus.new(status: 'ok')
end
end
50 changes: 50 additions & 0 deletions lib/api/middleware/correlation_id.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

module DocumentTransfer
module API
module Middleware
# Rack middleware that adds a correlation id to requests that don't
# include one.
#
# This middleware must be loaded before any middleware that may make use
# of the correlation id (such as logging).
class CorrelationId
HEADER = 'x-correlation-id'
KEY = 'HTTP_X_CORRELATION_ID'

# Initialize the middleware
#
# @param [Rack::Events] app The Rack application.
def initialize(app)
@app = app
end

# Ensure that the request has a correlation id.
#
# @param [Hash] env The environment hash.
# @return [Array<Integer, Rack::Headers, Rack::BodyProxy] The response
# for the request.
def call(env)
# Prefer the existing correlation id, if it exists.
correlation_id = env.fetch(KEY, generate)
env[KEY] ||= correlation_id

# Add the correlation id to to the response headers.
status, headers, body = @app.call(env)
headers[HEADER] = correlation_id

[status, headers, body]
end

private

# Generates a random correlation id.
#
# @return [String]
def generate
SecureRandom.uuid
end
end
end
end
end
Loading

0 comments on commit aae4fde

Please sign in to comment.