airfoil 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +33 -4
- data/lib/airfoil/railtie.rb +0 -25
- data/lib/airfoil/version.rb +1 -1
- data/lib/airfoil.rb +10 -13
- metadata +4 -37
- data/lib/airfoil/middleware/database.rb +0 -17
- data/lib/airfoil/middleware/datadog.rb +0 -23
- data/lib/airfoil/middleware/sentry_catcher.rb +0 -40
- data/lib/airfoil/middleware/sentry_monitoring.rb +0 -42
- data/lib/airfoil/middleware/step_function.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19253bd2e6d9a47232400ac4ee4d7a1fe7953c462af51d419ce6d6fbc941dc50
|
4
|
+
data.tar.gz: '08854bb418dbf76f5254d4132ffaf9f9ba496e7c2398b214e51b55658321c504'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '084ee1915eca4bfe74e7743d439b5f8fd8db205fed067073a5ad320be13b7164c91a2008b9ec72c394b9cff2189ea1df1835f876a3d11f0a1254f8f72544e1a2'
|
7
|
+
data.tar.gz: 3f03cfdfd2baf6554d344cd2f79dbb6a24c9b71eef66178643f74c3d3edd32369fe85382d9c01f2afb93345fdaf917f9f5b99f182b6fd5f2f027e901de61f5be
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Airfoil
|
2
|
+
|
2
3
|
_Enough structure to get our Lambda handlers in the air._
|
3
4
|
|
4
5
|
Airfoil is curated middleware stack that abstracts away common infrastructure
|
@@ -14,11 +15,11 @@ gem 'airfoil'
|
|
14
15
|
|
15
16
|
And then execute:
|
16
17
|
|
17
|
-
|
18
|
+
bundle install
|
18
19
|
|
19
20
|
Or install it yourself as:
|
20
21
|
|
21
|
-
|
22
|
+
gem install airfoil
|
22
23
|
|
23
24
|
## Usage
|
24
25
|
|
@@ -54,10 +55,38 @@ Existing middleware include:
|
|
54
55
|
|
55
56
|
- `FunctionName` - dispatch any calls made to a specific Lambda function (by name) to a specified handler class
|
56
57
|
- `LogEvent` - log AWS events in a pretty format
|
57
|
-
- `SentryCatcher` - catch exceptions and report them to Sentry, including context
|
58
|
-
- `SentryMonitoring` - instrument your function code for Sentry's performance monitoring
|
59
58
|
- `SetRequestId` - set the `AWS_REQUEST_ID` environment variable for your function code
|
60
59
|
|
60
|
+
## Additional Middleware
|
61
|
+
|
62
|
+
There are additional middleware available as separate gems that provide specific functionality. They must be explicitly added to your middleware stack in `create_stack`.
|
63
|
+
|
64
|
+
### Sentry
|
65
|
+
|
66
|
+
Add the `airfoil-sentry` gem to your Gemfile.This provides three middlewares:
|
67
|
+
|
68
|
+
- `SentryCatcher` - catch exceptions and report them to Sentry, including context:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
b.use Airfoil::Middleware::SentryCatcher
|
72
|
+
```
|
73
|
+
|
74
|
+
- `SentryMonitoring` - instrument your function code for Sentry's performance monitoring
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
b.use Airfoil::Middleware::SentryMonitoring
|
78
|
+
```
|
79
|
+
|
80
|
+
### ActiveRecord
|
81
|
+
|
82
|
+
Add the `airfoil-activerecord` gem to your Gemfile. This provides a single middleware:
|
83
|
+
|
84
|
+
- `DatabaseConnection` - Check a connection in/out and enable the query cache per handler
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
b.use Airfoil::Middleware::DatabaseConnection
|
88
|
+
```
|
89
|
+
|
61
90
|
## Development
|
62
91
|
|
63
92
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/airfoil/railtie.rb
CHANGED
@@ -1,33 +1,8 @@
|
|
1
1
|
require "airfoil/cloudwatch_formatter"
|
2
|
-
require "datadog/lambda"
|
3
2
|
|
4
3
|
module Airfoil
|
5
4
|
class Railtie < Rails::Railtie
|
6
5
|
# Format logs for consistent parsing by Cloudwatch
|
7
6
|
config.log_formatter = Airfoil::CloudwatchFormatter.new
|
8
|
-
config.datadog_enabled = ENV.fetch("DATADOG_ENABLED", Rails.env.production?).to_s == "true"
|
9
|
-
|
10
|
-
initializer "airfoil.datadog" do
|
11
|
-
if Rails.configuration.datadog_enabled
|
12
|
-
require "ddtrace/auto_instrument"
|
13
|
-
|
14
|
-
::Datadog::Lambda.configure_apm do |c|
|
15
|
-
c.env = ENV.fetch("SENTRY_ENVIRONMENT", Rails.env).dasherize
|
16
|
-
# downscasing first ensures we don't attempt to snake case things that don't already have dashes
|
17
|
-
c.service = ENV.fetch("AWS_LAMBDA_FUNCTION_NAME", "brokersuite").downcase.underscore
|
18
|
-
|
19
|
-
# Set trace rate via DD_TRACE_SAMPLE_RATE
|
20
|
-
c.tracing.enabled = true
|
21
|
-
c.tracing.instrument :aws
|
22
|
-
c.tracing.instrument :faraday
|
23
|
-
c.tracing.instrument :rest_client
|
24
|
-
c.tracing.instrument :httpclient
|
25
|
-
c.tracing.instrument :http
|
26
|
-
c.tracing.instrument :rails
|
27
|
-
end
|
28
|
-
|
29
|
-
Rails.logger.debug "=====DATADOG LOADED (RAILTIE)====="
|
30
|
-
end
|
31
|
-
end
|
32
7
|
end
|
33
8
|
end
|
data/lib/airfoil/version.rb
CHANGED
data/lib/airfoil.rb
CHANGED
@@ -3,38 +3,35 @@
|
|
3
3
|
require "middleware"
|
4
4
|
|
5
5
|
require_relative "airfoil/version"
|
6
|
-
require_relative "airfoil/middleware/database"
|
7
|
-
require_relative "airfoil/middleware/datadog"
|
8
6
|
require_relative "airfoil/middleware/function_name"
|
9
7
|
require_relative "airfoil/middleware/log_event"
|
10
8
|
require_relative "airfoil/middleware/logger_tagging"
|
11
|
-
require_relative "airfoil/middleware/sentry_catcher"
|
12
|
-
require_relative "airfoil/middleware/sentry_monitoring"
|
13
9
|
require_relative "airfoil/middleware/set_request_id"
|
14
|
-
require_relative "airfoil/middleware/step_function"
|
15
10
|
require_relative "airfoil/logger_patch"
|
16
11
|
require_relative "airfoil/railtie" if defined?(Rails::Railtie)
|
17
12
|
|
18
13
|
module Airfoil
|
19
14
|
class << self
|
20
|
-
def create_stack
|
15
|
+
def create_stack(logger)
|
21
16
|
# ensure that STDOUT streams are synchronous so we don't lose logs
|
22
17
|
$stdout.sync = true
|
23
18
|
|
24
19
|
Signal.trap("TERM") do
|
25
20
|
# We can't use the Rails logger here as the logger is not available in the trap context
|
26
|
-
puts "Received SIGTERM, shutting down gracefully..."
|
21
|
+
puts "Received SIGTERM, shutting down gracefully..."
|
27
22
|
end
|
28
23
|
|
24
|
+
logger ||= defined?(::Rails) ? Rails.logger : Logger.new($stdout, level: (ENV["LOG_LEVEL"] || :info).to_sym)
|
25
|
+
|
29
26
|
::Middleware::Builder.new { |b|
|
30
|
-
|
27
|
+
if defined?(::Rails)
|
28
|
+
b.use Middleware::LoggerTagging, logger
|
29
|
+
end
|
31
30
|
b.use Middleware::SetRequestId
|
32
|
-
|
33
|
-
b.use Middleware::
|
34
|
-
b.use Middleware::SentryMonitoring
|
35
|
-
b.use Middleware::LogEvent, Rails.logger
|
31
|
+
# This is causing infinite recursion for some reason
|
32
|
+
# b.use Middleware::LogEvent, logger
|
36
33
|
yield b
|
37
|
-
}.inject_logger(
|
34
|
+
}.inject_logger(logger)
|
38
35
|
end
|
39
36
|
end
|
40
37
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: airfoil
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Highwing Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: datadog-lambda
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: dry-monads
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +30,14 @@ dependencies:
|
|
44
30
|
requirements:
|
45
31
|
- - "~>"
|
46
32
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.4.
|
33
|
+
version: 0.4.3
|
48
34
|
type: :runtime
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
38
|
- - "~>"
|
53
39
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.4.
|
40
|
+
version: 0.4.3
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: railties
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,20 +52,6 @@ dependencies:
|
|
66
52
|
- - ">="
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: sentry-ruby
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
56
|
name: aws_lambda_ric
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,15 +106,10 @@ files:
|
|
134
106
|
- lib/airfoil/cloudwatch_formatter.rb
|
135
107
|
- lib/airfoil/logger_patch.rb
|
136
108
|
- lib/airfoil/middleware/base.rb
|
137
|
-
- lib/airfoil/middleware/database.rb
|
138
|
-
- lib/airfoil/middleware/datadog.rb
|
139
109
|
- lib/airfoil/middleware/function_name.rb
|
140
110
|
- lib/airfoil/middleware/log_event.rb
|
141
111
|
- lib/airfoil/middleware/logger_tagging.rb
|
142
|
-
- lib/airfoil/middleware/sentry_catcher.rb
|
143
|
-
- lib/airfoil/middleware/sentry_monitoring.rb
|
144
112
|
- lib/airfoil/middleware/set_request_id.rb
|
145
|
-
- lib/airfoil/middleware/step_function.rb
|
146
113
|
- lib/airfoil/railtie.rb
|
147
114
|
- lib/airfoil/version.rb
|
148
115
|
homepage:
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require "airfoil/middleware/base"
|
2
|
-
|
3
|
-
module Airfoil
|
4
|
-
module Middleware
|
5
|
-
class Database < Airfoil::Middleware::Base
|
6
|
-
def call(env)
|
7
|
-
# TODO: This explicitly makes a connection, which we may want to re-evaluate at some point
|
8
|
-
ActiveRecord::Base.connection_pool.with_connection do |conn|
|
9
|
-
conn.enable_query_cache!
|
10
|
-
@app.call(env)
|
11
|
-
ensure
|
12
|
-
conn.disable_query_cache!
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require "datadog/lambda"
|
2
|
-
require "datadog/tracing"
|
3
|
-
require "airfoil/middleware/base"
|
4
|
-
|
5
|
-
module Airfoil
|
6
|
-
module Middleware
|
7
|
-
class Datadog < Base
|
8
|
-
# See the Airfoil railtie for config loaded at Rails load-time
|
9
|
-
# Note: Rails loads prior to Airfoil because of the inclusion of `require_relative "config/environment"`
|
10
|
-
# in the engine lambda handler
|
11
|
-
def call(env)
|
12
|
-
event, context = env.values_at(:event, :context)
|
13
|
-
|
14
|
-
::Datadog::Lambda.wrap(event, context) do
|
15
|
-
@app.call(env)
|
16
|
-
rescue => err
|
17
|
-
::Datadog::Tracing.active_span&.set_error(err)
|
18
|
-
raise err
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require "sentry-ruby"
|
2
|
-
require "airfoil/middleware/base"
|
3
|
-
|
4
|
-
module Airfoil
|
5
|
-
module Middleware
|
6
|
-
class SentryCatcher < Base
|
7
|
-
def initialize(app, logger)
|
8
|
-
super(app)
|
9
|
-
@logger = logger
|
10
|
-
end
|
11
|
-
|
12
|
-
def call(env)
|
13
|
-
@app.call(env)
|
14
|
-
rescue => err
|
15
|
-
Sentry.with_scope do |scope|
|
16
|
-
context = env[:context]
|
17
|
-
event = env[:event]
|
18
|
-
|
19
|
-
scope.set_extras(
|
20
|
-
function_name: context.function_name,
|
21
|
-
function_version: context.function_version,
|
22
|
-
invoked_function_arn: context.invoked_function_arn,
|
23
|
-
memory_limit_in_mb: context.memory_limit_in_mb,
|
24
|
-
aws_request_id: context.aws_request_id,
|
25
|
-
log_group_name: context.log_group_name,
|
26
|
-
log_stream_name: context.log_stream_name,
|
27
|
-
identity: context.identity,
|
28
|
-
event: event
|
29
|
-
)
|
30
|
-
|
31
|
-
Sentry.capture_exception(err)
|
32
|
-
@logger.error(err)
|
33
|
-
raise err
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class RequestError < StandardError; end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require "sentry-ruby"
|
2
|
-
require "airfoil/middleware/base"
|
3
|
-
|
4
|
-
module Airfoil
|
5
|
-
module Middleware
|
6
|
-
class SentryMonitoring < Base
|
7
|
-
def call(env)
|
8
|
-
event, context = env.values_at(:event, :context)
|
9
|
-
sentry_trace_id = get_first_instance(event, "sentry_trace_id")
|
10
|
-
identity = get_first_instance(event, "identity")
|
11
|
-
|
12
|
-
options = {name: context.function_name, op: "handler"}
|
13
|
-
options[:transaction] = Sentry::Transaction.from_sentry_trace(sentry_trace_id, **options) if sentry_trace_id.present?
|
14
|
-
|
15
|
-
Sentry.set_user(username: identity.dig("username"), ip_address: identity.dig("source_ip", 0)) if identity.present?
|
16
|
-
transaction = Sentry.start_transaction(**options)
|
17
|
-
# Add transaction to the global scope so it is accessible throughout the app
|
18
|
-
Sentry.get_current_hub&.configure_scope do |scope|
|
19
|
-
scope.set_span(transaction)
|
20
|
-
end
|
21
|
-
|
22
|
-
result = @app.call(env)
|
23
|
-
transaction.finish if transaction.present?
|
24
|
-
|
25
|
-
result
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def get_first_instance(event, key)
|
31
|
-
case event
|
32
|
-
in [e, *rest]
|
33
|
-
e.dig(key)
|
34
|
-
in Hash
|
35
|
-
event.dig(key)
|
36
|
-
else
|
37
|
-
nil
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
require_relative "function_name"
|
2
|
-
|
3
|
-
module Airfoil
|
4
|
-
module Middleware
|
5
|
-
class StepFunction < FunctionName
|
6
|
-
def initialize(app, handler_class, *function_names_to_match, retried_exceptions: [])
|
7
|
-
super(app, handler_class, *function_names_to_match)
|
8
|
-
@retried_exceptions = retried_exceptions.map(&:to_s)
|
9
|
-
end
|
10
|
-
|
11
|
-
def call(env)
|
12
|
-
context = env[:context]
|
13
|
-
|
14
|
-
ignore_exceptions(context) do
|
15
|
-
super
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def ignore_exceptions(context)
|
20
|
-
disable_exceptions
|
21
|
-
result = yield
|
22
|
-
enable_exceptions
|
23
|
-
|
24
|
-
result
|
25
|
-
end
|
26
|
-
|
27
|
-
def disable_exceptions
|
28
|
-
@before = Sentry.configuration.excluded_exceptions
|
29
|
-
Sentry.configuration.excluded_exceptions += @retried_exceptions
|
30
|
-
end
|
31
|
-
|
32
|
-
def enable_exceptions
|
33
|
-
Sentry.configuration.excluded_exceptions = @before
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|