@bsb/observable-axiom 1.0.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,14 @@
1
- # BSB Observable Axiom
1
+ # @bsb/observable-axiom
2
2
 
3
- Axiom.co integration for the Better Service Base (BSB) framework - unified observability for logs, metrics, and traces.
3
+ Axiom.co observability integration for BSB. Exports logs, metrics, and traces to Axiom with OTLP tracing support.
4
4
 
5
- ## Features
5
+ ## Key Features
6
6
 
7
- - **Unified Observability** - Logs, metrics, and traces in one platform
8
- - **Axiom SDK** - Official `@axiomhq/js` for logs and events
9
- - **OpenTelemetry Traces** - OTLP export to Axiom (native support)
10
- - **Structured Metrics** - Metrics as queryable events
11
- - **Batch Processing** - Efficient batching with configurable flush intervals
12
- - **Full TypeScript Support** - Type-safe configuration and no `any` types
7
+ - Unified observability: logs, metrics, and traces
8
+ - Axiom SDK for logs and events
9
+ - OpenTelemetry tracing via OTLP
10
+ - Batch processing with configurable flush interval
11
+ - Resource attributes for consistent service metadata
13
12
 
14
13
  ## Installation
15
14
 
@@ -19,233 +18,85 @@ npm install @bsb/observable-axiom
19
18
 
20
19
  ## Configuration
21
20
 
22
- Add to your BSB configuration file:
21
+ Add the plugin to your BSB configuration file:
23
22
 
24
23
  ```yaml
25
24
  plugins:
26
- observable:
27
- - observable-axiom
28
-
29
- observable-axiom:
30
- serviceName: my-service
31
- serviceVersion: 1.0.0
32
-
33
- axiom:
34
- token: ${AXIOM_TOKEN} # Your Axiom API token
35
- dataset: bsb-logs # Axiom dataset name
36
- orgId: ${AXIOM_ORG_ID} # Optional: for Axiom Cloud
37
- # url: https://axiom.mycompany.com # Optional: for self-hosted
38
-
39
- enabled:
40
- logs: true
41
- metrics: true
42
- traces: true
43
-
44
- export:
45
- flushIntervalMs: 5000 # Flush every 5 seconds
46
- maxBatchSize: 1000 # Max events per batch
47
-
48
- # Optional resource attributes (added to all telemetry)
49
- resourceAttributes:
50
- environment: production
51
- region: us-east-1
52
- cluster: main
25
+ observables:
26
+ - plugin: "@bsb/observable-axiom"
27
+ enabled: true
28
+ config:
29
+ serviceName: "my-service"
30
+ serviceVersion: "1.0.0"
31
+ axiom:
32
+ token: "${AXIOM_TOKEN}"
33
+ dataset: "bsb-logs"
34
+ orgId: "${AXIOM_ORG_ID}"
35
+ # url: "https://axiom.mycompany.com" # Optional for self-hosted
36
+ enabled:
37
+ logs: true
38
+ metrics: true
39
+ traces: true
40
+ export:
41
+ flushIntervalMs: 5000
42
+ maxBatchSize: 1000
43
+ resourceAttributes:
44
+ environment: "production"
45
+ region: "us-east-1"
46
+ cluster: "main"
53
47
  ```
54
48
 
49
+ ### Configuration Options
50
+
51
+ | Option | Description | Default |
52
+ |--------|-------------|---------|
53
+ | `serviceName` | Service identifier | `"bsb-service"` |
54
+ | `serviceVersion` | Service version tag | - |
55
+ | `axiom.token` | Axiom API token | Required |
56
+ | `axiom.dataset` | Axiom dataset name | `"bsb-logs"` |
57
+ | `axiom.orgId` | Organization ID (Axiom Cloud) | - |
58
+ | `axiom.url` | Custom Axiom URL (self-hosted) | - |
59
+ | `enabled.logs` | Enable log export | `true` |
60
+ | `enabled.metrics` | Enable metric export | `true` |
61
+ | `enabled.traces` | Enable trace export | `true` |
62
+ | `export.flushIntervalMs` | Flush interval (ms) | `5000` |
63
+ | `export.maxBatchSize` | Max events per batch | `1000` |
64
+ | `resourceAttributes` | Custom attributes | `{}` |
65
+
55
66
  ## Environment Variables
56
67
 
57
68
  ```bash
58
- export AXIOM_TOKEN="xaat-your-token-here"
59
- export AXIOM_ORG_ID="your-org-id" # Optional for cloud
60
- export AXIOM_DATASET="bsb-logs"
69
+ AXIOM_TOKEN="xaat-your-token-here"
70
+ AXIOM_ORG_ID="your-org-id"
71
+ AXIOM_DATASET="bsb-logs"
61
72
  ```
62
73
 
63
- ## What Gets Exported
64
-
65
- ### Logs (to Axiom Dataset)
66
- - Structured JSON with trace context
67
- - Log levels: debug, info, warn, error
68
- - Plugin name and service metadata
69
- - Custom metadata from log calls
70
- - Resource attributes
71
-
72
- ### Metrics (to Axiom Dataset as Events)
73
- - Counters, gauges, histograms
74
- - Stored as structured events
75
- - Queryable with Axiom Processing Language (APL)
76
- - Automatic timestamp and service tagging
77
-
78
- ### Traces (to Axiom via OTLP)
79
- - Full distributed tracing
80
- - Span lifecycle (start, end, error)
81
- - Trace context propagation
82
- - Exception tracking
83
-
84
74
  ## Usage
85
75
 
86
- The plugin automatically integrates with BSB's Observable pattern:
76
+ Once configured, logs, metrics, and traces are exported automatically:
87
77
 
88
78
  ```typescript
89
- export class MyService extends BSBService<InstanceType<typeof Config>, typeof EventSchemas> {
90
- static Config = Config;
91
- static EventSchemas = EventSchemas;
92
-
93
- public async run(obs: Observable) {
94
- // Logs automatically sent to Axiom
95
- obs.log.info("Service started", { userId: 123 });
96
-
97
- // Create child span (sent to Axiom traces)
98
- const workObs = obs.span("process-request");
99
- workObs.setAttribute("request.id", "req-123");
100
-
101
- try {
102
- await this.processRequest();
103
- workObs.end();
104
- } catch (error) {
105
- workObs.recordException(error);
106
- workObs.end();
107
- }
108
-
109
- // Metrics also sent to Axiom
110
- // (BSB metrics API will emit these as events)
111
- }
112
- }
113
- ```
114
-
115
- ## Axiom Setup
116
-
117
- ### 1. Create Account
118
- Sign up at https://axiom.co
119
-
120
- ### 2. Create Dataset
121
- ```bash
122
- axiom dataset create bsb-logs
123
- ```
124
-
125
- ### 3. Create API Token
126
- ```bash
127
- axiom token create bsb-ingest --scopes ingest --datasets bsb-logs
128
- ```
129
-
130
- ### 4. Configure BSB
131
- Add token to your configuration or environment variables.
132
-
133
- ## Querying Data in Axiom
134
-
135
- ### Logs Query (APL)
136
- ```apl
137
- ['bsb-logs']
138
- | where service == "my-service"
139
- | where level == "error"
140
- | order by _time desc
141
- | limit 100
79
+ this.log.info("Service started", { userId: 123 });
80
+ const workObs = this.obs.span("process-request");
81
+ await this.events.emitEvent("user.created", { userId: "123" });
82
+ workObs.end();
142
83
  ```
143
84
 
144
- ### Metrics Query
145
- ```apl
146
- ['bsb-logs']
147
- | where metric_type == "counter"
148
- | where metric_name == "requests_total"
149
- | summarize sum(value) by bin(_time, 1m)
150
- ```
85
+ ## Axiom Setup (Quick)
151
86
 
152
- ### Trace Analysis
153
- Use Axiom's Trace view to explore distributed traces with automatic service maps and performance analytics.
87
+ 1. Create a dataset
88
+ 2. Create an ingest token with dataset access
89
+ 3. Set `AXIOM_TOKEN` and `AXIOM_DATASET` in the environment
154
90
 
155
- ## Architecture
91
+ ## Documentation
156
92
 
157
- ```
158
- BSB Application
159
-
160
- Observable (DTrace)
161
-
162
- observable-axiom
163
- ├── Logs/Metrics → Axiom SDK → Axiom Dataset
164
- └── Traces → OTLP Exporter → Axiom Traces
165
- ```
93
+ Detailed documentation (used by the BSB Registry): `https://github.com/BetterCorp/better-service-base/blob/master/plugins/nodejs/observable-axiom/docs/plugin.md`
166
94
 
167
- ## Configuration Options
168
-
169
- | Option | Type | Default | Description |
170
- |--------|------|---------|-------------|
171
- | `serviceName` | string | `"bsb-service"` | Service identifier |
172
- | `serviceVersion` | string | - | Service version tag |
173
- | `axiom.token` | string | **required** | Axiom API token |
174
- | `axiom.dataset` | string | `"bsb-logs"` | Dataset name |
175
- | `axiom.orgId` | string | - | Organization ID (cloud) |
176
- | `axiom.url` | string | - | Custom URL (self-hosted) |
177
- | `enabled.logs` | boolean | `true` | Enable log export |
178
- | `enabled.metrics` | boolean | `true` | Enable metrics export |
179
- | `enabled.traces` | boolean | `true` | Enable trace export |
180
- | `export.flushIntervalMs` | number | `5000` | Flush interval (ms) |
181
- | `export.maxBatchSize` | number | `1000` | Max events per batch |
182
- | `resourceAttributes` | object | `{}` | Custom attributes |
183
-
184
- ## Comparison with Other Observability Plugins
185
-
186
- | Plugin | Logs | Metrics | Traces | Backend |
187
- |--------|------|---------|--------|---------|
188
- | observable-axiom | ✅ | ✅ | ✅ | Axiom.co |
189
- | observable-opentelemetry | ✅ | ✅ | ✅ | OTLP Collector |
190
- | observable-zipkin | Console | ❌ | ✅ | Zipkin |
191
- | observable-default | Console | ❌ | ❌ | - |
192
-
193
- ## Best Practices
194
-
195
- ### 1. Use Datasets Strategically
196
- ```yaml
197
- # Production
198
- axiom:
199
- dataset: prod-logs
95
+ ## Links
200
96
 
201
- # Development
202
- axiom:
203
- dataset: dev-logs
204
- ```
205
-
206
- ### 2. Set Resource Attributes
207
- ```yaml
208
- resourceAttributes:
209
- environment: ${ENVIRONMENT}
210
- deployment: ${DEPLOYMENT_ID}
211
- datacenter: ${DATACENTER}
212
- ```
213
-
214
- ### 3. Control Batch Size
215
- ```yaml
216
- export:
217
- # High-volume services
218
- flushIntervalMs: 1000
219
- maxBatchSize: 5000
220
-
221
- # Low-volume services
222
- flushIntervalMs: 10000
223
- maxBatchSize: 100
224
- ```
225
-
226
- ### 4. Use Structured Logging
227
- ```typescript
228
- // Good - queryable fields
229
- obs.log.info("User logged in", {
230
- userId: user.id,
231
- email: user.email
232
- });
233
-
234
- // Bad - unstructured
235
- obs.log.info(`User ${user.email} logged in`);
236
- ```
237
-
238
- ## Axiom Features
239
-
240
- - **APL (Axiom Processing Language)** - Powerful query language
241
- - **Service Maps** - Automatic trace visualization
242
- - **Monitors & Alerts** - Real-time alerting on anomalies
243
- - **Dashboards** - Custom visualizations
244
- - **Notebooks** - Collaborative analysis
245
- - **Retention** - Configurable data retention policies
97
+ - GitHub: `https://github.com/BetterCorp/better-service-base/tree/master/plugins/nodejs/observable-axiom`
98
+ - BSB Registry (package): `https://io.bsbcode.dev/packages/nodejs/@bsb/observable-axiom`
246
99
 
247
100
  ## License
248
101
 
249
- AGPL-3.0 - See LICENSE file for details.
250
-
251
- Commercial licenses available at https://www.bettercorp.dev
102
+ (AGPL-3.0-only OR Commercial)
package/bsb-tests.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "nodejs": [
3
+ {
4
+ "id": "observable-axiom",
5
+ "skip": true,
6
+ "default": {
7
+ "config": {},
8
+ "setup": null,
9
+ "dispose": null
10
+ },
11
+ "tests": []
12
+ }
13
+ ]
14
+ }
@@ -26,27 +26,27 @@
26
26
  */
27
27
  import { BSBObservable, BSBObservableConstructor, BSBError } from "@bsb/base";
28
28
  import { DTrace, LogMeta } from "@bsb/base";
29
- import { z } from "zod";
30
- export declare const Config: import("@bsb/base").BSBPluginConfigClass<z.ZodObject<{
31
- serviceName: z.ZodDefault<z.ZodString>;
32
- serviceVersion: z.ZodOptional<z.ZodString>;
33
- axiom: z.ZodObject<{
34
- token: z.ZodString;
35
- dataset: z.ZodDefault<z.ZodString>;
36
- orgId: z.ZodOptional<z.ZodString>;
37
- url: z.ZodOptional<z.ZodURL>;
38
- }, z.core.$strip>;
39
- enabled: z.ZodObject<{
40
- logs: z.ZodDefault<z.ZodBoolean>;
41
- metrics: z.ZodDefault<z.ZodBoolean>;
42
- traces: z.ZodDefault<z.ZodBoolean>;
43
- }, z.core.$strip>;
44
- export: z.ZodObject<{
45
- flushIntervalMs: z.ZodDefault<z.ZodNumber>;
46
- maxBatchSize: z.ZodDefault<z.ZodNumber>;
47
- }, z.core.$strip>;
48
- resourceAttributes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
49
- }, z.core.$strip>>;
29
+ import * as av from "@anyvali/js";
30
+ export declare const Config: import("@bsb/base").BSBPluginConfigClass<av.ObjectSchema<{
31
+ serviceName: av.OptionalSchema<av.StringSchema>;
32
+ serviceVersion: av.OptionalSchema<av.StringSchema>;
33
+ axiom: av.ObjectSchema<{
34
+ token: av.StringSchema;
35
+ dataset: av.OptionalSchema<av.StringSchema>;
36
+ orgId: av.OptionalSchema<av.StringSchema>;
37
+ url: av.OptionalSchema<av.StringSchema>;
38
+ }>;
39
+ enabled: av.ObjectSchema<{
40
+ logs: av.OptionalSchema<av.BoolSchema>;
41
+ metrics: av.OptionalSchema<av.BoolSchema>;
42
+ traces: av.OptionalSchema<av.BoolSchema>;
43
+ }>;
44
+ export: av.ObjectSchema<{
45
+ flushIntervalMs: av.OptionalSchema<av.Int32Schema>;
46
+ maxBatchSize: av.OptionalSchema<av.Int32Schema>;
47
+ }>;
48
+ resourceAttributes: av.OptionalSchema<av.RecordSchema<av.StringSchema>>;
49
+ }>>;
50
50
  /**
51
51
  * Axiom observable plugin for unified observability
52
52
  *
@@ -55,26 +55,26 @@ export declare const Config: import("@bsb/base").BSBPluginConfigClass<z.ZodObjec
55
55
  * Exports metrics as structured events
56
56
  */
57
57
  export declare class Plugin extends BSBObservable<InstanceType<typeof Config>> {
58
- static Config: import("@bsb/base").BSBPluginConfigClass<z.ZodObject<{
59
- serviceName: z.ZodDefault<z.ZodString>;
60
- serviceVersion: z.ZodOptional<z.ZodString>;
61
- axiom: z.ZodObject<{
62
- token: z.ZodString;
63
- dataset: z.ZodDefault<z.ZodString>;
64
- orgId: z.ZodOptional<z.ZodString>;
65
- url: z.ZodOptional<z.ZodURL>;
66
- }, z.core.$strip>;
67
- enabled: z.ZodObject<{
68
- logs: z.ZodDefault<z.ZodBoolean>;
69
- metrics: z.ZodDefault<z.ZodBoolean>;
70
- traces: z.ZodDefault<z.ZodBoolean>;
71
- }, z.core.$strip>;
72
- export: z.ZodObject<{
73
- flushIntervalMs: z.ZodDefault<z.ZodNumber>;
74
- maxBatchSize: z.ZodDefault<z.ZodNumber>;
75
- }, z.core.$strip>;
76
- resourceAttributes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
77
- }, z.core.$strip>>;
58
+ static Config: import("@bsb/base").BSBPluginConfigClass<av.ObjectSchema<{
59
+ serviceName: av.OptionalSchema<av.StringSchema>;
60
+ serviceVersion: av.OptionalSchema<av.StringSchema>;
61
+ axiom: av.ObjectSchema<{
62
+ token: av.StringSchema;
63
+ dataset: av.OptionalSchema<av.StringSchema>;
64
+ orgId: av.OptionalSchema<av.StringSchema>;
65
+ url: av.OptionalSchema<av.StringSchema>;
66
+ }>;
67
+ enabled: av.ObjectSchema<{
68
+ logs: av.OptionalSchema<av.BoolSchema>;
69
+ metrics: av.OptionalSchema<av.BoolSchema>;
70
+ traces: av.OptionalSchema<av.BoolSchema>;
71
+ }>;
72
+ export: av.ObjectSchema<{
73
+ flushIntervalMs: av.OptionalSchema<av.Int32Schema>;
74
+ maxBatchSize: av.OptionalSchema<av.Int32Schema>;
75
+ }>;
76
+ resourceAttributes: av.OptionalSchema<av.RecordSchema<av.StringSchema>>;
77
+ }>>;
78
78
  private logFormatter;
79
79
  private axiom;
80
80
  private tracerProvider;
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  /**
3
2
  * BSB (Better-Service-Base) is an event-bus based microservice framework.
4
3
  * Copyright (C) 2016 - 2025 BetterCorp (PTY) Ltd
@@ -25,71 +24,36 @@
25
24
  * You should have received a copy of the GNU Affero General Public License
26
25
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
27
26
  */
28
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
29
- if (k2 === undefined) k2 = k;
30
- var desc = Object.getOwnPropertyDescriptor(m, k);
31
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
32
- desc = { enumerable: true, get: function() { return m[k]; } };
33
- }
34
- Object.defineProperty(o, k2, desc);
35
- }) : (function(o, m, k, k2) {
36
- if (k2 === undefined) k2 = k;
37
- o[k2] = m[k];
38
- }));
39
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
40
- Object.defineProperty(o, "default", { enumerable: true, value: v });
41
- }) : function(o, v) {
42
- o["default"] = v;
43
- });
44
- var __importStar = (this && this.__importStar) || (function () {
45
- var ownKeys = function(o) {
46
- ownKeys = Object.getOwnPropertyNames || function (o) {
47
- var ar = [];
48
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
49
- return ar;
50
- };
51
- return ownKeys(o);
52
- };
53
- return function (mod) {
54
- if (mod && mod.__esModule) return mod;
55
- var result = {};
56
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
57
- __setModuleDefault(result, mod);
58
- return result;
59
- };
60
- })();
61
- Object.defineProperty(exports, "__esModule", { value: true });
62
- exports.Plugin = exports.Config = void 0;
63
- const base_1 = require("@bsb/base");
64
- const zod_1 = require("zod");
65
- const js_1 = require("@axiomhq/js");
66
- const api = __importStar(require("@opentelemetry/api"));
67
- const resources_1 = require("@opentelemetry/resources");
68
- const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
69
- const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
70
- const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
71
- const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
72
- const ConfigSchema = zod_1.z.object({
73
- serviceName: zod_1.z.string().default("bsb-service"),
74
- serviceVersion: zod_1.z.string().optional(),
75
- axiom: zod_1.z.object({
76
- token: zod_1.z.string().describe("Axiom API token"),
77
- dataset: zod_1.z.string().default("bsb-logs").describe("Axiom dataset name"),
78
- orgId: zod_1.z.string().optional().describe("Axiom organization ID (for cloud)"),
79
- url: zod_1.z.url().optional().describe("Custom Axiom URL (for self-hosted)"),
80
- }),
81
- enabled: zod_1.z.object({
82
- logs: zod_1.z.boolean().default(true),
83
- metrics: zod_1.z.boolean().default(true),
84
- traces: zod_1.z.boolean().default(true),
85
- }),
86
- export: zod_1.z.object({
87
- flushIntervalMs: zod_1.z.number().int().min(100).default(5000),
88
- maxBatchSize: zod_1.z.number().int().min(1).default(1000),
89
- }),
90
- resourceAttributes: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).default({}),
91
- });
92
- exports.Config = (0, base_1.createConfigSchema)({
27
+ import { BSBObservable, createConfigSchema, LogFormatter, BSBError } from "@bsb/base";
28
+ import * as av from "@anyvali/js";
29
+ import { Axiom } from "@axiomhq/js";
30
+ import * as api from "@opentelemetry/api";
31
+ import { defaultResource, resourceFromAttributes } from "@opentelemetry/resources";
32
+ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
33
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
34
+ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
35
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
36
+ const ConfigSchema = av.object({
37
+ serviceName: av.optional(av.string()).default("bsb-service"),
38
+ serviceVersion: av.optional(av.string()),
39
+ axiom: av.object({
40
+ token: av.string(),
41
+ dataset: av.optional(av.string()).default("bsb-logs"),
42
+ orgId: av.optional(av.string()),
43
+ url: av.optional(av.string().format("url")),
44
+ }, { unknownKeys: "strip" }),
45
+ enabled: av.object({
46
+ logs: av.optional(av.bool()).default(true),
47
+ metrics: av.optional(av.bool()).default(true),
48
+ traces: av.optional(av.bool()).default(true),
49
+ }, { unknownKeys: "strip" }),
50
+ export: av.object({
51
+ flushIntervalMs: av.optional(av.int32().min(100)).default(5000),
52
+ maxBatchSize: av.optional(av.int32().min(1)).default(1000),
53
+ }, { unknownKeys: "strip" }),
54
+ resourceAttributes: av.optional(av.record(av.string())).default({}),
55
+ }, { unknownKeys: "strip" });
56
+ export const Config = createConfigSchema({
93
57
  name: 'observable-axiom',
94
58
  description: 'Axiom.co observability integration for logs, metrics, and traces',
95
59
  version: '1.0.0',
@@ -140,10 +104,10 @@ class BSBIdGenerator {
140
104
  * Exports traces via OTLP (Axiom supports OpenTelemetry)
141
105
  * Exports metrics as structured events
142
106
  */
143
- class Plugin extends base_1.BSBObservable {
107
+ export class Plugin extends BSBObservable {
144
108
  constructor(config) {
145
109
  super(config);
146
- this.logFormatter = new base_1.LogFormatter();
110
+ this.logFormatter = new LogFormatter();
147
111
  this.axiom = null;
148
112
  this.tracerProvider = null;
149
113
  this.tracer = null;
@@ -165,20 +129,20 @@ class Plugin extends base_1.BSBObservable {
165
129
  if (this.config.axiom.url) {
166
130
  axiomOptions.url = this.config.axiom.url;
167
131
  }
168
- this.axiom = new js_1.Axiom(axiomOptions);
132
+ this.axiom = new Axiom(axiomOptions);
169
133
  }
170
134
  // Initialize OpenTelemetry for traces (Axiom supports OTLP natively)
171
135
  if (this.config.enabled.traces) {
172
- const resource = (0, resources_1.defaultResource)().merge((0, resources_1.resourceFromAttributes)({
173
- [semantic_conventions_1.ATTR_SERVICE_NAME]: this.config.serviceName,
174
- ...(this.config.serviceVersion && { [semantic_conventions_1.ATTR_SERVICE_VERSION]: this.config.serviceVersion }),
136
+ const resource = defaultResource().merge(resourceFromAttributes({
137
+ [ATTR_SERVICE_NAME]: this.config.serviceName,
138
+ ...(this.config.serviceVersion && { [ATTR_SERVICE_VERSION]: this.config.serviceVersion }),
175
139
  ...this.config.resourceAttributes,
176
140
  }));
177
141
  // Axiom OTLP endpoint (from docs: https://axiom.co/docs/send-data/opentelemetry)
178
142
  const otlpUrl = this.config.axiom.url
179
143
  ? `${this.config.axiom.url}/v1/traces`
180
144
  : `https://api.axiom.co/v1/traces`;
181
- const traceExporter = new exporter_trace_otlp_http_1.OTLPTraceExporter({
145
+ const traceExporter = new OTLPTraceExporter({
182
146
  url: otlpUrl,
183
147
  headers: {
184
148
  "Authorization": `Bearer ${this.config.axiom.token}`,
@@ -189,11 +153,11 @@ class Plugin extends base_1.BSBObservable {
189
153
  this.idGenerator = new BSBIdGenerator();
190
154
  // Create tracer provider with custom ID generator and span processor
191
155
  // Use shorter batch delay for faster exports during debugging
192
- this.tracerProvider = new sdk_trace_node_1.NodeTracerProvider({
156
+ this.tracerProvider = new NodeTracerProvider({
193
157
  resource,
194
158
  idGenerator: this.idGenerator,
195
159
  spanProcessors: [
196
- new sdk_trace_base_1.BatchSpanProcessor(traceExporter, {
160
+ new BatchSpanProcessor(traceExporter, {
197
161
  scheduledDelayMillis: 1000, // Export every 1 second (default is 5000)
198
162
  maxQueueSize: 2048,
199
163
  maxExportBatchSize: 512,
@@ -280,7 +244,7 @@ class Plugin extends base_1.BSBObservable {
280
244
  this.queueLog("warn", trace, pluginName, message, meta);
281
245
  }
282
246
  error(trace, pluginName, message, meta) {
283
- if (message instanceof base_1.BSBError) {
247
+ if (message instanceof BSBError) {
284
248
  if (message.raw !== null) {
285
249
  this.queueLog("error", message.raw.trace, pluginName, message.raw.message, message.raw.meta);
286
250
  }
@@ -441,5 +405,4 @@ class Plugin extends base_1.BSBObservable {
441
405
  this.axiom = null;
442
406
  }
443
407
  }
444
- exports.Plugin = Plugin;
445
- Plugin.Config = exports.Config;
408
+ Plugin.Config = Config;
@@ -2,110 +2,6 @@
2
2
  "pluginName": "observable-axiom",
3
3
  "version": "1.0.0",
4
4
  "events": {},
5
- "configSchema": {
6
- "$schema": "https://json-schema.org/draft/2020-12/schema",
7
- "type": "object",
8
- "properties": {
9
- "serviceName": {
10
- "default": "bsb-service",
11
- "type": "string"
12
- },
13
- "serviceVersion": {
14
- "type": "string"
15
- },
16
- "axiom": {
17
- "type": "object",
18
- "properties": {
19
- "token": {
20
- "type": "string",
21
- "description": "Axiom API token"
22
- },
23
- "dataset": {
24
- "default": "bsb-logs",
25
- "description": "Axiom dataset name",
26
- "type": "string"
27
- },
28
- "orgId": {
29
- "description": "Axiom organization ID (for cloud)",
30
- "type": "string"
31
- },
32
- "url": {
33
- "description": "Custom Axiom URL (for self-hosted)",
34
- "type": "string",
35
- "format": "uri"
36
- }
37
- },
38
- "required": [
39
- "token",
40
- "dataset"
41
- ],
42
- "additionalProperties": false
43
- },
44
- "enabled": {
45
- "type": "object",
46
- "properties": {
47
- "logs": {
48
- "default": true,
49
- "type": "boolean"
50
- },
51
- "metrics": {
52
- "default": true,
53
- "type": "boolean"
54
- },
55
- "traces": {
56
- "default": true,
57
- "type": "boolean"
58
- }
59
- },
60
- "required": [
61
- "logs",
62
- "metrics",
63
- "traces"
64
- ],
65
- "additionalProperties": false
66
- },
67
- "export": {
68
- "type": "object",
69
- "properties": {
70
- "flushIntervalMs": {
71
- "default": 5000,
72
- "type": "integer",
73
- "minimum": 100,
74
- "maximum": 9007199254740991
75
- },
76
- "maxBatchSize": {
77
- "default": 1000,
78
- "type": "integer",
79
- "minimum": 1,
80
- "maximum": 9007199254740991
81
- }
82
- },
83
- "required": [
84
- "flushIntervalMs",
85
- "maxBatchSize"
86
- ],
87
- "additionalProperties": false
88
- },
89
- "resourceAttributes": {
90
- "default": {},
91
- "type": "object",
92
- "propertyNames": {
93
- "type": "string"
94
- },
95
- "additionalProperties": {
96
- "type": "string"
97
- }
98
- }
99
- },
100
- "required": [
101
- "serviceName",
102
- "axiom",
103
- "enabled",
104
- "export",
105
- "resourceAttributes"
106
- ],
107
- "additionalProperties": false
108
- },
109
5
  "pluginType": "observable",
110
6
  "capabilities": {
111
7
  "logging": {
@@ -14,109 +14,5 @@
14
14
  ],
15
15
  "documentation": [],
16
16
  "dependencies": [],
17
- "image": "./axiom-co-logo.png",
18
- "configSchema": {
19
- "$schema": "https://json-schema.org/draft/2020-12/schema",
20
- "type": "object",
21
- "properties": {
22
- "serviceName": {
23
- "default": "bsb-service",
24
- "type": "string"
25
- },
26
- "serviceVersion": {
27
- "type": "string"
28
- },
29
- "axiom": {
30
- "type": "object",
31
- "properties": {
32
- "token": {
33
- "type": "string",
34
- "description": "Axiom API token"
35
- },
36
- "dataset": {
37
- "default": "bsb-logs",
38
- "description": "Axiom dataset name",
39
- "type": "string"
40
- },
41
- "orgId": {
42
- "description": "Axiom organization ID (for cloud)",
43
- "type": "string"
44
- },
45
- "url": {
46
- "description": "Custom Axiom URL (for self-hosted)",
47
- "type": "string",
48
- "format": "uri"
49
- }
50
- },
51
- "required": [
52
- "token",
53
- "dataset"
54
- ],
55
- "additionalProperties": false
56
- },
57
- "enabled": {
58
- "type": "object",
59
- "properties": {
60
- "logs": {
61
- "default": true,
62
- "type": "boolean"
63
- },
64
- "metrics": {
65
- "default": true,
66
- "type": "boolean"
67
- },
68
- "traces": {
69
- "default": true,
70
- "type": "boolean"
71
- }
72
- },
73
- "required": [
74
- "logs",
75
- "metrics",
76
- "traces"
77
- ],
78
- "additionalProperties": false
79
- },
80
- "export": {
81
- "type": "object",
82
- "properties": {
83
- "flushIntervalMs": {
84
- "default": 5000,
85
- "type": "integer",
86
- "minimum": 100,
87
- "maximum": 9007199254740991
88
- },
89
- "maxBatchSize": {
90
- "default": 1000,
91
- "type": "integer",
92
- "minimum": 1,
93
- "maximum": 9007199254740991
94
- }
95
- },
96
- "required": [
97
- "flushIntervalMs",
98
- "maxBatchSize"
99
- ],
100
- "additionalProperties": false
101
- },
102
- "resourceAttributes": {
103
- "default": {},
104
- "type": "object",
105
- "propertyNames": {
106
- "type": "string"
107
- },
108
- "additionalProperties": {
109
- "type": "string"
110
- }
111
- }
112
- },
113
- "required": [
114
- "serviceName",
115
- "axiom",
116
- "enabled",
117
- "export",
118
- "resourceAttributes"
119
- ],
120
- "additionalProperties": false
121
- }
17
+ "image": "./axiom-co-logo.png"
122
18
  }
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@bsb/observable-axiom",
3
- "version": "1.0.0",
3
+ "version": "1.1.2",
4
4
  "description": "Axiom.co observability integration for BSB framework",
5
+ "type": "module",
5
6
  "main": "lib/plugins/observable-axiom/index.js",
6
7
  "types": "lib/plugins/observable-axiom/index.d.ts",
7
8
  "scripts": {
@@ -26,13 +27,14 @@
26
27
  "license": "(AGPL-3.0-only OR Commercial)",
27
28
  "author": {
28
29
  "name": "BetterCorp (PTY) Ltd",
29
- "email": "nick@bettercorp.dev",
30
+ "email": "ninja@bettercorp.dev",
30
31
  "url": "https://bettercorp.dev/"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "@bsb/base": "^9.0.0"
34
35
  },
35
36
  "dependencies": {
37
+ "@anyvali/js": "^0.2.0",
36
38
  "@axiomhq/js": "^1.4.0",
37
39
  "@opentelemetry/api": "^1.9.0",
38
40
  "@opentelemetry/exporter-trace-otlp-http": "^0.211.0",
@@ -40,15 +42,13 @@
40
42
  "@opentelemetry/sdk-node": "^0.211.0",
41
43
  "@opentelemetry/sdk-trace-base": "^2.5.0",
42
44
  "@opentelemetry/sdk-trace-node": "^2.5.0",
43
- "@opentelemetry/semantic-conventions": "^1.39.0",
44
- "zod": "^4.3.6"
45
+ "@opentelemetry/semantic-conventions": "^1.39.0"
45
46
  },
46
47
  "devDependencies": {
47
- "@bsb/base": "file:../../../nodejs",
48
+ "@bsb/base": "^9.0.0",
48
49
  "@types/node": "^25.2.1",
49
50
  "rimraf": "^6.1.2",
50
51
  "typescript": "^5.9.3"
51
52
  },
52
- "homepage": "https://io.bsbcode.dev/plugins/bsb/observable-axiom"
53
+ "homepage": "https://io.bsbcode.dev/packages/nodejs/@bsb/observable-axiom"
53
54
  }
54
-
@@ -33,7 +33,7 @@ import {
33
33
  BSBError
34
34
  } from "@bsb/base";
35
35
  import { DTrace, LogMeta } from "@bsb/base";
36
- import { z } from "zod";
36
+ import * as av from "@anyvali/js";
37
37
  import { Axiom } from "@axiomhq/js";
38
38
  import * as api from "@opentelemetry/api";
39
39
  import { defaultResource, resourceFromAttributes } from "@opentelemetry/resources";
@@ -43,30 +43,26 @@ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
43
43
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
44
44
  import type { IdGenerator } from "@opentelemetry/sdk-trace-base";
45
45
 
46
- const ConfigSchema = z.object({
47
- serviceName: z.string().default("bsb-service"),
48
- serviceVersion: z.string().optional(),
49
-
50
- axiom: z.object({
51
- token: z.string().describe("Axiom API token"),
52
- dataset: z.string().default("bsb-logs").describe("Axiom dataset name"),
53
- orgId: z.string().optional().describe("Axiom organization ID (for cloud)"),
54
- url: z.url().optional().describe("Custom Axiom URL (for self-hosted)"),
55
- }),
56
-
57
- enabled: z.object({
58
- logs: z.boolean().default(true),
59
- metrics: z.boolean().default(true),
60
- traces: z.boolean().default(true),
61
- }),
62
-
63
- export: z.object({
64
- flushIntervalMs: z.number().int().min(100).default(5000),
65
- maxBatchSize: z.number().int().min(1).default(1000),
66
- }),
67
-
68
- resourceAttributes: z.record(z.string(), z.string()).default({}),
69
- });
46
+ const ConfigSchema = av.object({
47
+ serviceName: av.optional(av.string()).default("bsb-service"),
48
+ serviceVersion: av.optional(av.string()),
49
+ axiom: av.object({
50
+ token: av.string(),
51
+ dataset: av.optional(av.string()).default("bsb-logs"),
52
+ orgId: av.optional(av.string()),
53
+ url: av.optional(av.string().format("url")),
54
+ }, { unknownKeys: "strip" }),
55
+ enabled: av.object({
56
+ logs: av.optional(av.bool()).default(true),
57
+ metrics: av.optional(av.bool()).default(true),
58
+ traces: av.optional(av.bool()).default(true),
59
+ }, { unknownKeys: "strip" }),
60
+ export: av.object({
61
+ flushIntervalMs: av.optional(av.int32().min(100)).default(5000),
62
+ maxBatchSize: av.optional(av.int32().min(1)).default(1000),
63
+ }, { unknownKeys: "strip" }),
64
+ resourceAttributes: av.optional(av.record(av.string())).default({}),
65
+ }, { unknownKeys: "strip" });
70
66
 
71
67
  export const Config = createConfigSchema(
72
68
  {
package/tsconfig.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2020",
4
- "module": "commonjs",
4
+ "module": "NodeNext",
5
5
  "lib": ["ES2020"],
6
6
  "declaration": true,
7
7
  "outDir": "./lib",
@@ -10,7 +10,7 @@
10
10
  "esModuleInterop": true,
11
11
  "skipLibCheck": true,
12
12
  "forceConsistentCasingInFileNames": true,
13
- "moduleResolution": "node",
13
+ "moduleResolution": "NodeNext",
14
14
  "resolveJsonModule": true,
15
15
  "types": ["node"]
16
16
  },