stackify-ruby-apm 1.8.0 → 1.9.5
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/lib/stackify-ruby-apm-lambda.rb +1 -0
- data/lib/stackify_apm/agent.rb +4 -0
- data/lib/stackify_apm/config.rb +27 -7
- data/lib/stackify_apm/context.rb +8 -1
- data/lib/stackify_apm/root_info.rb +11 -1
- data/lib/stackify_apm/transport/aws_lambda_logging.rb +53 -0
- data/lib/stackify_apm/transport_selector.rb +2 -0
- data/lib/stackify_apm/version.rb +1 -1
- data/lib/stackify_ruby_apm.rb +1 -0
- data/lib/stackify_ruby_apm_lambda.rb +62 -0
- metadata +5 -8
- data/.gemignore +0 -8
- data/.gitignore +0 -76
- data/.rubocop.yml +0 -38
- data/.ruby-version +0 -1
- data/docker-compose.yml +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f7fd8fff02ed67e5112d4b59f84063cdebd84862e05f84376bd98c3237c3625
|
4
|
+
data.tar.gz: 0b59b1c98df2313f254e0f3f10fbe9291510e9316360d839c1073beee9100ab0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f347b093845bedd23269048c352aeac43b9bde434f22d9805e60fba67d08bd0eb1907d052f8f3b522f1b3452faaf4a1c02fb332ae28abd49bdba37e65bd6d714
|
7
|
+
data.tar.gz: 71f935e7e9096c5a4e4c1d365b8e45790364f77a4e6045f0af2fe79e5475662eaa93937de6a95a7c5e8b15a55e0b57c947bbe28da7f9c0d081da6cdf8886efa6
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'stackify_ruby_apm_lambda'
|
data/lib/stackify_apm/agent.rb
CHANGED
@@ -118,6 +118,10 @@ module StackifyRubyAPM
|
|
118
118
|
# Stores transaction in queue
|
119
119
|
#
|
120
120
|
def enqueue_transaction(transaction)
|
121
|
+
if !@config.queue
|
122
|
+
return @trace_logger.post([transaction])
|
123
|
+
end
|
124
|
+
|
121
125
|
boot_worker unless worker_running?
|
122
126
|
pending_transactions.push(transaction)
|
123
127
|
return unless should_flush_transactions?
|
data/lib/stackify_apm/config.rb
CHANGED
@@ -8,7 +8,12 @@ require 'logger'
|
|
8
8
|
require 'yaml'
|
9
9
|
require 'socket'
|
10
10
|
module StackifyRubyAPM
|
11
|
-
TRANSPORT = [
|
11
|
+
TRANSPORT = [
|
12
|
+
TRACE_LOG = 'default'.freeze,
|
13
|
+
UNIX_SOCKET = 'agent_socket'.freeze,
|
14
|
+
AGENT_HTTP = 'agent_http'.freeze,
|
15
|
+
LOGGING = 'logging'.freeze
|
16
|
+
].freeze
|
12
17
|
|
13
18
|
# @api private
|
14
19
|
class Config
|
@@ -62,7 +67,10 @@ module StackifyRubyAPM
|
|
62
67
|
stackify_properties_file: '/usr/local/stackify/stackify-ruby-apm/stackify.properties',
|
63
68
|
agent_traces_url: '/traces',
|
64
69
|
unix_socket_path: '/usr/local/stackify/stackify.sock',
|
65
|
-
transport_http_endpoint: 'https://localhost:10601'
|
70
|
+
transport_http_endpoint: 'https://localhost:10601',
|
71
|
+
|
72
|
+
queue: true,
|
73
|
+
lambda_handler: ''
|
66
74
|
}.freeze
|
67
75
|
|
68
76
|
ENV_TO_KEY = {
|
@@ -87,7 +95,9 @@ module StackifyRubyAPM
|
|
87
95
|
'STACKIFY_SPAN_FRAMES_MIN_DURATION' => [:int, 'span_frames_min_duration'],
|
88
96
|
'STACKIFY_MAX_QUEUE_SIZE' => [:int, 'max_queue_size'],
|
89
97
|
'STACKIFY_FLUSH_INTERVAL' => 'flush_interval_seconds',
|
90
|
-
'STACKIFY_DISABLED_SPIES' => [:list, 'disabled_spies']
|
98
|
+
'STACKIFY_DISABLED_SPIES' => [:list, 'disabled_spies'],
|
99
|
+
'STACKIFY_QUEUE' => [:bool, 'queue'],
|
100
|
+
'STACKIFY_LAMBDA_HANDLER' => 'lambda_handler'
|
91
101
|
}.freeze
|
92
102
|
|
93
103
|
def initialize(options = {})
|
@@ -158,6 +168,9 @@ module StackifyRubyAPM
|
|
158
168
|
attr_accessor :root_path
|
159
169
|
attr_accessor :http_status
|
160
170
|
|
171
|
+
attr_accessor :queue
|
172
|
+
attr_accessor :lambda_handler
|
173
|
+
|
161
174
|
attr_reader :client_id
|
162
175
|
attr_reader :device_id
|
163
176
|
attr_reader :apm_disabled_in_rake
|
@@ -221,11 +234,18 @@ module StackifyRubyAPM
|
|
221
234
|
new_available_spies - disabled_spies
|
222
235
|
end
|
223
236
|
|
237
|
+
# Default Transport
|
238
|
+
#
|
239
|
+
# initialize default logger transport
|
224
240
|
def debug_logger
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
241
|
+
case @transport.downcase
|
242
|
+
# For unix socket we don't create a trace log file
|
243
|
+
when StackifyRubyAPM::TRACE_LOG
|
244
|
+
debugger_logpath = log_path == '-' ? $stdout : log_path
|
245
|
+
logger = StackifyLogger.new(debugger_logpath, debugger_filenum_rotate, debugger_byte_size)
|
246
|
+
logger.level = log_level
|
247
|
+
self.logger = logger
|
248
|
+
end
|
229
249
|
end
|
230
250
|
|
231
251
|
private
|
data/lib/stackify_apm/context.rb
CHANGED
@@ -12,12 +12,19 @@ module StackifyRubyAPM
|
|
12
12
|
class Context
|
13
13
|
include NaivelyHashable
|
14
14
|
|
15
|
-
attr_accessor :request, :response
|
15
|
+
attr_accessor :request, :response, :aws
|
16
16
|
attr_reader :custom, :tags
|
17
17
|
|
18
18
|
def initialize
|
19
19
|
@custom = {}
|
20
20
|
@tags = {}
|
21
21
|
end
|
22
|
+
|
23
|
+
# add aws context to context instance
|
24
|
+
#
|
25
|
+
# values {arn}
|
26
|
+
def add_aws_context ctx
|
27
|
+
@aws = ctx
|
28
|
+
end
|
22
29
|
end
|
23
30
|
end
|
@@ -15,6 +15,15 @@ module StackifyRubyAPM
|
|
15
15
|
def build
|
16
16
|
# get process id
|
17
17
|
pid = $PID || Process.pid
|
18
|
+
# get hostname and remove extra unwanted characters
|
19
|
+
begin
|
20
|
+
hostname = (@config.hostname || `hostname`).strip
|
21
|
+
rescue StandardError
|
22
|
+
# use socket to get hostname if `hostname` throws exception
|
23
|
+
require 'socket'
|
24
|
+
hostname = Socket.gethostname
|
25
|
+
end
|
26
|
+
|
18
27
|
hash = {
|
19
28
|
CATEGORY: 'Ruby',
|
20
29
|
APPLICATION_PATH: '/',
|
@@ -26,7 +35,7 @@ module StackifyRubyAPM
|
|
26
35
|
THREAD_ID: Thread.current.object_id,
|
27
36
|
TRACE_SOURCE: 'RUBY',
|
28
37
|
TRACE_TARGET: 'RETRACE',
|
29
|
-
HOST_NAME:
|
38
|
+
HOST_NAME: hostname,
|
30
39
|
OS_TYPE: StackifyRubyAPM::Util.host_os,
|
31
40
|
PROCESS_ID: pid,
|
32
41
|
TRACE_VERSION: '2.0',
|
@@ -36,6 +45,7 @@ module StackifyRubyAPM
|
|
36
45
|
hash[:STATUS] = @transaction.context.response.status_code if @transaction.context && @transaction.context.response && @transaction.context.response.status_code
|
37
46
|
hash[:URL] = @transaction.context.request.url[:full] if @transaction.context && @transaction.context.request && @transaction.context.request.url[:full]
|
38
47
|
hash[:RUM] = true if @config.rum_enabled.is_a?(TrueClass)
|
48
|
+
hash[:AWS_LAMBDA_ARN] = @transaction.context.aws[:arn] if @transaction.context && @transaction.context.aws && @transaction.context.aws[:arn]
|
39
49
|
hash
|
40
50
|
end
|
41
51
|
# rubocop:enable Metrics/CyclomaticComplexity
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'stringio'
|
3
|
+
require 'zlib'
|
4
|
+
require 'stackify_apm/root_info'
|
5
|
+
require 'stackify_apm/serializers'
|
6
|
+
|
7
|
+
|
8
|
+
module StackifyRubyAPM
|
9
|
+
# This class will handle the writing of messages through a logfile.
|
10
|
+
# @api private
|
11
|
+
class AWSLoggerClient
|
12
|
+
include Log
|
13
|
+
COMPRESS_DEFAULT_LEVEL = 6
|
14
|
+
|
15
|
+
def initialize(config)
|
16
|
+
@config = config
|
17
|
+
@transaction_serializers = Serializers::Transactions.new(@config)
|
18
|
+
@logger = Logger.new(STDOUT)
|
19
|
+
@logger.level = :debug
|
20
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
21
|
+
"STACKIFY-TRACE: #{msg}\n"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# This method will build an Array of Transactions in a json format.
|
26
|
+
# It will accept Array of transactions.
|
27
|
+
def post(transactions = [])
|
28
|
+
# convert transactions to json
|
29
|
+
json_traces = []
|
30
|
+
transactions.each do |transaction|
|
31
|
+
# convert transaction to json
|
32
|
+
json_transaction = @transaction_serializers.build_json(@config, transaction).to_json
|
33
|
+
|
34
|
+
# add to json traces array
|
35
|
+
json_traces.push(json_transaction)
|
36
|
+
end
|
37
|
+
|
38
|
+
return unless ENV['STACKIFY_RUBY_ENV'] != 'rspec'
|
39
|
+
|
40
|
+
json_traces.each do |json_trace|
|
41
|
+
str = StringIO.new mode='w'
|
42
|
+
gz = Zlib::GzipWriter.new str, 6
|
43
|
+
gz.write json_trace.to_s
|
44
|
+
gz.close
|
45
|
+
compressed = Base64.strict_encode64 str.string
|
46
|
+
@logger.debug compressed
|
47
|
+
end
|
48
|
+
debug '[LogClient] post() Successfully write to logfile.' if ENV['STACKIFY_TRANSPORT_LOG_LEVEL'] == '0'
|
49
|
+
rescue StandardError => e
|
50
|
+
debug "[LogClient] post() exception: #{e.inspect}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -14,6 +14,8 @@ module StackifyRubyAPM
|
|
14
14
|
StackifyRubyAPM::UnixSocketClient.new(config)
|
15
15
|
when StackifyRubyAPM::AGENT_HTTP
|
16
16
|
StackifyRubyAPM::AgentHTTPClient.new(config)
|
17
|
+
when StackifyRubyAPM::LOGGING
|
18
|
+
StackifyRubyAPM::AWSLoggerClient.new(config)
|
17
19
|
else
|
18
20
|
StackifyRubyAPM::LogClient.new(config)
|
19
21
|
end
|
data/lib/stackify_apm/version.rb
CHANGED
data/lib/stackify_ruby_apm.rb
CHANGED
@@ -33,6 +33,7 @@ require 'stackify_apm/transport/agent_base'
|
|
33
33
|
require 'stackify_apm/transport/log_client'
|
34
34
|
require 'stackify_apm/transport/unix_socket_client'
|
35
35
|
require 'stackify_apm/transport/agent_http_client'
|
36
|
+
require 'stackify_apm/transport/aws_lambda_logging'
|
36
37
|
|
37
38
|
require 'google/protobuf'
|
38
39
|
require 'proto/stackify_trace'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'stackify_ruby_apm'
|
2
|
+
|
3
|
+
module AWS
|
4
|
+
def self.instrument
|
5
|
+
config = {
|
6
|
+
transport: 'logging',
|
7
|
+
queue: false,
|
8
|
+
}
|
9
|
+
StackifyRubyAPM.start config
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.handler(event:, context:, &block)
|
13
|
+
begin
|
14
|
+
ctx = StackifyRubyAPM::Context.new
|
15
|
+
ctx.add_aws_context({:arn => context.invoked_function_arn})
|
16
|
+
transaction = StackifyRubyAPM.transaction context.function_name, 'TASK', context: ctx
|
17
|
+
ret = block.call
|
18
|
+
rescue StackifyRubyAPM::InternalError
|
19
|
+
raise # Don't report StackifyRubyAPM errors
|
20
|
+
rescue StandardError => e
|
21
|
+
StackifyRubyAPM.report e
|
22
|
+
raise e
|
23
|
+
ensure
|
24
|
+
transaction.submit()
|
25
|
+
end
|
26
|
+
ret
|
27
|
+
end
|
28
|
+
|
29
|
+
# STACKIFY LAMBDA HANDLER
|
30
|
+
#
|
31
|
+
# @return original function execution
|
32
|
+
def self.stackify_handler(event:, context:)
|
33
|
+
begin
|
34
|
+
if !StackifyRubyAPM.running?
|
35
|
+
config = {
|
36
|
+
transport: 'logging',
|
37
|
+
queue: false,
|
38
|
+
}
|
39
|
+
StackifyRubyAPM.start config
|
40
|
+
end
|
41
|
+
|
42
|
+
ctx = StackifyRubyAPM::Context.new
|
43
|
+
ctx.add_aws_context({:arn => context.invoked_function_arn})
|
44
|
+
transaction = StackifyRubyAPM.transaction context.function_name, 'TASK', context: ctx
|
45
|
+
|
46
|
+
lambda_handler = StackifyRubyAPM.agent.config.lambda_handler.split('.')
|
47
|
+
function_file = lambda_handler[0]
|
48
|
+
function_name = lambda_handler[-1]
|
49
|
+
|
50
|
+
require "./#{function_file}"
|
51
|
+
|
52
|
+
send(function_name, event: event, context: context)
|
53
|
+
rescue StackifyRubyAPM::InternalError
|
54
|
+
raise # Don't report StackifyRubyAPM errors
|
55
|
+
rescue StandardError => e
|
56
|
+
StackifyRubyAPM.report e
|
57
|
+
raise e
|
58
|
+
ensure
|
59
|
+
transaction.submit()
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stackify-ruby-apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stackify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -143,18 +143,13 @@ executables: []
|
|
143
143
|
extensions: []
|
144
144
|
extra_rdoc_files: []
|
145
145
|
files:
|
146
|
-
- ".gemignore"
|
147
|
-
- ".gitignore"
|
148
|
-
- ".rspec"
|
149
|
-
- ".rubocop.yml"
|
150
|
-
- ".ruby-version"
|
151
146
|
- Gemfile
|
152
147
|
- Gemfile.lock
|
153
148
|
- LICENSE.md
|
154
149
|
- README.md
|
155
150
|
- Rakefile
|
156
|
-
- docker-compose.yml
|
157
151
|
- lib/proto/stackify_trace.rb
|
152
|
+
- lib/stackify-ruby-apm-lambda.rb
|
158
153
|
- lib/stackify-ruby-apm.rb
|
159
154
|
- lib/stackify_apm/agent.rb
|
160
155
|
- lib/stackify_apm/config.rb
|
@@ -218,6 +213,7 @@ files:
|
|
218
213
|
- lib/stackify_apm/transaction.rb
|
219
214
|
- lib/stackify_apm/transport/agent_base.rb
|
220
215
|
- lib/stackify_apm/transport/agent_http_client.rb
|
216
|
+
- lib/stackify_apm/transport/aws_lambda_logging.rb
|
221
217
|
- lib/stackify_apm/transport/log_client.rb
|
222
218
|
- lib/stackify_apm/transport/unix_socket_client.rb
|
223
219
|
- lib/stackify_apm/transport_selector.rb
|
@@ -230,6 +226,7 @@ files:
|
|
230
226
|
- lib/stackify_apm/version.rb
|
231
227
|
- lib/stackify_apm/worker.rb
|
232
228
|
- lib/stackify_ruby_apm.rb
|
229
|
+
- lib/stackify_ruby_apm_lambda.rb
|
233
230
|
- stackify-ruby-apm.gemspec
|
234
231
|
homepage: http://www.stackify.com
|
235
232
|
licenses:
|
data/.gemignore
DELETED
data/.gitignore
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
/.bundle/
|
2
|
-
/.yardoc
|
3
|
-
/_yardoc/
|
4
|
-
/coverage/
|
5
|
-
/doc/
|
6
|
-
/pkg/
|
7
|
-
/spec/reports/
|
8
|
-
/tmp/
|
9
|
-
|
10
|
-
# rspec failure tracking
|
11
|
-
.rspec_status
|
12
|
-
|
13
|
-
# Ignore bundler config.
|
14
|
-
/.bundle
|
15
|
-
|
16
|
-
# Ignore all logfiles and tempfiles.
|
17
|
-
/log/*
|
18
|
-
/tmp/*
|
19
|
-
!/log/.keep
|
20
|
-
!/tmp/.keep
|
21
|
-
|
22
|
-
# Ignore bundler config.
|
23
|
-
/.bundle
|
24
|
-
|
25
|
-
# Deps
|
26
|
-
node_modules
|
27
|
-
|
28
|
-
# IDE
|
29
|
-
.idea
|
30
|
-
|
31
|
-
# VS Code
|
32
|
-
.vscode
|
33
|
-
.vs
|
34
|
-
|
35
|
-
# Unnecessary since this will be a plugin
|
36
|
-
package-lock.json
|
37
|
-
|
38
|
-
# Folder config file
|
39
|
-
Desktop.ini
|
40
|
-
|
41
|
-
# Windows shortcuts
|
42
|
-
*.lnk
|
43
|
-
|
44
|
-
### OSX ###
|
45
|
-
/**/.DS_Store
|
46
|
-
/.DS_Store
|
47
|
-
.AppleDouble
|
48
|
-
.LSOverride
|
49
|
-
|
50
|
-
# Thumbnails
|
51
|
-
._*
|
52
|
-
|
53
|
-
# Files that might appear on external disk
|
54
|
-
.Spotlight-V100
|
55
|
-
.Trashes
|
56
|
-
|
57
|
-
### Linux ###
|
58
|
-
*~
|
59
|
-
|
60
|
-
# Windows image file caches
|
61
|
-
Thumbs.db
|
62
|
-
ehthumbs.db
|
63
|
-
|
64
|
-
# Ignore .byebug_history, .rspec, brakeman.html
|
65
|
-
.byebug_history
|
66
|
-
.rspec
|
67
|
-
brakeman.html
|
68
|
-
|
69
|
-
|
70
|
-
# Gem
|
71
|
-
*.gem
|
72
|
-
|
73
|
-
#bin
|
74
|
-
bin
|
75
|
-
bin/*
|
76
|
-
|
data/.rubocop.yml
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
AllCops:
|
2
|
-
RubyInterpreters:
|
3
|
-
- ruby
|
4
|
-
- macruby
|
5
|
-
- rake
|
6
|
-
- jruby
|
7
|
-
- rbx
|
8
|
-
|
9
|
-
Exclude:
|
10
|
-
- 'node_modules/**/*'
|
11
|
-
- 'vendor/**/*'
|
12
|
-
- '.git/**/*'
|
13
|
-
- 'spec/**/*'
|
14
|
-
|
15
|
-
Naming/FileName:
|
16
|
-
Exclude:
|
17
|
-
- 'lib/stackify-ruby-apm.rb'
|
18
|
-
|
19
|
-
Metrics/LineLength:
|
20
|
-
Max: 1000
|
21
|
-
|
22
|
-
Metrics/BlockLength:
|
23
|
-
Max: 1000
|
24
|
-
|
25
|
-
Metrics/MethodLength:
|
26
|
-
Enabled: false
|
27
|
-
|
28
|
-
Metrics/ClassLength:
|
29
|
-
Max: 1000
|
30
|
-
|
31
|
-
Style/RedundantFreeze:
|
32
|
-
Enabled: false
|
33
|
-
|
34
|
-
Style/SafeNavigation:
|
35
|
-
Enabled: false
|
36
|
-
|
37
|
-
Metrics/AbcSize:
|
38
|
-
Enabled: false
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
2.0.0-p648
|
data/docker-compose.yml
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
version: "3"
|
2
|
-
services:
|
3
|
-
|
4
|
-
dbpostgresql:
|
5
|
-
image: postgres
|
6
|
-
volumes:
|
7
|
-
- postgresdata:/var/lib/postgresql/data
|
8
|
-
ports:
|
9
|
-
- 5433:5432
|
10
|
-
environment:
|
11
|
-
POSTGRES_DB: PostgresTestDB
|
12
|
-
POSTGRES_USER: bob
|
13
|
-
POSTGRES_PASSWORD: password
|
14
|
-
container_name: sinatra_activerecord_postgres_test
|
15
|
-
|
16
|
-
dbmysql:
|
17
|
-
image: mysql:5.7
|
18
|
-
volumes:
|
19
|
-
- mysqldata:/var/lib/mysql
|
20
|
-
ports:
|
21
|
-
- 3305:3306
|
22
|
-
environment:
|
23
|
-
- MYSQL_ROOT_PASSWORD=secret
|
24
|
-
- MYSQL_USER=bob
|
25
|
-
- MYSQL_PASSWORD=password
|
26
|
-
- MYSQL_DATABASE=MysqlTestDB
|
27
|
-
container_name: sinatra_activerecord_mysql_test
|
28
|
-
|
29
|
-
dbmongo:
|
30
|
-
image: mongo:3.6
|
31
|
-
volumes:
|
32
|
-
- mongodata:/data/db
|
33
|
-
- mongodata_config:/data/configdb
|
34
|
-
ports:
|
35
|
-
- 27018:27017
|
36
|
-
container_name: sinatra_activerecord_mongo_test
|
37
|
-
|
38
|
-
volumes:
|
39
|
-
postgresdata:
|
40
|
-
driver: local
|
41
|
-
mysqldata:
|
42
|
-
driver: local
|
43
|
-
mongodata:
|
44
|
-
driver: local
|
45
|
-
mongodata_config:
|
46
|
-
driver: local
|