Skip to content

Commit bca7097

Browse files
authored
Run logging manual (GoogleCloudPlatform#2513)
* Adding cloud run manual logging sample * Updating test file * Cleaning up * Fixing nits
1 parent 3369fd0 commit bca7097

File tree

6 files changed

+197
-0
lines changed

6 files changed

+197
-0
lines changed

run/logging-manual/.dockerignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Dockerfile
2+
.dockerignore
3+
__pycache__
4+
.pytest_cache

run/logging-manual/Dockerfile

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright 2019 Google, LLC.
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+
# Use the official Python image.
16+
# https://hub.docker.com/_/python
17+
FROM python:3.7
18+
19+
# Copy application dependency manifests to the container image.
20+
# Copying this separately prevents re-running pip install on every code change.
21+
COPY requirements.txt .
22+
23+
# Install production dependencies.
24+
RUN pip install -r requirements.txt
25+
26+
# Copy local code to the container image.
27+
ENV APP_HOME /app
28+
WORKDIR $APP_HOME
29+
COPY . .
30+
31+
# Run the web service on container startup.
32+
# Use gunicorn webserver with one worker process and 8 threads.
33+
# For environments with multiple CPU cores, increase the number of workers
34+
# to be equal to the cores available.
35+
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 main:app

run/logging-manual/README.md

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Cloud Run Manual Logging Sample
2+
3+
This sample shows how to send structured logs to Stackdriver Logging.
4+
5+
[![Run in Google Cloud][run_img]][run_link]
6+
7+
[run_img]: https://storage.googleapis.com/cloudrun/button.svg
8+
[run_link]: https://console.cloud.google.com/cloudshell/editor?shellonly=true&cloudshell_image=gcr.io/cloudrun/button&cloudshell_git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&cloudshell_working_dir=run/logging-manual
9+
10+
## Build
11+
12+
```
13+
docker build --tag logging-manual:python .
14+
```
15+
16+
## Run Locally
17+
18+
```
19+
docker run --rm -p 9090:8080 logging-manual:python
20+
```
21+
22+
## Test
23+
24+
```
25+
pytest
26+
```
27+
28+
_Note: you may need to install `pytest` using `pip install pytest`._
29+
30+
## Deploy
31+
32+
```sh
33+
# Set an environment variable with your GCP Project ID
34+
export GOOGLE_CLOUD_PROJECT=<PROJECT_ID>
35+
36+
# Submit a build using Google Cloud Build
37+
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/logging-manual
38+
39+
# Deploy to Cloud Run
40+
gcloud beta run deploy logging-manual \
41+
--image gcr.io/${GOOGLE_CLOUD_PROJECT}/logging-manual \
42+
--set-env-vars GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT}
43+
```
44+
45+
Read more about Cloud Run logging in the [Logging How-to Guide](http://cloud.google.com/run/docs/logging).
46+
47+
For more details on how to work with this sample read the [Python Cloud Run Samples README](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/run)

run/logging-manual/main.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright 2019 Google, LLC.
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+
from flask import Flask, request
16+
import json
17+
import os
18+
import sys
19+
20+
app = Flask(__name__)
21+
22+
23+
@app.route('/', methods=['GET'])
24+
def index():
25+
PROJECT = os.environ['GOOGLE_CLOUD_PROJECT']
26+
27+
# [START run_manual_logging]
28+
# Uncomment and populate this variable in your code:
29+
# PROJECT = 'The project ID of your Cloud Run service';
30+
31+
# Build structured log messages as an object.
32+
global_log_fields = {}
33+
34+
# Add log correlation to nest all log messages
35+
# beneath request log in Log Viewer.
36+
trace_header = request.headers.get('X-Cloud-Trace-Context')
37+
38+
if trace_header and PROJECT:
39+
trace = trace_header.split('/')
40+
global_log_fields['logging.googleapis.com/trace'] = (
41+
f"projects/{PROJECT}/traces/{trace[0]}")
42+
43+
# Complete a structured log entry.
44+
entry = dict(severity='NOTICE',
45+
message='This is the default display field.',
46+
# Log viewer accesses 'component' as jsonPayload.component'.
47+
component='arbitrary-property',
48+
**global_log_fields)
49+
50+
print(json.dumps(entry))
51+
# [END run_manual_logging]
52+
53+
# Flush the stdout to avoid log buffering.
54+
sys.stdout.flush()
55+
56+
return 'Hello Logger!'
57+
58+
59+
if __name__ == '__main__':
60+
PORT = int(os.getenv('PORT')) if os.getenv('PORT') else 8080
61+
62+
# This is used when running locally. Gunicorn is used to run the
63+
# application on Cloud Run. See entrypoint in Dockerfile.
64+
app.run(host='127.0.0.1', port=PORT, debug=True)

run/logging-manual/main_test.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2019 Google, LLC.
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+
# NOTE:
16+
# These unit tests mock logging.
17+
18+
19+
import pytest
20+
import main
21+
22+
23+
@pytest.fixture
24+
def client():
25+
main.app.testing = True
26+
return main.app.test_client()
27+
28+
29+
def test_no_trace_in_headers(client, capsys):
30+
r = client.get('/')
31+
assert r.status_code == 200
32+
33+
out, _ = capsys.readouterr()
34+
print(out)
35+
assert 'trace' not in out
36+
37+
38+
def test_with_cloud_headers(client, capsys):
39+
r = client.get('/', headers={'X-Cloud-Trace-Context': 'foo/bar'})
40+
assert r.status_code == 200
41+
42+
out, _ = capsys.readouterr()
43+
print(out)
44+
assert 'trace' in out

run/logging-manual/requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Flask==1.1.1
2+
pytest==5.1.3
3+
gunicorn==19.9.0

0 commit comments

Comments
 (0)