UNPKG

10.1 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('node-fetch')) :
3 typeof define === 'function' && define.amd ? define(['node-fetch'], factory) :
4 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.stagnant = factory(global.fetch));
5}(this, (function (fetch) { 'use strict';
6
7 function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
9 var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
10
11 function defaultConfig(){
12
13 function onevent(){}
14 function onerror(){}
15 function onsuccess(){}
16 function onflush(){}
17
18 // eslint-disable-next-line no-undef
19
20 const ourConsole = {
21 log(){},
22 error(){},
23 warn(){}
24 };
25
26 function generateId(){
27 return Math.random().toString(15).slice(2,8)
28 }
29
30 return {
31 onevent, onerror, onsuccess, onflush, console: ourConsole, generateId
32 }
33 }
34
35 function Main(config={}){
36
37 config = { ...defaultConfig(), ...config };
38
39 const { generateId } = config;
40
41 async function dispatchEvent(event){
42 await config.onevent(event);
43 if( event.error ) {
44 await config.onerror(event);
45 } else {
46 await config.onsuccess(event);
47 }
48 }
49
50 function Event({
51 parentId, id, startTime, endTime, name, data, error
52 }){
53 return { parentId, id, startTime, endTime, name, data, error }
54 }
55
56 function RootEvent(){
57 const event = Event({
58 parentId: null
59 ,id: generateId()
60 ,startTime: Date.now()
61 ,endTime: Date.now()
62 ,error: null
63 ,data: {}
64 });
65
66 event.flush = async function flush(){
67 delete event.flush;
68 event.endTime = Date.now();
69 await dispatchEvent(event);
70 await config.onflush(event);
71 return event
72 };
73
74 return event
75 }
76
77 function setupEvent({ parentEvent, name, data, sync }){
78 const event = Event({
79 parentId: parentEvent.id
80 ,id: generateId()
81 ,name
82 ,startTime: null
83 ,endTime: null
84 ,data: { ...parentEvent.data || {}, ...data }
85 ,error: null
86 ,sync: parentEvent.sync || sync
87 });
88 const childP = Instance(event);
89 return { childP, event }
90 }
91
92
93 function Instance(parentEvent){
94
95 function handler(...args){
96 const callbackIndex = args.findIndex( x => typeof x == 'function' );
97 let cb = args[callbackIndex];
98 let rest = callbackIndex > 0 ? args.slice(0,callbackIndex) : args;
99
100 let [name, data] = rest;
101
102 if (typeof name != 'string') {
103 data = name;
104 if( cb ) {
105 name = data.name || cb.toString().replace( /\s/g, '' ).replace( /(.)*=>/, '' );
106 }
107 }
108
109 if ( data == null ) data = {};
110
111 return { name, data, callback: cb }
112 }
113
114 function handlerData({ data }){
115 parentEvent.data = { ...parentEvent.data, ...data };
116 return null
117 }
118
119 async function handlerAsync({ event, childP, callback }){
120 if ( parentEvent.sync ) {
121 throw new Error('Cannot use an async trace within a synchronous trace')
122 }
123
124 try {
125 event.startTime = Date.now();
126 const out = await callback(childP);
127 event.endTime = Date.now();
128 return out
129 } catch (e) {
130 event.endTime = Date.now();
131 event.error = e;
132 throw e
133 } finally {
134 dispatchEvent(event)
135 .catch( e => config.console.error('Failed to dispatch event', e));
136 }
137
138 }
139
140 function handlerSync({ callback, name, event, childP }){
141 try {
142 event.startTime = Date.now();
143 const out = callback(childP);
144 event.endTime = Date.now();
145 if( out != null && 'then' in out ) {
146 config.console.warn(name, 'A call to trace.sync was made but the response was async. This is likely a mistake and should be corrected.');
147 }
148 return out
149 } catch (e) {
150 event.endTime = Date.now();
151 event.error = e;
152 throw e
153 } finally {
154 dispatchEvent(event)
155 .catch( e => config.console.error('Failed to dispatch event', e));
156 }
157 }
158
159 function routerOptions({ sync }, ...args){
160 const { data, callback, name } = handler(...args);
161
162 const {event,childP} =
163 callback ? setupEvent({ parentEvent, name, data, sync }) : {};
164
165 if( callback && sync ) {
166 return handlerSync({ data, callback, childP, name, event })
167 } else if ( callback && !sync ) {
168 return handlerAsync({ data, callback, childP, name, event })
169 } else {
170 return handlerData({ data })
171 }
172 }
173
174 async function routerAsync(...args){
175 const out = await routerOptions({ sync: false }, ...args);
176 return out
177 }
178
179 function routerSync(...args){
180 const out = routerOptions({ sync: true }, ...args);
181 return out
182 }
183
184 routerAsync.sync = routerSync;
185 return routerAsync
186 }
187
188 let rootEvent = RootEvent();
189 let handlerInstance = Instance(rootEvent);
190 handlerInstance.flush = rootEvent.flush;
191 handlerInstance.config = config;
192 return handlerInstance
193 }
194
195 /**
196 * Safely invoke a callback even if trace is null.
197 *
198 * useful when there are multiple entry points into a function and some are not
199 * passing in a trace.
200 *
201 * @param {*} trace
202 * @param {...any} args
203 * @returns
204 */
205 function call(trace, ...args){
206 const cb = args.find( x => typeof x == 'function' );
207
208 if ( trace ) {
209 return trace( ...args )
210 } else if ( cb ) {
211 return cb( (...args) => call(null, ...args) )
212 }
213 return null
214 }
215
216 Main.call = call;
217
218 /**
219 * Create a no-op trace if provided trace is null
220 *
221 * Much like stagnant.call, useful when there are multiple entry points into a function and some are not
222 * passing in a trace.
223 *
224 * @param {*} trace
225 * @param {...any} args
226 * @returns
227 */
228 function ensure(trace){
229 if( trace ) {
230 return trace
231 } else {
232 return (...args) => call(null, ...args)
233 }
234 }
235
236 Main.ensure = ensure;
237
238 /* globals process */
239
240 function Honey({
241 name: rootName='root'
242 , dataset='default'
243 , writeKey=process.env.HONEYCOMB_WRITE_KEY
244 // pass in traceId/parentId from an existing trace
245 // e.g. to continue a trace from another server, or
246 // even the browser
247 // otherwise leave blank
248 , traceId= Math.random().toString(15).slice(2,8)
249 , parentId= undefined
250 , data={}
251 , config={}
252 }={}){
253
254 // Traces aren't nested, but a trace can contain nested spans.
255 // We create a root trace for every invocation of Honey
256 // every other event subsequently is a span within that root trace
257 const root = Main({
258 async onevent(event){
259 const name = event.parentId ? event.name : rootName;
260 const body = JSON.stringify({
261 ...event.data
262 , ...data
263 , name
264 , error: event.error ? event.error.message : undefined
265 ,'error.stack': event.error ? event.error.stack : undefined
266 , 'trace.trace_id': 'trace-'+ traceId
267 , 'trace.span_id': 'span-' + event.id
268 , 'trace.parent_id': event.parentId ? 'span-' + event.parentId : parentId
269 , service_name: 'stagnant'
270 , duration_ms: event.endTime - event.startTime
271 });
272
273 try {
274
275 // console.log(name, event.error, event.endTime - event.startTime)
276 const response = await fetch__default['default'](`https://api.honeycomb.io/1/events/${dataset}`, {
277 method: 'post'
278 ,headers: {
279 'X-Honeycomb-Team': writeKey
280 ,'X-Honeycomb-Event-Time': event.startTime
281 ,'Content-Type': 'application/json'
282 }
283 ,body
284 });
285 root.config.console.log(name, response.status, body);
286 if( !event.parentId ) {
287 root.config.console.log('flushed', response.status );
288 }
289 } catch (e) {
290 root.config.console.error(e);
291 }
292
293 }
294 , ...config
295 });
296
297 return root
298 }
299
300 return Honey;
301
302})));
303//# sourceMappingURL=stagnant-honeycomb.browser.js.map