appengine 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|