new_relic-starter 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 071bbc4b2947e8e8c3cd27bd000aa0681125cd47
4
+ data.tar.gz: 9a36ca48347142dfe955b7dd4616afc568f1c0ee
5
+ SHA512:
6
+ metadata.gz: b44dd21f7b242fe05d92950cbabc3ad4228ed03a5573596eae07c9486b113817730fcd517b0031b870d330d84ac8362172f4c4526de4de0db110292334fe3b82
7
+ data.tar.gz: 6052fe1c0a8c71cb4abd48fa563c0fa21b34f52924abfdd0e23d8448877eac169a4cc150201cb2abef7064f65c4ebc88d0b31769c5d7064c929162dc0377c652
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Satoshi Matsumoto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # NewRelic::Starter
2
+
3
+ [![Gem](https://img.shields.io/gem/v/new_relic-starter.svg?style=flat-square)](https://rubygems.org/gems/new_relic-starter)
4
+ [![Travis](https://img.shields.io/travis/kaorimatz/new_relic-starter.svg?style=flat-square)](https://travis-ci.org/kaorimatz/new_relic-starter)
5
+
6
+ A library that provides a way to start the New Relic agent in a running process.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'new_relic-starter'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install new_relic-starter
23
+
24
+ ## Usage
25
+
26
+ ### Rack
27
+
28
+ The gem provides a Rack middleware which provides an endpoint to start the New Relic agent.
29
+
30
+ ```ruby
31
+ # config.ru
32
+ require 'new_relic/starter/rack'
33
+ use NewRelic::Starter::Rack
34
+ ```
35
+
36
+ By default, the middleware uses the path `/_new_relic/start`:
37
+
38
+ ```
39
+ $ curl -s http://localhost:8080/_new_relic/start
40
+ Started the New Relic agent.
41
+ ```
42
+
43
+ You can specify the path of the endpoint using the `path` option:
44
+
45
+ ```ruby
46
+ # config.ru
47
+ require 'new_relic/starter/rack'
48
+ use NewRelic::Starter::Rack, path: '/foo'
49
+ ```
50
+
51
+ If your Rack web server is a pre-forking web server and doesn't load the Rack application before forking, you will need to create a global latch before forking:
52
+
53
+ ```ruby
54
+ $latch = NewRelic::Starter::Latch.new
55
+
56
+ # config.ru
57
+ require 'new_relic/starter/rack'
58
+ use NewRelic::Starter::Rack, latch: $latch
59
+ ```
60
+
61
+ Or you will need to create a latch backed by a file:
62
+
63
+ ```ruby
64
+ # config.ru
65
+ require 'new_relic/starter/rack'
66
+ use NewRelic::Starter::Rack, latch: NewRelic::Starter::Latch.new("/path/to/latch")
67
+ ```
68
+
69
+ When a latch is backed by a file, you can also have the middleware start the New Relic agent by writing a single byte 1 to the file:
70
+
71
+ ```sh
72
+ $ echo -n -e '\x1' > /path/to/latch
73
+ ```
74
+
75
+ ### Rails
76
+
77
+ You can configure Rails to add the Rack middleware to the middleware stack with the default options by requiring `new_relic/starter/rails.rb`:
78
+
79
+ ```ruby
80
+ # Gemfile
81
+ gem 'new_relic-starter', require: 'new_relic/starter/rails'
82
+ ```
83
+
84
+ If you want to specify options, you can manually configure the middleware:
85
+
86
+ ```ruby
87
+ # config/initializers/new_relic_starter.rb
88
+ Rails.application.config.middleware.use NewRelic::Starter::Rack, path: '/foo'
89
+ ```
90
+
91
+ ### Resque
92
+
93
+ If your Resque worker forks before processing a job, you can add a hook to start the New Relic agent before forking:
94
+
95
+ ```ruby
96
+ starter = NewRelic::Starter.new(NewRelic::Starter::Latch.new("/path/to/latch"))
97
+ Resque.before_fork do
98
+ starter.start
99
+ end
100
+ ```
101
+
102
+ Then you can have the starter start the New Relic agent by writing a single byte 1 to the file backing the latch:
103
+
104
+ ```sh
105
+ $ echo -n -e '\x1' > /path/to/latch
106
+ ```
107
+
108
+ ## Development
109
+
110
+ 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.
111
+
112
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
113
+
114
+ ## Contributing
115
+
116
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kaorimatz/new_relic-starter.
117
+
118
+ ## License
119
+
120
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
121
+
122
+ ## Credits
123
+
124
+ The idea of starting the New Relic agent using the Rack middleware is borrowed from [partiarelic](https://github.com/wata-gh/partiarelic).
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mkmf'
4
+
5
+ create_makefile('new_relic_starter')
@@ -0,0 +1,176 @@
1
+ #include <errno.h>
2
+ #include <fcntl.h>
3
+ #include <sys/file.h>
4
+ #include <sys/mman.h>
5
+ #include <sys/stat.h>
6
+ #include <unistd.h>
7
+ #include "new_relic_starter.h"
8
+
9
+ VALUE eError;
10
+
11
+ static void
12
+ latch_free(void *ptr)
13
+ {
14
+ munmap(ptr, 1);
15
+ }
16
+
17
+ static size_t
18
+ latch_size(const void *ptr)
19
+ {
20
+ return sysconf(_SC_PAGE_SIZE);
21
+ }
22
+
23
+ static const rb_data_type_t latch_data_type = {
24
+ "latch",
25
+ { NULL, latch_free, latch_size, },
26
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
27
+ };
28
+
29
+ static VALUE
30
+ latch_s_allocate(VALUE klass)
31
+ {
32
+ return TypedData_Wrap_Struct(klass, &latch_data_type, 0);
33
+ }
34
+
35
+ static int
36
+ open_latch_file(const char *path)
37
+ {
38
+ struct stat st;
39
+
40
+ int fd = open(path, O_CREAT|O_RDWR, 0666);
41
+ if (fd == -1) {
42
+ rb_raise(eError, "failed to open file %s: %s", path, strerror(errno));
43
+ }
44
+
45
+ if (flock(fd, LOCK_EX) == -1) {
46
+ int e = errno;
47
+ close(fd);
48
+ rb_raise(eError, "failed to acquire an advisory lock for %s: %s", path, strerror(e));
49
+ }
50
+
51
+ if (fstat(fd, &st) == -1) {
52
+ int e = errno;
53
+ close(fd);
54
+ rb_raise(eError, "failed to get file status for %s: %s", path, strerror(e));
55
+ }
56
+
57
+ if (st.st_size == 0) {
58
+ if (write(fd, "", 1) == -1) {
59
+ int e = errno;
60
+ close(fd);
61
+ rb_raise(eError, "failed to write to file %s: %s", path, strerror(e));
62
+ }
63
+ }
64
+
65
+ if (flock(fd, LOCK_UN) == -1) {
66
+ int e = errno;
67
+ close(fd);
68
+ rb_raise(eError, "failed to release an advisory lock for %s: %s", path, strerror(e));
69
+ }
70
+
71
+ return fd;
72
+ }
73
+
74
+ /*
75
+ * call-seq:
76
+ * NewRelic::Starter::Latch.new -> latch
77
+ * NewRelic::Starter::Latch.new(path) -> latch
78
+ *
79
+ * Returns a new {Latch} object.
80
+ *
81
+ * The state of the latch is stored in memory mapped by mmap(2) and shared with
82
+ * a forked process.
83
+ *
84
+ * If +path+ is specified, the memory mapping is backed by the file and the
85
+ * state of the latch is shared with other latches backed by the same file.
86
+ *
87
+ * NewRelic::Starter::Latch.new #=> #<NewRelic::Starter::Latch:0x00007fbecb04f038>
88
+ * NewRelic::Starter::Latch.new("/path/to/latch") #=> #<NewRelic::Starter::Latch:0x00007fbec9808010>
89
+ */
90
+ static VALUE
91
+ latch_initialize(int argc, VALUE *argv, VALUE self)
92
+ {
93
+ int fd = -1;
94
+ void *addr;
95
+
96
+ char *path = rb_check_arity(argc, 0, 1) ? RSTRING_PTR(argv[0]) : NULL;
97
+ if (path != NULL) {
98
+ fd = open_latch_file(path);
99
+ }
100
+
101
+ addr = mmap(NULL, 1, PROT_READ|PROT_WRITE, (fd == -1 ? MAP_ANONYMOUS|MAP_SHARED : MAP_SHARED), fd, 0);
102
+ if (addr == MAP_FAILED) {
103
+ int e = errno;
104
+ close(fd);
105
+ rb_raise(eError, "failed to create mapping for latch: %s", strerror(e));
106
+ }
107
+ close(fd);
108
+
109
+ DATA_PTR(self) = addr;
110
+
111
+ return self;
112
+ }
113
+
114
+ static inline uint8_t *
115
+ check_latch(VALUE self)
116
+ {
117
+ return rb_check_typeddata(self, &latch_data_type);
118
+ }
119
+
120
+ /*
121
+ * call-seq:
122
+ * latch.open -> nil
123
+ *
124
+ * Opens the latch.
125
+ *
126
+ * latch = NewRelic::Starter::Latch.new
127
+ * latch.opened? #=> false
128
+ * latch.open
129
+ * latch.opened? #=> true
130
+ */
131
+ static VALUE
132
+ latch_open(VALUE self)
133
+ {
134
+ uint8_t *l = check_latch(self);
135
+ *l = 1;
136
+ return Qnil;
137
+ }
138
+
139
+ /*
140
+ * call-seq:
141
+ * latch.opened? -> boolean
142
+ *
143
+ * Returns true if the latch is opened.
144
+ *
145
+ * latch = NewRelic::Starter::Latch.new
146
+ * latch.opened? #=> false
147
+ * latch.open
148
+ * latch.opened? #=> true
149
+ */
150
+ static VALUE
151
+ latch_opened(VALUE self)
152
+ {
153
+ return *check_latch(self) == 1 ? Qtrue : Qfalse;
154
+ }
155
+
156
+ void
157
+ Init_new_relic_starter(void)
158
+ {
159
+ VALUE mNewRelic, cStarter, cLatch;
160
+
161
+ mNewRelic = rb_define_module("NewRelic");
162
+ cStarter = rb_define_class_under(mNewRelic, "Starter", rb_cObject);
163
+ eError = rb_define_class_under(cStarter, "Error", rb_eStandardError);
164
+
165
+ /*
166
+ * Document-class: NewRelic::Starter::Latch
167
+ *
168
+ * NewRelic::Starter::Latch indicates whether the New Relic agent should be
169
+ * started.
170
+ */
171
+ cLatch = rb_define_class_under(cStarter, "Latch", rb_cObject);
172
+ rb_define_alloc_func(cLatch, latch_s_allocate);
173
+ rb_define_method(cLatch, "initialize", latch_initialize, -1);
174
+ rb_define_method(cLatch, "open", latch_open, 0);
175
+ rb_define_method(cLatch, "opened?", latch_opened, 0);
176
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef NEW_RELIC_STARTER_H
2
+ #define NEW_RELIC_STARTER_H 1
3
+
4
+ #include "ruby.h"
5
+
6
+ #endif /* NEW_RELIC_STARTER_H */
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'new_relic/agent'
4
+
5
+ module NewRelic
6
+ # NewRelic::Starter starts the New Relic agent by calling
7
+ # {NewRelic::Agent.manual_start}.
8
+ class Starter
9
+ # NewRelic::Starter::Error is a base class for errors.
10
+ class Error < StandardError; end
11
+
12
+ autoload :Latch, 'new_relic_starter'
13
+ autoload :Rack, 'new_relic/starter/rack'
14
+
15
+ # Return a new {Starter} object.
16
+ #
17
+ # @param latch [NewRelic::Starter::Latch] the latch object
18
+ # @return [NewRelic::Starter] A new starter object
19
+ def initialize(latch)
20
+ @latch = latch
21
+ @started = false
22
+ end
23
+
24
+ # Starts the new Relic agent if the agent is not started and the latch is
25
+ # opened.
26
+ #
27
+ # @return [Boolean] true if the new Relic agent is started
28
+ def start
29
+ return false if @started || !@latch.opened?
30
+
31
+ NewRelic::Agent.manual_start
32
+ @started = true
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'new_relic/agent'
4
+
5
+ module NewRelic
6
+ class Starter
7
+ # NewRelic::Starter::Rack is a Rack middleware that provides an endpoint to
8
+ # start the New Relic agent.
9
+ class Rack
10
+ # Returns a new {Rack} which implements the Rack interface.
11
+ #
12
+ # @param app [Object] the Rack application
13
+ # @param latch [NewRelic::Starter::Latch] the latch object
14
+ # @param path [String] the path of the endpoint to start the New Relic
15
+ # agent
16
+ # @return [NewRelic::Starter::Rack] A new rack middleware object
17
+ def initialize(app, latch: Latch.new, path: nil)
18
+ @app = app
19
+ @latch = latch
20
+ @path = path || '/_new_relic/start'
21
+ @starter = Starter.new(latch)
22
+ end
23
+
24
+ # Opens a latch and start the New Relic agent if the path of the request
25
+ # matches with the path of the endpoint.
26
+ #
27
+ # When the path doesn't match, if a latch is opened, the agent is started
28
+ # before calling the next application.
29
+ #
30
+ # @param env [Hash] the Rack environment
31
+ # @return [Array] the Rack response
32
+ def call(env)
33
+ if env['PATH_INFO'] == @path
34
+ handle
35
+ else
36
+ @starter.start
37
+ @app.call(env)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def handle
44
+ @latch.open
45
+ headers = { 'Content-Type' => 'text/plain' }
46
+ body = if @starter.start
47
+ 'Started the New Relic agent.'
48
+ else
49
+ 'The New Relic agent is already started.'
50
+ end
51
+ [200, headers, [body]]
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'new_relic/starter/rack'
4
+
5
+ module NewRelic
6
+ class Starter
7
+ # NewRelic::Starter::Railtie implements a railtie which creates an
8
+ # initializer to add the Rack middleware to the middleware stack with the
9
+ # default options.
10
+ class Railtie < Rails::Railtie
11
+ initializer 'new_relic-starter.middleware' do |app|
12
+ app.middleware.use NewRelic::Starter::Rack
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NewRelic
4
+ class Starter
5
+ VERSION = '0.1.1'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: new_relic-starter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Satoshi Matsumoto
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-04-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: newrelic_rpm
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
+ - !ruby/object:Gem::Dependency
28
+ name: benchmark_driver
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake-compiler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec-mocks
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: |2
140
+ A library that provides a way to start the New Relic agent in a running
141
+ process.
142
+ email:
143
+ - kaorimatz@gmail.com
144
+ executables: []
145
+ extensions:
146
+ - ext/new_relic_starter/extconf.rb
147
+ extra_rdoc_files: []
148
+ files:
149
+ - LICENSE.txt
150
+ - README.md
151
+ - ext/new_relic_starter/extconf.rb
152
+ - ext/new_relic_starter/new_relic_starter.c
153
+ - ext/new_relic_starter/new_relic_starter.h
154
+ - lib/new_relic/starter.rb
155
+ - lib/new_relic/starter/rack.rb
156
+ - lib/new_relic/starter/rails.rb
157
+ - lib/new_relic/starter/version.rb
158
+ homepage: https://github.com/kaorimatz/new_relic-starter
159
+ licenses:
160
+ - MIT
161
+ metadata: {}
162
+ post_install_message:
163
+ rdoc_options: []
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ required_rubygems_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ requirements: []
177
+ rubyforge_project:
178
+ rubygems_version: 2.6.14.3
179
+ signing_key:
180
+ specification_version: 4
181
+ summary: Start the New Relic agent in a running process.
182
+ test_files: []