scout_apm 3.0.0.pre0 → 3.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: afa1d7b25baf3d94a547899966b95419ac560d07
4
- data.tar.gz: d6201d2acc85f2e94c92315b8193d110a4febd0a
3
+ metadata.gz: d4de385d6ffca53d6dd6e5de87fdc5e89fba54a3
4
+ data.tar.gz: 5147b662a5b8c7454b8fdf50d6e82cc24c6cbbf7
5
5
  SHA512:
6
- metadata.gz: 9a125da761d39c62094fc48ca66163bac787c0de62e9b6a3f38e984ae2ab8f41dcc7d271519764694c6b9a194e084c626bbf7b042a364ee56afe524c912fd83a
7
- data.tar.gz: cce9304bd70490eb711c92a6df03d656710c01eb0343f809e58d0bb49c02ec152a99f668b21f7e51424aec34e432286c0ec83f7cdc74cdc994300ae6aa0b4ed6
6
+ metadata.gz: 790273c395b6b7f96bc0ca6a260a3372ca931362991553a9d7fb544d30959c769552532e1873bd0b2d4e9e6e0f86e5567964a75f8b75cb66c8d67f411a1a2384
7
+ data.tar.gz: 37ab78f6e6f001ae3c82729daf9b1a008c61d5e70d9139795d4b80ae172de5100a82efe57ba3890301172902aa486a8404e8779914448c8b146584c246e8d0c4
data/CHANGELOG.markdown CHANGED
@@ -1,11 +1,19 @@
1
- # 2.2.0
1
+ # 3.0.0
2
2
 
3
3
  * ScoutProf BETA
4
4
 
5
+ # 2.1.8
6
+
7
+ * Adds Git revision detection, which is reported on app load and associated with transaction traces
8
+
9
+ # 2.1.7
10
+
11
+ * Fix allocations extension compilation on Ruby 1.8.7
12
+ >>>>>>> master
13
+
5
14
  # 2.1.6
6
15
 
7
16
  * Support older versions of Grape (0.10 onward)
8
- * Fix issue with complex AR queries
9
17
  * Vendor rusage library
10
18
  * Fix double-exit that caused error messages when running under Passenger
11
19
 
data/Rakefile CHANGED
@@ -23,4 +23,3 @@ require 'rake/extensiontask'
23
23
  Rake::ExtensionTask.new('allocations')
24
24
  Rake::ExtensionTask.new('stacks')
25
25
  Rake::ExtensionTask.new('rusage')
26
-
@@ -1,4 +1,8 @@
1
+ #ifdef HAVE_RUBY_RUBY_H
1
2
  #include <ruby/ruby.h>
3
+ #else // Ruby <= 1.8.7
4
+ #include <ruby.h>
5
+ #endif
2
6
 
3
7
  VALUE mScoutApm;
4
8
  VALUE mInstruments;
@@ -1,3 +1,4 @@
1
1
  require 'mkmf'
2
2
 
3
+ have_header("ruby/ruby.h") # Needed to check for Ruby <= 1.8.7
3
4
  create_makefile('allocations')
@@ -121,11 +121,6 @@ module ScoutApm
121
121
 
122
122
  @ignored_uris = ScoutApm::IgnoredUris.new(config.value('ignore'))
123
123
 
124
- if environment.deploy_integration
125
- logger.info "Starting monitoring for [#{environment.deploy_integration.name}]]."
126
- return environment.deploy_integration.install
127
- end
128
-
129
124
  load_instruments if should_load_instruments?(options)
130
125
 
131
126
  return false unless preconditions_met?(options)
@@ -330,10 +325,6 @@ module ScoutApm
330
325
  instance.install
331
326
  end
332
327
 
333
- def deploy_integration
334
- environment.deploy_integration
335
- end
336
-
337
328
  def app_server_missing?(options = {})
338
329
  !environment.app_server_integration(true).found? && !options[:skip_app_server_check]
339
330
  end
@@ -38,7 +38,8 @@ module ScoutApm
38
38
  :database_adapter => ScoutApm::Environment.instance.raw_database_adapter, # Raw
39
39
  :application_name => ScoutApm::Environment.instance.application_name,
40
40
  :libraries => ScoutApm::Utils::InstalledGems.new.run,
41
- :paas => ScoutApm::Environment.instance.platform_integration.name
41
+ :paas => ScoutApm::Environment.instance.platform_integration.name,
42
+ :git_sha => ScoutApm::Environment.instance.git_revision.sha
42
43
  }
43
44
  end
44
45
  end
@@ -41,13 +41,8 @@ module ScoutApm
41
41
  ScoutApm::PlatformIntegrations::Server.new,
42
42
  ]
43
43
 
44
- DEPLOY_INTEGRATIONS = [
45
- ScoutApm::DeployIntegrations::Capistrano3.new(STDOUT_LOGGER),
46
- # ScoutApm::DeployIntegrations::Capistrano2.new(STDOUT_LOGGER),
47
- ]
48
-
49
44
  def env
50
- @env ||= deploy_integration? ? deploy_integration.env : framework_integration.env
45
+ @env ||= framework_integration.env
51
46
  end
52
47
 
53
48
  def framework
@@ -88,7 +83,7 @@ module ScoutApm
88
83
  end
89
84
 
90
85
  def root
91
- @root ||= deploy_integration? ? deploy_integration.root : framework_root
86
+ @root ||= framework_root
92
87
  end
93
88
 
94
89
  def framework_root
@@ -110,6 +105,10 @@ module ScoutApm
110
105
  @hostname ||= Agent.instance.config.value("hostname") || platform_integration.hostname
111
106
  end
112
107
 
108
+ def git_revision
109
+ @git_revision ||= ScoutApm::GitRevision.new
110
+ end
111
+
113
112
  # Returns the whole integration object
114
113
  # This needs to be improved. Frequently, multiple app servers gem are present and which
115
114
  # ever is checked first becomes the designated app server.
@@ -143,14 +142,6 @@ module ScoutApm
143
142
  background_job_integration && background_job_integration.name
144
143
  end
145
144
 
146
- def deploy_integration
147
- @deploy_integration ||= DEPLOY_INTEGRATIONS.detect{ |integration| integration.present? }
148
- end
149
-
150
- def deploy_integration?
151
- !@deploy_integration.nil?
152
- end
153
-
154
145
  ### ruby checks
155
146
 
156
147
  def rubinius?
@@ -0,0 +1,51 @@
1
+ module ScoutApm
2
+ class GitRevision
3
+
4
+ attr_accessor :sha
5
+
6
+ def initialize
7
+ @sha = detect
8
+ ScoutApm::Agent.instance.logger.debug "Detected Git Revision [#{@sha}]"
9
+ end
10
+
11
+ private
12
+
13
+ def detect
14
+ detect_from_env_var ||
15
+ detect_from_heroku ||
16
+ detect_from_capistrano ||
17
+ detect_from_git
18
+ end
19
+
20
+ def detect_from_heroku
21
+ ENV['HEROKU_SLUG_COMMIT']
22
+ end
23
+
24
+ def detect_from_env_var
25
+ ENV['SCOUT_REVISION_SHA']
26
+ end
27
+
28
+ def detect_from_capistrano
29
+ version = File.read(File.join(app_root, 'REVISION')).strip
30
+ # Capistrano 3.0 - 3.1.x
31
+ version || File.open(File.join(app_root, '..', 'revisions.log')).to_a.last.strip.sub(/.*as release ([0-9]+).*/, '\1')
32
+ rescue
33
+ ScoutApm::Agent.instance.logger.debug "Unable to detect Git Revision from Capistrano: #{$!.message}"
34
+ nil
35
+ end
36
+
37
+ def detect_from_git
38
+ if File.directory?(".git")
39
+ `git rev-parse --short HEAD`.strip
40
+ end
41
+ rescue
42
+ ScoutApm::Agent.instance.logger.debug "Unable to detect Git Revision from Git: #{$!.message}"
43
+ nil
44
+ end
45
+
46
+ def app_root
47
+ ScoutApm::Environment.instance.root
48
+ end
49
+
50
+ end
51
+ end
@@ -21,6 +21,7 @@ module ScoutApm
21
21
  "allocations" => job.allocations,
22
22
  "seconds_since_startup" => job.seconds_since_startup,
23
23
  "hostname" => job.hostname,
24
+ "git_sha" => job.git_sha,
24
25
  "metrics" => MetricsToJsonSerializer.new(job.metrics).as_json, # New style of metrics
25
26
  "allocation_metrics" => MetricsToJsonSerializer.new(job.allocation_metrics).as_json, # New style of metrics
26
27
  "context" => job.context.to_hash,
@@ -20,6 +20,7 @@ module ScoutApm
20
20
  attr_reader :hostname
21
21
  attr_reader :seconds_since_startup
22
22
  attr_reader :score
23
+ attr_reader :git_sha
23
24
 
24
25
  def initialize(queue_name, job_name, time, total_time, exclusive_time, context, metrics, allocation_metrics, mem_delta, allocations, score)
25
26
  @queue_name = queue_name
@@ -34,6 +35,7 @@ module ScoutApm
34
35
  @allocations = allocations
35
36
  @seconds_since_startup = (Time.now - ScoutApm::Agent.instance.process_start_time)
36
37
  @hostname = ScoutApm::Environment.instance.hostname
38
+ @git_sha = ScoutApm::Environment.instance.git_revision.sha
37
39
  @score = score
38
40
  ScoutApm::Agent.instance.logger.debug { "Slow Job [#{metric_name}] - Call Time: #{total_call_time} Mem Delta: #{mem_delta}"}
39
41
  end
@@ -15,6 +15,7 @@ module ScoutApm
15
15
  attr_reader :allocations
16
16
  attr_accessor :hostname # hack - we need to reset these server side.
17
17
  attr_accessor :seconds_since_startup # hack - we need to reset these server side.
18
+ attr_accessor :git_sha # hack - we need to reset these server side.
18
19
 
19
20
  def initialize(uri, metric_name, total_call_time, metrics, allocation_metrics, context, time, raw_stackprof, mem_delta, allocations, score)
20
21
  @uri = uri
@@ -30,6 +31,7 @@ module ScoutApm
30
31
  @seconds_since_startup = (Time.now - ScoutApm::Agent.instance.process_start_time)
31
32
  @hostname = ScoutApm::Environment.instance.hostname
32
33
  @score = score
34
+ @git_sha = ScoutApm::Environment.instance.git_revision.sha
33
35
  ScoutApm::Agent.instance.logger.debug { "Slow Request [#{uri}] - Call Time: #{total_call_time} Mem Delta: #{mem_delta} Score: #{score}"}
34
36
  end
35
37
 
@@ -44,7 +46,7 @@ module ScoutApm
44
46
  end
45
47
 
46
48
  def as_json
47
- json_attributes = [:key, :time, :total_call_time, :uri, [:context, :context_hash], :score, :prof, :mem_delta, :allocations, :seconds_since_startup, :hostname]
49
+ json_attributes = [:key, :time, :total_call_time, :uri, [:context, :context_hash], :score, :prof, :mem_delta, :allocations, :seconds_since_startup, :hostname, :git_sha]
48
50
  ScoutApm::AttributeArranger.call(self, json_attributes)
49
51
  end
50
52
 
@@ -1,4 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "3.0.0.pre0"
3
- end
4
-
2
+ VERSION = "3.0.0.pre1"
3
+ end
data/lib/scout_apm.rb CHANGED
@@ -63,9 +63,6 @@ require 'scout_apm/platform_integrations/server'
63
63
 
64
64
  require 'scout_apm/histogram'
65
65
 
66
- require 'scout_apm/deploy_integrations/capistrano_3'
67
- # require 'scout_apm/deploy_integrations/capistrano_2'
68
-
69
66
  require 'scout_apm/instruments/net_http'
70
67
  require 'scout_apm/instruments/http_client'
71
68
  require 'scout_apm/instruments/moped'
@@ -136,6 +133,7 @@ require 'scout_apm/request_histograms'
136
133
 
137
134
  require 'scout_apm/capacity'
138
135
  require 'scout_apm/attribute_arranger'
136
+ require 'scout_apm/git_revision'
139
137
 
140
138
  require 'scout_apm/serializers/payload_serializer'
141
139
  require 'scout_apm/serializers/payload_serializer_to_json'
@@ -144,7 +142,6 @@ require 'scout_apm/serializers/slow_jobs_serializer_to_json'
144
142
  require 'scout_apm/serializers/metrics_to_json_serializer'
145
143
  require 'scout_apm/serializers/directive_serializer'
146
144
  require 'scout_apm/serializers/app_server_load_serializer'
147
- require 'scout_apm/serializers/deploy_serializer'
148
145
 
149
146
  require 'scout_apm/middleware'
150
147
 
@@ -0,0 +1,15 @@
1
+ require 'test_helper'
2
+
3
+ require 'scout_apm/git_revision'
4
+
5
+ class GitRevisionTest < Minitest::Test
6
+ # TODO - other tests that would be nice:
7
+ # * ensure we only detect once, on initialize.
8
+ # * tests for reading cap files
9
+
10
+ def test_sha_from_heroku
11
+ ENV['HEROKU_SLUG_COMMIT'] = 'heroku_slug'
12
+ revision = ScoutApm::GitRevision.new
13
+ assert_equal 'heroku_slug', revision.sha
14
+ end
15
+ end
@@ -78,6 +78,7 @@ class PayloadSerializerTest < Minitest::Test
78
78
  "min_call_time" => 0.000613518,
79
79
  "total_call_time" => 0.033245704,
80
80
  "total_exclusive_time" => 0.033245704,
81
+ "traces" => []
81
82
  },
82
83
  {
83
84
  "key" => {
@@ -92,6 +93,7 @@ class PayloadSerializerTest < Minitest::Test
92
93
  "min_call_time" => 0.034881757,
93
94
  "total_call_time" => 0.113403176,
94
95
  "total_exclusive_time" => 0.07813208899999999,
96
+ "traces" => []
95
97
  }
96
98
  ]
97
99
  assert_equal formatted_metrics, JSON.parse(payload)["metrics"]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout_apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.pre0
4
+ version: 3.0.0.pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Haynes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-08-29 00:00:00.000000000 Z
12
+ date: 2016-09-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -135,16 +135,13 @@ files:
135
135
  - lib/scout_apm/capacity.rb
136
136
  - lib/scout_apm/config.rb
137
137
  - lib/scout_apm/context.rb
138
- - lib/scout_apm/deploy_integrations/capistrano_2.cap
139
- - lib/scout_apm/deploy_integrations/capistrano_2.rb
140
- - lib/scout_apm/deploy_integrations/capistrano_3.cap
141
- - lib/scout_apm/deploy_integrations/capistrano_3.rb
142
138
  - lib/scout_apm/environment.rb
143
139
  - lib/scout_apm/fake_store.rb
144
140
  - lib/scout_apm/framework_integrations/rails_2.rb
145
141
  - lib/scout_apm/framework_integrations/rails_3_or_4.rb
146
142
  - lib/scout_apm/framework_integrations/ruby.rb
147
143
  - lib/scout_apm/framework_integrations/sinatra.rb
144
+ - lib/scout_apm/git_revision.rb
148
145
  - lib/scout_apm/histogram.rb
149
146
  - lib/scout_apm/ignored_uris.rb
150
147
  - lib/scout_apm/instant/assets/xmlhttp_instrumentation.html
@@ -195,7 +192,6 @@ files:
195
192
  - lib/scout_apm/request_manager.rb
196
193
  - lib/scout_apm/scored_item_set.rb
197
194
  - lib/scout_apm/serializers/app_server_load_serializer.rb
198
- - lib/scout_apm/serializers/deploy_serializer.rb
199
195
  - lib/scout_apm/serializers/directive_serializer.rb
200
196
  - lib/scout_apm/serializers/jobs_serializer_to_json.rb
201
197
  - lib/scout_apm/serializers/metrics_to_json_serializer.rb
@@ -238,6 +234,7 @@ files:
238
234
  - test/unit/config_test.rb
239
235
  - test/unit/context_test.rb
240
236
  - test/unit/environment_test.rb
237
+ - test/unit/git_revision_test.rb
241
238
  - test/unit/histogram_test.rb
242
239
  - test/unit/ignored_uris_test.rb
243
240
  - test/unit/instruments/active_record_instruments_test.rb
@@ -273,8 +270,30 @@ required_rubygems_version: !ruby/object:Gem::Requirement
273
270
  version: 1.3.1
274
271
  requirements: []
275
272
  rubyforge_project: scout_apm
276
- rubygems_version: 2.2.2
273
+ rubygems_version: 2.4.6
277
274
  signing_key:
278
275
  specification_version: 4
279
276
  summary: Ruby application performance monitoring
280
- test_files: []
277
+ test_files:
278
+ - test/data/config_test_1.yml
279
+ - test/test_helper.rb
280
+ - test/unit/agent_test.rb
281
+ - test/unit/background_job_integrations/sidekiq_test.rb
282
+ - test/unit/config_test.rb
283
+ - test/unit/context_test.rb
284
+ - test/unit/environment_test.rb
285
+ - test/unit/git_revision_test.rb
286
+ - test/unit/histogram_test.rb
287
+ - test/unit/ignored_uris_test.rb
288
+ - test/unit/instruments/active_record_instruments_test.rb
289
+ - test/unit/layaway_test.rb
290
+ - test/unit/metric_set_test.rb
291
+ - test/unit/scored_item_set_test.rb
292
+ - test/unit/serializers/payload_serializer_test.rb
293
+ - test/unit/slow_job_policy_test.rb
294
+ - test/unit/slow_request_policy_test.rb
295
+ - test/unit/sql_sanitizer_test.rb
296
+ - test/unit/store_test.rb
297
+ - test/unit/utils/active_record_metric_name_test.rb
298
+ - test/unit/utils/backtrace_parser_test.rb
299
+ has_rdoc:
@@ -1,12 +0,0 @@
1
- namespace :scout_apm do
2
- namespace :deploy do
3
- task :starting do
4
- # Warn if missing scout apm deploy creds?
5
- end
6
- task :finished do
7
- ScoutApm::Agent.instance.deploy_integration.report
8
- end
9
- end
10
- end
11
-
12
- after 'deploy:finished', 'scout_apm:deploy:finished'
@@ -1,83 +0,0 @@
1
- require 'scout_apm'
2
-
3
- module ScoutApm
4
- module DeployIntegrations
5
- class Capistrano2
6
- attr_reader :logger
7
-
8
- def initialize(logger)
9
- @logger = logger
10
- @cap = defined?(Capistrano::Configuration) ? ObjectSpace.each_object(Capistrano::Configuration).map.first : nil rescue nil
11
- end
12
-
13
- def name
14
- :capistrano_2
15
- end
16
-
17
- def version
18
- present? ? Capistrano::VERSION : nil
19
- end
20
-
21
- def present?
22
- if !@cap.nil? && @cap.is_a?(Capistrano::Configuration)
23
- require 'capistrano/version'
24
- defined?(Capistrano::VERSION) && Gem::Dependency.new('', '~> 2.0').match?('', Capistrano::VERSION.to_s)
25
- else
26
- return false
27
- end
28
- return true
29
- rescue
30
- return false
31
- end
32
-
33
- def install
34
- logger.debug "Initializing Capistrano2 Deploy Integration."
35
- @cap.load File.expand_path("../capistrano_2.cap", __FILE__)
36
- end
37
-
38
- def root
39
- '.'
40
- end
41
-
42
- def env
43
- @cap.fetch(:stage)
44
- end
45
-
46
- def found?
47
- true
48
- end
49
-
50
- def report
51
- if reporter.can_report?
52
- data = deploy_data
53
- logger.debug "Sending deploy hook data: #{data}"
54
- payload = ScoutApm::Serializers::DeploySerializer.serialize(data)
55
- reporter.report(payload, ScoutApm::Serializers::DeploySerializer::HTTP_HEADERS)
56
- else
57
- logger.warn "Unable to post deploy hook data"
58
- end
59
- end
60
-
61
- def reporter
62
- @reporter ||= ScoutApm::Reporter.new(:deploy_hook, ScoutApm::Agent.instance.config, @logger)
63
- end
64
-
65
- def deploy_data
66
- {:revision => current_revision, :branch => branch, :deployed_by => deployed_by}
67
- end
68
-
69
- def branch
70
- @cap.fetch(:branch)
71
- end
72
-
73
- def current_revision
74
- @cap.fetch(:current_revision) || `git rev-list --max-count=1 --abbrev-commit --abbrev=12 #{branch}`.chomp
75
- end
76
-
77
- def deployed_by
78
- ScoutApm::Agent.instance.config.value('deployed_by')
79
- end
80
-
81
- end
82
- end
83
- end
@@ -1,12 +0,0 @@
1
- namespace :scout_apm do
2
- namespace :deploy do
3
- task :starting do
4
- # Warn if missing scout apm deploy creds?
5
- end
6
- task :finished do
7
- ScoutApm::Agent.instance.deploy_integration.report
8
- end
9
- end
10
- end
11
-
12
- after 'deploy:finished', 'scout_apm:deploy:finished'
@@ -1,88 +0,0 @@
1
- require 'scout_apm'
2
-
3
- module ScoutApm
4
- module DeployIntegrations
5
- class Capistrano3
6
- attr_reader :logger
7
-
8
- def initialize(logger)
9
- @logger = logger
10
- @cap = Rake.application rescue nil
11
- end
12
-
13
- def name
14
- :capistrano_3
15
- end
16
-
17
- def version
18
- present? ? Capistrano::VERSION : nil
19
- end
20
-
21
- def present?
22
- if !@cap.nil? && @cap.is_a?(Capistrano::Application)
23
- require 'capistrano/version'
24
- defined?(Capistrano::VERSION) && Gem::Dependency.new('', '~> 3.0').match?('', Capistrano::VERSION.to_s)
25
- else
26
- return false
27
- end
28
- rescue
29
- return false
30
- end
31
-
32
- def install
33
- logger.debug "Initializing Capistrano3 Deploy Integration."
34
- load File.expand_path("../capistrano_3.cap", __FILE__)
35
- end
36
-
37
- def root
38
- '.'
39
- end
40
-
41
- def env
42
- @cap.fetch(:stage).to_s
43
- end
44
-
45
- def found?
46
- true
47
- end
48
-
49
- def report
50
- if reporter.can_report?
51
- data = deploy_data
52
- logger.debug "Sending deploy hook data: #{data}"
53
- payload = ScoutApm::Serializers::DeploySerializer.serialize(data)
54
- reporter.report(payload, ScoutApm::Serializers::DeploySerializer::HTTP_HEADERS)
55
- else
56
- logger.warn "Unable to post deploy hook data"
57
- end
58
- end
59
-
60
- def reporter
61
- config = if env == ''
62
- ScoutApm::Agent.instance.config
63
- else
64
- ScoutApm::Config.with_file(nil, {:file => { :environment => env }}) # instantiate our own config, with an overridden environment for the deploy-to app name instead of deploy-from app name)
65
- end
66
-
67
- @reporter ||= ScoutApm::Reporter.new(:deploy_hook, config, @logger)
68
- end
69
-
70
- def deploy_data
71
- {:revision => current_revision, :branch => branch, :deployed_by => deployed_by}
72
- end
73
-
74
- def branch
75
- @cap.fetch(:branch)
76
- end
77
-
78
- def current_revision
79
- @cap.fetch(:current_revision) || `git rev-list --max-count=1 --abbrev-commit --abbrev=12 #{branch}`.chomp
80
- end
81
-
82
- def deployed_by
83
- ScoutApm::Agent.instance.config.value('deployed_by')
84
- end
85
-
86
- end
87
- end
88
- end
@@ -1,16 +0,0 @@
1
- # Serialize & deserialize deploy data up to the APM server
2
- module ScoutApm
3
- module Serializers
4
- class DeploySerializer
5
- HTTP_HEADERS = {'Content-Type' => 'application/x-www-form-urlencoded'}
6
-
7
- def self.serialize(data)
8
- URI.encode_www_form(data)
9
- end
10
-
11
- def self.deserialize(data)
12
- Marshal.load(data)
13
- end
14
- end
15
- end
16
- end