Skip to content

Commit 2006c55

Browse files
author
Bill Prin
committed
Add Logging API Samples
1 parent 63896b1 commit 2006c55

File tree

6 files changed

+371
-0
lines changed

6 files changed

+371
-0
lines changed

logging/api/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Cloud Logging v2 API Samples
2+
3+
Sample command-line programs for retrieving Google Logging API V2 data.
4+
5+
`logs_api.py` is a simple command-line program to demonstrate writing to a log,
6+
listing its entries to view it, then deleting it via CLI operations.
7+
8+
`export_logs_api.py` demonstrates how to interact with Logging sinks, which can send
9+
logs to Google Cloud Storage, Cloud Pub/Sub, or BigQuery. In this example
10+
we use Google Cloud Storage. It similarly exposes a CLI to run these operations.
11+
12+
## Prerequisites to run locally:
13+
14+
15+
* A Google Cloud Project
16+
17+
Go to the [Google Cloud Console](https://console.cloud.google.com) to create
18+
a project. Take note of the project ID, which is sometimes but not always
19+
the same as your given name for a project.
20+
21+
To run `export.py`, you will also need a Google Cloud Storage Bucket.
22+
23+
gsutil mb gs://[YOUR_PROJECT_ID]
24+
25+
You must add Cloud Logging as an owner to the bucket. To do so, add cloud-logs@google.com as
26+
an owner to the bucket. See the [exportings logs](https://cloud.google.com/logging/docs/export/configure_export#configuring_log_sinks)
27+
docs for complete details.
28+
29+
# Set Up Your Local Dev Environment
30+
To install, run the following commands. If you want to use [virtualenv](https://virtualenv.readthedocs.org/en/latest/)
31+
(recommended), run the commands within a virtualenv.
32+
33+
Create local credentials by running the following command and following the oauth2 flow:
34+
35+
gcloud beta auth application-default login
36+
37+
To run the list_logs example
38+
39+
python logs_api.py --project_id=<YOUR-PROJECT-ID> write_entry "hello world"
40+
python logs_api.py --project_id=<YOUR-PROJECT-ID> list_entries
41+
python logs_api.py --project_id=<YOUR-PROJECT-ID> delete_logger
42+
43+
44+
The `exports_logs_api.py` samples requires a Cloud bucket that has added cloud-logs@google.com
45+
as an owner. See:
46+
47+
https://cloud.google.com/logging/docs/export/configure_export#setting_product_name_short_permissions_for_writing_exported_logs
48+
49+
python export_logs_api.py --project_id=YOUR_PROJECT_ID create_sink \
50+
--destination_bucket=YOUR_BUCKET
51+
52+
python export_logs_api.py --project_id=YOUR_PROJECT_ID update_sink\
53+
--destination_bucket=YOUR_BUCKET
54+
55+
python export_logs_api.py --project_id=YOUR_PROJECT_ID list_sinks
56+
57+
python export_logs_api.py --project_id=YOUR_PROJECT_ID delete_sink \
58+
--destination_bucket=YOUR_BUCKET
59+
60+
61+
## Running on GCE, GAE, or other environments
62+
63+
See our [Cloud Platform authentication guide](https://cloud.google.com/docs/authentication).
64+

logging/api/export_logs_api.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2016 Google Inc. All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import argparse
18+
19+
from gcloud import logging
20+
from oauth2client.client import GoogleCredentials
21+
22+
FILTER = 'logName="projects/{}/logs/syslog" AND severity>=ERROR'
23+
DESTINATION = 'storage.googleapis.com/{}'
24+
25+
26+
def create_sink_if_not_exists(client, args):
27+
# [START create]
28+
sink = client.sink(
29+
args.sink_name,
30+
FILTER.format(args.project_id),
31+
DESTINATION.format(args.destination_bucket))
32+
33+
if not sink.exists():
34+
sink.create()
35+
print('Created sink {}'.format(sink.name))
36+
# [END create]
37+
return sink
38+
39+
40+
def list_sinks(client, args):
41+
print('Listing sinks available')
42+
43+
# [START list]
44+
sinks = []
45+
while True:
46+
new_sinks, token = client.list_sinks()
47+
sinks += new_sinks
48+
if token is None:
49+
break
50+
51+
for sink in sinks:
52+
print('{}: {}'.format(sink.name, sink.destination))
53+
# [END list]
54+
55+
return sinks
56+
57+
58+
def update_sink(client, args):
59+
"""Changes the filter of a sink.
60+
61+
The filter is used to determine which log statements match this sink and
62+
will be exported to the destination.
63+
"""
64+
# Removes the robot in textPayload part of filter
65+
sink = client.sink(
66+
args.sink_name,
67+
FILTER.format(args.project_id),
68+
DESTINATION.format(args.destination_bucket))
69+
# [START update]
70+
sink.filter = ('logName="projects/{}/logs/syslog" '
71+
'AND severity>= INFO'.format(sink.project))
72+
print('Updated sink {}'.format(sink.name))
73+
sink.update()
74+
# [END update]
75+
76+
77+
def delete_sink(client, args):
78+
"""Deletes a sink"""
79+
sink = client.sink(
80+
args.sink_name,
81+
FILTER.format(args.project_id),
82+
DESTINATION.format(args.destination_bucket))
83+
# [START delete]
84+
sink.delete()
85+
# [END delete]
86+
print('Deleted sink {}'.format(sink.name))
87+
88+
89+
def get_client(project_id):
90+
"""Builds an http client authenticated with the service account
91+
credentials."""
92+
credentials = GoogleCredentials.get_application_default()
93+
return logging.Client(project=project_id, credentials=credentials)
94+
95+
96+
if __name__ == '__main__':
97+
parser = argparse.ArgumentParser(
98+
description=__doc__,
99+
formatter_class=argparse.RawDescriptionHelpFormatter
100+
)
101+
parser.add_argument(
102+
'--project_id', help='Project ID you want to access.', required=True,)
103+
104+
parser.add_argument(
105+
'--sink_name', help='Output bucket to direct sink to',
106+
default="mysink")
107+
108+
subparsers = parser.add_subparsers()
109+
110+
create_parser = subparsers.add_parser('create_sink')
111+
create_parser.set_defaults(func=create_sink_if_not_exists)
112+
create_parser.add_argument(
113+
'--destination_bucket', help='Output bucket to direct sink to',
114+
required=True)
115+
116+
list_parser = subparsers.add_parser('list_sinks')
117+
list_parser.set_defaults(func=list_sinks)
118+
119+
update_parser = subparsers.add_parser('update_sink')
120+
update_parser.set_defaults(func=update_sink)
121+
update_parser.add_argument(
122+
'--destination_bucket', help='Output bucket to direct sink to',
123+
required=True)
124+
125+
delete_parser = subparsers.add_parser('delete_sink')
126+
delete_parser.add_argument(
127+
'--destination_bucket', help='Output bucket to direct sink to',
128+
required=True)
129+
delete_parser.set_defaults(func=delete_sink)
130+
131+
args = parser.parse_args()
132+
client = get_client(args.project_id)
133+
args.func(client, args)

logging/api/export_logs_api_test.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2016 Google Inc. All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from collections import namedtuple
18+
19+
import export_logs_api
20+
21+
SINK_NAME = 'test_sink'
22+
23+
24+
def test_sinks(cloud_config):
25+
client = export_logs_api.get_client(cloud_config.project)
26+
27+
Args = namedtuple('Args', 'project_id destination_bucket sink_name')
28+
args = Args(
29+
destination_bucket=cloud_config.storage_bucket,
30+
project_id=cloud_config.project,
31+
sink_name=SINK_NAME)
32+
33+
export_logs_api.create_sink_if_not_exists(client, args)
34+
sinks = export_logs_api.list_sinks(client, args)
35+
matched_sinks = [s for s in sinks if s.name == SINK_NAME]
36+
assert len(matched_sinks) == 1
37+
export_logs_api.update_sink(client, args)
38+
export_logs_api.delete_sink(client, args)

logging/api/logs_api.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2016 Google Inc. All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import argparse
18+
19+
from gcloud import logging
20+
from oauth2client.client import GoogleCredentials
21+
22+
23+
def write_entry(client, args):
24+
print('Writing log entry for logger '.format(args.logger_name))
25+
mylogger = client.logger(args.logger_name)
26+
# [START write]
27+
mylogger.log_text(args.entry)
28+
# [END write]
29+
30+
31+
def list_entries(client, args):
32+
"""Lists all entries for a logger"""
33+
logger = client.logger(args.logger_name)
34+
print('Listing all log entries for logger {}'.format(logger.name))
35+
# [START list]
36+
entries = []
37+
while True:
38+
new_entries, token = client.list_entries(filter_='logName="{}"'.format(
39+
logger.full_name))
40+
entries += new_entries
41+
if token is None:
42+
break
43+
44+
for entry in entries:
45+
timestamp = entry.timestamp.isoformat()
46+
print('{}: {}'.format
47+
(timestamp, entry.payload))
48+
# [END list]
49+
return entries
50+
51+
52+
def delete_logger(client, args):
53+
"""Deletes a logger and all its entries.
54+
55+
Note that a deletion can take several minutes to take effect.
56+
"""
57+
logger = client.logger(args.logger_name)
58+
print('Deleting all logging entries for {}'.format(logger.name))
59+
# [START delete]
60+
logger.delete()
61+
# [END delete]
62+
63+
64+
def get_client(project_id):
65+
"""Builds an http client authenticated with the service account
66+
credentials."""
67+
# [START auth]
68+
credentials = GoogleCredentials.get_application_default()
69+
return logging.Client(project=project_id, credentials=credentials)
70+
# [END auth]
71+
72+
73+
if __name__ == '__main__':
74+
parser = argparse.ArgumentParser(
75+
description=__doc__,
76+
formatter_class=argparse.RawDescriptionHelpFormatter
77+
)
78+
parser.add_argument(
79+
'--project_id', help='Project ID you want to access.', required=True)
80+
parser.add_argument(
81+
'--logger_name', help='Logger name', default='mylogger')
82+
83+
subparsers = parser.add_subparsers()
84+
85+
write_parser = subparsers.add_parser('write_entry')
86+
write_parser.add_argument('entry')
87+
write_parser.set_defaults(func=write_entry)
88+
89+
list_parser = subparsers.add_parser('list_entries')
90+
list_parser.set_defaults(func=list_entries)
91+
92+
delete_parser = subparsers.add_parser('delete_logger')
93+
delete_parser.set_defaults(func=delete_logger)
94+
95+
args = parser.parse_args()
96+
client = get_client(args.project_id)
97+
args.func(client, args)

logging/api/logs_api_test.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2016 Google Inc. All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from collections import namedtuple
18+
import time
19+
20+
from gcp.testing.flaky import flaky
21+
import logs_api
22+
23+
24+
@flaky
25+
def test_logs(cloud_config):
26+
client = logs_api.get_client(cloud_config.project)
27+
28+
LOG_MESSAGE = 'hello world'
29+
Args = namedtuple('Args', 'logger_name entry')
30+
args = Args(logger_name='test_log', entry=LOG_MESSAGE)
31+
32+
logs_api.write_entry(client, args)
33+
time.sleep(3)
34+
entries = logs_api.list_entries(client, args)
35+
matched_entries = [e for e in entries if e.payload == LOG_MESSAGE]
36+
assert len(matched_entries) > 0
37+
38+
logs_api.delete_logger(client, args)

logging/api/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
gcloud==0.15.0

0 commit comments

Comments
 (0)