Skip to content

Commit 7f40378

Browse files
ymotongpooahmetb
authored andcommitted
log: change log format to JSON payload for better log in Stackdriver (GoogleCloudPlatform#66)
change the log format in Python and Node.js services. Effected services are currencyservice, emailservice, paymentservice, and recommendationservice. Loadgenerator is left as is because of the diffculty to change the log format and log target in locust. ref. GoogleCloudPlatform#47
1 parent 2771a03 commit 7f40378

16 files changed

+286
-35
lines changed

src/currencyservice/client.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ require('@google-cloud/trace-agent').start();
2020
const path = require('path');
2121
const grpc = require('grpc');
2222
const leftPad = require('left-pad');
23+
const pino = require('pino');
2324

2425
const PROTO_PATH = path.join(__dirname, './proto/demo.proto');
2526
const PORT = 7000;
@@ -28,6 +29,13 @@ const shopProto = grpc.load(PROTO_PATH).hipstershop;
2829
const client = new shopProto.CurrencyService(`localhost:${PORT}`,
2930
grpc.credentials.createInsecure());
3031

32+
const logger = pino({
33+
name: 'currencyservice-client',
34+
messageKey: 'message',
35+
changeLevelName: 'severity',
36+
useLevelLabels: true
37+
});
38+
3139
const request = {
3240
from: {
3341
currency_code: 'CHF',
@@ -43,16 +51,16 @@ function _moneyToString (m) {
4351

4452
client.getSupportedCurrencies({}, (err, response) => {
4553
if (err) {
46-
console.error(`Error in getSupportedCurrencies: ${err}`);
54+
logger.error(`Error in getSupportedCurrencies: ${err}`);
4755
} else {
48-
console.log(`Currency codes: ${response.currency_codes}`);
56+
logger.info(`Currency codes: ${response.currency_codes}`);
4957
}
5058
});
5159

5260
client.convert(request, (err, response) => {
5361
if (err) {
54-
console.error(`Error in convert: ${err}`);
62+
logger.error(`Error in convert: ${err}`);
5563
} else {
56-
console.log(`Convert: ${_moneyToString(request.from)} to ${_moneyToString(response)}`);
64+
logger.log(`Convert: ${_moneyToString(request.from)} to ${_moneyToString(response)}`);
5765
}
5866
});

src/currencyservice/package-lock.json

+62
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/currencyservice/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"google-protobuf": "^3.0.0",
1818
"grpc": "^1.0.0",
1919
"left-pad": "^1.3.0",
20+
"pino": "^5.6.2",
2021
"request": "^2.87.0",
2122
"xml2js": "^0.4.19"
2223
},

src/currencyservice/server.js

+14-7
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const path = require('path');
3232
const grpc = require('grpc');
3333
const request = require('request');
3434
const xml2js = require('xml2js');
35+
const pino = require('pino');
3536
const protoLoader = require('@grpc/proto-loader');
3637

3738
const MAIN_PROTO_PATH = path.join(__dirname, './proto/demo.proto');
@@ -43,6 +44,13 @@ const DATA_URL = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';
4344
const shopProto = _loadProto(MAIN_PROTO_PATH).hipstershop;
4445
const healthProto = _loadProto(HEALTH_PROTO_PATH).grpc.health.v1;
4546

47+
const logger = pino({
48+
name: 'currencyservice-server',
49+
messageKey: 'message',
50+
changeLevelName: 'severity',
51+
useLevelLabels: true
52+
});
53+
4654
/**
4755
* Helper function that loads a protobuf file.
4856
*/
@@ -67,7 +75,7 @@ function _loadProto (path) {
6775
let _data;
6876
function _getCurrencyData (callback) {
6977
if (!_data) {
70-
console.log('Fetching currency data...');
78+
logger.info('Fetching currency data...');
7179
request(DATA_URL, (err, res) => {
7280
if (err) {
7381
throw new Error(`Error getting data: ${err}`);
@@ -108,7 +116,7 @@ function _carry (amount) {
108116
* Lists the supported currencies
109117
*/
110118
function getSupportedCurrencies (call, callback) {
111-
console.log('Getting supported currencies...');
119+
logger.info('Getting supported currencies...');
112120
_getCurrencyData((data) => {
113121
callback(null, {currency_codes: Object.keys(data)});
114122
});
@@ -118,7 +126,7 @@ function getSupportedCurrencies (call, callback) {
118126
* Converts between currencies
119127
*/
120128
function convert (call, callback) {
121-
console.log('received conversion request');
129+
logger.info('received conversion request');
122130
try {
123131
_getCurrencyData((data) => {
124132
const request = call.request;
@@ -142,12 +150,11 @@ function convert (call, callback) {
142150
result.nanos = Math.floor(result.nanos);
143151
result.currency_code = request.to_code;
144152

145-
console.log(`conversion request successful`);
153+
logger.info(`conversion request successful`);
146154
callback(null, result);
147155
});
148156
} catch (err) {
149-
console.error('conversion request failed.');
150-
console.error(err);
157+
logger.error(`conversion request failed: ${err}`);
151158
callback(err.message);
152159
}
153160
}
@@ -164,7 +171,7 @@ function check (call, callback) {
164171
* CurrencyConverter service at the sample server port
165172
*/
166173
function main () {
167-
console.log(`Starting gRPC server on port ${PORT}...`);
174+
logger.info(`Starting gRPC server on port ${PORT}...`);
168175
const server = new grpc.Server();
169176
server.addService(shopProto.CurrencyService.service, {getSupportedCurrencies, convert});
170177
server.addService(healthProto.Health.service, {check});

src/emailservice/email_client.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import demo_pb2
2020
import demo_pb2_grpc
2121

22+
from logger import getJSONLogger
23+
logger = getJSONLogger('emailservice-client')
24+
2225
# from opencensus.trace.tracer import Tracer
2326
# from opencensus.trace.exporters import stackdriver_exporter
2427
# from opencensus.trace.ext.grpc import client_interceptor
@@ -39,10 +42,10 @@ def send_confirmation_email(email, order):
3942
email = email,
4043
order = order
4144
))
42-
print('Request sent.')
45+
logger.info('Request sent.')
4346
except grpc.RpcError as err:
44-
print(err.details())
45-
print('{}, {}'.format(err.code().name, err.code().value))
47+
logger.error(err.details())
48+
logger.error('{}, {}'.format(err.code().name, err.code().value))
4649

4750
if __name__ == '__main__':
48-
print('Client for email service.')
51+
logger.info('Client for email service.')

src/emailservice/email_server.py

+10-7
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
# except:
5151
# pass
5252

53+
from logger import getJSONLogger
54+
logger = getJSONLogger('emailservice-server')
55+
5356
# Loads confirmation email template from file
5457
env = Environment(
5558
loader=FileSystemLoader('templates'),
@@ -78,14 +81,14 @@ def send_email(client, email_address, content):
7881
"from": {
7982
"address_spec": from_address,
8083
},
81-
"to": [{
82-
"address_spec": email_address
84+
"to": [{
85+
"address_spec": email_address
8386
}],
8487
"subject": "Your Confirmation Email",
8588
"html_body": content
8689
}
8790
)
88-
print("Message sent: {}".format(response.rfc822_message_id))
91+
logger.info("Message sent: {}".format(response.rfc822_message_id))
8992

9093
def SendOrderConfirmation(self, request, context):
9194
email = request.email
@@ -95,7 +98,7 @@ def SendOrderConfirmation(self, request, context):
9598
confirmation = template.render(order = order)
9699
except TemplateError as err:
97100
context.set_details("An error occurred when preparing the confirmation mail.")
98-
print(err.message)
101+
logger.error(err.message)
99102
context.set_code(grpc.StatusCode.INTERNAL)
100103
return demo_pb2.Empty()
101104

@@ -111,7 +114,7 @@ def SendOrderConfirmation(self, request, context):
111114

112115
class DummyEmailService(BaseEmailService):
113116
def SendOrderConfirmation(self, request, context):
114-
print('A request to send order confirmation email to {} has been received.'.format(request.email))
117+
logger.info('A request to send order confirmation email to {} has been received.'.format(request.email))
115118
return demo_pb2.Empty()
116119

117120
class HealthCheck():
@@ -131,7 +134,7 @@ def start(dummy_mode):
131134
health_pb2_grpc.add_HealthServicer_to_server(service, server)
132135

133136
port = os.environ.get('PORT', "8080")
134-
print("listening on port: "+port)
137+
logger.info("listening on port: "+port)
135138
server.add_insecure_port('[::]:'+port)
136139
server.start()
137140
try:
@@ -142,5 +145,5 @@ def start(dummy_mode):
142145

143146

144147
if __name__ == '__main__':
145-
print('starting the email service in dummy mode.')
148+
logger.info('starting the email service in dummy mode.')
146149
start(dummy_mode = True)

src/emailservice/logger.py

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/python
2+
#
3+
# Copyright 2018 Google LLC
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 logging
18+
import sys
19+
from pythonjsonlogger import jsonlogger
20+
21+
# TODO(yoshifumi) this class is duplicated since other Python services are
22+
# not sharing the modules for logging.
23+
class CustomJsonFormatter(jsonlogger.JsonFormatter):
24+
def add_fields(self, log_record, record, message_dict):
25+
super(CustomJsonFormatter, self).add_fields(log_record, record, message_dict)
26+
if not log_record.get('timestamp'):
27+
log_record['timestamp'] = record.created
28+
if log_record.get('severity'):
29+
log_record['severity'] = log_record['severity'].upper()
30+
else:
31+
log_record['severity'] = record.levelname
32+
33+
def getJSONLogger(name):
34+
logger = logging.getLogger(name)
35+
handler = logging.StreamHandler(sys.stdout)
36+
formatter = CustomJsonFormatter('(timestamp) (severity) (name) (message)')
37+
handler.setFormatter(formatter)
38+
logger.addHandler(handler)
39+
logger.setLevel(logging.INFO)
40+
return logger

src/emailservice/requirements.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ pycairo==1.17.1
3232
pycparser==2.19
3333
pycrypto==2.6.1
3434
PyGObject==3.30.1
35+
python-json-logger==0.1.9
3536
pytz==2018.5
3637
pyxdg==0.26
3738
requests==2.19.1
3839
rsa==4.0
3940
SecretStorage==3.1.0
4041
six==1.11.0
41-
urllib3==1.23
42+
urllib3==1.23

src/paymentservice/charge.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@
1414

1515
const cardValidator = require('simple-card-validator');
1616
const uuid = require('uuid/v4');
17+
const pino = require('pino');
18+
19+
const logger = pino({
20+
name: 'paymentservice-charge',
21+
messageKey: 'message',
22+
changeLevelName: 'severity',
23+
useLevelLabels: true
24+
});
25+
1726

1827
class CreditCardError extends Error {
1928
constructor (message) {
@@ -67,7 +76,7 @@ module.exports = function charge (request) {
6776
const { credit_card_expiration_year: year, credit_card_expiration_month: month } = creditCard;
6877
if ((currentYear * 12 + currentMonth) > (year * 12 + month)) { throw new ExpiredCreditCard(cardNumber.replace('-', ''), month, year); }
6978

70-
console.log(`Transaction processed: ${cardType} ending ${cardNumber.substr(-4)} \
79+
logger.info(`Transaction processed: ${cardType} ending ${cardNumber.substr(-4)} \
7180
Amount: ${amount.currency_code}${amount.units}.${amount.nanos}`);
7281

7382
return { transaction_id: uuid() };

0 commit comments

Comments
 (0)