@emartech/telemetry-javascript

中文文档
1.5.2发布于 2024/04/04MITTypeScriptESM + CJS近 30 天下载 5780

Telemetry package for Typescript/Javascript code use

原始使用文档
文档目录10 节
  1. Telemetry Client Library Wrapper
  2. Quickstart
  3. Features
  4. Recommended Usage
  5. Metric Generation
  6. Large, Multi-module Applications
  7. Advanced Usage
  8. Including Metrics in Shared Code
  9. Metric Types
  10. Utility Methods

telemetry-javascript

Telemetry Client Library Wrapper

Library for emitting telemetry to an OpenTelmetry Collector via StatsD.

Quickstart

Setup

Environment Variable (optional)
TELEMETRY_CONFIG = {
    'application':'personalization service', 
    'domain':'personalization', 
    'module':'compilation', 
    'metrics': {
        'client':'statsd', 
        'hostname':'127.0.0.1', 
        'port':8125, 
        'enabled':true
    }
}
  • application (default: ''): application using the library.
  • domain (default: ''): default namespace domain of the application.
  • module (default: ''): default namespace module of the applcation.
  • metrics
    • client (default: 'statsd'): statsd metrics client to use.
    • hostname (default: '127.0.0.1'): host to which the metrics client will send metrics data. If running locally in Docker this will be the name of the collector service.
      • Example
        services:
            app:
                environment:
                    - 'TELEMETRY_CONFIG={"metrics":{"hostname":"collector"}}'
        
    • port (default: 8125): port to which the metrics client will send metrics data.
    • enabled (default: true): true|false controls whether metris will be emitted or not.

Installation

Install the Emarsys telemetry package

npm install @emartech/telemetry-javascript

Write your first metric

import * as metrics from '@emartech/telemetry-javascript';

// With default namespace set
metrics.setDefaultNamespace('personalization', 'compilation');
metrics.incrementCounter('request_count');

Features

  • Instrument your code with OpenTelemetry-compatible metrics with an established path to Google Cloud Monitoring

  • Use out-of-the-box defaults to hook into the Emarsys OpenTelemetry pipeline automatically

  • Insulate your application code from changes to OpenTelemetry or telemetry data platforms

  • Automatically capture the duration of functions

metrics.setDefaultNamespace('personalization', 'compilation');

// Callable
const personalizationString = metrics.time(
        'total_duration',
        [500, 1000, 1500, 2000, 2500, 3000, 3500, 4000]],
        { 'attribute1': 'value1', 'attribute2': 'value2' },
        computePersonalizationString,
        'Hello <name>',
        '<name>',
        'World!',
      );

function computePersonalizationString(
    personlizationString: string, 
    token: string, 
    tokenValue: string): string {

    return personalizationString.replace(token, tokenValue);
}

/*
Result: 'Hello World!'

Metric Name: personalization_compilation_total_duration
Value: Time to execute function in milliseconds
*/

// Decorator
const myClass = new MyClass();
const result = myClass.computePersonalizationString('Hello <name>', '<name>', 'World!');

class MyClass {
    @metrics.timeDecorator('total_duration')
    function computePersonalizationString(
        personlizationString: string, 
        token: string, 
        tokenValue: string): string {

            return personalizationString.replace(token, tokenValue);
    }
}

/*
Result: 'Hello World!'

Metric Name: personalization_compilation_total_duration
Value: Time to execute function in milliseconds
*/
  • Automatically capture the duration and reliability of functions
metrics.setDefaultNamespace('personalization', 'compilation');

// Callable

// Success
const personalizationString = 'Hello <name>';
const token ='<name>';
const tokenValue = 'World!'

const result = metrics.timeAndCount(
        'total',
        () => {
            computePersonalizationString(personalizationString, token, tokenValue);
        },
        [500, 1000, 1500, 2000, 2500, 3000, 3500, 4000]],
        { 'attribute1': 'value1', 'attribute2': 'value2' },
      );

// OR

const result = metrics.metricWrapper(
        'total',
        [500, 1000, 1500, 2000, 2500, 3000, 3500, 4000]],
        { 'attribute1': 'value1', 'attribute2': 'value2' },
        computePersonalizationString,
        'Hello <name>',
        '<name>',
        'World!',
      );

function computePersonalizationString(
    personlizationString: string, 
    token: string, 
    tokenValue: string): string {

    return personalizationString.replace(token, tokenValue);
}

/*
Result: 'Hello World!'

Metric Name: personalization_compilation_total_duration
Value: Time to execute function in milliseconds
Auto-Attribute: 'success:true'

Metric Name: personalization_compilation_total_count
Value: Time to execute function in milliseconds
Auto-Attribute: 'success:true'
*/

metrics.setDefaultNamespace('personalization', 'compilation');

// Failure
const personalizationString = 'Hello <name>';
const token ='<name>';
const tokenValue = 'World!'

const result = metrics.timeAndCount(
        'total',
        () => {
            computePersonalizationString(personalizationString, token, tokenValue);
        },
        [500, 1000, 1500, 2000, 2500, 3000, 3500, 4000]],
        { 'attribute1': 'value1', 'attribute2': 'value2' },
      );

// OR

const personalizationString = metrics.metricWrapper(
        'total',
        [500, 1000, 1500, 2000, 2500, 3000, 3500, 4000]],
        { 'attribute1': 'value1', 'attribute2': 'value2' },
        computePersonalizationString,
        'Hello <name>',
        '<name>',
        'World!',
      );

function computePersonalizationString(
    personlizationString: string, 
    token: string, 
    tokenValue: string): string {
    
    throw new Error();
}

/*
Result: Error

Metric Name: personalization_compilation_total_duration
Value: Time to execute function in milliseconds
Auto-Attribute: 'success:false'

Metric Name: personalization_compilation_total_count
Value: Time to execute function in milliseconds
Auto-Attribute: 'success:false'
*/

// Decorator
const myClass = new MyClass();
const result = myClass.computePersonalizationString('Hello <name>', '<name>', 'World!');

class MyClass {
    @metrics.timeAndCountDecorator('total')
    function computePersonalizationString(
        personlizationString: string, 
        token: string, 
        tokenValue: string): string {

            return personalizationString.replace(token, tokenValue);
    }
}

/*
Result: 'Hello World!'

Metric Name: personalization_compilation_total_duration
Value: Time to execute function in milliseconds
Auto-Attribute: 'success:true'

Metric Name: personalization_compilation_total_count
Value: Time to execute function in milliseconds
Auto-Attribute: 'success:true'
*/
  • Namespace metrics into units based on module, component, or other criteria
const compilationNamespace = metrics.getNamespace(domain='personalization', module='compilation');
compilationNamespace.recordTimer('total_duration', 1000);

// Metric Name: personalization_compilation_total_duration

const executionNamespace = metrics.getNamespace(domain='personalization', module='execution');
executionNamespace.recordTimer('total_duration', 1000);

// Metric Name: personalization_execution_total_duration
  • Flexibly tag metrics at any scope to add metadata to your metrics without extra configuration
// Global attribute
metrics.setAttribute('hostname', 'server4');

// Namespace attribute
const compilationNamespace = metrics.getNamespace('personalization', 'compilation');
compilationNamespace.setAttribute('worker_type', 'reticulator');

// Dynamic attribute
const compilationNamespace = metrics.getNamespace('personalization', 'compilation');
compilationNamespace.incrementCounter(
    'completedJobs_count', 
    { 'attribute1': 'value1', 'attribute2': 'value2' }
);
  • Add metrics to shared code and see them reported independently in consuming applications

Application Startup

import * as metrics from '@emartech/telemetry-javascript';

// We will use a single namespace to cover all of the metrics generated by our application, so we set it up as the default. 
metrics.setDefaultNamespace('personalization', 'compilation');
metrics.incrementCounter('total_duration');

// We will explicitly create our metrics in order to provide a description and unit for our metrics
metrics.createCounter('request_Count', 'Number of requests the service received', 'requests');
metrics.createGauge('queue_depth', 'Number of requests currently in the queue', 'requests');

​

Metric Generation

// Application code - throughout the application, metrics will be generated
import * as metrics from '@emartech/telemetry-javascript';

def process_incoming_request(request):
  metrics.increment_counter('total_requests')
  generated_error = handle_request()
  
  if generated_error:
    metrics.increment_counter('error_requests')

Large, Multi-module Applications

import * as metrics from '@emartech/telemetry-javascript';

// Namespace objects can be created and stored at the module, component, or other level to create separate units for metrics
const compilationNamespace = metrics.getNamespace('personalization', 'compilation');
compilationNamespace.incrementCounter('request_count');

const executionNamespace = metrics.getNamespace('personalization', 'execution');
executionNamespace.incrementCounter('request_count');

Advanced Usage

import * as metrics from '@emartech/telemetry-javascript';

// For each default entity or setting, the deftaults can overriden by using lower-level APIs.
metrics.setDefaultClient(new StatsDClient('endpoint', 0));
metrics.setAttribute('hostname', 'server4');
​
const compilationNamespace = metrics.getNamespace('personalization', 'compilation');
compilationNamespace.setAttribute('namespace_attribute', 'foo');
​
const totalDurationMetric = compilationNamespace.createTimer('total_duration', 'Total duration of blah', 'ms');
totalDurationMetric.setAttribute('metric_attribute', 'bar');
​
totalDurationMetric.record(
    1000, 
    [200, 400, 600, 800, 1000, 1200, 1400],
    { 'attribute1': 'value1', 'attribute2': 'value2' }
);

Including Metrics in Shared Code

import * as metrics from '@emartech/telemetry-javascript';

// When instrumenting a shared library, the shared library should create it's own namespace and write it's metrics there.
// The shared library will rely upon the consuming application to attach any attributes it want's applied to the data to the default metric client.

compilationSharedLibraryNamespace = metrics.getNamespace('personalization', 'compilation_shared_library');
compilationSharedLibraryNamespace.createCounter('compilation_invocations', 'Number of compilation invocations', 'invocations');

compilationSharedLibraryNamespace.incrementCounter('compilation_invocations');

Metric Types

Four types of metrics are supported in Emarsys telemetry libraries:

  • Counters
  • Timers
  • Gauges
  • Histograms
Counter

A counter metric can be incremented or decremented over time and captures the total (sum) of all provided values.

Examples
  • Number of messages processed
  • Number of errors generated
/*
incrementCounter(
    metricName: string,
    value = 1,
    attributes: Record<string, string> = {},
): void;
*/

metrics.incrementCounter(
    'request_count', 
    100, 
    { 'attribute1': 'value1', 'attribute2': 'value2' }
);    
Gauge

A gauge metric stores the current value of something at the time that it was observed. Gauge metrics should be used for data which does not make sense to add together, but instead new measurements should replace old.

The last value of a gauge which is written during a time interval (generally every 60s) overwrites any previous values.

Examples
  • Number of items in a queue
  • Current memory usage of a process
/*
recordGauge(
    metricName: string,
    value: number,
    attributes: Record<string, string> = {},
): void;
*/

metrics.recordGauge(
    'queue_depth', 
    100000, 
    { 'attribute1': 'value1', 'attribute2': 'value2' }
);    
Histogram

A histogram metric stores a statistical summary of multiple values which are recorded within a time interval. Each observation that is recorded is counted in one of a series of “buckets” which are configured for the histogram. This storage method allows for the calculation of statistics after collection such as the minimum, maximum, median, and percentiles (P99, etc.) at a later date.

Visualization of a Histogram

Histogram chart with intervals of 100 showing numerous observations in each interval.

Examples
  • Size of incoming requests
  • Number of personalization tokens in each email
/*
recordHistogram(
    metricName: string,
    value: number,
    buckets: number[] = [0, 5, 10, 25, 50, 75, 100, 250, 500, 1000],
    attributes: Record<string, string> = {},
): void;
*/

metrics.recordHistogram(
    'total_size', 
    2500, 
    [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000],
    { 'attribute1': 'value1', 'attribute2': 'value2' },
);    
Timer

A timer metric can capture how long multiple observations of the same code or process have taken and results in a distribution of these time values. The distribution can be processed to determine the median, P99 or other values of interest.

A timer is a special case of a Histogram metric, which can store the statistical distribution of any type of data.

Examples
  • Time taken to process a message
  • Time taken to run a database query
/*
recordTimer(
    metricName: string,
    value: number,
    buckets: number[] = [0, 10, 25, 50, 75, 100, 250, 500, 1000, 5000, 10000, 30000, 60000, 240000],
    attributes: Record<string, string> = {},
): void;
*/

metrics.recordTimer(
    'total_duration', 
    850, 
    [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000],
    { 'attribute1': 'value1', 'attribute2': 'value2' }
);    

Utility Methods

The Emarsys telemetry library provides utility methods

toLowerCamelCase

A hyphenated string is turned into a camel-cased string for use in GCP attribute fields

/*
toLowerCamelCase(
    name: string | undefined
): string
*/

metrics.Util.toLowerCamelCase(
    'telemetry-attribute-example'
);    

版本与发布记录

@emartech/telemetry-javascript 共发布 16 个版本,首次发布于 2022/05/24;过去 12 个月发布了 0 个版本。

版本发布日期距今
2.0.0-rc.12024/04/112 年前
1.5.2latest2024/04/042 年前
2.0.0-rc.02024/03/272 年前
1.5.12023/01/263 年前
1.5.02023/01/213 年前
1.4.02023/01/143 年前

关于 @emartech/telemetry-javascript 的常见问题

@emartech/telemetry-javascript 是什么?

Telemetry package for Typescript/Javascript code use

如何安装 @emartech/telemetry-javascript?

在项目目录运行 npm install @emartech/telemetry-javascript,或使用 pnpm add / yarn add / bun add。

@emartech/telemetry-javascript 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@emartech/telemetry-javascript 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@emartech/telemetry-javascript 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=12.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@emartech/telemetry-javascript 可以商用吗?

@emartech/telemetry-javascript 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@emartech/telemetry-javascript 的下载量有多少?

根据 npm 官方下载统计,@emartech/telemetry-javascript 在 2026/10/14 – 2026/11/12 这 30 天内被下载 5,780 次,日均约 193 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@emartech/telemetry-javascript 还在维护吗?

最新版本 1.5.2 发布于 2024/04/04(2 年前);至今共发布 16 个版本,其中过去 12 个月发布了 0 个;首次发布于 2022/05/24。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。