@statly/observe 0.1.2 → 1.1.0

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/dist/index.d.ts CHANGED
@@ -2,6 +2,55 @@ export { expressErrorHandler, requestHandler } from './integrations/express.js';
2
2
  export { captureNextJsError, withStatly, withStatlyGetServerSideProps, withStatlyGetStaticProps, withStatlyPagesApi, withStatlyServerAction } from './integrations/nextjs.js';
3
3
  export { createRequestCapture, statlyFastifyPlugin, statlyPlugin } from './integrations/fastify.js';
4
4
 
5
+ /**
6
+ * Span module for Statly Observe SDK
7
+ * Handles distributed tracing spans
8
+ */
9
+ declare enum SpanStatus {
10
+ OK = "ok",
11
+ ERROR = "error"
12
+ }
13
+ interface SpanContext {
14
+ traceId: string;
15
+ spanId: string;
16
+ parentId?: string | null;
17
+ }
18
+ interface SpanData {
19
+ name: string;
20
+ traceId: string;
21
+ spanId: string;
22
+ parentId?: string | null;
23
+ startTime: number;
24
+ endTime?: number;
25
+ durationMs?: number;
26
+ status: SpanStatus;
27
+ tags: Record<string, string>;
28
+ metadata: Record<string, unknown>;
29
+ }
30
+ declare class Span {
31
+ readonly name: string;
32
+ readonly context: SpanContext;
33
+ readonly startTime: number;
34
+ private _endTime?;
35
+ private _durationMs?;
36
+ private _status;
37
+ private _tags;
38
+ private _metadata;
39
+ private _finished;
40
+ constructor(name: string, context: SpanContext, tags?: Record<string, string>);
41
+ /**
42
+ * Finish the span and calculate duration
43
+ */
44
+ finish(endTime?: number): void;
45
+ setTag(key: string, value: string): this;
46
+ setMetadata(key: string, value: unknown): this;
47
+ setStatus(status: SpanStatus): this;
48
+ get status(): SpanStatus;
49
+ get tags(): Record<string, string>;
50
+ get durationMs(): number | undefined;
51
+ toDict(): SpanData;
52
+ }
53
+
5
54
  /**
6
55
  * Statly Observe SDK Types
7
56
  */
@@ -79,9 +128,10 @@ interface DeviceInfo {
79
128
  interface StatlyEvent {
80
129
  message: string;
81
130
  timestamp?: number;
82
- level?: 'debug' | 'info' | 'warning' | 'error' | 'fatal';
131
+ level?: 'debug' | 'info' | 'warning' | 'error' | 'fatal' | 'span';
83
132
  stack?: string;
84
133
  exception?: Exception;
134
+ span?: SpanData;
85
135
  environment?: string;
86
136
  release?: string;
87
137
  url?: string;
@@ -130,6 +180,18 @@ declare class StatlyClient {
130
180
  * Capture a message
131
181
  */
132
182
  captureMessage(message: string, level?: EventLevel): string;
183
+ /**
184
+ * Capture a completed span
185
+ */
186
+ captureSpan(span: Span): string;
187
+ /**
188
+ * Start a new tracing span
189
+ */
190
+ startSpan(name: string, tags?: Record<string, string>): Span;
191
+ /**
192
+ * Execute a function within a trace span
193
+ */
194
+ trace<T>(name: string, operation: (span: Span) => Promise<T> | T, tags?: Record<string, string>): Promise<T>;
133
195
  /**
134
196
  * Internal method to capture an error
135
197
  */
@@ -181,47 +243,6 @@ declare class StatlyClient {
181
243
  flush(): Promise<void>;
182
244
  }
183
245
 
184
- /**
185
- * Statly Observe SDK
186
- *
187
- * Error tracking and monitoring for JavaScript applications.
188
- *
189
- * @example
190
- * ```typescript
191
- * import { Statly } from '@statly/observe-sdk';
192
- *
193
- * Statly.init({
194
- * dsn: 'https://sk_live_xxx@statly.live/your-org',
195
- * release: '1.0.0',
196
- * environment: 'production',
197
- * });
198
- *
199
- * // Errors are captured automatically
200
- *
201
- * // Manual capture
202
- * try {
203
- * riskyOperation();
204
- * } catch (error) {
205
- * Statly.captureException(error);
206
- * }
207
- *
208
- * // Capture a message
209
- * Statly.captureMessage('Something happened', 'warning');
210
- *
211
- * // Set user context
212
- * Statly.setUser({
213
- * id: 'user-123',
214
- * email: 'user@example.com',
215
- * });
216
- *
217
- * // Add breadcrumb
218
- * Statly.addBreadcrumb({
219
- * category: 'auth',
220
- * message: 'User logged in',
221
- * });
222
- * ```
223
- */
224
-
225
246
  /**
226
247
  * Initialize the Statly SDK
227
248
  *
@@ -274,6 +295,18 @@ declare function close(): Promise<void>;
274
295
  * Get the current client instance
275
296
  */
276
297
  declare function getClient(): StatlyClient | null;
298
+ /**
299
+ * Execute a function within a trace span
300
+ */
301
+ declare function trace<T>(name: string, operation: (span: Span) => Promise<T> | T, tags?: Record<string, string>): Promise<T>;
302
+ /**
303
+ * Start a new tracing span
304
+ */
305
+ declare function startSpan(name: string, tags?: Record<string, string>): Span | null;
306
+ /**
307
+ * Capture a completed span
308
+ */
309
+ declare function captureSpan(span: Span): string;
277
310
  declare const Statly: {
278
311
  readonly init: typeof init;
279
312
  readonly captureException: typeof captureException;
@@ -285,6 +318,9 @@ declare const Statly: {
285
318
  readonly flush: typeof flush;
286
319
  readonly close: typeof close;
287
320
  readonly getClient: typeof getClient;
321
+ readonly trace: typeof trace;
322
+ readonly startSpan: typeof startSpan;
323
+ readonly captureSpan: typeof captureSpan;
288
324
  };
289
325
 
290
- export { type Breadcrumb, type BrowserInfo, type DeviceInfo, type EventLevel, type Exception, type OSInfo, type StackFrame, Statly, StatlyClient, type StatlyEvent, type StatlyOptions, type User, addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, setTag, setTags, setUser };
326
+ export { type Breadcrumb, type BrowserInfo, type DeviceInfo, type EventLevel, type Exception, type OSInfo, type StackFrame, Statly, StatlyClient, type StatlyEvent, type StatlyOptions, type User, addBreadcrumb, captureException, captureMessage, captureSpan, close, flush, getClient, init, setTag, setTags, setUser, startSpan, trace };
package/dist/index.js CHANGED
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
6
9
  var __export = (target, all) => {
7
10
  for (var name in all)
8
11
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -17,6 +20,155 @@ var __copyProps = (to, from, except, desc) => {
17
20
  };
18
21
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
22
 
23
+ // src/span.ts
24
+ var Span, TraceContext;
25
+ var init_span = __esm({
26
+ "src/span.ts"() {
27
+ "use strict";
28
+ Span = class {
29
+ constructor(name, context, tags) {
30
+ this._status = "ok" /* OK */;
31
+ this._tags = {};
32
+ this._metadata = {};
33
+ this._finished = false;
34
+ this.name = name;
35
+ this.context = context;
36
+ this.startTime = Date.now();
37
+ if (tags) this._tags = { ...tags };
38
+ }
39
+ /**
40
+ * Finish the span and calculate duration
41
+ */
42
+ finish(endTime) {
43
+ if (this._finished) return;
44
+ this._endTime = endTime || Date.now();
45
+ this._durationMs = this._endTime - this.startTime;
46
+ this._finished = true;
47
+ }
48
+ setTag(key, value) {
49
+ this._tags[key] = value;
50
+ return this;
51
+ }
52
+ setMetadata(key, value) {
53
+ this._metadata[key] = value;
54
+ return this;
55
+ }
56
+ setStatus(status) {
57
+ this._status = status;
58
+ return this;
59
+ }
60
+ get status() {
61
+ return this._status;
62
+ }
63
+ get tags() {
64
+ return { ...this._tags };
65
+ }
66
+ get durationMs() {
67
+ return this._durationMs;
68
+ }
69
+ toDict() {
70
+ return {
71
+ name: this.name,
72
+ traceId: this.context.traceId,
73
+ spanId: this.context.spanId,
74
+ parentId: this.context.parentId,
75
+ startTime: this.startTime,
76
+ endTime: this._endTime,
77
+ durationMs: this._durationMs,
78
+ status: this._status,
79
+ tags: this._tags,
80
+ metadata: this._metadata
81
+ };
82
+ }
83
+ };
84
+ TraceContext = class {
85
+ static getActiveSpan() {
86
+ return this.currentSpan;
87
+ }
88
+ static setActiveSpan(span) {
89
+ this.currentSpan = span;
90
+ }
91
+ };
92
+ TraceContext.currentSpan = null;
93
+ }
94
+ });
95
+
96
+ // src/telemetry.ts
97
+ var telemetry_exports = {};
98
+ __export(telemetry_exports, {
99
+ TelemetryProvider: () => TelemetryProvider,
100
+ trace: () => trace
101
+ });
102
+ async function trace(name, operation, tags) {
103
+ const provider = TelemetryProvider.getInstance();
104
+ const span = provider.startSpan(name, tags);
105
+ try {
106
+ const result = await operation(span);
107
+ return result;
108
+ } catch (error) {
109
+ span.setStatus("error" /* ERROR */);
110
+ span.setTag("error", "true");
111
+ if (error instanceof Error) {
112
+ span.setTag("exception.type", error.name);
113
+ span.setTag("exception.message", error.message);
114
+ }
115
+ throw error;
116
+ } finally {
117
+ provider.finishSpan(span);
118
+ }
119
+ }
120
+ var TelemetryProvider;
121
+ var init_telemetry = __esm({
122
+ "src/telemetry.ts"() {
123
+ "use strict";
124
+ init_span();
125
+ TelemetryProvider = class _TelemetryProvider {
126
+ constructor() {
127
+ this.client = null;
128
+ }
129
+ static getInstance() {
130
+ if (!_TelemetryProvider.instance) {
131
+ _TelemetryProvider.instance = new _TelemetryProvider();
132
+ }
133
+ return _TelemetryProvider.instance;
134
+ }
135
+ setClient(client2) {
136
+ this.client = client2;
137
+ }
138
+ /**
139
+ * Start a new span
140
+ */
141
+ startSpan(name, tags) {
142
+ const parent = TraceContext.getActiveSpan();
143
+ const traceId = parent ? parent.context.traceId : this.generateId();
144
+ const parentId = parent ? parent.context.spanId : null;
145
+ const span = new Span(name, {
146
+ traceId,
147
+ spanId: this.generateId(),
148
+ parentId
149
+ }, tags);
150
+ TraceContext.setActiveSpan(span);
151
+ return span;
152
+ }
153
+ /**
154
+ * Finish and report a span
155
+ */
156
+ finishSpan(span) {
157
+ span.finish();
158
+ if (TraceContext.getActiveSpan() === span) {
159
+ TraceContext.setActiveSpan(null);
160
+ }
161
+ if (this.client) {
162
+ this.client.captureSpan(span);
163
+ }
164
+ }
165
+ generateId() {
166
+ return Math.random().toString(16).substring(2, 18);
167
+ }
168
+ };
169
+ }
170
+ });
171
+
20
172
  // src/index.ts
21
173
  var index_exports = {};
22
174
  __export(index_exports, {
@@ -26,6 +178,7 @@ __export(index_exports, {
26
178
  captureException: () => captureException,
27
179
  captureMessage: () => captureMessage,
28
180
  captureNextJsError: () => captureNextJsError,
181
+ captureSpan: () => captureSpan,
29
182
  close: () => close,
30
183
  createRequestCapture: () => createRequestCapture,
31
184
  expressErrorHandler: () => expressErrorHandler,
@@ -36,8 +189,10 @@ __export(index_exports, {
36
189
  setTag: () => setTag,
37
190
  setTags: () => setTags,
38
191
  setUser: () => setUser,
192
+ startSpan: () => startSpan,
39
193
  statlyFastifyPlugin: () => statlyFastifyPlugin,
40
194
  statlyPlugin: () => statlyPlugin,
195
+ trace: () => trace2,
41
196
  withStatly: () => withStatly,
42
197
  withStatlyGetServerSideProps: () => withStatlyGetServerSideProps,
43
198
  withStatlyGetStaticProps: () => withStatlyGetStaticProps,
@@ -390,6 +545,7 @@ var ConsoleIntegration = class {
390
545
  };
391
546
 
392
547
  // src/client.ts
548
+ init_telemetry();
393
549
  var SDK_NAME = "@statly/observe-sdk";
394
550
  var SDK_VERSION = "0.1.0";
395
551
  var StatlyClient = class {
@@ -404,6 +560,7 @@ var StatlyClient = class {
404
560
  this.breadcrumbs = new BreadcrumbManager(this.options.maxBreadcrumbs);
405
561
  this.globalHandlers = new GlobalHandlers();
406
562
  this.consoleIntegration = new ConsoleIntegration();
563
+ TelemetryProvider.getInstance().setClient(this);
407
564
  }
408
565
  mergeOptions(options) {
409
566
  return {
@@ -489,6 +646,30 @@ var StatlyClient = class {
489
646
  });
490
647
  return this.sendEvent(event);
491
648
  }
649
+ /**
650
+ * Capture a completed span
651
+ */
652
+ captureSpan(span) {
653
+ const event = this.buildEvent({
654
+ message: `Span: ${span.name}`,
655
+ level: "span",
656
+ span: span.toDict()
657
+ });
658
+ return this.sendEvent(event);
659
+ }
660
+ /**
661
+ * Start a new tracing span
662
+ */
663
+ startSpan(name, tags) {
664
+ return TelemetryProvider.getInstance().startSpan(name, tags);
665
+ }
666
+ /**
667
+ * Execute a function within a trace span
668
+ */
669
+ async trace(name, operation, tags) {
670
+ const { trace: traceFn } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
671
+ return traceFn(name, operation, tags);
672
+ }
492
673
  /**
493
674
  * Internal method to capture an error
494
675
  */
@@ -798,66 +979,81 @@ function sanitizeBody(body) {
798
979
  // src/integrations/nextjs.ts
799
980
  function withStatlyPagesApi(handler) {
800
981
  return async (req, res) => {
801
- Statly.addBreadcrumb({
802
- category: "http",
803
- message: `${req.method} ${req.url}`,
804
- level: "info",
805
- data: {
806
- method: req.method,
807
- url: req.url
808
- }
809
- });
810
- try {
811
- return await handler(req, res);
812
- } catch (error) {
813
- const context = {
814
- request: {
982
+ return Statly.trace(`${req.method} ${req.url}`, async (span) => {
983
+ span.setTag("component", "nextjs-pages-api");
984
+ span.setTag("http.method", req.method || "GET");
985
+ span.setTag("http.url", req.url || "unknown");
986
+ Statly.addBreadcrumb({
987
+ category: "http",
988
+ message: `${req.method} ${req.url}`,
989
+ level: "info",
990
+ data: {
815
991
  method: req.method,
816
- url: req.url,
817
- headers: sanitizeHeaders2(req.headers),
818
- query: req.query
992
+ url: req.url
819
993
  }
820
- };
821
- Statly.captureException(error, context);
822
- throw error;
823
- }
994
+ });
995
+ try {
996
+ const result = await handler(req, res);
997
+ return result;
998
+ } catch (error) {
999
+ const context = {
1000
+ request: {
1001
+ method: req.method,
1002
+ url: req.url,
1003
+ headers: sanitizeHeaders2(req.headers),
1004
+ query: req.query
1005
+ }
1006
+ };
1007
+ Statly.captureException(error, context);
1008
+ throw error;
1009
+ }
1010
+ });
824
1011
  };
825
1012
  }
826
1013
  function withStatly(handler) {
827
1014
  const wrappedHandler = async (request, context) => {
828
- Statly.addBreadcrumb({
829
- category: "http",
830
- message: `${request.method} ${request.nextUrl?.pathname || request.url}`,
831
- level: "info",
832
- data: {
833
- method: request.method,
834
- url: request.nextUrl?.pathname || request.url
835
- }
836
- });
837
- try {
838
- return await handler(request, context);
839
- } catch (error) {
840
- const headers = {};
841
- request.headers.forEach((value, key) => {
842
- headers[key] = value;
843
- });
844
- const errorContext = {
845
- request: {
1015
+ return Statly.trace(`${request.method} ${request.nextUrl?.pathname || request.url}`, async (span) => {
1016
+ span.setTag("component", "nextjs-app-router");
1017
+ span.setTag("http.method", request.method);
1018
+ span.setTag("http.url", request.nextUrl?.pathname || request.url);
1019
+ Statly.addBreadcrumb({
1020
+ category: "http",
1021
+ message: `${request.method} ${request.nextUrl?.pathname || request.url}`,
1022
+ level: "info",
1023
+ data: {
846
1024
  method: request.method,
847
- url: request.nextUrl?.pathname || request.url,
848
- headers: sanitizeHeaders2(headers),
849
- searchParams: request.nextUrl?.searchParams?.toString()
1025
+ url: request.nextUrl?.pathname || request.url
850
1026
  }
851
- };
852
- if (context?.params) {
853
- try {
854
- errorContext.params = await context.params;
855
- } catch {
1027
+ });
1028
+ try {
1029
+ const result = await handler(request, context);
1030
+ if (result instanceof Response) {
1031
+ span.setTag("http.status_code", result.status.toString());
856
1032
  }
1033
+ return result;
1034
+ } catch (error) {
1035
+ const headers = {};
1036
+ request.headers.forEach((value, key) => {
1037
+ headers[key] = value;
1038
+ });
1039
+ const errorContext = {
1040
+ request: {
1041
+ method: request.method,
1042
+ url: request.nextUrl?.pathname || request.url,
1043
+ headers: sanitizeHeaders2(headers),
1044
+ searchParams: request.nextUrl?.searchParams?.toString()
1045
+ }
1046
+ };
1047
+ if (context?.params) {
1048
+ try {
1049
+ errorContext.params = await context.params;
1050
+ } catch {
1051
+ }
1052
+ }
1053
+ Statly.captureException(error, errorContext);
1054
+ throw error;
857
1055
  }
858
- Statly.captureException(error, errorContext);
859
- throw error;
860
- }
1056
+ });
861
1057
  };
862
1058
  return wrappedHandler;
863
1059
  }
@@ -896,20 +1092,24 @@ function withStatlyGetStaticProps(handler) {
896
1092
  }
897
1093
  function withStatlyServerAction(action, actionName) {
898
1094
  return async (...args) => {
899
- Statly.addBreadcrumb({
900
- category: "action",
901
- message: `Server action: ${actionName || "unknown"}`,
902
- level: "info"
903
- });
904
- try {
905
- return await action(...args);
906
- } catch (error) {
907
- Statly.captureException(error, {
908
- source: "server-action",
909
- actionName
1095
+ return Statly.trace(`Action: ${actionName || "unknown"}`, async (span) => {
1096
+ span.setTag("component", "nextjs-server-action");
1097
+ span.setTag("action.name", actionName || "unknown");
1098
+ Statly.addBreadcrumb({
1099
+ category: "action",
1100
+ message: `Server action: ${actionName || "unknown"}`,
1101
+ level: "info"
910
1102
  });
911
- throw error;
912
- }
1103
+ try {
1104
+ return await action(...args);
1105
+ } catch (error) {
1106
+ Statly.captureException(error, {
1107
+ source: "server-action",
1108
+ actionName
1109
+ });
1110
+ throw error;
1111
+ }
1112
+ });
913
1113
  };
914
1114
  }
915
1115
  function sanitizeHeaders2(headers) {
@@ -1122,6 +1322,20 @@ async function close() {
1122
1322
  function getClient() {
1123
1323
  return client;
1124
1324
  }
1325
+ async function trace2(name, operation, tags) {
1326
+ if (!client) {
1327
+ return operation(null);
1328
+ }
1329
+ return client.trace(name, operation, tags);
1330
+ }
1331
+ function startSpan(name, tags) {
1332
+ if (!client) return null;
1333
+ return client.startSpan(name, tags);
1334
+ }
1335
+ function captureSpan(span) {
1336
+ if (!client) return "";
1337
+ return client.captureSpan(span);
1338
+ }
1125
1339
  var Statly = {
1126
1340
  init,
1127
1341
  captureException,
@@ -1132,7 +1346,10 @@ var Statly = {
1132
1346
  addBreadcrumb,
1133
1347
  flush,
1134
1348
  close,
1135
- getClient
1349
+ getClient,
1350
+ trace: trace2,
1351
+ startSpan,
1352
+ captureSpan
1136
1353
  };
1137
1354
  // Annotate the CommonJS export names for ESM import in node:
1138
1355
  0 && (module.exports = {
@@ -1142,6 +1359,7 @@ var Statly = {
1142
1359
  captureException,
1143
1360
  captureMessage,
1144
1361
  captureNextJsError,
1362
+ captureSpan,
1145
1363
  close,
1146
1364
  createRequestCapture,
1147
1365
  expressErrorHandler,
@@ -1152,8 +1370,10 @@ var Statly = {
1152
1370
  setTag,
1153
1371
  setTags,
1154
1372
  setUser,
1373
+ startSpan,
1155
1374
  statlyFastifyPlugin,
1156
1375
  statlyPlugin,
1376
+ trace,
1157
1377
  withStatly,
1158
1378
  withStatlyGetServerSideProps,
1159
1379
  withStatlyGetStaticProps,
package/dist/index.mjs CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  captureException,
6
6
  captureMessage,
7
7
  captureNextJsError,
8
+ captureSpan,
8
9
  close,
9
10
  createRequestCapture,
10
11
  expressErrorHandler,
@@ -15,14 +16,17 @@ import {
15
16
  setTag,
16
17
  setTags,
17
18
  setUser,
19
+ startSpan,
18
20
  statlyFastifyPlugin,
19
21
  statlyPlugin,
22
+ trace,
20
23
  withStatly,
21
24
  withStatlyGetServerSideProps,
22
25
  withStatlyGetStaticProps,
23
26
  withStatlyPagesApi,
24
27
  withStatlyServerAction
25
- } from "./chunk-UNDSALI5.mjs";
28
+ } from "./chunk-HYFH22G6.mjs";
29
+ import "./chunk-J5AHUFP2.mjs";
26
30
  export {
27
31
  Statly,
28
32
  StatlyClient,
@@ -30,6 +34,7 @@ export {
30
34
  captureException,
31
35
  captureMessage,
32
36
  captureNextJsError,
37
+ captureSpan,
33
38
  close,
34
39
  createRequestCapture,
35
40
  expressErrorHandler,
@@ -40,8 +45,10 @@ export {
40
45
  setTag,
41
46
  setTags,
42
47
  setUser,
48
+ startSpan,
43
49
  statlyFastifyPlugin,
44
50
  statlyPlugin,
51
+ trace,
45
52
  withStatly,
46
53
  withStatlyGetServerSideProps,
47
54
  withStatlyGetStaticProps,