From dc129eca7b987d9b5f1aafdf9d188baa97cb9fc7 Mon Sep 17 00:00:00 2001 From: Laurynas Keturakis Date: Fri, 16 Jun 2023 15:34:35 +0200 Subject: [PATCH] Edit Readme (and fix bugs) (#71) * fix: typescript plugin bug * shorten the readme * Apply suggestions from code review Co-authored-by: Evan Schwartz * readme changes based on comments --------- Co-authored-by: Evan Schwartz --- README.md | 313 +++++-------------- packages/typescript-plugin/src/astHelpers.ts | 1 + 2 files changed, 75 insertions(+), 239 deletions(-) diff --git a/README.md b/README.md index bbebd37e..608eab42 100644 --- a/README.md +++ b/README.md @@ -2,305 +2,140 @@

Autometrics

-Autometrics CI status +Autometrics CI status License discord server

-> A TypeScript port of the Rust -> [autometrics-rs](https://github.com/fiberplane/autometrics-rs) library +Metrics are a powerful and cost-efficient tool for understanding the health and performance of your code in production. But it's hard to decide what metrics to track and even harder to write queries to understand the data. -**A library that makes it easy to understand the error rate, response time, and -production usage of any function in your code.** Jump straight from your IDE to -live Prometheus charts for each HTTP/RPC handler, database method, or other -piece of application logic. +Autometrics provides a wrapper function and decorator to instrument functions, classes, and methods with the most useful metrics: request rate, error rate, and latency. It standardizes these metrics and then generates powerful Prometheus queries based on your function details to help you quickly identify and debug issues in production. -Autometrics for TypeScript provides a wrapper function and a decorator that can -create Prometheus or OpenTelemetry metrics for your functions and class methods -throughout your code base, as well as a language service plugin that will write -corresponding Prometheus queries for you. +[Learn more about Autometrics at autometrics.dev](https://autometrics.dev/). -See [Why Autometrics?](https://github.com/autometrics-dev#why-autometrics) for -more details on the ideas behind autometrics +## Benefits -![AutometricsTS demo](./assets/autometrics-ts-demo.gif) - -## Features - -- ✨ `autometrics` wrapper instruments any function or class method to track its - most useful metrics -- 🌳 Works in NodeJS and browser environments (Deno and serverless support coming soon) -- 💡 Writes Prometheus queries so you can understand the data generated without - knowing PromQL +- ✨ `autometrics()` wrapper / `@Autometrics()` decorator instruments any function or class method to track its most useful metrics +- 🌳 Works in NodeJS and has experimental support for Deno and browser environments +- 💡 Writes Prometheus queries so you can understand the data generated without knowing PromQL - 🔗 Injects links to live Prometheus charts directly into each function's doc - comments -- [📊 Grafana dashboards](#dashboards) work out of the box to visualize the performance of instrumented functions & SLOs -- 🚨 Generates Prometheus alerting rules using SLO best practices - from simple annotations in your code +- 🔍 Helps you to [identify commits](https://docs.autometrics.dev/typescript/adding-version-information) that introduced errors or increased latency +- 📊 [Grafana dashboards](https://github.com/autometrics-dev/autometrics-shared#dashboards) work out of the box and visualize the performance of instrumented functions & SLOs - ⚡ Minimal runtime overhead -## How it works +## Advanced Features +- 🚨 Allows you to [define alerts](https://docs.autometrics.dev/typescript/adding-alerts-and-slos) using SLO best practices directly in your source code comments -The Autometrics library: - -- Automatically instruments any wrapped function with OpenTelemetry metrics -- Uses a Prometheus Exporter to write metrics to a `/metrics` endpoint (by - default on port `:9464`) or pushes them to a specified gateway (if used in - browser) -- Uses a TypeScript plugin / VSCode extension to automatically add useful Prometheus queries in the doc comments for instrumented functions - -## Quickstart - -```shell -npm install --save autometrics -``` - -Use the library in your code: +## Example ```typescript import { autometrics } from "autometrics"; -``` -> Note for VSCode users: Make sure you set your VSCode TypeScript server -> to be local to the project (where you have TypeScript installed in your -> `devDependencies`). -> -> In `.vscode/settings.json` set: -> -> ```json -> { -> "typescript.tsdk": "node_modules/typescript/lib" -> } -> ``` - -### For projects already using OpenTelemetry metrics - -The default `autometrics` package bundles `@opentelemetry/sdk-metrics` and -`@opentelemetry/exporter-prometheus` dependencies. If you are already using -these in your codebase or want to use other custom metrics, use the following -installation option. - -Install the wrappers: - -```shell -npm install --save @autometrics/autometrics -``` - -Import and use the library in your code: +const createUserWithMetrics = autometrics(async function createUser(payload: User) { + // ... +}); -```typescript -import { autometrics } from "@autometrics/autometrics" +createUserWithMetrics(); ``` -## Getting PromQL queries +![AutometricsTS demo](./assets/autometrics-ts-demo.gif) -In order to get PromQL query links in your IDE download the [Autometrics VSCode -extension](https://marketplace.visualstudio.com/items?itemName=Fiberplane.autometrics). +## Quickstart -If you're on any other IDE you can install and add the TypeScript plugin -directly: +1. **Install the library** ```bash -npm install --save-dev @autometrics/typescript-plugin -``` - -Add the language service plugin to the `tsconfig.json` file: - -```json -{ - "compilerOptions": { - "plugins": [ - { - "name": "@autometrics/typescript-plugin", - "prometheusUrl": "" - } - ] - } -} -``` - -## Dashboards - -Autometrics provides [Grafana dashboards](https://github.com/autometrics-dev/autometrics-shared#dashboards) that will work for any project instrumented with the library. - -## Identifying commits that introduce errors - -Autometrics makes it easy to [spot versions and commits that introduce errors or latency](https://fiberplane.com/blog/autometrics-rs-0-4-spot-commits-that-introduce-errors-or-slow-down-your-application). - -| Label | Run-Time Environment Variables | Default value | -|---|---|---| -| `version` | `AUTOMETRICS_VERSION` or `PACKAGE_VERSION` | `npm_package_version` (set by npm/yarn/pnpm by default) | -| `commit` | `AUTOMETRICS_COMMIT` or `COMMIT_SHA` | `""` | -| `branch` | `AUTOMETRICS_BRANCH` or `BRANCH_NAME` | `""` | - -## Alerts / SLOs - -Autometrics makes it easy to add Prometheus alerts using Service-Level Objectives (SLOs) to a function or group of functions. - -This works using pre-defined [Prometheus alerting rules](https://github.com/autometrics-dev/autometrics-shared/blob/main/autometrics.rules.yml) (read more about alerting rules in general [here](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/)). -By default, most of the recording rules are dormant. They are enabled by specific metric labels that can be automatically attached by autometrics. - -To use autometrics SLOs and alerts, create one or multiple [`Objective`s](https://github.com/autometrics-dev/autometrics-ts/blob/main/packages/autometrics-lib/src/objectives.ts) based on the function(s) success rate and/or latency, as shown below. The `Objective` can be passed as an argument to the `autometrics` wrapper function to include the given function in that objective. - -```ts -import { autometrics, Objective, ObjectiveLatency, ObjectivePercentile } from "@autometrics/autometrics"; - -const API_SLO: Objective = { - name: 'api', - successRate: ObjectivePercentile.P99_9, - latency: [ObjectiveLatency.Ms250, ObjectivePercentile.P99], -}; - -const apiHandlerFn = autometrics({ objective: API_SLO }, function apiHandler( - // ... -)); +npm install --save autometrics +# or +yarn add --save autometrics +# or +pnpm add --save autometrics ``` -## Using wrappers and decorators in NodeJS - -Use Autometrics wrappers to instrument the functions you want to track (e.g.: -request handlers or database calls). - -### Wrapping plain-old functions - -Wrappers are simple functions that wrap the original function declaration and -instrument it with metrics. They allow the language service plugin to add -additional information in the type docs. - -Use function wrappers to wrap request handlers, database calls, or other -pieces of important business logic that you want to measure. - -> **Note**: Wrapped functions must be named. Autometrics will throw an error if -> it can't access the name of the function. - -Example: +2. **Instrument your code using the `autometrics` wrapper or `Autometrics` decorator** ```typescript import { autometrics } from "autometrics"; -const createUser = autometrics(async function createUser(payload: User) { +const createUserWithMetrics = autometrics(async function createUser(payload: User) { // ... }); -const user = createUser(); +createUserWithMetrics(); ``` -> **Note**: If you're using the `@autometrics/autometrics` package instead of -> `autometrics`, import the helper functions from there: -> -> ```typescript -> import { autometrics } from "@autometrics/autometrics"; -> ``` - -### Decorating class methods - -When using a decorator for a class method, it is wrapped in additional code that -instruments the method with OpenTelemetry metrics. - -Here's a snippet from the example code: - ```typescript -import { Controller, Get } from "@nestjs/common"; -import { AppService } from "./app.service"; import { Autometrics } from "autometrics"; -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() +class User { @Autometrics() - getHello(): string { - return this.appService.getHello(); + async createUser(payload: User) { + // ... } } ``` -Alternatively, you can apply the same decorator to a class to instrument all of -its methods: +3. **Configure Prometheus to scrape the data** -```typescript -// ... -@Autometrics() -export class AppController { - // ... - @Get() - getHello(): string { - return this.appService.getHello(); - } -} -``` +By default the TypeScript library makes the metrics available on `:9464/metrics`. Make sure your Prometheus is configured correctly to scrape it. -### Getting the generated queries +Here's a minimal sample Prometheus configuration you can use: -Hover over any Autometrics-instrumented function or class to see its augmented documentation. -Clicking on any of the links will go directly to the Prometheus -chart for that function. - -![Autometrics demo](./assets/demo.png) +```yaml +scrape_configs: + - job_name: my-app + metrics_path: /metrics + static_configs: + - targets: ['localhost:9464'] + # For a real deployment, you would want the scrape interval to be + # longer but for testing, you want the data to show up quickly + scrape_interval: 200ms +``` -## Using wrappers in the browser +[See the docs](https://docs.autometrics.dev/configuring-prometheus/local) for more Prometheus configurations. -### Set up the push gateway +4. **Install the IDE extension** -In order for Prometheus to succesfully get your client-side app metrics, you -will need to push them to an aggregating push gateway [like this -one](https://github.com/zapier/prom-aggregation-gateway). +In order to get charts in VSCode, download the [Autometrics VSCode extension](https://marketplace.visualstudio.com/items?itemName=Fiberplane.autometrics). -Use the `init` function to configure the gateway URL that autometrics should push -data to. You can also set the push interval with the `pushInterval` property -(default is every 5000 miliseconds); +
+ + If you're on any other IDE you can install and add the TypeScript plugin + directly: + -```typescript -init({ pushGateway: "" }); +```bash +npm install --save-dev @autometrics/typescript-plugin ``` -### Use Autometrics wrapper with options - -The same wrapper functions can be used in browser environments. Note that bundlers often change the names of functions and modules in production, which can impact the library. - -To get around this issue, wrappers accept an options object as -their first argument, which explicitly assigns a function and module name. +Add the language service plugin to the `tsconfig.json` file: -```typescript -const myFunction = autometrics( - { - functionName: "myFunction", - moduleName: "Module", - }, - async () => { - // ... myFunction body +```json +{ + "compilerOptions": { + "plugins": [ + { + "name": "@autometrics/typescript-plugin", + "prometheusUrl": "" + } + ] } -); +} ``` -## Configuration +
-### Set your own Exporter - -By default, autometrics exposes your metrics with OpenTelemetry's Prometheus -Exporter on port `:9464`, using the endpoint `/metrics`. You can configure it as you wish, -however, by using the `init` function. - -Here is an example that sets the exporter to use port 7777: - -```javascript -import { autometrics, init } from "@autometrics/autometrics"; -import { PrometheusExporter } from "@opentelemetry/exporter-prometheus"; - -const exporter = new PrometheusExporter({ port: 7777 }); -init({ exporter }); -``` +## [Documentation](https://docs.autometrics.dev/typescript/quickstart) -### Language service plugin +## [API Reference](./packages/lib/reference/README.md) -The language service plugin can be configured in the `tsconfig.json` file. +## Contributing -#### Options +Issues, feature suggestions, and pull requests are very welcome! -| key | description | -| --------------- | --------------------------------------------------------------- | -| `name` | always `@autometrics/typescript-plugin` | -| `prometheusUrl` | sets the base URL for PromQL queries. Default: `localhost:9090` | +If you are interested in getting involved: +- Join the conversation on [Discord](https://discord.gg/9eqGEs56UB) +- Ask questions and share ideas in the [Github Discussions](https://github.com/orgs/autometrics-dev/discussions) +- Take a look at the overall [Autometrics Project Roadmap](https://github.com/orgs/autometrics-dev/projects/1) diff --git a/packages/typescript-plugin/src/astHelpers.ts b/packages/typescript-plugin/src/astHelpers.ts index 5c3a78b9..0e814bb6 100644 --- a/packages/typescript-plugin/src/astHelpers.ts +++ b/packages/typescript-plugin/src/astHelpers.ts @@ -153,6 +153,7 @@ export function getNodeType(node: ts.Node, typechecker: ts.TypeChecker) { // const funcWithMetrics = autometrics(originalFunc); if ( ts.isVariableDeclaration(declaration?.valueDeclaration) && + declaration?.valueDeclaration?.initializer && ts.isCallExpression(declaration?.valueDeclaration?.initializer) ) { return "function";