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 = _interopDefaultLegacy(fetch);
|
10 |
|
11 | function defaultConfig(){
|
12 |
|
13 | function onevent(){}
|
14 | function onerror(){}
|
15 | function onsuccess(){}
|
16 | function onflush(){}
|
17 |
|
18 |
|
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 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
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 |
|
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
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 |
|
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 |
|
246 |
|
247 |
|
248 | , traceId= Math.random().toString(15).slice(2,8)
|
249 | , parentId= undefined
|
250 | , data={}
|
251 | , config={}
|
252 | }={}){
|
253 |
|
254 |
|
255 |
|
256 |
|
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 |
|
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 |
|