appengine 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/CONTRIBUTING.md +27 -0
- data/LICENSE +4 -5
- data/README.md +48 -22
- data/Rakefile +33 -7
- data/lib/appengine.rb +26 -3
- data/lib/appengine/env.rb +40 -0
- data/lib/appengine/logger.rb +188 -0
- data/lib/appengine/railtie.rb +84 -0
- data/lib/appengine/version.rb +20 -2
- data/test/test_env.rb +57 -0
- data/test/test_logger.rb +151 -0
- metadata +46 -29
- data/.gitignore +0 -9
- data/.travis.yml +0 -4
- data/Gemfile +0 -4
- data/appengine.gemspec +0 -33
- data/bin/console +0 -14
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5603a0560a10f3f6d0c6a6cd9a35ad8dcec9965
|
4
|
+
data.tar.gz: 92d67d00bfb992d723ccb5628f4f3f53c75e1a48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 381abeeea878aa676f40fbd444c022f2639f05d175c6457efd02e26c0eaa54ade5b1e032ac2dd553aa067afc594cb790be2828c230122d121a9d221dcc239657
|
7
|
+
data.tar.gz: 880a1741725629a5588795d1d7f38bbd340cd3a814a93e597bacdfa595080d191ecc25e1313aa2fd8e931406fb74415227ca561d3794c7bca6d0a8fbfb79433f
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
This is the change history for the appengine gem.
|
4
|
+
|
5
|
+
## v0.2.0 (2016-05-04)
|
6
|
+
|
7
|
+
* Tools for integration with the Google Cloud Console logger, including
|
8
|
+
Rack middleware and a Railtie.
|
9
|
+
|
10
|
+
## v0.1.0 (2016-04-07)
|
11
|
+
|
12
|
+
* Initial release, reserving the appengine name. No functionality.
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Want to contribute? Great! First, read this page (including the small print at the end).
|
2
|
+
|
3
|
+
### Before you contribute
|
4
|
+
Before we can use your code, you must sign the
|
5
|
+
[Google Individual Contributor License Agreement]
|
6
|
+
(https://cla.developers.google.com/about/google-individual)
|
7
|
+
(CLA), which you can do online. The CLA is necessary mainly because you own the
|
8
|
+
copyright to your changes, even after your contribution becomes part of our
|
9
|
+
codebase, so we need your permission to use and distribute your code. We also
|
10
|
+
need to be sure of various other things—for instance that you'll tell us if you
|
11
|
+
know that your code infringes on other people's patents. You don't have to sign
|
12
|
+
the CLA until after you've submitted your code for review and a member has
|
13
|
+
approved it, but you must do it before we can put your code into our codebase.
|
14
|
+
Before you start working on a larger contribution, you should get in touch with
|
15
|
+
us first through the issue tracker with your idea so that we can help out and
|
16
|
+
possibly guide you. Coordinating up front makes it much easier to avoid
|
17
|
+
frustration later on.
|
18
|
+
|
19
|
+
### Code reviews
|
20
|
+
All submissions, including submissions by project members, require review. We
|
21
|
+
use Github pull requests for this purpose.
|
22
|
+
|
23
|
+
### The small print
|
24
|
+
Contributions made by corporations are covered by a different agreement than
|
25
|
+
the one above, the
|
26
|
+
[Software Grant and Corporate Contributor License Agreement]
|
27
|
+
(https://cla.developers.google.com/about/google-corporate).
|
data/LICENSE
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
Apache License
|
1
|
+
Apache License
|
3
2
|
Version 2.0, January 2004
|
4
3
|
http://www.apache.org/licenses/
|
5
4
|
|
@@ -179,7 +178,7 @@
|
|
179
178
|
APPENDIX: How to apply the Apache License to your work.
|
180
179
|
|
181
180
|
To apply the Apache License to your work, attach the following
|
182
|
-
boilerplate notice, with the fields enclosed by brackets "
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "{}"
|
183
182
|
replaced with your own identifying information. (Don't include
|
184
183
|
the brackets!) The text should be enclosed in the appropriate
|
185
184
|
comment syntax for the file format. We also recommend that a
|
@@ -187,7 +186,7 @@
|
|
187
186
|
same "printed page" as the copyright notice for easier
|
188
187
|
identification within third-party archives.
|
189
188
|
|
190
|
-
Copyright
|
189
|
+
Copyright {yyyy} {name of copyright owner}
|
191
190
|
|
192
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
193
192
|
you may not use this file except in compliance with the License.
|
@@ -199,4 +198,4 @@
|
|
199
198
|
distributed under the License is distributed on an "AS IS" BASIS,
|
200
199
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
201
200
|
See the License for the specific language governing permissions and
|
202
|
-
limitations under the License.
|
201
|
+
limitations under the License.
|
data/README.md
CHANGED
@@ -1,41 +1,67 @@
|
|
1
|
-
|
1
|
+
Google App Engine Integration Tools
|
2
|
+
===================================
|
2
3
|
|
3
|
-
|
4
|
+
This repository contains the "appengine" gem, a collection of libraries and
|
5
|
+
plugins for integrating Ruby apps with Google App Engine. It is not required
|
6
|
+
for deploying a ruby application to Google App Engine, but it provides a
|
7
|
+
number of convenience hooks for better integrating into the App Engine
|
8
|
+
environment.
|
4
9
|
|
5
|
-
|
10
|
+
Currently, it includes:
|
6
11
|
|
7
|
-
|
8
|
-
|
9
|
-
Add this line to your application's Gemfile:
|
12
|
+
* A way to configure the Logger to log to the Google Cloud Console.
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
```
|
14
|
+
For more information on using Google Cloud Platform to deploy Ruby apps,
|
15
|
+
please visit http://cloud.google.com/ruby
|
14
16
|
|
15
|
-
|
17
|
+
## Installation
|
16
18
|
|
17
|
-
|
19
|
+
To install, include the "appengine" gem in your Gemfile. e.g.
|
18
20
|
|
19
|
-
|
21
|
+
gem "appengine"
|
20
22
|
|
21
|
-
|
23
|
+
If you are running [Ruby On Rails](http://rubyonrails.org/) 4.0 or later, this
|
24
|
+
gem will automatically install a Railtie that provides its capabilities. You
|
25
|
+
may need to include the line:
|
22
26
|
|
23
|
-
|
27
|
+
require "appengine"
|
24
28
|
|
25
|
-
|
29
|
+
in your `config/application.rb` file if you aren't already requiring all
|
30
|
+
bundled gems. You may provide additional configuration via the
|
31
|
+
`config.appengine` object. See below for more details.
|
26
32
|
|
27
|
-
|
33
|
+
If you are using a different Rack-based framework such as
|
34
|
+
[Sinatra](http://sinatrarb.com/), you can use the provided middlewares. See
|
35
|
+
the more detailed instructions below.
|
28
36
|
|
29
|
-
|
37
|
+
## Google Cloud Console logger integration
|
30
38
|
|
31
|
-
|
39
|
+
In order for your application logs to appear in the Google Cloud Console with
|
40
|
+
the correct severities and other metadata, they should be written in a
|
41
|
+
specific format to a specific location. The logger module in this gem provides
|
42
|
+
tools to make that happen.
|
32
43
|
|
33
|
-
|
44
|
+
If you are using Ruby on Rails, and you do not otherwise customize your
|
45
|
+
Rails logger, then the provided Railtie will direct your logs to the Cloud
|
46
|
+
Console "out of the box". Normally, this is configured to take effect when
|
47
|
+
running in the `production` environment, but you may also configure it for
|
48
|
+
other environments. See the documentation for the `AppEngine::Railtie` class
|
49
|
+
for more details.
|
34
50
|
|
35
|
-
|
51
|
+
If you are running a different Rack-based framework such as Sinatra, you
|
52
|
+
should install the provided `AppEngine::Logger::Middleware` in your middleware
|
53
|
+
stack. This should be installed instead of the normal `Rack::Logger`. It will
|
54
|
+
automatically create a logger that directs entries to the Cloud Console, and
|
55
|
+
will make it available via the standard `Rack::RACK_LOGGER` key in the Rack
|
56
|
+
environment. You may also create your own logger directly using the
|
57
|
+
`AppEngine::Logger.create()` method.
|
36
58
|
|
59
|
+
## Development and support
|
37
60
|
|
38
|
-
|
61
|
+
The source code for this gem is available on Github at
|
62
|
+
https://github.com/GoogleCloudPlatform/appengine-ruby
|
39
63
|
|
40
|
-
|
64
|
+
Report bugs on Github issues at
|
65
|
+
https://github.com/GoogleCloudPlatform/appengine-ruby/issues
|
41
66
|
|
67
|
+
Contributions are welcome. Please review the CONTRIBUTING.md file.
|
data/Rakefile
CHANGED
@@ -1,10 +1,36 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
3
15
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
16
|
+
require 'bundler/gem_tasks'
|
17
|
+
require 'rake/testtask'
|
18
|
+
require 'rdoc/task'
|
19
|
+
|
20
|
+
CLEAN << ['pkg', 'doc']
|
21
|
+
|
22
|
+
::Rake::TestTask.new do |t|
|
23
|
+
t.libs << 'test'
|
24
|
+
t.libs << 'lib'
|
25
|
+
t.test_files = ::FileList['test/test_*.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
::RDoc::Task.new do |rd|
|
29
|
+
rd.rdoc_dir = 'doc'
|
30
|
+
rd.main = 'README.md'
|
31
|
+
rd.rdoc_files.include 'README.md', 'CONTRIBUTING.md', 'CHANGELOG.md', 'lib/**/*.rb'
|
32
|
+
rd.options << '--line-numbers'
|
33
|
+
rd.options << '--all'
|
8
34
|
end
|
9
35
|
|
10
|
-
task :default => :
|
36
|
+
task :default => [:test]
|
data/lib/appengine.rb
CHANGED
@@ -1,5 +1,28 @@
|
|
1
|
-
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
2
15
|
|
3
|
-
|
4
|
-
|
16
|
+
# == Google AppEngine integration
|
17
|
+
#
|
18
|
+
# The AppEngine module includes optional tools helping Ruby applications to
|
19
|
+
# integrate more closely with the Google App Engine environment.
|
20
|
+
|
21
|
+
module AppEngine
|
5
22
|
end
|
23
|
+
|
24
|
+
|
25
|
+
require 'appengine/version'
|
26
|
+
require 'appengine/env'
|
27
|
+
require 'appengine/logger'
|
28
|
+
require 'appengine/railtie' if defined?(::Rails)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
15
|
+
|
16
|
+
|
17
|
+
module AppEngine
|
18
|
+
|
19
|
+
|
20
|
+
# == Environment information
|
21
|
+
#
|
22
|
+
# A collection of functions for extracting App Engine environment information
|
23
|
+
# from the Rack environment
|
24
|
+
|
25
|
+
module Env
|
26
|
+
|
27
|
+
|
28
|
+
# Returns the Trace ID string from a Rack environment, or nil if no trace
|
29
|
+
# ID was found.
|
30
|
+
|
31
|
+
def self.extract_trace_id(env)
|
32
|
+
trace_context = env['HTTP_X_CLOUD_TRACE_CONTEXT'].to_s
|
33
|
+
return nil if trace_context.empty?
|
34
|
+
return trace_context.sub(/\/.*/, '')
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
15
|
+
|
16
|
+
require 'json'
|
17
|
+
require 'logger'
|
18
|
+
|
19
|
+
|
20
|
+
module AppEngine
|
21
|
+
|
22
|
+
|
23
|
+
# == AppEngine Logger integration
|
24
|
+
#
|
25
|
+
# A collection of tools for talking to the StackDriver logs in the Google
|
26
|
+
# Cloud Console.
|
27
|
+
#
|
28
|
+
# For logs to appear in the Google Cloud Console, they must be written in
|
29
|
+
# JSON format to files matching the pattern "/var/log/app_engine/app*.json".
|
30
|
+
# We provide an appropriate formatter, and a LogDevice that omits the
|
31
|
+
# log header line.
|
32
|
+
#
|
33
|
+
# Logs should also be annotated with the request's Trace ID. We provide
|
34
|
+
# a Rack Middleware to extract that ID from the environment and annotate
|
35
|
+
# log entries. It uses a fiber-local variable to store the Trace ID for
|
36
|
+
# the current request being handled by that fiber.
|
37
|
+
|
38
|
+
module Logger
|
39
|
+
|
40
|
+
|
41
|
+
# The name of a fiber-local variable storing the trace ID for the current
|
42
|
+
# request.
|
43
|
+
DEFAULT_TRACE_ID_VAR = :_google_appengine_trace_id
|
44
|
+
|
45
|
+
# The default path to the log file.
|
46
|
+
DEFAULT_LOG_FILENAME = '/var/log/app_engine/app-ruby.json'
|
47
|
+
|
48
|
+
# A map from Ruby severity names to Google Cloud severity names.
|
49
|
+
SEV_MAP = {
|
50
|
+
'DEBUG' => 'DEBUG',
|
51
|
+
'INFO' => 'INFO',
|
52
|
+
'WARN' => 'WARNING',
|
53
|
+
'ERROR' => 'ERROR',
|
54
|
+
'FATAL' => 'CRITICAL'
|
55
|
+
}
|
56
|
+
|
57
|
+
|
58
|
+
# == AppEngie Formatter
|
59
|
+
#
|
60
|
+
# A formatter that generates the appropriate JSON format for log entries.
|
61
|
+
# Pulls the trace ID from the fiber-local variable, if present.
|
62
|
+
#
|
63
|
+
# (see ::Logger::Formatter)
|
64
|
+
|
65
|
+
class Formatter
|
66
|
+
|
67
|
+
|
68
|
+
# Create a new formatter. You may optionally override the fiber-local
|
69
|
+
# variable name used for the trace ID.
|
70
|
+
|
71
|
+
def initialize(trace_id_var: DEFAULT_TRACE_ID_VAR)
|
72
|
+
@trace_id_var = trace_id_var
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def call(severity, time, progname, msg) # :nodoc:
|
77
|
+
msg = msg.to_s
|
78
|
+
return '' if msg.empty?
|
79
|
+
entry = {
|
80
|
+
message: (progname.to_s != '') ? "#{progname}: #{msg}" : msg,
|
81
|
+
timestamp: {seconds: time.to_i, nanos: time.nsec},
|
82
|
+
severity: SEV_MAP.fetch(severity.to_s, 'CRITICAL')
|
83
|
+
}
|
84
|
+
trace_id = ::Thread.current[@trace_id_var]
|
85
|
+
if trace_id
|
86
|
+
entry[:traceId] = trace_id.to_s
|
87
|
+
end
|
88
|
+
::JSON.generate(entry) + "\n"
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# == AppEngine LogDevice
|
95
|
+
#
|
96
|
+
# A ::Logger::LogDevice subclass that omits the log header line.
|
97
|
+
|
98
|
+
class LogDevice < ::Logger::LogDevice
|
99
|
+
|
100
|
+
def add_log_header(file) # :nodoc:
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# == Rack Middleware for AppEngine logger
|
107
|
+
#
|
108
|
+
# A Rack middleware that sets the logger in the Rack environment, and
|
109
|
+
# stashes the trace ID for the current request in a fiber-local variable
|
110
|
+
# for the logger to use when formatting log entries.
|
111
|
+
#
|
112
|
+
# In a standard Rack application, you should use this middleware instead
|
113
|
+
# of the standard ::Rack::Logger.
|
114
|
+
|
115
|
+
class Middleware
|
116
|
+
|
117
|
+
|
118
|
+
# Create a new AppEngine logging Middleware.
|
119
|
+
# The argument is an options hash supporting the following keys:
|
120
|
+
#
|
121
|
+
# [<tt>:logger</tt>]
|
122
|
+
# A global logger to use. This should generally be a logger created
|
123
|
+
# by AppEngine::Logger.create(). The middleware sets env["rack.logger"]
|
124
|
+
# accordingly. If omitted, a default logger is created automatically
|
125
|
+
# when the middleware is constructed.
|
126
|
+
# [<tt>:trace_id_var</tt>]
|
127
|
+
# The name of the fiber-local variable to use to stack the trace ID.
|
128
|
+
# Defaults to the value of DEFAULT_TRACE_ID_VAR. You generally should
|
129
|
+
# not need to modify this value unless you need to control fiber-local
|
130
|
+
# variable names.
|
131
|
+
# [<tt>:logfile</tt>]
|
132
|
+
# If you do not specify a <tt>:logger</tt>, a logger is created that
|
133
|
+
# opens this file. Defaults to the value of DEFAULT_LOG_FILENAME.
|
134
|
+
# Generally, you should leave this setting to the default for
|
135
|
+
# deployments, because App Engine expects log files in a particular
|
136
|
+
# location. However, if you want to test log generation into a
|
137
|
+
# different directory in development, you may set it here.
|
138
|
+
|
139
|
+
def initialize(app,
|
140
|
+
logger: nil,
|
141
|
+
trace_id_var: DEFAULT_TRACE_ID_VAR,
|
142
|
+
logfile: DEFAULT_LOG_FILENAME)
|
143
|
+
@app = app
|
144
|
+
@trace_id_var = trace_id_var
|
145
|
+
@logger = logger || Logger.create(trace_id_var: trace_id_var, logfile: logfile)
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def call(env) # :nodoc:
|
150
|
+
env['rack.logger'] = @logger
|
151
|
+
::Thread.current[@trace_id_var] = Env.extract_trace_id(env)
|
152
|
+
begin
|
153
|
+
@app.call(env)
|
154
|
+
ensure
|
155
|
+
::Thread.current[@trace_id_var] = nil
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
# Creates a new logger for AppEngine that writes to the correct location
|
163
|
+
# using the correct formatting.
|
164
|
+
# The argument is an options hash supporting the following keys:
|
165
|
+
#
|
166
|
+
# [<tt>:trace_id_var</tt>]
|
167
|
+
# The name of the fiber-local variable to use to stack the trace ID.
|
168
|
+
# Defaults to the value of DEFAULT_TRACE_ID_VAR. You generally should
|
169
|
+
# not need to modify this value unless you need to control fiber-local
|
170
|
+
# variable names.
|
171
|
+
# [<tt>:logfile</tt>]
|
172
|
+
# Log file to write to. Defaults to the value of DEFAULT_LOG_FILENAME.
|
173
|
+
# Generally, you should leave this setting to the default for
|
174
|
+
# deployments, because App Engine expects log files in a particular
|
175
|
+
# location. However, if you want to test log generation into a
|
176
|
+
# different directory in development, you may set it here.
|
177
|
+
|
178
|
+
def self.create(trace_id_var: DEFAULT_TRACE_ID_VAR, logfile: DEFAULT_LOG_FILENAME)
|
179
|
+
if logfile.kind_of?(::String)
|
180
|
+
::FileUtils.mkdir_p(::File.dirname(logfile))
|
181
|
+
end
|
182
|
+
logger = ::Logger.new(LogDevice.new(logfile))
|
183
|
+
logger.formatter = Formatter.new(trace_id_var: trace_id_var)
|
184
|
+
logger
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
15
|
+
|
16
|
+
require 'fileutils'
|
17
|
+
|
18
|
+
|
19
|
+
module AppEngine
|
20
|
+
|
21
|
+
|
22
|
+
# == AppEngine Rails integration
|
23
|
+
#
|
24
|
+
# A Railtie providing Rails integration with the Google App Engine runtime
|
25
|
+
# environment. Sets up the Rails logger to log to the Google Cloud Console
|
26
|
+
# in production.
|
27
|
+
#
|
28
|
+
# To use, just include the "appengine" gem in your gemfile, and make sure
|
29
|
+
# it is required in your config/application.rb (if you are not already
|
30
|
+
# using Bundler.require).
|
31
|
+
#
|
32
|
+
# === Configuration
|
33
|
+
#
|
34
|
+
# The following configuration parameters are supported.
|
35
|
+
#
|
36
|
+
# [<tt>config.appengine.use_cloud_logger</tt>]
|
37
|
+
# Set to true to cause the Rails logger to log to Google Cloud Console.
|
38
|
+
# By default, this is true in the production environment and false in
|
39
|
+
# all other environments. You may override this setting in individual
|
40
|
+
# environment config files.
|
41
|
+
#
|
42
|
+
# [<tt>config.appengine.logfile</tt>]
|
43
|
+
# The path to the log file when <tt>use_cloud_logger</tt> is active.
|
44
|
+
# You should normally leave this as the default when deploying to Google
|
45
|
+
# App Engine, but you may set it to a different path if you want to test
|
46
|
+
# logging in a development environment.
|
47
|
+
#
|
48
|
+
# [<tt>config.appengine.trace_id_var</tt>]
|
49
|
+
# The name of a fiber-local variable to store the current request's trace
|
50
|
+
# ID. This is used to communicate request and trace information between
|
51
|
+
# Rack and the cloud logger. You may change it if you need to control
|
52
|
+
# fiber-local variable names.
|
53
|
+
|
54
|
+
class Railtie < ::Rails::Railtie
|
55
|
+
|
56
|
+
# :stopdoc:
|
57
|
+
|
58
|
+
config.appengine = ::ActiveSupport::OrderedOptions.new
|
59
|
+
|
60
|
+
config.appengine.use_cloud_logger = ::Rails.env.to_s == 'production'
|
61
|
+
config.appengine.logfile = ::AppEngine::Logger::DEFAULT_LOG_FILENAME
|
62
|
+
config.appengine.trace_id_var = ::AppEngine::Logger::DEFAULT_TRACE_ID_VAR
|
63
|
+
|
64
|
+
|
65
|
+
initializer 'google.appengine.logger', before: :initialize_logger do |app|
|
66
|
+
if app.config.appengine.use_cloud_logger
|
67
|
+
app.config.logger = ::AppEngine::Logger.create(
|
68
|
+
logfile: app.config.appengine.logfile,
|
69
|
+
trace_id_var: app.config.appengine.trace_id_var)
|
70
|
+
app.config.log_formatter = app.config.logger.formatter
|
71
|
+
|
72
|
+
app.middleware.insert_before(::Rails::Rack::Logger,
|
73
|
+
::AppEngine::Logger::Middleware,
|
74
|
+
logger: app.config.logger,
|
75
|
+
trace_id_var: app.config.appengine.trace_id_var)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# :startdoc:
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
end
|
data/lib/appengine/version.rb
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
15
|
+
|
16
|
+
module AppEngine
|
17
|
+
|
18
|
+
# The current version of this gem, as a string.
|
19
|
+
VERSION = '0.2.0'.freeze
|
20
|
+
|
3
21
|
end
|
data/test/test_env.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
15
|
+
|
16
|
+
require 'minitest/autorun'
|
17
|
+
require 'appengine'
|
18
|
+
|
19
|
+
|
20
|
+
module AppEngine
|
21
|
+
module Tests # :nodoc:
|
22
|
+
|
23
|
+
class TestEnv < ::Minitest::Test # :nodoc:
|
24
|
+
|
25
|
+
|
26
|
+
def test_extract_trace_id_absent
|
27
|
+
env = {}
|
28
|
+
trace_id = Env.extract_trace_id(env)
|
29
|
+
assert_nil(trace_id)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def test_extract_trace_id_empty
|
34
|
+
env = {'HTTP_X_CLOUD_TRACE_CONTEXT' => ''}
|
35
|
+
trace_id = Env.extract_trace_id(env)
|
36
|
+
assert_nil(trace_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def test_extract_trace_id_simple
|
41
|
+
env = {'HTTP_X_CLOUD_TRACE_CONTEXT' => 'abcdefg'}
|
42
|
+
trace_id = Env.extract_trace_id(env)
|
43
|
+
assert_equal('abcdefg', trace_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def test_extract_trace_id_with_suffix
|
48
|
+
env = {'HTTP_X_CLOUD_TRACE_CONTEXT' => 'abcdefg/hijk/lmnop'}
|
49
|
+
trace_id = Env.extract_trace_id(env)
|
50
|
+
assert_equal('abcdefg', trace_id)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
data/test/test_logger.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
15
|
+
|
16
|
+
require 'minitest/autorun'
|
17
|
+
require 'appengine'
|
18
|
+
|
19
|
+
|
20
|
+
module AppEngine
|
21
|
+
module Tests # :nodoc:
|
22
|
+
|
23
|
+
class TestFormatter < ::Minitest::Test # :nodoc:
|
24
|
+
|
25
|
+
|
26
|
+
def setup
|
27
|
+
@time_sec = 1461544128
|
28
|
+
@time_nsec = 580791000
|
29
|
+
@time = ::Time.at(@time_sec, @time_nsec / 1000)
|
30
|
+
@trace_id = 'a1b2c3d4e5f6'
|
31
|
+
::Thread.current[::AppEngine::Logger::DEFAULT_TRACE_ID_VAR] = @trace_id
|
32
|
+
@formatter = ::AppEngine::Logger::Formatter.new
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def test_format_empty
|
37
|
+
result = @formatter.call('ERROR', @time, 'prog', '')
|
38
|
+
assert_equal('', result)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def test_format_with_progname
|
43
|
+
result = @formatter.call('ERROR', @time, 'prog', 'this message')
|
44
|
+
assert_equal(
|
45
|
+
"{\"message\":\"prog: this message\"," +
|
46
|
+
"\"timestamp\":{\"seconds\":#{@time_sec},\"nanos\":#{@time_nsec}}," +
|
47
|
+
"\"severity\":\"ERROR\"," +
|
48
|
+
"\"traceId\":\"#{@trace_id}\"}\n",
|
49
|
+
result)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def test_format_with_no_trace_id
|
54
|
+
::Thread.current[::AppEngine::Logger::DEFAULT_TRACE_ID_VAR] = nil
|
55
|
+
result = @formatter.call('ERROR', @time, '', 'this message')
|
56
|
+
assert_equal(
|
57
|
+
"{\"message\":\"this message\"," +
|
58
|
+
"\"timestamp\":{\"seconds\":#{@time_sec},\"nanos\":#{@time_nsec}}," +
|
59
|
+
"\"severity\":\"ERROR\"}\n",
|
60
|
+
result)
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def test_format_with_warning_severity
|
65
|
+
result = @formatter.call('WARN', @time, '', 'this message')
|
66
|
+
assert_equal(
|
67
|
+
"{\"message\":\"this message\"," +
|
68
|
+
"\"timestamp\":{\"seconds\":#{@time_sec},\"nanos\":#{@time_nsec}}," +
|
69
|
+
"\"severity\":\"WARNING\"," +
|
70
|
+
"\"traceId\":\"#{@trace_id}\"}\n",
|
71
|
+
result)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def test_format_with_critical_severity
|
76
|
+
result = @formatter.call('FATAL', @time, '', 'this message')
|
77
|
+
assert_equal(
|
78
|
+
"{\"message\":\"this message\"," +
|
79
|
+
"\"timestamp\":{\"seconds\":#{@time_sec},\"nanos\":#{@time_nsec}}," +
|
80
|
+
"\"severity\":\"CRITICAL\"," +
|
81
|
+
"\"traceId\":\"#{@trace_id}\"}\n",
|
82
|
+
result)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def test_format_with_unknown_severity
|
87
|
+
result = @formatter.call('UNKNOWN', @time, '', 'this message')
|
88
|
+
assert_equal(
|
89
|
+
"{\"message\":\"this message\"," +
|
90
|
+
"\"timestamp\":{\"seconds\":#{@time_sec},\"nanos\":#{@time_nsec}}," +
|
91
|
+
"\"severity\":\"CRITICAL\"," +
|
92
|
+
"\"traceId\":\"#{@trace_id}\"}\n",
|
93
|
+
result)
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
class TestLogger < ::Minitest::Test # :nodoc:
|
101
|
+
|
102
|
+
|
103
|
+
def test_no_logging
|
104
|
+
lines = run_test do |logger|
|
105
|
+
end
|
106
|
+
assert_equal([], lines)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def test_basic_log
|
111
|
+
trace_id = "tracetrace"
|
112
|
+
lines = run_test(trace_id) do |logger|
|
113
|
+
logger.progname = "rails"
|
114
|
+
logger.info("Hello")
|
115
|
+
logger.warn("This is a warning")
|
116
|
+
end
|
117
|
+
assert_equal(2, lines.size)
|
118
|
+
assert_log_entry("rails: Hello", "INFO", trace_id, lines[0])
|
119
|
+
assert_log_entry("rails: This is a warning", "WARNING", trace_id, lines[1])
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def assert_log_entry(expected_message, expected_severity, expected_trace_id, line)
|
124
|
+
if line =~ /^\{"message":"(.*)","timestamp":\{"seconds":\d+,"nanos":\d+\},"severity":"(\w+)"(,"traceId":"(\w+)")?\}\n$/
|
125
|
+
message = $1
|
126
|
+
severity = $2
|
127
|
+
trace_id = $4
|
128
|
+
assert_equal(expected_message, message)
|
129
|
+
assert_equal(expected_severity, severity)
|
130
|
+
assert_equal(expected_trace_id, trace_id)
|
131
|
+
else
|
132
|
+
flunk("Bad format: #{line.inspect}")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
def run_test(trace_id=nil)
|
138
|
+
stringio = ::StringIO.new('', 'w')
|
139
|
+
app = ::Proc.new { |env|
|
140
|
+
yield(env['rack.logger'])
|
141
|
+
}
|
142
|
+
middleware = ::AppEngine::Logger::Middleware.new(app, logfile: stringio)
|
143
|
+
middleware.call({'HTTP_X_CLOUD_TRACE_CONTEXT' => trace_id})
|
144
|
+
::StringIO.new(stringio.string).each_line.to_a
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appengine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Tanner
|
8
8
|
- Daniel Azuma
|
9
9
|
autorequire:
|
10
|
-
bindir:
|
10
|
+
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
date: 2016-05-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
@@ -15,86 +15,103 @@ dependencies:
|
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - ~>
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '1.11'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - ~>
|
25
|
+
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.11'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: minitest
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '5.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '5.0'
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: rake
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
31
45
|
requirements:
|
32
|
-
- - ~>
|
46
|
+
- - "~>"
|
33
47
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
48
|
+
version: '11.0'
|
35
49
|
type: :development
|
36
50
|
prerelease: false
|
37
51
|
version_requirements: !ruby/object:Gem::Requirement
|
38
52
|
requirements:
|
39
|
-
- - ~>
|
53
|
+
- - "~>"
|
40
54
|
- !ruby/object:Gem::Version
|
41
|
-
version: '
|
55
|
+
version: '11.0'
|
42
56
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
57
|
+
name: rdoc
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
45
59
|
requirements:
|
46
|
-
- - ~>
|
60
|
+
- - "~>"
|
47
61
|
- !ruby/object:Gem::Version
|
48
|
-
version: '
|
62
|
+
version: '4.2'
|
49
63
|
type: :development
|
50
64
|
prerelease: false
|
51
65
|
version_requirements: !ruby/object:Gem::Requirement
|
52
66
|
requirements:
|
53
|
-
- - ~>
|
67
|
+
- - "~>"
|
54
68
|
- !ruby/object:Gem::Version
|
55
|
-
version: '
|
56
|
-
description:
|
69
|
+
version: '4.2'
|
70
|
+
description: The appengine gem is a set of classes, plugins, and tools for integration
|
71
|
+
with Google App Engine. It provides access to the App Engine runtime environment,
|
72
|
+
including logging to the Google Cloud Console and interpretation of App Engine headers.
|
73
|
+
However, it is not required for deploying your Ruby application to App Engine.
|
57
74
|
email:
|
58
75
|
- adamtanner@google.com
|
59
|
-
- dazuma@
|
76
|
+
- dazuma@gmail.com
|
60
77
|
executables: []
|
61
78
|
extensions: []
|
62
79
|
extra_rdoc_files: []
|
63
80
|
files:
|
64
|
-
- .
|
65
|
-
- .
|
66
|
-
- Gemfile
|
81
|
+
- CHANGELOG.md
|
82
|
+
- CONTRIBUTING.md
|
67
83
|
- LICENSE
|
68
84
|
- README.md
|
69
85
|
- Rakefile
|
70
|
-
- appengine.gemspec
|
71
|
-
- bin/console
|
72
|
-
- bin/setup
|
73
86
|
- lib/appengine.rb
|
87
|
+
- lib/appengine/env.rb
|
88
|
+
- lib/appengine/logger.rb
|
89
|
+
- lib/appengine/railtie.rb
|
74
90
|
- lib/appengine/version.rb
|
75
|
-
|
91
|
+
- test/test_env.rb
|
92
|
+
- test/test_logger.rb
|
93
|
+
homepage: https://github.com/GoogleCloudPlatform/appengine-ruby
|
76
94
|
licenses:
|
77
95
|
- Apache 2.0
|
78
|
-
metadata:
|
79
|
-
allowed_push_host: 'TODO: Set to ''http://mygemserver.com'''
|
96
|
+
metadata: {}
|
80
97
|
post_install_message:
|
81
98
|
rdoc_options: []
|
82
99
|
require_paths:
|
83
100
|
- lib
|
84
101
|
required_ruby_version: !ruby/object:Gem::Requirement
|
85
102
|
requirements:
|
86
|
-
- -
|
103
|
+
- - ">="
|
87
104
|
- !ruby/object:Gem::Version
|
88
|
-
version:
|
105
|
+
version: 2.0.0
|
89
106
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
107
|
requirements:
|
91
|
-
- -
|
108
|
+
- - ">="
|
92
109
|
- !ruby/object:Gem::Version
|
93
110
|
version: '0'
|
94
111
|
requirements: []
|
95
112
|
rubyforge_project:
|
96
|
-
rubygems_version: 2.
|
113
|
+
rubygems_version: 2.4.5.1
|
97
114
|
signing_key:
|
98
115
|
specification_version: 4
|
99
|
-
summary:
|
116
|
+
summary: Google App Engine integration tools
|
100
117
|
test_files: []
|
data/.gitignore
DELETED
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/appengine.gemspec
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'appengine/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "appengine"
|
8
|
-
spec.version = Appengine::VERSION
|
9
|
-
spec.authors = ["Adam Tanner", "Daniel Azuma"]
|
10
|
-
spec.email = ["adamtanner@google.com", "dazuma@google.com"]
|
11
|
-
|
12
|
-
spec.summary = ""
|
13
|
-
spec.description = ""
|
14
|
-
spec.homepage = ""
|
15
|
-
spec.license = "Apache 2.0"
|
16
|
-
|
17
|
-
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
|
-
# delete this section to allow pushing this gem to any host.
|
19
|
-
if spec.respond_to?(:metadata)
|
20
|
-
spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
21
|
-
else
|
22
|
-
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
|
-
end
|
24
|
-
|
25
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
-
spec.bindir = "exe"
|
27
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
-
spec.require_paths = ["lib"]
|
29
|
-
|
30
|
-
spec.add_development_dependency "bundler", "~> 1.11"
|
31
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
-
spec.add_development_dependency "minitest", "~> 5.0"
|
33
|
-
end
|
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "appengine"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|