Skip to content

Commit

Permalink
[paymentservice] add basic metrics support (#583)
Browse files Browse the repository at this point in the history
* [paymentservice] add basic metrics support

* add changelog

add changelog

* update docs

* fix typos.

* fix linting errors

* clarify 'wrapper' approach

Co-authored-by: Carter Socha <[email protected]>
  • Loading branch information
pichlermarc and cartersocha authored Nov 16, 2022
1 parent e8699ac commit d25a8fb
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 97 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,5 @@ significant modifications will be credited to OpenTelemetry Authors.
([#569](https://github.com/open-telemetry/opentelemetry-demo/pull/569))
* Optimize GitHub Builds and fix broken emulation of featureflag
([#536](https://github.com/open-telemetry/opentelemetry-demo/pull/536))
* Add basic metrics support for payment service
([#583](https://github.com/open-telemetry/opentelemetry-demo/pull/583))
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ services:
environment:
- PAYMENT_SERVICE_PORT
- OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
- OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
- OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE
- OTEL_SERVICE_NAME=paymentservice
depends_on:
- otelcol
Expand Down
2 changes: 1 addition & 1 deletion docs/metric_service_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Emoji Legend
| Email | Ruby | :construction: | :construction: | :construction: | :construction: | :construction: | :construction: |
| Feature Flag | Erlang / Elixir | :construction: | :construction: | :construction: | :construction: | :construction: | :construction: |
| Frontend | JavaScript | :construction: | :construction: | :construction: | :construction: | :construction: | :construction: |
| Payment | JavaScript | :construction: | :construction: | :construction: | :construction: | :construction: | :construction: |
| Payment | JavaScript | :construction: | :100: | :construction: | :construction: | :construction: | :construction: |
| Product Catalog | Go | :construction: | :construction: | :construction: | :construction: | :construction: | :construction: |
| Recommendation | Python | :100: | :100: | :construction: | :construction: | :construction: | :construction: |
| Shipping | Rust | :construction: | :construction: | :construction: | :construction: | :construction: | :construction: |
47 changes: 34 additions & 13 deletions docs/services/paymentservice.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,40 @@ processed.

## Initializing OpenTelemetry

It is recommended to use a Node required module when starting your NodeJS
application to initialize the SDK and auto-instrumentation. When initializing
the OpenTelemetry NodeJS SDK, you optionally specify which auto-instrumentation
libraries to leverage, or make use of the `getNodeAutoInstrumentations()`
function which includes most popular frameworks. The `tracing.js` contains all
code required to initialize the SDK and auto-instrumentation based on standard
It is recommended to `require` Node.js app using an initializer file that
initializes the SDK and auto-instrumentation. When initializing the
OpenTelemetry NodeJS SDK in that module, you optionally specify which
auto-instrumentation libraries to leverage, or make use of the
`getNodeAutoInstrumentations()` function which includes most popular frameworks.
The below example of an intiailizer file (`opentelemetry.js`) contains all code
required to initialize the SDK and auto-instrumentation based on standard
OpenTelemetry environment variables for OTLP export, resource attributes, and
service name.
service name. It then `require`s your app at `./index.js` to start it up once
the SDK is initialized.

```javascript
const opentelemetry = require("@opentelemetry/sdk-node")
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node")
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc')
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc')
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc')
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');

const sdk = new opentelemetry.NodeSDK({
traceExporter: new OTLPTraceExporter(),
instrumentations: [ getNodeAutoInstrumentations() ]
instrumentations: [ getNodeAutoInstrumentations() ],
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter()
}),
})

sdk.start()
sdk.start().then(() => require("./index"));
```

Node required modules are loaded using the `--require` command line argument.
You can then use `opentelemetry.js` to start your app.
This can be done in the `ENTRYPOINT` command for the service's `Dockerfile`.

```dockerfile
ENTRYPOINT [ "node", "--require", "./tracing.js", "./index.js" ]
ENTRYPOINT [ "node", "./opentelemetry.js" ]
```

## Traces
Expand Down Expand Up @@ -72,7 +79,21 @@ be sure to set the span's status accordingly. You can see this in the

## Metrics

TBD
### Creating Meters and Instruments

Meters can be created using the `@opentelemetry/api-metrics` package. You can
create meters as seen below, and then use the created meter to create
instruments.

```javascript
const { metrics } = require('@opentelemetry/api-metrics');

const meter = metrics.getMeter('paymentservice');
const transactionsCounter = meter.createCounter('app.payment.transactions')
```

Meters and Instruments are supposed to stick around. This means you should
get a Meter or an Instrument once , and then re-use it as needed, if possible.

## Logs

Expand Down
2 changes: 1 addition & 1 deletion src/paymentservice/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ COPY ./src/paymentservice/ ./
COPY ./pb/demo.proto ./

EXPOSE ${PAYMENT_SERVICE_PORT}
ENTRYPOINT [ "node", "--require", "./tracing.js", "./index.js" ]
ENTRYPOINT [ "npm", "run", "start" ]
12 changes: 8 additions & 4 deletions src/paymentservice/charge.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@
// limitations under the License.

const {context, propagation, trace} = require('@opentelemetry/api');
const { metrics } = require('@opentelemetry/api-metrics');
const cardValidator = require('simple-card-validator');
const { v4: uuidv4 } = require('uuid');

const logger = require('./logger');
const tracer = trace.getTracer('paymentservice');
const meter = metrics.getMeter('paymentservice');
const transactionsCounter = meter.createCounter('app.payment.transactions')

module.exports.charge = request => {
const span = tracer.startSpan('charge');

const { creditCardNumber: number,
const {
creditCardNumber: number,
creditCardExpirationYear: year,
creditCardExpirationMonth: month
} = request.creditCard;
Expand All @@ -32,7 +36,7 @@ module.exports.charge = request => {
const transactionId = uuidv4();

const card = cardValidator(number);
const {card_type: cardType, valid } = card.getCardDetails();
const { card_type: cardType, valid } = card.getCardDetails();

span.setAttributes({
'app.payment.card_type': cardType,
Expand All @@ -53,7 +57,7 @@ module.exports.charge = request => {

// 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);
Expand All @@ -63,6 +67,6 @@ module.exports.charge = request => {

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 }
}
15 changes: 15 additions & 0 deletions src/paymentservice/opentelemetry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const opentelemetry = require("@opentelemetry/sdk-node")
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node")
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc')
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc')
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');

const sdk = new opentelemetry.NodeSDK({
traceExporter: new OTLPTraceExporter(),
instrumentations: [ getNodeAutoInstrumentations() ],
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter()
}),
})

sdk.start().then(() => require("./index"));
Loading

0 comments on commit d25a8fb

Please sign in to comment.