traceview 3.8.1 → 3.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +43 -0
- data/.travis.yml +5 -4
- data/CHANGELOG.md +114 -114
- data/Gemfile +5 -6
- data/README.md +3 -3
- data/Rakefile +18 -21
- data/examples/DNT.md +3 -3
- data/examples/carrying_context.rb +26 -31
- data/examples/instrumenting_metal_controller.rb +1 -1
- data/examples/puma_on_heroku_config.rb +3 -3
- data/examples/tracing_async_threads.rb +9 -9
- data/examples/tracing_background_jobs.rb +5 -7
- data/examples/tracing_forked_processes.rb +13 -14
- data/examples/unicorn_on_heroku_config.rb +4 -4
- data/gemfiles/rails50.gemfile +1 -1
- data/lib/joboe_metal.rb +2 -5
- data/lib/oboe/backward_compatibility.rb +3 -5
- data/lib/oboe_metal.rb +37 -43
- data/lib/traceview/api/logging.rb +1 -2
- data/lib/traceview/base.rb +3 -0
- data/lib/traceview/config.rb +19 -3
- data/lib/traceview/frameworks/rails/inst/action_controller.rb +2 -2
- data/lib/traceview/frameworks/rails/inst/{action_controller5_api.rb → action_controller_api.rb} +0 -0
- data/lib/traceview/frameworks/rails/inst/connection_adapters/utils.rb +2 -2
- data/lib/traceview/frameworks/rails/inst/connection_adapters/utils5x.rb +3 -4
- data/lib/traceview/inst/httpclient.rb +12 -12
- data/lib/traceview/inst/mongo2.rb +7 -8
- data/lib/traceview/inst/moped.rb +334 -343
- data/lib/traceview/inst/rack.rb +14 -2
- data/lib/traceview/inst/redis.rb +104 -110
- data/lib/traceview/inst/sequel.rb +43 -37
- data/lib/traceview/inst/twitter-cassandra.rb +12 -6
- data/lib/traceview/support.rb +34 -32
- data/lib/traceview/util.rb +7 -4
- data/lib/traceview/version.rb +1 -1
- data/test/instrumentation/curb_test.rb +2 -24
- data/test/instrumentation/httpclient_test.rb +7 -18
- data/test/instrumentation/{cassandra_test.rb → twitter-cassandra_test.rb} +32 -0
- data/test/minitest_helper.rb +0 -3
- data/test/settings +0 -0
- data/test/support/avw_handling_test.rb +13 -23
- data/test/support/config_test.rb +1 -1
- data/test/support/tracing_mode_test.rb +44 -0
- metadata +10 -6
data/Gemfile
CHANGED
@@ -15,13 +15,13 @@ group :development, :test do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
group :development do
|
18
|
-
gem 'ruby-debug', :platforms => [
|
19
|
-
gem 'debugger', :platform =>
|
20
|
-
gem 'byebug', :platforms => [
|
21
|
-
# gem 'perftools.rb', :platforms => [ :mri_20, :mri_21 ], :require => 'perftools'
|
18
|
+
gem 'ruby-debug', :platforms => [:mri_18, :jruby]
|
19
|
+
gem 'debugger', :platform => :mri_19
|
20
|
+
gem 'byebug', :platforms => [:mri_20, :mri_21, :mri_22]
|
21
|
+
# gem 'perftools.rb', :platforms => [ :mri_20, :mri_21 ], :require => 'perftools'
|
22
22
|
if RUBY_VERSION > '1.8.7'
|
23
23
|
gem 'pry'
|
24
|
-
gem 'pry-byebug', :platforms => [
|
24
|
+
gem 'pry-byebug', :platforms => [:mri_20, :mri_21, :mri_22]
|
25
25
|
else
|
26
26
|
gem 'pry', '0.9.12.4'
|
27
27
|
end
|
@@ -34,4 +34,3 @@ else
|
|
34
34
|
end
|
35
35
|
|
36
36
|
gemspec
|
37
|
-
|
data/README.md
CHANGED
@@ -8,9 +8,9 @@ It has the ability to report performance metrics on an array of libraries, datab
|
|
8
8
|
|
9
9
|
It requires a [TraceView](http://www.appneta.com/products/traceview/) account to view metrics. Get yours, [it's free](http://www.appneta.com/products/traceview-free-account/).
|
10
10
|
|
11
|
-
[![Gem Version](https://badge.fury.io/rb/traceview.png)](
|
11
|
+
[![Gem Version](https://badge.fury.io/rb/traceview.png)](https://badge.fury.io/rb/traceview)
|
12
12
|
[![Build Status](https://travis-ci.org/appneta/ruby-traceview.png?branch=master)](https://travis-ci.org/appneta/ruby-traceview)
|
13
|
-
[![Code Climate](https://codeclimate.com/github/appneta/
|
13
|
+
[![Code Climate](https://codeclimate.com/github/appneta/ruby-traceview.png)](https://codeclimate.com/github/appneta/ruby-traceview)
|
14
14
|
|
15
15
|
_Note: The repository name has been changed to ruby-traceview. Please update your github remotes with `git remote set-url origin git@github.com:appneta/ruby-traceview.git`._
|
16
16
|
|
@@ -301,7 +301,7 @@ That is a very quick example of a simple instrumentation implementation. If you
|
|
301
301
|
|
302
302
|
Some other tips and guidelines:
|
303
303
|
|
304
|
-
* You can point your Gemfile directly at your cloned traceview gem source by using `gem 'traceview', :path => '/path/to/
|
304
|
+
* You can point your Gemfile directly at your cloned traceview gem source by using `gem 'traceview', :path => '/path/to/ruby-traceview'`
|
305
305
|
|
306
306
|
* If instrumenting a library, database or service, place your new instrumentation file into the `lib/traceview/inst/` directory. From there, the traceview gem will detect it and automatically load the instrumentation file.
|
307
307
|
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ Rake::TestTask.new do |t|
|
|
9
9
|
t.verbose = false
|
10
10
|
t.warning = false
|
11
11
|
t.ruby_opts = []
|
12
|
-
t.libs <<
|
12
|
+
t.libs << 'test'
|
13
13
|
|
14
14
|
# Since we support so many libraries and frameworks, tests
|
15
15
|
# runs are segmented into gemfiles that have different
|
@@ -45,13 +45,13 @@ Rake::TestTask.new do |t|
|
|
45
45
|
end
|
46
46
|
|
47
47
|
if defined?(JRUBY_VERSION)
|
48
|
-
t.ruby_opts << [
|
48
|
+
t.ruby_opts << ['-J-javaagent:/usr/local/tracelytics/tracelyticsagent.jar']
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
desc "Build the gem's c extension"
|
53
53
|
task :compile do
|
54
|
-
|
54
|
+
if !defined?(JRUBY_VERSION)
|
55
55
|
puts "== Building the c extension against Ruby #{RUBY_VERSION}"
|
56
56
|
|
57
57
|
pwd = Dir.pwd
|
@@ -66,64 +66,61 @@ task :compile do
|
|
66
66
|
sh '/usr/bin/env make'
|
67
67
|
File.delete symlink if File.exist? symlink
|
68
68
|
|
69
|
-
if File.
|
69
|
+
if File.exist? so_file
|
70
70
|
File.symlink so_file, symlink
|
71
71
|
Dir.chdir pwd
|
72
72
|
puts "== Extension built and symlink'd to #{symlink}"
|
73
73
|
else
|
74
74
|
Dir.chdir pwd
|
75
|
-
puts
|
76
|
-
puts
|
75
|
+
puts '!! Extension failed to build (see above). Are the base TraceView packages installed?'
|
76
|
+
puts '!! See https://docs.appneta.com/install-instrumentation'
|
77
77
|
end
|
78
78
|
else
|
79
|
-
puts
|
79
|
+
puts '== Nothing to do under JRuby.'
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
-
desc
|
83
|
+
desc 'Clean up extension build files'
|
84
84
|
task :clean do
|
85
|
-
|
85
|
+
if !defined?(JRUBY_VERSION)
|
86
86
|
pwd = Dir.pwd
|
87
87
|
ext_dir = File.expand_path('ext/oboe_metal')
|
88
|
-
lib_dir = File.expand_path('lib')
|
89
88
|
symlink = File.expand_path('lib/oboe_metal.so')
|
90
|
-
so_file = File.expand_path('ext/oboe_metal/oboe_metal.so')
|
91
89
|
|
90
|
+
File.delete symlink if File.exist? symlink
|
92
91
|
Dir.chdir ext_dir
|
93
92
|
sh '/usr/bin/env make clean'
|
94
93
|
|
95
94
|
Dir.chdir pwd
|
96
95
|
else
|
97
|
-
puts
|
96
|
+
puts '== Nothing to do under JRuby.'
|
98
97
|
end
|
99
98
|
end
|
100
99
|
|
101
|
-
desc
|
100
|
+
desc 'Remove all built files and extensions'
|
102
101
|
task :distclean do
|
103
|
-
|
102
|
+
if !defined?(JRUBY_VERSION)
|
104
103
|
pwd = Dir.pwd
|
105
104
|
ext_dir = File.expand_path('ext/oboe_metal')
|
106
|
-
lib_dir = File.expand_path('lib')
|
107
105
|
symlink = File.expand_path('lib/oboe_metal.so')
|
108
|
-
so_file = File.expand_path('ext/oboe_metal/oboe_metal.so')
|
109
106
|
mkmf_log = File.expand_path('ext/oboe_metal/mkmf.log')
|
110
107
|
|
111
|
-
if File.
|
108
|
+
if File.exist? mkmf_log
|
112
109
|
File.delete symlink if File.exist? symlink
|
113
110
|
Dir.chdir ext_dir
|
114
111
|
sh '/usr/bin/env make distclean'
|
115
112
|
|
116
113
|
Dir.chdir pwd
|
117
114
|
else
|
118
|
-
puts
|
115
|
+
puts 'Nothing to distclean. (nothing built yet?)'
|
119
116
|
end
|
120
117
|
else
|
121
|
-
puts
|
118
|
+
puts '== Nothing to do under JRuby.'
|
122
119
|
end
|
123
120
|
end
|
124
121
|
|
125
122
|
desc "Rebuild the gem's c extension"
|
126
|
-
task :recompile => [
|
123
|
+
task :recompile => [:distclean, :compile]
|
127
124
|
|
128
125
|
task :environment do
|
129
126
|
ENV['TRACEVIEW_GEM_VERBOSE'] = 'true'
|
@@ -146,6 +143,6 @@ task :console => :environment do
|
|
146
143
|
end
|
147
144
|
|
148
145
|
# Used when testing Resque locally
|
149
|
-
task
|
146
|
+
task 'resque:setup' => :environment do
|
150
147
|
require 'resque/tasks'
|
151
148
|
end
|
data/examples/DNT.md
CHANGED
@@ -3,7 +3,7 @@ for common static files. Examples of such files may be images,
|
|
3
3
|
javascript, pdfs and text files.
|
4
4
|
|
5
5
|
This is done by using the regular expression stored in
|
6
|
-
`
|
6
|
+
`TraceView::Config[:dnt_regexp]`:
|
7
7
|
|
8
8
|
.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|ttf|woff|svg|less)$
|
9
9
|
|
@@ -14,7 +14,7 @@ To replace the pattern in use, you can update this regular expression
|
|
14
14
|
string. Here are some examples.
|
15
15
|
|
16
16
|
If you prefer that you want your javascript and CSS files instrumented,
|
17
|
-
you can update `
|
17
|
+
you can update `TraceView::Config[:dnt_regexp]` with an updated regexp
|
18
18
|
pattern (without the "js" and "css" entries):
|
19
19
|
|
20
20
|
.(jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|flv|swf|ttf|woff|svg|less)$
|
@@ -26,7 +26,7 @@ regexp [negative
|
|
26
26
|
look-behind](http://www.regular-expressions.info/lookaround.html) that
|
27
27
|
isn't supported in Ruby 1.8):
|
28
28
|
|
29
|
-
|
29
|
+
TraceView::Config[:dnt_regexp] = "(\.js$)(?<!show.js)"
|
30
30
|
|
31
31
|
Since this pattern is used with the standard Ruby Regexp class, you can
|
32
32
|
use any Regexp supported pattern. See the documentation on Ruby Regexp
|
@@ -9,23 +9,23 @@
|
|
9
9
|
# in the TraceView dashboard.
|
10
10
|
#
|
11
11
|
# Tracing context is non-existent until established by calling
|
12
|
-
# `
|
12
|
+
# `TraceView::API.start_trace` or `TraceView::API.log_start`. Those methods
|
13
13
|
# are part of the high-level and low-level API respectively.
|
14
14
|
#
|
15
15
|
# After a tracing context is established, that context can be
|
16
|
-
# continued by calling `
|
16
|
+
# continued by calling `TraceView::API.trace` or `TraceView::API.log_entry`.
|
17
17
|
# These methods will advance an existing context but not start
|
18
18
|
# new one.
|
19
19
|
#
|
20
20
|
# For example, when a web request comes into a stack, a tracing
|
21
|
-
# context is established using `
|
22
|
-
# enters through the rack middleware via `::
|
21
|
+
# context is established using `TraceView::API.log_start` as the request
|
22
|
+
# enters through the rack middleware via `::TraceView::Rack`.
|
23
23
|
#
|
24
|
-
# That tracing context is then continued using `
|
25
|
-
# `
|
24
|
+
# That tracing context is then continued using `TraceView::API.trace` or
|
25
|
+
# `TraceView::API.log_entry` for each subsequent layer such as Rails,
|
26
26
|
# ActiveRecord, Redis, Memcache, ActionView, Mongo (etc...) until
|
27
27
|
# finally request processing is complete and the tracing context
|
28
|
-
# is cleared (
|
28
|
+
# is cleared (TraceView::Context.clear)
|
29
29
|
#
|
30
30
|
|
31
31
|
###############################################################
|
@@ -33,9 +33,9 @@
|
|
33
33
|
###############################################################
|
34
34
|
#
|
35
35
|
# The tracing context exists in the form of an X-Trace string and
|
36
|
-
# can be retrieved using '
|
36
|
+
# can be retrieved using 'TraceView::Context.toString'
|
37
37
|
#
|
38
|
-
# xtrace =
|
38
|
+
# xtrace = TraceView::Context.toString
|
39
39
|
#
|
40
40
|
# => "1B4EDAB9E028CA3C81BCD57CC4644B4C4AE239C7B713F0BCB9FAD6D562"
|
41
41
|
#
|
@@ -44,7 +44,7 @@
|
|
44
44
|
#
|
45
45
|
# xtrace = "1B4EDAB9E028CA3C81BCD57CC4644B4C4AE239C7B713F0BCB9FAD6D562"
|
46
46
|
#
|
47
|
-
#
|
47
|
+
# TraceView::Context.fromString(xtrace)
|
48
48
|
#
|
49
49
|
# With these two methods, context can be passed across threads,
|
50
50
|
# processes (via fork) and in requests (such as external HTTP
|
@@ -81,25 +81,23 @@
|
|
81
81
|
# Thread - with separated traces
|
82
82
|
###############################################################
|
83
83
|
|
84
|
-
|
84
|
+
TraceView::API.log_start('parent')
|
85
85
|
|
86
86
|
# Get the work to be done
|
87
87
|
job = get_work
|
88
88
|
|
89
89
|
Thread.new do
|
90
90
|
# This is a new thread so there is no pre-existing context so
|
91
|
-
# we'll call `
|
92
|
-
|
91
|
+
# we'll call `TraceView::API.log_start` to start a new trace context.
|
92
|
+
TraceView::API.log_start('worker_thread', :job_id => job.id)
|
93
93
|
|
94
94
|
# Do the work
|
95
95
|
do_the_work(job)
|
96
96
|
|
97
|
-
|
97
|
+
TraceView::API.log_end('worker_thread')
|
98
98
|
end
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
100
|
+
TraceView::API.log_end('parent')
|
103
101
|
|
104
102
|
###############################################################
|
105
103
|
#
|
@@ -122,27 +120,27 @@ Oboe::API.log_end('parent')
|
|
122
120
|
# trace generated in that thread to be asynchronous using
|
123
121
|
# the `Async` flag.
|
124
122
|
|
125
|
-
|
123
|
+
TraceView::API.log_start('parent')
|
126
124
|
|
127
125
|
# Save the context to be imported in spawned thread
|
128
|
-
tracing_context =
|
126
|
+
tracing_context = TraceView::Context.toString
|
129
127
|
|
130
128
|
# Get the work to be done
|
131
129
|
job = get_work
|
132
130
|
|
133
131
|
Thread.new do
|
134
132
|
# Restore context
|
135
|
-
|
133
|
+
TraceView::Context.fromString(tracing_context)
|
136
134
|
|
137
|
-
|
135
|
+
TraceView::API.log_entry('worker_thread')
|
138
136
|
|
139
137
|
# Do the work
|
140
138
|
do_the_work(job)
|
141
139
|
|
142
|
-
|
140
|
+
TraceView::API.log_exit('worker_thread', :Async => 1)
|
143
141
|
end
|
144
142
|
|
145
|
-
|
143
|
+
TraceView::API.log_end('parent')
|
146
144
|
|
147
145
|
###############################################################
|
148
146
|
#
|
@@ -161,8 +159,7 @@ Oboe::API.log_end('parent')
|
|
161
159
|
# Process via fork - with separated traces
|
162
160
|
###############################################################
|
163
161
|
|
164
|
-
|
165
|
-
|
162
|
+
TraceView::API.start_trace('parent_process') do
|
166
163
|
# Get some work to process
|
167
164
|
job = get_job
|
168
165
|
|
@@ -170,9 +167,9 @@ Oboe::API.start_trace('parent_process') do
|
|
170
167
|
fork do
|
171
168
|
# Since fork does a complete process copy, the tracing_context still exists
|
172
169
|
# so we have to clear it and start again.
|
173
|
-
|
170
|
+
TraceView::Context.clear
|
174
171
|
|
175
|
-
|
172
|
+
TraceView::API.start_trace('worker_process', nil, :job_id => job.id) do
|
176
173
|
do_work(job)
|
177
174
|
end
|
178
175
|
end
|
@@ -193,8 +190,7 @@ end
|
|
193
190
|
# Process via fork - with linked asynchronous traces
|
194
191
|
###############################################################
|
195
192
|
|
196
|
-
|
197
|
-
|
193
|
+
TraceView::API.start_trace('parent_process') do
|
198
194
|
# Get some work to process
|
199
195
|
job = get_job
|
200
196
|
|
@@ -204,11 +200,10 @@ Oboe::API.start_trace('parent_process') do
|
|
204
200
|
# although we'll have to mark these traces as asynchronous to denote
|
205
201
|
# that it has split off from the main program flow
|
206
202
|
|
207
|
-
|
203
|
+
TraceView::API.trace('worker_process', :Async => 1) do
|
208
204
|
do_work(job)
|
209
205
|
end
|
210
206
|
end
|
211
|
-
|
212
207
|
end
|
213
208
|
|
214
209
|
###############################################################
|
@@ -9,9 +9,9 @@ port ENV['PORT'] || 3000
|
|
9
9
|
environment ENV['RACK_ENV'] || 'development'
|
10
10
|
|
11
11
|
on_worker_boot do
|
12
|
-
::
|
12
|
+
::TraceView.reconnect! if defined?(::TraceView)
|
13
13
|
end
|
14
14
|
|
15
15
|
on_worker_shutdown do
|
16
|
-
::
|
17
|
-
end
|
16
|
+
::TraceView.disconnect! if defined?(::TraceView)
|
17
|
+
end
|
@@ -6,35 +6,34 @@
|
|
6
6
|
require 'math'
|
7
7
|
require 'oboe'
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
TraceView::Config[:tracing_mode] = :always
|
10
|
+
TraceView::Config[:verbose] = true
|
11
11
|
|
12
12
|
# The parent process/loop which collects data
|
13
|
-
|
13
|
+
Kernel.loop do
|
14
14
|
|
15
15
|
# For each loop, we instrument the work retrieval. These traces
|
16
16
|
# will show up as layer 'get_the_work'.
|
17
|
-
|
18
|
-
|
17
|
+
TraceView::API.start_trace('get_the_work') do
|
19
18
|
work = get_the_work
|
20
19
|
|
21
20
|
# Loop through work and pass to `do_the_work` method
|
22
21
|
# that spawns a thread each time
|
23
22
|
work.each do |j|
|
24
23
|
|
25
|
-
# In the new Thread block, the
|
24
|
+
# In the new Thread block, the TraceView tracing context isn't there
|
26
25
|
# so we carry it over manually and pass it to the `start_trace`
|
27
26
|
# method.
|
28
27
|
|
29
28
|
# In the TraceView dashboard, this will show up as parent traces
|
30
29
|
# (layer 'get_the_work') with child traces (layer 'do_the_work').
|
31
30
|
|
32
|
-
tracing_context =
|
31
|
+
tracing_context = TraceView::Context.toString
|
33
32
|
|
34
33
|
Thread.new do
|
35
34
|
result = nil
|
36
35
|
|
37
|
-
|
36
|
+
TraceView::API.start_trace('do_the_work', tracing_context, :Async => 1) do
|
38
37
|
result = do_the_work(j)
|
39
38
|
end
|
40
39
|
|
@@ -42,6 +41,7 @@ while true do
|
|
42
41
|
end
|
43
42
|
end
|
44
43
|
end
|
44
|
+
sleep 5
|
45
45
|
end
|
46
46
|
|
47
47
|
|
@@ -112,7 +112,7 @@ end
|
|
112
112
|
# Thread.new do
|
113
113
|
# result = nil
|
114
114
|
#
|
115
|
-
#
|
115
|
+
# TraceView::API.start_trace('do_the_work') do
|
116
116
|
# result = do_the_work(j)
|
117
117
|
# end
|
118
118
|
#
|
@@ -8,7 +8,7 @@ Bundler.require
|
|
8
8
|
require 'oboe'
|
9
9
|
|
10
10
|
# Tracing mode can be 'never', 'through' (to follow upstream) or 'always'
|
11
|
-
|
11
|
+
TraceView::Config[:tracing_mode] = 'always'
|
12
12
|
|
13
13
|
#
|
14
14
|
# Update April 9, 2015 - this is done automagically now
|
@@ -16,14 +16,14 @@ Oboe::Config[:tracing_mode] = 'always'
|
|
16
16
|
#
|
17
17
|
# Load library instrumentation to auto-capture stuff we know about...
|
18
18
|
# e.g. ActiveRecord, Cassandra, Dalli, Redis, memcache, mongo
|
19
|
-
#
|
19
|
+
# TraceView::Ruby.load
|
20
20
|
|
21
21
|
# Some KVs to report to the dashboard
|
22
22
|
report_kvs = {}
|
23
23
|
report_kvs[:command_line_params] = ARGV.to_s
|
24
24
|
report_kvs[:user_id] = `whoami`
|
25
25
|
|
26
|
-
|
26
|
+
TraceView::API.start_trace('my_background_job', nil, report_kvs) do
|
27
27
|
#
|
28
28
|
# Initialization code
|
29
29
|
#
|
@@ -35,7 +35,7 @@ Oboe::API.start_trace('my_background_job', nil, report_kvs ) do
|
|
35
35
|
# work for each task. In the TV dashboard, this will show
|
36
36
|
# up as a large 'my_background_job' parent layer with many
|
37
37
|
# child 'task" layers.
|
38
|
-
|
38
|
+
TraceView::API.trace('task', :task_id => t.id) do
|
39
39
|
t.perform
|
40
40
|
end
|
41
41
|
end
|
@@ -47,6 +47,4 @@ end
|
|
47
47
|
# Note that we use 'start_trace' in the outer block and 'trace' for
|
48
48
|
# any sub-blocks of code we wish to instrument. The arguments for
|
49
49
|
# both methods vary slightly. Details in RubyDoc:
|
50
|
-
# https://www.omniref.com/ruby/gems/oboe/2.7.10.1/symbols/
|
51
|
-
|
52
|
-
|
50
|
+
# https://www.omniref.com/ruby/gems/oboe/2.7.10.1/symbols/TraceView::API::Tracing#tab=Methods
|