{"id":13820987,"url":"https://github.com/isItObservable/otelmetrics","last_synced_at":"2025-05-16T11:30:59.575Z","repository":{"id":157321326,"uuid":"624791754","full_name":"isItObservable/otelmetrics","owner":"isItObservable","description":null,"archived":false,"fork":false,"pushed_at":"2023-04-07T13:17:33.000Z","size":627,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2024-11-19T20:47:01.230Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/isItObservable.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-07T09:11:20.000Z","updated_at":"2023-06-01T10:23:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"8932aa9c-5e2c-400b-812b-2dc31d480a6d","html_url":"https://github.com/isItObservable/otelmetrics","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isItObservable%2Fotelmetrics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isItObservable%2Fotelmetrics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isItObservable%2Fotelmetrics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isItObservable%2Fotelmetrics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/isItObservable","download_url":"https://codeload.github.com/isItObservable/otelmetrics/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254521745,"owners_count":22084977,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T08:01:12.952Z","updated_at":"2025-05-16T11:30:54.558Z","avatar_url":"https://github.com/isItObservable.png","language":"JavaScript","funding_links":[],"categories":["others"],"sub_categories":[],"readme":"# Is it Observable\n\u003cp align=\"center\"\u003e\u003cimg src=\"/image/logo.png\" width=\"40%\" alt=\"Is It observable Logo\" /\u003e\u003c/p\u003e\n\n## Episode : How to produce metrics with OpenTelemetry\nThis repository contains the files utilized during the tutorial presented in the dedicated IsItObservable episode related to OpenTelemetry Metrics.\n\u003cp align=\"center\"\u003e\u003cimg src=\"/image/opentelemetry-stacked-color.png\" width=\"40%\" alt=\"OpenTelemetry Logo\" /\u003e\u003c/p\u003e\n\nWhat you will learn\n* How to create [OpenTelemetry Metrics](https://opentelemetry.io/docs/concepts/signals/metrics/)\n\nThis repository showcase the usage of OpenTelemetry Metrics  with :\n* The OpenTelemetry Operator\n* Nginx ingress controller\n* Dynatrace\n* Prometheus Operator\n* NodeJs demo application : todo\n\nWe will send all Telemetry data produced by the todo application to Dynatrace.\n\n## Prerequisite\nThe following tools need to be install on your machine :\n- jq\n- kubectl\n- git\n- gcloud ( if you are using GKE)\n- Helm\n\n\n## Deployment Steps in GCP\n\nYou will first need a Kubernetes cluster with 2 Nodes.\nYou can either deploy on Minikube or K3s or follow the instructions to create GKE cluster:\n### 1.Create a Google Cloud Platform Project\n```shell\nPROJECT_ID=\"\u003cyour-project-id\u003e\"\ngcloud services enable container.googleapis.com --project ${PROJECT_ID}\ngcloud services enable monitoring.googleapis.com \\\n    cloudtrace.googleapis.com \\\n    clouddebugger.googleapis.com \\\n    cloudprofiler.googleapis.com \\\n    --project ${PROJECT_ID}\n```\n### 2.Create a GKE cluster\n```shell\nZONE=europe-west3-a\nNAME=isitobservable-fluentbitv2\ngcloud container clusters create \"${NAME}\" --zone ${ZONE} --machine-type=e2-standard-2 --num-nodes=3 \n```\n\n\n## Getting started\n### Dynatrace Tenant\n#### 1. Dynatrace Tenant - start a trial\nIf you don't have any Dyntrace tenant , then i suggest to create a trial using the following link : [Dynatrace Trial](https://bit.ly/3KxWDvY)\nOnce you have your Tenant save the Dynatrace tenant hostname in the variable `DT_TENANT_URL` (for example : https://dedededfrf.live.dynatrace.com)\n```\nDT_TENANT_URL=\u003cYOUR TENANT URL\u003e\n```\n\n#### 2. Create the Dynatrace API Tokens\nCreate a Dynatrace token with the following scope ( left menu Acces Token):\n* ingest metrics\n* ingest OpenTelemetry traces\n* ingest logs\n\u003cp align=\"center\"\u003e\u003cimg src=\"/image/data_ingest.png\" width=\"40%\" alt=\"data token\" /\u003e\u003c/p\u003e\nSave the value of the token . We will use it later to store in a k8S secret\n\n```\nDATA_INGEST_TOKEN=\u003cYOUR TOKEN VALUE\u003e\n```\n### 3.Clone the Github Repository\n```shell\nhttps://github.com/isItObservable/otelmetrics\ncd otelmetrics\n```\n### 4.Deploy most of the components\n#### Define a user and Password for mongo\n```shell\nMONGOUSER=\u003cyour user of your choice\u003e\nMONGOPWD=\u003cYOUR password of your choice\u003e\n```\n#### Deploy\nThe application will deploy the otel demo v1.2.1\n```shell\nchmod 777 deployment.sh\n./deployment.sh  --clustername \"${NAME}\" --dturl \"${DT_TENANT_URL}\" --dttoken \"${DATA_INGEST_TOKEN}\" --mongouser \"${MONGOUSER}\" --mongopwd \"${MONGOPWD}\"\n```\n### 5.Create metrics\n\n#### 1. Define the MeterProvider\n\nUdpate the `instrumentation.js` located in : `src/instrumentation.js`\nAdd the following lines :\n```js\n\nconst metricReader = new PeriodicExportingMetricReader({\n    exporter: new OTLPMetricExporter({temporalityPreference: AggregationTemporality.DELTA}),\n    exportIntervalMillis: 3000,\n});\n\nconst myServiceMeterProvider = new MeterProvider({\n    resource: resource,\n});\n\nmyServiceMeterProvider.addMetricReader(metricReader);\notel.metrics.setGlobalMeterProvider(myServiceMeterProvider)\n```\n#### 2. Define A meter\nIn the `app.js` file located in : ` src/app.js`\nLet's define a meter by adding the following line of code: \n```js\nconst meter = otel.metrics.getMeter('Todo-demo');\n```\n#### 3. Define Instruments\n\n##### Counter\nLet's create 2 counter, reporting :\n- the number of todo created\n  ```js\n  const counter_item_total=meter.createCounter('todo.item.created.total',{\n       description: 'Total number of item created',\n       unit: 'items',\n    });\n  ```\n- the number of todo deleted\n  ```js\n  const counter_item_deleted_total=meter.createCounter('todo.item.deleted.total',{\n        description: 'Total number of item deleted',\n        unit: 'items',\n     });  \n  ```\nThen we can define both metrics in 2 different section :\n- Creating item:\nafter :\n  ```js\n  app.post(\"/\", function (req, res) {\n  const newItemName = req.body.newItem;\n  const listTitle = req.body.button;\n  const newItem = new Item({\n    name: newItemName,\n  });\n\n  if (listTitle === \"Today\") {\n    newItem.save();\n  ```\n  replace the line `newItem.save()` by:\n  ```js\n  newItem.save(function(err,result){\n      if(err){\n        console.log(err);\n      }\n      else{\n        const label = { \n                  list_title: listTitle\n                  };\n        counter_item_total.add(1,label);\n      }\n  });\n   ```\n- Removing item:\nwe will replace :\n  ```js\n  app.post(\"/remove\", function (req, res) {\n  const checkboxValue = req.body.checkbox;\n  const listTitle = req.body.listTitle;\n\n  if (listTitle === \"Today\") {\n  Item.deleteOne({ _id: checkboxValue }, function (err) {\n    console.log(err);\n    });\n  ```\n  by:\n  ```js\n  app.post(\"/remove\", function (req, res) {\n  const checkboxValue = req.body.checkbox;\n  const listTitle = req.body.listTitle;\n\n  if (listTitle === \"Today\") {\n  Item.deleteOne({ _id: checkboxValue }).then( ()=\u003e {\n    const label = { \n                    list_title: listTitle\n                };\n    counter_item_deleted_total.add(1,label);\n  }\n  ).catch(function (err) {\n    console.log(err);\n    });\n  ```\n##### Gauge\nFor this example we will create a Gauge metric to report the latency to :\n- save a document in mongo database\n- remove a document in mongo database\nFor this we can create only one Gauge Metric with a label to seperate the type of operation by adding label with the operation type.\n  ```js\n  const mongo_operation_latency=meter.createObservableGauge('mongo.operation.latency',{\n  description: 'latency in ms',\n  unit: 'ms',\n  });\n  ```\nReplace the following line:\n```js\n  newItem.save(function(err,result){\n    if(err){\n       console.log(err);\n    }\n    else{\n      const label = { \n          list_title: listTitle\n      };\n      counter_item_total.add(1,label);\n    }\n  });\n```\nby:\n```js\n  start=Date.now()\n  newItem.save(function(err,result){\n    if(err){\n       console.log(err);\n    }\n    else{\n      const elapse =Date.now() - start\n      const label = { \n          list_title: listTitle\n      };\n      const labeloperation = { \n          database: 'todo', operation: 'save'\n      };\n      counter_item_total.add(1,label);\n       mongo_operation_latency.addCallback(\n                                          (result) =\u003e {\n                                            result.observe(elapse,labeloperation)\n                                          }\n                                        )\n    }\n  });\n```\nand for removing item, replace: \n  ```js\n  app.post(\"/remove\", function (req, res) {\n  const checkboxValue = req.body.checkbox;\n  const listTitle = req.body.listTitle;\n\n  if (listTitle === \"Today\") {\n  Item.deleteOne({ _id: checkboxValue }).then( () =\u003e {\n    const label = {\n                    list_title: listTitle\n                };\n    counter_item_deleted_total.add(1,label);\n    }\n  ).catch(function (err) {\n    console.log(err);\n    });\n  ```\nby:\n  ```js\n  app.post(\"/remove\", function (req, res) {\n  const checkboxValue = req.body.checkbox;\n  const listTitle = req.body.listTitle;\n\n  if (listTitle === \"Today\") {\n  cont start= Date.now();\n  Item.deleteOne({ _id: checkboxValue }).then( =\u003e {\n    const elapse= Date.now() - start;\n    const label = { \n                    list_title: listTitle\n                };\n    const labeloperation = { \n          database: 'todo', operation: 'delete'\n      };\n    counter_item_deleted_total.add(1,label);\n    mongo_operation_latency.addCallback(\n                                          (result) =\u003e {\n                                            result.observe(elapse,labeloperation)\n                                          }\n                                        )\n    }\n  ).catch(function (err) {\n    console.log(err);\n    });\n  ```\n### 6.Create the new Container image \nRebuild the container and update the `manifest/deployment.yaml`\n```shell\ncd src\ndocker build . - t \u003cyour docker registry\u003e/todoapp-node:0.2\ndocker push \u003cyour docker registry\u003e/todoapp-node:0.2\n```\nUpdate the deployment of the application to use your container image :\n```shell\nsed -i \"s,hrexed/todoapp-node:0.1,\u003cyour docker registry\u003e/todoapp-node:0.2,\" manifest/deployment.yaml\n```\n### 7.Re-Deploy\nDeploy the upated workload:\n```shell\nkubectl apply -f manifest/deployment.yaml -n todo\n```\n\u003cp align=\"center\"\u003e\u003cimg src=\"/image/graph.png\" width=\"40%\" alt=\"Custom metrics\" /\u003e\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FisItObservable%2Fotelmetrics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FisItObservable%2Fotelmetrics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FisItObservable%2Fotelmetrics/lists"}