From 1f3024702069d41d005e29dce123cd6a540e5bb7 Mon Sep 17 00:00:00 2001 From: Robert Castley Date: Thu, 12 Dec 2024 20:19:10 +0000 Subject: [PATCH] Variable paymentservice error rate and introduce loyalty level attributes (#1815) * 75% error by version number included tenant.level as span attribute * Include service.name in logs and change level to from numeric to text * Updated to reflect changes to paymentservice * Added pull request * Added variants for % failure * Change to loyalty_level and service.version * Change to use app.variant * Updated log entry * Revert changes as deployment YAML is generated after Helm release * Remove app.variant and tidy code for app.loyalty.level * Fixed markdown linting * Typo correction * Add gold back into array and also include loyalty_level in success log --------- Co-authored-by: Juliano Costa Co-authored-by: Pierre Tessier --- CHANGELOG.md | 4 ++++ src/flagd/demo.flagd.json | 11 ++++++--- src/paymentservice/charge.js | 46 ++++++++++++++++++++++++++---------- src/paymentservice/logger.js | 17 +++++++++++-- 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e8cdde9c8..7c9a41b936 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ the release. ## Unreleased +* [flagd] Update `paymentServiceFailure` to use a list of variants. +* [paymentservice] Add loyalty level attributes to spans. + Added `service.name` to logs. + ([#1815](https://github.com/open-telemetry/opentelemetry-demo/pull/1815)) * [grafana] Update grafana to 11.3.0 ([#1764](https://github.com/open-telemetry/opentelemetry-demo/pull/1764)) * [chore] Move build args to .env file diff --git a/src/flagd/demo.flagd.json b/src/flagd/demo.flagd.json index 2d46891ef2..6af1dd51a7 100644 --- a/src/flagd/demo.flagd.json +++ b/src/flagd/demo.flagd.json @@ -65,11 +65,16 @@ "defaultVariant": "off" }, "paymentServiceFailure": { - "description": "Fail payment service charge requests", + "description": "Fail payment service charge requests n%", "state": "ENABLED", "variants": { - "on": true, - "off": false + "100%": 1.00, + "90%": 0.95, + "75%": 0.75, + "50%": 0.50, + "25%": 0.25, + "10%": 0.10, + "off": 0.00 }, "defaultVariant": "off" }, diff --git a/src/paymentservice/charge.js b/src/paymentservice/charge.js index 7fcb7fbdaa..4d02ba24e8 100644 --- a/src/paymentservice/charge.js +++ b/src/paymentservice/charge.js @@ -1,24 +1,41 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -const {context, propagation, trace, metrics} = require('@opentelemetry/api'); +const { context, propagation, trace, metrics } = require('@opentelemetry/api'); const cardValidator = require('simple-card-validator'); const { v4: uuidv4 } = require('uuid'); const { OpenFeature } = require('@openfeature/server-sdk'); -const { FlagdProvider} = require('@openfeature/flagd-provider'); +const { FlagdProvider } = require('@openfeature/flagd-provider'); const flagProvider = new FlagdProvider(); const logger = require('./logger'); const tracer = trace.getTracer('paymentservice'); const meter = metrics.getMeter('paymentservice'); -const transactionsCounter = meter.createCounter('app.payment.transactions') +const transactionsCounter = meter.createCounter('app.payment.transactions'); + +const LOYALTY_LEVEL = ['platinum', 'gold', 'silver', 'bronze']; + +/** Return random element from given array */ +function random(arr) { + const index = Math.floor(Math.random() * arr.length); + return arr[index]; +} module.exports.charge = async request => { const span = tracer.startSpan('charge'); await OpenFeature.setProviderAndWait(flagProvider); - if (await OpenFeature.getClient().getBooleanValue("paymentServiceFailure", false)) { - throw new Error("PaymentService Fail Feature Flag Enabled"); + + const numberVariant = await OpenFeature.getClient().getNumberValue("paymentServiceFailure", 0); + + if (numberVariant > 0) { + // n% chance to fail with app.loyalty.level=gold + if (Math.random() < numberVariant) { + span.setAttributes({'app.loyalty.level': 'gold' }); + span.end(); + + throw new Error('Payment request failed. Invalid token. app.loyalty.level=gold'); + } } const { @@ -34,9 +51,12 @@ module.exports.charge = async request => { const card = cardValidator(number); const { card_type: cardType, valid } = card.getCardDetails(); + const loyalty_level = random(LOYALTY_LEVEL); + span.setAttributes({ 'app.payment.card_type': cardType, - 'app.payment.card_valid': valid + 'app.payment.card_valid': valid, + 'app.loyalty.level': loyalty_level }); if (!valid) { @@ -51,18 +71,18 @@ module.exports.charge = async request => { throw new Error(`The credit card (ending ${lastFourDigits}) expired on ${month}/${year}.`); } - // check baggage for synthetic_request=true, and add charged attribute accordingly + // Check baggage for synthetic_request=true, and add charged attribute accordingly const baggage = propagation.getBaggage(context.active()); - if (baggage && baggage.getEntry("synthetic_request") && baggage.getEntry("synthetic_request").value === "true") { + if (baggage && baggage.getEntry('synthetic_request') && baggage.getEntry('synthetic_request').value === 'true') { span.setAttribute('app.payment.charged', false); } else { span.setAttribute('app.payment.charged', true); } + const { units, nanos, currencyCode } = request.amount; + logger.info({ transactionId, cardType, lastFourDigits, amount: { units, nanos, currencyCode }, loyalty_level }, 'Transaction complete.'); + transactionsCounter.add(1, { 'app.payment.currency': currencyCode }); span.end(); - const { units, nanos, currencyCode } = request.amount; - logger.info({transactionId, cardType, lastFourDigits, amount: { units, nanos, currencyCode }}, "Transaction complete."); - transactionsCounter.add(1, {"app.payment.currency": currencyCode}) - return { transactionId } -} + return { transactionId }; +}; diff --git a/src/paymentservice/logger.js b/src/paymentservice/logger.js index ecc4a528d7..66f8a354f6 100644 --- a/src/paymentservice/logger.js +++ b/src/paymentservice/logger.js @@ -1,6 +1,19 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -const pino = require('pino'); +const pino = require('pino') -module.exports = pino(); +const logger = pino({ + mixin() { + return { + 'service.name': process.env['OTEL_SERVICE_NAME'], + } + }, + formatters: { + level: (label) => { + return { 'level': label }; + }, + }, +}); + +module.exports = logger;